Как я могу комбинировать парсеры nom, чтобы получить более бит-ориентированный интерфейс для данных?

Я работаю над декодированием сообщений AIS в Rust, используя nom.

Сообщения AIS состоят из битового вектора; различные поля в каждом сообщении имеют произвольное количество битов и не всегда выравниваются по границам байтов.

Затем этот битовый вектор кодируется в кодировке ASCII и вставляется в предложение NMEA.

Из http://catb.org/gpsd/AIVDM.html:

Полезные данные - это битовый вектор в кодировке ASCII. Каждый символ представляет шесть бит данных. Чтобы восстановить шесть битов, вычтите 48 из значения символа ASCII; если результат больше 40, вычтите 8. Согласно [IEC-PAS] допустимые символы ASCII для этой кодировки начинаются с «0» (64) и заканчиваются «w» (87); однако промежуточный диапазон от «X» (88) до «_» (95) не используется.

Пример

  • !AIVDM,1,1,,A,D03Ovk1T1N>5N8ffqMhNfp0,0*68 - это предложение NMEA
  • D03Ovk1T1N>5N8ffqMhNfp0 - это закодированные данные AIS
  • 010100000000000011011111111110110011000001100100000001011110001110000101011110001000101110101110111001011101110000011110101110111000000000 - это декодированные данные AIS в виде битового вектора

Проблемы

Я перечисляю их вместе, потому что думаю, что они могут быть связаны ...

1. Декодирование ASCII в битовый вектор

Я могу сделать это вручную, перебирая символы, вычитая соответствующие значения и создавая байтовый массив, выполняя много операций по сдвигу битов и так далее. Это нормально, но похоже, что я смогу сделать это внутри nom и связать это с фактическим битовым парсером AIS, исключив промежуточный массив байтов.

2. Чтение произвольного количества бит

Можно прочитать, скажем, 3 бита из массива байтов в ном. Но каждый вызов bits!, кажется, потребляет сразу полный байт (при чтении в u8).

Например:

named!(take_3_bits<u8>, bits!(take_bits!(u8, 3)));

прочитает 3 бита в u8. Но если я запущу take_3_bits дважды, я израсходую 16 бит моего потока.

Могу совместить чтения:

named!(get_field_1_and_2<(u8, u8)>, bits!(pair!(take_bits!(u8, 2), take_bits!(u8, 3))));

Вызов get_field_1_and_2 даст мне (u8, u8) кортеж, где первый элемент содержит первые 2 бита, а второй элемент содержит следующие 3 бита, но после этого чтения nom все равно продвинется на полный байт.

Я могу использовать peek, чтобы предотвратить продвижение указателя чтения nom, а затем управлять им вручную, но опять же, это кажется ненужной дополнительной работой.


person squidpickles    schedule 06.01.2018    source источник
comment
Что касается части 2, кажется, проблема не такая уж и большая; Мне просто нужно написать все мои парсеры, не осознавая bits!, а затем поместить bits! в парсер самого верхнего уровня, который я вызываю. Я напишу больше, если смогу разобраться в части 1. (Прямо сейчас я вручную создаю этот битовый вектор.)   -  person squidpickles    schedule 15.01.2018
comment
Да, я сделал нечто подобное, и ответ на 2 состоит в том, что вам нужно разделить побайтно-ориентированный и побитовый синтаксический анализ и вызвать bits! один раз, когда вы меняете режим.   -  person Dan Hulme    schedule 24.01.2018
comment
@DanHulme, спасибо за это. Я начал прорабатывать эту часть. Я надеялся на глубокое понимание части 1, но похоже, что это будет всего лишь двухэтапный процесс. Я отправлю здесь ответ с кодом в следующий раз, когда получу.   -  person squidpickles    schedule 27.01.2018