Спецификация 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
- Контейнер
И константы:
| Хранение | Биты | Hex | Dec |
|---|---|---|---|
| NOBYTES | 000 0 0000 | 0x00 | 0 |
| BYTE | 001 0 0000 | 0x20 | 32 |
| WORD | 010 0 0000 | 0x40 | 64 |
| DWORD | 011 0 0000 | 0x60 | 96 |
| QWORD | 100 0 0000 | 0x80 | 128 |
| STRING | 101 0 0000 | 0xA0 | 160 |
| BLOB | 110 0 0000 | 0xC0 | 192 |
| CONTAINER | 111 0 0000 | 0xE0 | 224 |
Размер подтипа
Следующий бит сообщает, использует ли тип 1 или 2 байта.
Если бит равен 0, тип использует только 1 байт, и подтип имеет 4 бита (от 0 до 15)
+-------- Тип хранения | +----- Размер подтипа | | +-- Подтип 000 0 0000
Когда бит равен 1, для типа используется еще один байт, и подтип имеет 12 бит (до 4096)
+-------- Тип хранения
| +----- Размер подтипа
| |
000 1 0000 0000 0000
| Подтип |
+-------------+
Подтип
Каждое хранилище может иметь до 4096 подтипов. Они содержат информацию о том, какое значение хранится в этом пространстве хранения.
Пример: DWORD может содержать знаковое целое число, беззнаковое целое число, число с плавающей запятой одинарной точности и многое другое... даже определенные пользователем типы
Вот значения для базовых типов данных с выделенным подтипом:
| Тип | Хранение | Биты | Hex | Dec |
|---|---|---|---|---|
| Null | NOBYTES | 0000 0000 | 0x00 | 0 |
| True | NOBYTES | 0000 0001 | 0x01 | 1 |
| False | NOBYTES | 0000 0010 | 0x02 | 2 |
| UInt8 | BYTE | 0010 0000 | 0x20 | 32 |
| Int8 | BYTE | 0010 0001 | 0x21 | 33 |
| UInt16 | WORD | 0100 0000 | 0x40 | 64 |
| Int16 | WORD | 0100 0001 | 0x41 | 65 |
| UInt32 | DWORD | 0110 0000 | 0x60 | 96 |
| Int32 | DWORD | 0110 0001 | 0x61 | 97 |
| Float | DWORD | 0110 0010 | 0x62 | 98 |
| UInt64 | QWORD | 1000 0000 | 0x80 | 128 |
| Int64 | QWORD | 1000 0001 | 0x81 | 129 |
| Double | QWORD | 1000 0010 | 0x82 | 130 |
| Text | STRING | 1010 0000 | 0xA0 | 160 |
| DateTime | STRING | 1010 0001 | 0xA1 | 161 |
| Date | STRING | 1010 0010 | 0xA2 | 162 |
| Time | STRING | 1010 0011 | 0xA3 | 163 |
| DecimalStr | STRING | 1010 0100 | 0xA4 | 164 |
| Blob | BLOB | 1100 0000 | 0xC0 | 192 |
| List | CONTAINER | 1110 0000 | 0xE0 | 224 |
| Map | CONTAINER | 1110 0001 | 0xE1 | 225 |
| Object | CONTAINER | 1110 0010 | 0xE2 | 226 |
Пользовательские типы
Приложение может использовать другой тип 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_MIN | UINT64_MAX |
| Числа с плавающей точкой | IEEE 754 | |
| Строки | 0 | 2 ГБ |
| Блобы | 0 | 2 ГБ |
| Контейнеры | 4 | 2 ГБ |
Ассоциативные массивы
| Тип ключа | Мин | Макс |
|---|---|---|
| Число | INT32_MIN | INT32_MAX |
| Текст | 0 | 255 байт |
Подтипы: до 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 // [данные] (с нулевым символом-терминатором)