LINUX.ORG.RU

Kaitai Struct 0.5

 , , ,


8

5

После трёх месяцев разработки состоялся релиз Kaitai Struct 0.5 — языка описания форматов структур данных. Идея проекта состоит в том, что описав структуру формата файла или сетевого протокола единожды на формальном языке .ksy, можно скомплировать такое описание в исходный код парсера на любом поддерживаемом языке программирования.

Список нововведений внушительный, самые заметные из них:

  • полная поддержка C++/STL;
  • поддержка новых целевых языков PHP7 (благодаря ProtoH) и Perl;
  • генерация GraphViz-диаграмм для форматов (ранние примеры демонстрировались в галерее);
  • новые возможности языка: switch-подобная конструкция для типов (чтобы не писать много условий), атрибут doc (для генерации документации в комментариях на целевом языке), цикл repeat-until, поддержка булевых типов, поддержка операций с объектом потока из языка выражений (_io.eof, _io.size, _io.pos);
  • существенное улучшение строгости парсинга .ksy компилятором, понятные сообщения об ошибках;
  • работа консольного визуализатора на Windows.

Семейство инструментов, поддерживающих Kaitai Struct, пополнилось:

Как всегда, доступна онлайн-версия на JavaScript, в которой можно поэкспериментировать с компилятором без установки.

>>> Подробности

Ответ на: комментарий от coyotl

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

GreyCat ★★ ()

Это вроде Protobuf, Cap'n'Proto, Thrift, Avro или что-то другое?

vertexua ★★★☆☆ ()

В этом проекте есть генератор самих данных, для которых описали структуру ? Это было бы крайне удобно генерироват ьиз проекта и парсер и сами данные, которые прожевывает этот парсер.

nullb0t ()

Генерация структуры это даже не пол дела. А вот например доступ по a.b.c.d когда b undefined.... 90% работы со структурами это доступ на чтение или запись...

dmxrand ()

можно скомплировать такое описание в исходный код парсера на любом поддерживаемом языке программирования.

Можно, но зачем

loz ★★★★★ ()

Вопрос к знатокам: можно ли на нём описать например sbe?

pon4ik ★★★★★ ()

Вещь годная, но когда пользовался, не понял что делать, если например есть поле даты в виде «просто побайтово datetime записали». Как это вписать, если язык парсера несколько иначе в памяти дату раскладывает.

Svoloch ★★★ ()

поздравляю с переизобретением asn1

anonymous ()

описав структуру формата файла или сетевого протокола единожды на формальном языке .ksy, можно скомплировать

Не взирая на Лисп, на котором можно описать эти самые «форматы файлов или сетевого протокола единожды» был придуман некий ksy :-) Лол :-)

anonymous ()
Ответ на: комментарий от vertexua

Это вроде Protobuf, Cap'n'Proto, Thrift, Avro или что-то другое?

Нет, это вроде 010 Editor, Hexinator, Preon, DFDL и других куда менее известных штук.

GreyCat ★★ ()
Ответ на: комментарий от nullb0t

В этом проекте есть генератор самих данных, для которых описали структуру ?

Пока нет, хотя разговоры идут. К сожалению, это относительно нетривиально: у нас все-таки речь идет не о простеньких C-подобных struct'ах, а об их хитром сплетении. Можно, например, одной командой парсить какую-нибудь файловую систему и начать из нее вытаскивать файлы. В общем случае записывать файлы в файловую систему - не самая тривиальная задача, в том числе потому, что ее можно делать десятком разных способов.

GreyCat ★★ ()
Ответ на: комментарий от kirk_johnson

Эээ... растующие массивы это reallocarray и структура.

