Спецификация Binn

June 2, 2023 · View on GitHub

Формат

Каждое значение хранится с 4 возможными параметрами:

[type][size][count][data]

Но большинство из них являются необязательными. Только параметр типа используется во всех из них. Вот список используемых параметров для базовых типов данных:

boolean, null:
[type]

int, float (хранение: byte, word, dword или qword):
[type][data]

string, blob:
[type][size][data]

list, object, map:
[type][size][count][data]

Каждый параметр может храниться с полиморфным размером:

ПараметрРазмер
[type]1 или 2 байта
[size]1 или 4 байта
[count]1 или 4 байта
[data]n байт

[Type]

Каждое значение хранится, начиная с типа данных. Он может использовать 1 или 2 байта. Первый байт делится следующим образом:

 +-------- Тип хранения
 |  +----- Размер подтипа
 |  |  +-- Подтип
000 0 0000

Хранение

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

  • Нет дополнительных байтов
  • 1 байт
  • Word (2 байта, big endian)
  • Dword (4 байта, big endian)
  • Qword (8 байт, big endian)
  • String (UTF-8, с завершающим нулем)
  • Blob
  • Контейнер

И константы:

ХранениеБитыHexDec
NOBYTES000 0 00000x000
BYTE001 0 00000x2032
WORD010 0 00000x4064
DWORD011 0 00000x6096
QWORD100 0 00000x80128
STRING101 0 00000xA0160
BLOB110 0 00000xC0192
CONTAINER111 0 00000xE0224

Размер подтипа

Следующий бит сообщает, использует ли тип 1 или 2 байта.

Если бит равен 0, тип использует только 1 байт, и подтип имеет 4 бита (от 0 до 15)

 +-------- Тип хранения
 |  +----- Размер подтипа
 |  |  +-- Подтип
000 0 0000

Когда бит равен 1, для типа используется еще один байт, и подтип имеет 12 бит (до 4096)

 +-------- Тип хранения
 |  +----- Размер подтипа
 |  |
000 1 0000  0000 0000
      |  Подтип   |
      +-------------+

Подтип

Каждое хранилище может иметь до 4096 подтипов. Они содержат информацию о том, какое значение хранится в этом пространстве хранения.

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

Вот значения для базовых типов данных с выделенным подтипом:

ТипХранениеБитыHexDec
NullNOBYTES0000 00000x000
TrueNOBYTES0000 00010x011
FalseNOBYTES0000 00100x022
UInt8BYTE0010 00000x2032
Int8BYTE0010 00010x2133
UInt16WORD0100 00000x4064
Int16WORD0100 00010x4165
UInt32DWORD0110 00000x6096
Int32DWORD0110 00010x6197
FloatDWORD0110 00100x6298
UInt64QWORD1000 00000x80128
Int64QWORD1000 00010x81129
DoubleQWORD1000 00100x82130
TextSTRING1010 00000xA0160
DateTimeSTRING1010 00010xA1161
DateSTRING1010 00100xA2162
TimeSTRING1010 00110xA3163
DecimalStrSTRING1010 01000xA4164
BlobBLOB1100 00000xC0192
ListCONTAINER1110 00000xE0224
MapCONTAINER1110 00010xE1225
ObjectCONTAINER1110 00100xE2226

Пользовательские типы

Приложение может использовать другой тип DateTime и хранить значение в DWORD или QWORD.

Хранение = QWORD (0x80)
Подтип = 5 (0x05) [выберите любой неиспользуемый]

Тип DateTime = (0x80 | 0x05 => 0x85)

Приложение может отправлять HTML внутри структуры Binn и определить тип, отличный от обычного текста.

Хранение = STRING (0xA0)
Подтип = 9 (0x09) [выберите любой неиспользуемый]

Тип HTML = (0xA0 | 0x09 => 0xA9)

Если подтип больше 15, должен использоваться новый байт, и бит размера подтипа должен быть установлен:

Хранение = STRING (0xA000)
Размер подтипа = (0x0100)
Подтип = 21 (0x0015)

Тип HTML = (0xA000 | 0x1000 | 0x0015 => 0xB015)

Созданный параметр типа должен храниться в формате big-endian.

[Размер]

Этот параметр используется в строках, блобах и контейнерах. Он может иметь 1 или 4 байта.

Если первый бит размера равен 0, используется только 1 байт. Таким образом, когда размер данных составляет до 127 (0x7F) байт, параметр размера будет использовать только 1 байт.

В противном случае используется параметр размера 4 байта с msb 1. Это оставляет нам верхний предел в 2 гигабайта (0x7FFFFFFF).

Размер данныхИспользование параметра размера
<= 127 байт1 байт
> 127 байт4 байта

Нет проблемы, если маленький размер хранится с использованием 4 байт. Читатель должен принимать оба варианта.

