Получение на C двоичных данных от OCaml - PullRequest
5 голосов
/ 17 мая 2011

(Игнорирование порядка байтов для аргументации - это всего лишь тестовый пример / подтверждение концепции - и я бы никогда не использовал strcpy в реальном коде!)

Рассмотрим следующий тривиальный код C:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* variables of type message_t will be stored contiguously in memory */
typedef struct {
  int message_id;
  char message_text[80];
} message_t;

int main(int argc, char**argv) {
  message_t* m = (message_t*)malloc(sizeof(message_t));
  m->message_id = 1;
  strcpy(m->message_text,"the rain in spain falls mainly on the plain");

  /* write the memory to disk */
  FILE* fp = fopen("data.dat", "wb");
  fwrite((void*)m, sizeof(int) + strlen(m->message_text) + 1, 1, fp);
  fclose(fp);

  exit(EXIT_SUCCESS);
}

Файл, который он пишет, может быть легко прочитан обратно с диска:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
  int message_id;
  char message_text[80];
} message_t;

int main(int argc, char**argv) {
  message_t* m = (message_t*)malloc(sizeof(message_t));

  FILE* fp = fopen("data.dat", "rb");
  fread((void*)m, sizeof(message_t), 1, fp);
  fclose(fp);

  /* block of memory has structure "overlaid" onto it */
  printf("message_id=%d, message_text='%s'\n", m->message_id, m->message_text);

  exit(EXIT_SUCCESS);
}

Например,

$ ./write 
$ ./read 
message_id=1, message_text='the rain in spain falls mainly on the plain'

Мой вопрос в OCaml, если всеУ меня есть:

type message_t = {message_id:int; message_text:string}

Как я могу получить эти данные?Marshal не может и не может input_binary_int.Я могу вызвать вспомогательные функции в C, такие как «что такое sizeof(int)», затем получить n байтов и вызвать функцию C, например, для «преобразования этих байтов в int», но в этом случае я не могу добавить какой-либо новый код C«Распаковка» должна выполняться в OCaml, исходя из того, что, как я знаю, «должно» быть.Это просто вопрос перебора строки в блоках sizeof s или поиска '\ 0', или есть хитрый способ?Спасибо!

Ответы [ 4 ]

6 голосов
/ 17 мая 2011

Для такого рода низкоуровневой обработки структуры я считаю OCaml Bitstring очень удобным.Эквивалентный читатель для вашего message_t был бы таким, если вы записали все 80 символов на диск:

bitmatch (Bitstring.bitstring_from_file "data.dat") with
  | { message_id : 32;
      message_text : 8 * 80 : string;
    } -> 
      Printf.printf "message_id=%ld, message_text='%s'\n" 
                    message_id message_text
  | { _ } -> failwith "Not a valid message_t"

Как есть, вам придется обрезать message_text, но, возможно, вам понадобится цепочка битовЗадача в целом.

4 голосов
/ 17 мая 2011

Прежде чем вы сможете понять, как кодировать это в Ocaml, вам необходимо выяснить, каково ваше представление данных.Ваш код на C не согласован между читателем и писателем: писатель записывает только strlen(m->message_text)+1 байт для строки, тогда как читатель ожидает, что максимальные полные 80 байт.

Мой совет: все, что вам нужно сделать -на том же языке, C или Ocaml.Я рекомендую библиотеку маршаллинга Ocaml, которая уже работает, кроссплатформенна и проста в использовании.

Если вам нужна совместимость между кодом C и Ocaml, то вам нужно сесть в формат маршаллинга и внедрить тот же самыйспецификация на обоих языках.Прежде чем сделать это, подумайте, можете ли вы использовать текстовое представление, которое будет менее подвержено ошибкам и его будет легче проверять и манипулировать сторонними инструментами, но оно будет объемнее. JSON - это упрощенный формат представления данных, или вы можете обратиться к расширенному XML .Если все ваши данные действительно так же просты, как целое число и строка, и строки не содержат символов новой строки, вы можете записать целое число в десятичном виде, за которым следует пробел (или : или ,), за которым следуетстрока, за которой следует символ новой строки.

Если формат сортировки C предопределен и вы не можете его изменить, обратите внимание, что он зависит от платформы (зависит от архитектуры и компилятора C), а Ocaml не даетВы получаете доступ к таким деталям платформы.Поэтому лучше всего связать вашу программу Ocaml с помощником C, убедившись, что ваш помощник использует то же представление типа C (sizeof(int), порядковый номер, заполнение структуры), что и исходное приложение.

2 голосов
/ 17 мая 2011

Вы полагаетесь на использование одного и того же компилятора C на той же платформе, чтобы избежать необходимости думать о том, каков формат записанных и считанных данных.К сожалению, у вас нет такой роскоши, если вы пытаетесь взаимодействовать между C и OCaml.У вас есть , чтобы подсчитать байты в структуре, выяснить, является ли целое число младшим или старшим порядковым числом, и соответствующим образом кодировать на стороне OCaml.

Вам придется вручную отменять вызовкаждый тип отдельно, по сути, анализирует двоичный файл.Например, чтобы прочитать 32-разрядное целое число с прямым порядком байтов, вы должны будете использовать:

let input_le_int32 inch =
  let res = ref 0l in
  for i = 0 to 3 do
    let byte = input_byte inch in
    res := Int32.logor !res (Int32.shift_left (Int32.of_int byte) (8*i))
  done;
  !res

и прочитать строку, заканчивающуюся NUL:

let input_c_string inch =
  let res = Buffer.create 256 in
  try while true do
    let byte = input_byte inch in
    if byte = 0 then raise Exit else
    Buffer.add_char res (char_of_int byte)
  done; assert false with Exit ->
  Buffer.contents res

Если всеверно, вы можете читать свою структуру с помощью:

let input_message inch =
  let message_id   = input_le_int32 inch in
  let message_text = input_c_string inch in
  { message_id; message_text; }

Примечание: обязательно (!) упорядочивать операции чтения, чтобы избежать неправильного чтения полей. не используйте параллельные let назначения.

1 голос
/ 18 мая 2011

Спасибо за совет всем; Я написал подход, который я решил использовать в своем блоге.

...