Растущие массивы - это всякие GArray в glib, gsl_vector в gsl или там kvec в klib. Чтобы был четкий и понятный способ передать «единым комплектом» сразу некую структуру, из которой бы можно было понять, сколько элементов в массиве, пройтись по нему вперед-назад, чтобы можно было адресоваться по номеру и т.д. И чтобы всем нравилось. А с этим тяжко :(

GreyCat ★★ ()
Ответ на: комментарий от pon4ik

Насколько я понял, sbe описать можно, но это будет несколько мимо кассы. Судя по вот этому описанию, фиксированная часть sbe - это 4 поля - по которым будет, собственно, понятно, как парсить дальнейшее тело сообщения (какой шаблон прикладывать). Шаблоны же есть где-то в программе, которая будет этими сообщениями пользоваться, и дальше все упирается в трансляцию этих самых шаблонов sbe в аналогичный синтаксис в .ksy.

Т.е. в отличие от Protobuf, ASN.1 BER/DER, BSON и т.п. schemaless форматов, тут для парсинга сообщения явно нужна внешняя схема.

GreyCat ★★ ()
Ответ на: комментарий от GreyCat

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

Т.е. например sbe позволяет упаковать число 3 в 3 бита, а число 8 в пять бит, тогда как в схеме они оба integer.

pon4ik ★★★★★ ()
Ответ на: комментарий от GreyCat

Т.е. по сути, вопрос как раз про то - возможно ли будет сконвертировать схему sbe и подобных протоколов в схему ksy.

pon4ik ★★★★★ ()
Ответ на: комментарий от pon4ik

Если нужны unaligned bit integers - то пока нельзя, задача все еще висит, мы медленно к ней подбираемся. С aligned integers (флагами там и т.п.) вопрос в целом решается через instances, т.е. как-то типа того:

seq:
  - id: my_byte
    type: u1
instances:
  first_3_bits:
    value: (my_byte & 0b11100000) >> 5
  second_5_bits:
    value: (my_byte & 0b00011111)
GreyCat ★★ ()

Я не понял - это такой ASN.1 для бедных?

duger ()
Ответ на: комментарий от GreyCat

Если соображу как, я получше сформулирую вопрос у вас на багтрекере, может протокол чуть попроще возьму. Ибо мне сложно пока понять что записанно в спеке в примере :)

pon4ik ★★★★★ ()
Ответ на: комментарий от loz

Можно, но зачем

Затем, чтобы когда нужно будет поддерживать импорт очередного проприетарного формата картинок-документов-чего-нибудь-подобного, не нужно было заново переписывать парсер на нужном языке. Чтобы отлаживать можно было все на удобных скриптовых языках типа Python/Ruby, а затем использовать наработки на какой-то высокоскоростной реализации на C++/Java. Чтобы иметь наглядную человекочитаемую схему формата.

GreyCat ★★ ()
Ответ на: комментарий от duger

Ткните меня, пожалуйста, лицом в парсер, например, JPEG-файла или IP-стека с помощью ASN.1. Заранее спасибо.

GreyCat ★★ ()
Ответ на: комментарий от Svoloch

не понял что делать, если например есть поле даты в виде «просто побайтово datetime записали».

Можно конкретнее пример, что прямо подряд в байтах идет, и что хочется из этого получить?

GreyCat ★★ ()
Ответ на: комментарий от GreyCat

Ну у меня пример простой. Кто-то взял и в сделал что-то типа:

write(f,&timestamp,sizeof(timestamp));

Понятно что в зависимости от abi реальный layout этого timestamp может быть разным. Хотелось бы какой-нибудь метод, чтобы я зная layout в данном случае, мог увидеть результат как дату.

Svoloch ★★★ ()

c нет(привет привязкам для других языков), других языков тоже мало...

anonymous ()

Как оно соотносится с BinPAC ?

Manhunt ★★★★★ ()
Ответ на: комментарий от GreyCat