Для строк параметр размера не включает нулевой символ-терминатор.

Для контейнеров параметр размера включает параметр типа. Он хранит размер всей структуры.

Примечание: в версиях до 2.0 размеры блобов хранятся только с 4 байтами и без поддержки размеров 1 байт.

[Количество]

Этот параметр используется только в контейнерах для информирования о количестве элементов внутри них. Он может иметь 1 или 4 байта, отформатированных точно так же, как параметр размера.

КоличествоИспользование параметра количества
<= 127 элементов1 байт
> 127 элементов4 байта

Контейнеры

Список

Списки - это контейнеры, которые хранят значения одно за другим.

Параметр количества сообщает о количестве значений внутри контейнера.

[123, "test", 2.5, true]

Карта

Карты - это ассоциативные массивы с использованием целых чисел для ключей.

Ключи хранятся с использованием big-endian DWORD (4 байта), которые считываются как знаковые целые числа.

Таким образом, текущие пределы составляют от INT32_MIN до INT32_MAX. Но есть возможность увеличения, если это необходимо.

Параметр количества сообщает о количестве пар ключ/значение внутри контейнера.

{1: 10, 5: "the value", 7: true}

Объект

Объекты - это ассоциативные массивы с использованием текста для ключей.

Ключи не оканчиваются нулевым символом, и предел составляет 255 байт.

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

Параметр количества сообщает о количестве пар ключ/значение внутри контейнера.

{"id": 1, "name": "John", "points": 30.5, "active": true}

Ограничения

ТипМинМакс
Целые числаINT64_MINUINT64_MAX
Числа с плавающей точкойIEEE 754
Строки02 ГБ
Блобы02 ГБ
Контейнеры42 ГБ

Ассоциативные массивы

Тип ключаМинМакс
ЧислоINT32_MININT32_MAX
Текст0255 байт

Подтипы: до 4096 для каждого типа хранения

Примеры структур

Json-данные, такие как {"hello":"world"}, сериализуются как:

Binn: (17 байт)

  \xE2           // [тип] объект (контейнер)
  \x11           // [размер] общий размер контейнера
  \x01           // [количество] пар ключ/значение
  \x05hello      // ключ
  \xA0           // [тип] = строка
  \x05           // [размер]
  world\x00      // [данные] (с нулевым символом-терминатором)

Список из 3 целых чисел:

Json: (14 байт)

[123, -456, 789]

Binn: (11 байт)

  \xE0           // [тип] список (контейнер)
  \x0B           // [размер] общий размер контейнера
  \x03           // [количество] элементов
  \x20           // [тип] = uint8
  \x7B           // [данные] (123)
  \x41           // [тип] = int16
  \xFE\x38       // [данные] (-456)
  \x40           // [тип] = uint16
  \x03\x15       // [данные] (789)

Список внутри карты:

Json: (25 байт)

{1: "add", 2: [-12345, 6789]}

Binn: (26 байт)

 \xE1             // [тип] карта (контейнер)
 \x1A             // [размер] общий размер контейнера
 \x02             // [количество] пар ключ/значение
 \x00\x00\x00\x01 // ключ
 \xA0             // [тип] = строка
 \x03             // [размер]
 add\x00          // [данные] (с нулевым символом-терминатором)
 \x00\x00\x00\x02 // ключ
 \xE0             // [тип] список (контейнер)
 \x09             // [размер] общий размер контейнера
 \x02             // [количество] элементов
 \x41             // [тип] = int16
 \xCF\xC7         // [данные] (-12345)
 \x40             // [тип] = uint16
 \x1A\x85         // [данные] (6789)

Список объектов:

Json: (47 байт)

[ {"id": 1, "name": "John"}, {"id": 2, "name": "Eric"} ]

Binn: (43 байт)

 \xE0           // [тип] список (контейнер)
 \x2B           // [размер] общий размер контейнера
 \x02           // [количество] элементов

 \xE2           // [тип] объект (контейнер)
 \x14           // [размер] общий размер контейнера
 \x02           // [количество] пар ключ/значение

 \x02id         // ключ
 \x20           // [тип] = uint8
 \x01           // [данные] (1)

 \x04name       // ключ
 \xA0           // [тип] = строка
 \x04           // [размер]
 John\x00       // [данные] (с нулевым символом-терминатором)

 \xE2           // [тип] объект (контейнер)
 \x14           // [размер] общий размер контейнера
 \x02           // [количество] пар ключ/значение

 \x02id         // ключ
 \x20           // [тип] = uint8
 \x02           // [данные] (2)

 \x04name       // ключ
 \xA0           // [тип] = строка
 \x04           // [размер]
 Eric\x00       // [данные] (с нулевым символом-терминатором)