Erlang: демаршаллинг полей данных переменной длины в двоичном потоке - PullRequest
2 голосов
/ 05 февраля 2012

Я создаю приложение Erlang, которое должно анализировать двоичный поток TCP из сторонней программы.Один из типов пакетов, которые я могу получить, имеет данные, отформатированные следующим образом:

N_terms * [Флаги (8 бит), Тип (8 бит), [необязательные данные]].

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

Если бы я написал парсер на императивном языке, я просто прочитал бы в 2 полях и затем получил бы серию if (...) операторы, в которых я буду читать значение и увеличивать свою позицию в потоке.В Erlang мое первоначальное наивное предположение состоит в том, что у меня будет 2 ^ N функциональных предложений, чтобы соответствовать синтаксису байтов в потоке, где N - общее количество флагов + все типы с дополнительными необязательными данными.

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

Должен быть лучший идиоматичный способчтобы сделать это в Erlang - что мне не хватает?

Редактировать: Я должен уточнить, я не знаю количество терминов заранее.

Ответы [ 2 ]

4 голосов
/ 05 февраля 2012

Одно из решений состоит в том, чтобы взять

 <<Flag:8/integer, Type:8/integer, Rest/binary>>

и затем написать функцию decode(Flag, Type), которая возвращает описание того, что будет содержать Rest.Теперь это описание затем может быть передано в декодер для Rest, который затем может использовать описание, данное для его правильного анализа.Простое решение состоит в том, чтобы превратить описание в список, и всякий раз, когда вы декодируете что-то из потока, вы используете этот список описания, чтобы проверить его действительность.То есть описание действует как ваша конструкция if ..

Что касается перемещения указателя, то это легко.Если у вас есть Binary и вы декодируете его

<<Take:N/binary, Next/binary>> = Binary,

Следующим будет перемещенный указатель под капотом, который вы ищете.Таким образом, вы можете просто разбить ваш бинарный файл на такие части, чтобы получить следующую часть для работы.

1 голос
/ 05 февраля 2012

Я бы разобрал это примерно так:

parse_term(Acc,0,<<>>) ->  {ok,Acc};
parse_term(_,0,_)      ->  {error,garbage};
parse_term(Acc,N,<<Flag:8/integer,Type:8/integer,Rest/binary>>) ->
 {Optional,Rest1} = extract_optional(Flag,Type,Rest),   
 parse_term([{Flag,Type,Optional}|Acc],N-1,Rest1>>).

parse_stream(<<NTerms/integer,Rest/binary>>)->
    parse_term([],NTerms,Rest).
...