Растущие массивы - это всякие GArray в glib, gsl_vector в gsl или там kvec в klib. Чтобы был четкий и понятный способ передать «единым комплектом» сразу некую структуру, из которой бы можно было понять, сколько элементов в массиве, пройтись по нему вперед-назад, чтобы можно было адресоваться по номеру и т.д. И чтобы всем нравилось. А с этим тяжко :(

struct array {
    int    *elms;
    size_t  elms_cnt;
};

int array_add_elm(size_t pos, int value);
int array_get_elm(size_t pos, int *result);
int array_del_elm(size_t pos);

Функции сужения / расширения массива напишешь сам. Придется сделать немного макросной магии, чтобы генерировать структуру с даными нужного типа, но с этим, надеюсь, ты справишься.

kirk_johnson ★☆ ()
Ответ на: комментарий от kirk_johnson

сделать макросной магии

Чего только люди ни делают, лишь бы STL не пользоваться.

Manhunt ★★★★★ ()
Ответ на: комментарий от Manhunt

Чего только люди ни делают, лишь бы STL не пользоваться.

В C появился STL? Давно?

kirk_johnson ★☆ ()
Ответ на: комментарий от Manhunt

Как оно соотносится с BinPAC ?

Во, очень хороший вопрос! Весьма близко на самом деле соотносится, идеи все в итоге получились очень похожими. Только BinPAC - C++ only, а Kaitai Struct - уже 8 целевых языков (9, если считать GraphViz за язык) поддерживает.

Ну и BinPAC, кроме всего прочего, как бы мертв. Сейчас следующее поколение - это HILTI и Spicy, но там разработка как-то тоже застопорилась (да и в сторону поддержки чего-либо, кроме C++, там опять же не смотрят).

GreyCat ★★ ()
Ответ на: комментарий от Svoloch

Хотелось бы какой-нибудь метод, чтобы я зная layout в данном случае, мог увидеть результат как дату.

«Как дату» - в общем случае не получится, хотя бы потому, что далеко не во всех языках есть аналог типа Date / Time. Но если там внутри есть какая-то структура (например, как в ISO9660), то ее вполне можно разобрать по частям:

  datetime:
    seq:
      - id: year
        type: u1
      - id: month
        type: u1
      - id: day
        type: u1
      - id: hour
        type: u1
      - id: minute
        type: u1
      - id: sec
        type: u1
      - id: timezone
        type: u1

и затем обращаться к этому элементу как myElement->year(), myElement->month() и т.д., ну или как в целевом языке принято это делать.

GreyCat ★★ ()

Народ, упоминающий asn.1, что ж вы про protobuf забыли?

Deleted ()
Ответ на: комментарий от kirk_johnson

Окей, а теперь попробуй притащить такое в какой-нибудь C-проект, где уже используется glib или есть подобный нескучный велосипед, и посмотрим, что там на это скажут.

GreyCat ★★ ()
Ответ на: комментарий от Deleted

Ничего, я терпеливый ;) И про ASN.1, и про Protobuf, и про BSON, и Thrift, и про Avro, и про Cap'n Proto, и про любую другую безсхемную сериализацию могу по n-ному разу ;)

GreyCat ★★ ()
Ответ на: комментарий от GreyCat

Вооот. Я именно об этом. Хотелось бы, чтобы бы какую-нибудь интеграцию с таргет языком.

Svoloch ★★★ ()
Ответ на: комментарий от GreyCat

Окей, а теперь попробуй притащить такое в какой-нибудь C-проект, где уже используется glib или есть подобный нескучный велосипед, и посмотрим, что там на это скажут.

Ну сделай возможность переопределять методы и сам тип.

kirk_johnson ★☆ ()
Ответ на: комментарий от Svoloch

Так кто ж ее мешает сделать-то? И при чем тут универсальный язык описания бинарного формата? Сделал описание => получил исходные числа => оборачивай в какую нравится helper-структуру в своем языке.

GreyCat ★★ ()
Ответ на: комментарий от GreyCat

Ну и BinPAC, кроме всего прочего, как бы мертв.

Не только мертв, но ещё и исправно работает. А производительность сравнивали?

Manhunt ★★★★★ ()

Зачем это если есть grpc и protobuff?

anonymous ()
Ответ на: комментарий от kirk_johnson

Ну сделай возможность переопределять методы и сам тип.

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

struct GArray {
  gchar* data;
  guint len;
};

// новый чистый массив
GArray* g_array_new(gboolean zero_terminated, gboolean clear_, guint element_size);
// новый массив явно указанной длины
GArray* g_array_sized_new(gboolean zero_terminated, gboolean clear_, guint element_size, guint reserved_size);
// добавить в конец
#define g_array_append_val(a,v)
// получить указатель по индексу элемента
#define g_array_index(a,t,i)
// освободить массив
gchar* g_array_free(GArray* array, gboolean free_segment);

А вот, для сравнения, в klib:

// единого типа нет, структура определяется каждый раз явно макросом с параметром type
struct {
  size_t n; // сколько элементов allocated
  size_t m; // сколько элементов реально занято
  type *a;
}

// новый чистый массив
#define kv_init(v)
// новый массив явно указанной длины делается через чистый + resize
// сам resize
#define kv_resize(type, v, s)
// добавить в конец
#define kv_push(type, v, x)
// получить элемент по индексу (не указатель!)
#define kv_A(v, i)
// освободить массив
#define kv_destroy(v)

Общего мало. Формат структуры разный, методика инициализации разная, хранится разное, где-то что-то сделано макросами, где-то - функциями. Параметры разные и т.д.

Но в целом, наверное, да, в этом направлении надо двигаться, только тяжко.

GreyCat ★★ ()
Ответ на: комментарий от anonymous

CSN.1 я видел - это что-то весьма мертворожденное, к сожалению (а может и к счастью). Его обычно не компилируют, а приводят во всякой документации, как использовали бы что-то типа EBNF. И, соответственно, шаг влево - шаг вправо - сразу же снабжают комментариями на естественном языке или какой-то очередной нескучной только что изобретенной нотацией.

А вот за TSN.1, кстати, да - спасибо. Он, конечно, весьма примитивный, но в сравнения я его добавлю.

GreyCat ★★ ()

Попробовал скомпилировать на http://kaitai.io/repl/ пример GIF Image в graphviz, а он говорит scala.MatchError: io.kaitai.struct.GraphvizClassCompiler$@6 (of class io.kaitai.struct.GraphvizClassCompiler$)

anonymous ()

Посмотрел по диагонали на цпп-шную реализацию, оно аллоцирует память в рантайме (как минимум, внутри многочисленных std::string) и копирует все данные (как минимум, один раз). API, может быть, и удобный, но чтобы меряться пиписьками, хорошо бы обходиться без аллокаций на каждый чих и быть zero-copy. Кроме того, не ясно, как эти ваши kstream::seek() должны работать в связке с каким-нибудь boost::asio или libevent.

Manhunt ★★★★★ ()
Ответ на: комментарий от GreyCat

для поддержки таких штук, как банальных штук

Судя по всему вы сейчас плотно работаете над хаскелем. Я прав?

phoen ()
Ответ на: комментарий от Manhunt

хорошо бы обходиться без аллокаций на каждый чих и быть zero-copy

Я честно признаюсь, что C++ знаю сравнительно плохо, но из того, что я знаю - для того, чтобы быть более-менее zero-copy по хорошему надо привлекать std::unique_ptr / std::shared_ptr, а это C++11. По идее - назревает реализация нескольких отдельных вариантов - оставить «старый и работающий везде» (не требующий даже C++03), который есть сейчас, и добавить что-нибудь в более-менее новых реалиях, где пытаться делать максимум на стеке / в объекте, обойтись по возможности без деструкторов, new и явных *, заменив это все на & и std::move.

Если знаете / подскажете, как сделать лучше - добро пожаловать ;) Если захотите поспособствовать реализации на современном C++ - тем более :)

Кроме того, не ясно, как эти ваши kstream::seek() должны работать в связке с каким-нибудь boost::asio или libevent.

Никак. Нужный для разбора фрагмент загружается в память и оттуда разбирается. То, что потоки должны быть seekable - это вполне осознанное требование, исходящее из условий задачи.

GreyCat ★★ ()

Очередные клоуны с 14+1 стандартом. Нафига???

matumba ★★★★★ ()
Ответ на: комментарий от matumba

Назовете хотя бы парочку предыдущих стандартов, желательно живых, на которых какие-то проекты делаются, а не чисто теоретические рассуждения в вакууме?

GreyCat ★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.