Формат данных#
Стандартные типы данных представлены в виде комбинации кода типа и значения.
Поле |
Размер в байтах |
Описание |
|---|---|---|
|
1 |
Код в виде знакового однобайтового целого числа, который показывает тип значения |
|
Переменная |
Само значение. Его формат и размер зависят от |
Описание поддерживаемых типов данных и их форматы указаны ниже.
Примитивы#
Примитивы — базовые типы данных, например числа.
Byte#
Поле |
Размер в байтах |
Описание |
|---|---|---|
|
1 |
1 |
|
1 |
Однобайтовое значение |
Short#
Код типа: 2.
Знаковое двухбайтовое целое число в формате little-endian.
Структура:
Поле |
Размер в байтах |
Описание |
|---|---|---|
|
2 |
Значение |
Int#
Код типа: 3.
Знаковое четырехбайтовое целое число в формате little-endian.
Структура:
Поле |
Размер в байтах |
Описание |
|---|---|---|
|
4 |
Значение |
Long#
Код типа: 4.
Знаковое восьмибайтное целое число в формате little-endian.
Структура:
Поле |
Размер в байтах |
Описание |
|---|---|---|
|
8 |
Значение |
Float#
Код типа: 5.
Четырехбайтовое число с плавающей запятой по стандарту IEEE 754 в формате little-endian.
Структура:
Поле |
Размер в байтах |
Описание |
|---|---|---|
|
4 |
Значение |
Double#
Код типа: 6.
Восьмибайтное число с плавающей запятой по стандарту IEEE 754 в формате little-endian.
Структура:
Поле |
Размер в байтах |
Описание |
|---|---|---|
|
8 |
Значение |
Char#
Код типа: 7.
Единичный код в системе кодировки UTF-16 и формате little-endian.
Структура:
Поле |
Размер в байтах |
Описание |
|---|---|---|
|
2 |
Один символ в кодировке UTF-16 в формате little-endian |
Bool#
Код типа: 8.
Булевое значение. Ноль – false, не ноль — true.
Структура:
Поле |
Размер в байтах |
Описание |
|---|---|---|
|
1 |
Значение. Ноль – |
Null#
Код типа: 101.
Null — не совсем тип данных: это значение null, которое может быть присвоено объекту любого типа. Не имеет полезной нагрузки, является только кодом типа.
Стандартные объекты#
String#
Код типа: 9.
Строка в кодировке UTF-8. Всегда должна быть в формате UTF-8.
Структура:
Поле |
Размер в байтах |
Описание |
|---|---|---|
|
4 |
Знаковое целое число в формате little-endian. Длина строки в единицах кодировки UTF-8, то есть в байтах |
|
|
Строка данных в кодировке UTF-8 без BOM |
UUID (GUID)#
Код типа: 10.
Универсальный уникальный идентификатор (UUID) — 128-битное число, которое используется для идентификации информации в компьютерных системах.
Структура:
Поле |
Размер в байтах |
Описание |
|---|---|---|
|
8 |
64-битное число в формате little-endian, которое представляет 64 наиболее значимых бита |
|
8 |
64-битное число в формате little-endian, которое представляет 64 наименее значимых бита |
Timestamp#
Код типа: 33.
Более точный тип данных, чем Date. Кроме миллисекунд со времен эпохи Unix, содержит наносекундную долю последней миллисекунды, которая может варьироваться от 0 до 999999. Полную временную метку можно получить с помощью выражения msecs_since_epoch * 1000000 + msec_fraction_in_nsecs.
Внимание
Это выражение представлено исключительно в пояснительных целях. Не нужно использовать его в разработке, так как в некоторых языках программирования это может привести к переполнению целых чисел.
Структура:
Поле |
Размер в байтах |
Описание |
|---|---|---|
|
8 |
Знаковое целое число в формате little-endian. Количество миллисекунд в формате UTC, которые прошли с 00:00:00 1 января 1970 года. Этот формат известен как Unix- или POSIX-время |
|
4 |
Знаковое целое число в формате little-endian. Наносекундная доля миллисекунды |
Date#
Код типа: 11.
Дата в виде количества миллисекунд в формате UTC, которые прошли с 00:00:00 1 января 1970 года. Этот формат известен как Unix- или POSIX-время.
Структура:
Поле |
Размер в байтах |
Описание |
|---|---|---|
|
8 |
Значение. Знаковое целое число в формате little-endian |
Time#
Код типа: 36.
Время в формате количества миллисекунд, которые прошли с полуночи, то есть с 00:00:00 UTC.
Структура:
Поле |
Размер в байтах |
Описание |
|---|---|---|
|
8 |
Значение. Знаковое целое число в формате little-endian. Количество миллисекунд, которые прошли с полуночи, то есть с 00:00:00 UTC |
Decimal#
Код типа: 30.
Числовое значение любой точности и масштаба.
Структура:
Поле |
Размер в байтах |
Описание |
|---|---|---|
|
4 |
Знаковое целое число в формате little-endian. Это степень числа 10, на которое следует разделить немасштабированное (unscaled) значение. Например, 42 с масштабом 3 — 0,042. 42 в масштабе -3 — 42000. 42 в масштабе 1 — 42 |
|
4 |
Знаковое целое число в формате little-endian. Длина строки в байтах |
|
|
Первый бит — флаг отрицательности. Если он равен |
Enum#
Код типа: 28.
Перечисляемый тип, для которого задается конечное количество значений.
Структура:
Поле |
Размер в байтах |
Описание |
|---|---|---|
|
4 |
Знаковое целое число в формате little-endian. Подробнее написано ниже в разделе «Идентификатор типа» |
|
4 |
Знаковое целое число, которое хранится в формате little-endian. Порядковое расположение (ordinal) внутри декларации перечисления. Первой константе присваивается нулевой порядковый номер |
Массив примитивов#
Подобные массивы содержат только полезную нагрузку значений в виде элементов. У всех массивов примитивов одинаковый формат — подробнее описано в таблице ниже:
Важно
Обратите внимание, что массив содержит только данные полезной нагрузки, а не коды типов.
Поле |
Размер в байтах |
Описание |
|---|---|---|
|
4 |
Знаковое целое число. Количество элементов в массиве |
|
Зависит от типа |
Полезная нагрузка значения |
|
Зависит от типа |
Полезная нагрузка значения |
… |
… |
… |
|
Зависит от типа |
Полезная нагрузка значения |
Byte-массив#
Код типа: 12.
Массив байтов. Может быть фрагментом «сырых» (raw) данных или массивом маленьких знаковых целых чисел.
Структура:
Поле |
Размер в байтах |
Описание |
|---|---|---|
|
4 |
Знаковое целое число. Количество элементов в массиве |
|
length |
Последовательность элементов. Каждый элемент является полезной нагрузкой типа |
Short-массив#
Код типа: 13.
Массив знаковых двухбайтовых целых чисел.
Структура:
Поле |
Размер в байтах |
Описание |
|---|---|---|
|
4 |
Знаковое целое число. Количество элементов в массиве |
|
|
Последовательность элементов. Каждый элемент является полезной нагрузкой типа |
Int-массив#
Код типа: 14.
Массив целых чисел со знаком.
Структура:
Поле |
Размер в байтах |
Описание |
|---|---|---|
|
4 |
Знаковое целое число. Количество элементов в массиве |
|
|
Последовательность элементов. Каждый элемент является полезной нагрузкой типа |
Long-массив#
Код типа: 15.
Массив длинных целых чисел со знаком.
Структура:
Поле |
Размер в байтах |
Описание |
|---|---|---|
|
4 |
Знаковое целое число. Количество элементов в массиве |
|
|
Последовательность элементов. Каждый элемент является полезной нагрузкой типа |
Float-массив#
Код типа: 16.
Массив чисел с плавающей запятой.
Структура:
Поле |
Размер в байтах |
Описание |
|---|---|---|
|
4 |
Знаковое целое число. Количество элементов в массиве |
|
|
Последовательность элементов. Каждый элемент является полезной нагрузкой типа |
Double-массив#
Код типа: 17.
Массив чисел с плавающей запятой двойной точности (массив типа double).
Структура:
Поле |
Размер в байтах |
Описание |
|---|---|---|
|
4 |
Знаковое целое число. Количество элементов в массиве |
|
|
Последовательность элементов. Каждый элемент является полезной нагрузкой типа |
Char-массив#
Код типа: 18.
Массив кодовых единиц в системе кодировки UTF-16. В отличие от string, тип array не должен обязательно содержать допустимый текст в формате UTF-16.
Структура:
Поле |
Размер в байтах |
Описание |
|---|---|---|
|
4 |
Знаковое целое число. Количество элементов в массиве |
|
|
Последовательность элементов. Каждый элемент является полезной нагрузкой типа |
Bool-массив#
Код типа: 19.
Массив булевых значений.
Структура:
Поле |
Размер в байтах |
Описание |
|---|---|---|
|
4 |
Знаковое целое число. Количество элементов в массиве |
|
|
Последовательность элементов. Каждый элемент является полезной нагрузкой типа |
Массивы стандартных объектов#
Массивы стандартных объектов содержат полные значения в виде элементов — то есть они содержат код типа и полезную нагрузку. Этот формат позволяет элементам принимать значения NULL, поэтому они называются «объектами». У всех массивов стандартных объектов одинаковый формат — подробнее описано в таблице:
Поле |
Размер в байтах |
Описание |
|---|---|---|
|
4 |
Знаковое целое число. Количество элементов в массиве |
|
Зависит от типа значения |
Полное значение элемента |
|
Зависит от типа значения |
Полное значение элемента |
… |
… |
… |
|
Зависит от типа значения |
Полное значение элемента |
String-массив#
Код типа: 20.
Массив значений строк в кодировке UTF-8.
Структура:
Поле |
Размер в байтах |
Описание |
|---|---|---|
|
4 |
Знаковое целое число. Количество элементов в массиве |
|
Переменная. Зависит от длины каждой строки. Размер каждого элемента: |
Последовательность элементов. Каждый элемент представляет собой полное значение типа |
UUID-массив (GUID-массив)#
Код типа: 21.
Массив идентификаторов UUID (GUID).
Структура:
Поле |
Размер в байтах |
Описание |
|---|---|---|
|
4 |
Знаковое целое число. Количество элементов в массиве |
|
Переменная. Размер каждого элемента: |
Последовательность элементов. Каждый элемент представляет собой полное значение типа |
Timestamp-массив#
Код типа: 34.
Массив значений временных меток.
Структура:
Поле |
Размер в байтах |
Описание |
|---|---|---|
|
4 |
Знаковое целое число. Количество элементов в массиве |
|
Переменная. Размер каждого элемента: |
Последовательность элементов. Каждый элемент представляет собой полное значение типа |
Date-массив#
Код типа: 22.
Массив дат.
Структура:
Поле |
Размер в байтах |
Описание |
|---|---|---|
|
4 |
Знаковое целое число. Количество элементов в массиве |
|
Переменная. Размер каждого элемента: |
Последовательность элементов. Каждый элемент представляет собой полное значение типа |
Time-массив#
Код типа: 37.
Массив значений времени.
Структура:
Поле |
Размер в байтах |
Описание |
|---|---|---|
|
4 |
Знаковое целое число. Количество элементов в массиве |
|
Переменная. Размер каждого элемента: |
Последовательность элементов. Каждый элемент представляет собой полное значение типа |
Decimal-массив#
Код типа: 31.
Массив десятичных значений.
Структура:
Поле |
Размер в байтах |
Описание |
|---|---|---|
|
4 |
Знаковое целое число. Количество элементов в массиве |
|
Переменная. Размер каждого элемента: |
Последовательность элементов. Каждый элемент представляет собой полное значение типа |
Коллекции объектов#
Массив объектов#
Код типа: 23.
Массив объектов, который может содержать объекты любого типа: стандартные объекты любого типа, сложные объекты различных типов, значения NULL и любые комбинации. Также коллекции могут содержать другие коллекции.
Структура:
Поле |
Размер в байтах |
Описание |
|---|---|---|
|
4 |
Идентификатор типа объектов, которые содержатся в нем. Например, в Java этот тип используется для десериализации в |
|
4 |
Знаковое целое число. Количество элементов в массиве |
|
Переменная. Зависит от размера объектов |
Последовательность элементов. Каждый элемент представляет собой полное значение любого типа или |
Коллекция#
Код типа: 24.
Общий тип коллекции. Коллекция также содержит объекты, но в отличие от массива объектов, у нее есть подсказка (hint) для десериализации не просто в массив, а в коллекцию определенного типа, которая зависит от платформы. Типы коллекций:
USER_SET=-1. Общий тип множества, который нельзя привести к более конкретному типу — но точно известно, что это множество. Такую коллекцию стоит десериализовать до базового и наиболее широко используемого на платформе типа множества, напримерHASH_SET.USER_COL=0. Общий тип коллекции, который нельзя привести к более конкретному типу. Такую коллекцию стоит десериализовать до базового и наиболее широко используемого на платформе типа коллекции, например в изменяемый по размеру массив.ARR_LIST=1. Тип изменяемого по размеру массива.LINKED_LIST=2. Тип связного списка.HASH_SET=3. Базовый тип хеш-множеств (hash set).LINKED_HASH_SET=4. Тип набора хеш-множеств, который поддерживает порядок элементов.SINGLETON_LIST=5. Коллекция, которая содержит только один элемент, но ведет себя как коллекция. Может использоваться платформами в целях оптимизации. Если неприменимо, можно использовать любой тип коллекции.
Внимание
Тип коллекции
byteиспользуется определенными платформами как подсказка (hint) для десериализации коллекции в наиболее подходящий тип. Например, в JavaHASH_SETдесериализован вjava.util.HashSet, аLINKED_HASH_SET— вjava.util.LinkedHashSet. Для реализации на тонком клиенте рекомендуется использовать наиболее подходящий тип коллекции при сериализации и десериализации. Подсказку можно игнорировать, если она неактуальна или неприменима для платформы.
Структура:
Поле |
Размер в байтах |
Описание |
|---|---|---|
|
4 |
Знаковое целое число. Количество элементов в коллекции |
|
1 |
Тип коллекции. Подробнее о типах можно прочесть в описании выше |
Отображение (map)#
Код типа: 25.
Коллекция, подобная отображению (map-like). Содержит пары объектов «ключ-значение». Объекты ключей и значений могут быть различного типа: стандартные и сложные объекты различных типов и любые комбинации. Есть подсказка (hint) для десериализации отображений определенного типа. Типы отображений:
HASH_MAP=1. Базовое хеш-отображение (hash-map).LINKED_HASH_MAP=2. Хеш-отображение, которое поддерживает порядок элементов.
Внимание
Тип отображения
byteиспользуется определенными платформами как подсказка для десериализации коллекции в наиболее подходящий тип. Для реализации на тонком клиенте рекомендуется использовать наиболее подходящий тип отображения при сериализации и десериализации. Подсказку можно игнорировать, если она неактуальна или неприменима для платформы.
Структура:
Поле |
Размер в байтах |
Описание |
|---|---|---|
|
4 |
Знаковое целое число. Количество элементов в коллекции |
|
1 |
Тип коллекции. Подробнее о типах можно прочесть в описании выше |
|
Переменная. Зависит от размера объектов |
Последовательность элементов. Элементы в данном случае — ключи и значения, которые попарно следуют один за другим. Каждый элемент представляет собой полное значение любого типа или |
Массив перечислений (Enum array)#
Код типа: 29.
Массив значений перечислимого типа. Элемент массива может быть перечисляемым значением или NULL, то есть любой элемент занимает или 9 байтов, или 1 байт.
Структура:
Поле |
Размер в байтах |
Описание |
|---|---|---|
|
4 |
Идентификатор типа объектов, которые содержатся в нем. Например, в Java этот тип используется для десериализации в |
|
4 |
Знаковое целое число. Количество элементов в коллекции |
|
Переменная. Зависит от размера объектов |
Последовательность элементов. Каждый элемент представляет собой полное значение типа |
Сложные объекты#
Код типа: 103.
Сложный объект состоит из 24-байтового заголовка, набора полей (объекты данных) и схемы (идентификаторы полей и позиции). В зависимости от операции и модели данных объект может быть примитивного или сложного типа (набор полей).
Структура:
Поле |
Размер в байтах |
Опциональность |
|---|---|---|
|
1 |
Обязательное |
|
2 |
Обязательное |
|
4 |
Обязательное |
|
4 |
Обязательное |
|
4 |
Обязательное |
|
4 |
Обязательное |
|
Переменная |
|
|
Переменная |
|
|
4 |
Опциональное |
Версия#
Поле version нужно для обеспечения обратной совместимости: оно указывает на версию разметки (layout) сложного объекта. Клиенты должны проверить поле и сообщить пользователю об ошибке, если версия разметки объекта им неизвестна. Это нужно, чтобы предотвратить повреждение данных и непредсказуемые результаты десериализации.
Флаги#
Поле flags — битовая маска длиной в 16 бит в формате little-endian. Она содержит флаги объектов, которые указывают, как объект-читатель должен обрабатывать экземпляр объекта. Существуют следующие флаги:
USER_TYPE = 0x0001— указывает, что тип является пользовательским. Флаг всегда должен быть установлен для любого типа клиента. Можно игнорировать при десериализации.HAS_SCHEMA = 0x0002— указывает, что в нижнем колонтитуле (footer) разметки есть схема. Подробнее написано ниже в разделе «Схема».HAS_RAW_DATA = 0x0004— указывает, что в объекте есть необработанные данные. Подробнее написано ниже в разделе Смещение сырых (raw) данных.OFFSET_ONE_BYTE = 0x0008— указывает, что поле схемы смещено на 1 байт. Подробнее написано ниже в разделе «Схема».OFFSET_TWO_BYTES = 0x0010— указывает, что поле схемы смещено на 2 байта. Подробнее написано ниже в разделе «Схема».COMPACT_FOOTER = 0x0020— указывает, что в нижнем колонтитуле (footer) нет идентификаторов полей; есть только смещения. Подробнее написано ниже в разделе «Схема».
Идентификатор типа#
Поле содержит уникальный идентификатор типа. Оно имеет длину 4 байта и хранится в формате little-endian. По умолчанию идентификатор типа получают подобно хеш-коду в Java: передают на вход название типа. Алгоритм вычисления идентификатора типа должен быть одинаковым для всех платформ кластера, иначе они не смогут работать с объектами этого типа.
Алгоритм вычисления идентификатора типа по умолчанию, который рекомендуется использовать на всех тонких клиентах:
Java:
static int hashCode(String str) {
int len = str.length;
int h = 0;
for (int i = 0; i < len; i++) {
int c = str.charAt(i);
c = Character.toLowerCase(c);
h = 31 * h + c;
}
return h;
}
C:
int32_t HashCode(const char* val, size_t size)
{
if (!val && size == 0)
return 0;
int32_t hash = 0;
for (size_t i = 0; i < size; ++i)
{
char c = val[i];
if ('A' <= c && c <= 'Z')
c |= 0x20;
hash = 31 * hash + c;
}
return hash;
}
Хеш-код#
Хеш-код значения. Он хранится в виде 4-байтового значения в формате little-endian и вычисляется как хеш содержимого без заголовка в Java-стиле. Поле используется движком DataGrid для сравнений, например сравнения ключей.
Алгоритм вычисления хеша:
Java:
static int dataHashCode(byte[] data) {
int len = data.length;
int h = 0;
for (int i = 0; i < len; i++)
h = 31 * h + data[i];
return h;
}
C:
int32_t GetDataHashCode(const void* data, size_t size)
{
if (!data)
return 0;
int32_t hash = 1;
const int8_t* bytes = static_cast<const int8_t*>(data);
for (int i = 0; i < size; ++i)
hash = 31 * hash + bytes[i];
return hash;
}
Длина#
Поле length содержит полную длину объекта с заголовком. Оно хранится в виде целого числа длиной 4 байта в формате little-endian. С помощью этого поля можно легко пропустить чтение объекта в потоке данных — для этого увеличьте текущую позицию в потоке данных (data stream) на значение этого поля.
Идентификатор схемы (Schema ID)#
Идентификатор схемы объекта хранится в виде 4-байтового значения в формате little-endian и вычисляется как хеш всех идентификаторов полей объекта. Идентификатор схемы используется для комплексной оптимизации размера объекта. DataGrid использует идентификатор, чтобы избежать записи всей схемы в конец каждого значения сложного объекта. Вместо этого он сохраняет все схемы в хранилище binary metadata и записывает в объект только смещения полей. Такая оптимизация помогает значительно уменьшить размер сложного объекта, который содержит множество коротких полей, например int.
Если схема отсутствует, например весь объект записан в «сыром» (raw) режиме или не содержит полей, значение поля идентификатора схемы — 0.
Подробнее о структуре схемы написано ниже в разделе «Схема».
Внимание
Идентификатор схемы нельзя определить с помощью идентификаторов типов. У объектов одного типа (следовательно, с одинаковым идентификатором типа) может быть несколько схем, то есть последовательностей полей.
Алгоритм расчета идентификатора схемы:
Java:
/** База смещения хеша FNV1. */
private static final int FNV1_OFFSET_BASIS = 0x811C9DC5;
/** Простое число хеша FNV1. */
private static final int FNV1_PRIME = 0x01000193;
static int calculateSchemaId(int fieldIds[])
{
if (fieldIds == null || fieldIds.length == 0)
return 0;
int len = fieldIds.length;
int schemaId = FNV1_OFFSET_BASIS;
for (size_t i = 0; i < len; ++i)
{
fieldId = fieldIds[i];
schemaId = schemaId ^ (fieldId & 0xFF);
schemaId = schemaId * FNV1_PRIME;
schemaId = schemaId ^ ((fieldId >> 8) & 0xFF);
schemaId = schemaId * FNV1_PRIME;
schemaId = schemaId ^ ((fieldId >> 16) & 0xFF);
schemaId = schemaId * FNV1_PRIME;
schemaId = schemaId ^ ((fieldId >> 24) & 0xFF);
schemaId = schemaId * FNV1_PRIME;
}
}
C:
/** База смещения хеша FNV1. */
enum { FNV1_OFFSET_BASIS = 0x811C9DC5 };
/** Простое число хеша FNV1. */
enum { FNV1_PRIME = 0x01000193 };
int32_t CalculateSchemaId(const int32_t* fieldIds, size_t num)
{
if (!fieldIds || num == 0)
return 0;
int32_t schemaId = FNV1_OFFSET_BASIS;
for (size_t i = 0; i < num; ++i)
{
fieldId = fieldIds[i];
schemaId ^= fieldId & 0xFF;
schemaId *= FNV1_PRIME;
schemaId ^= (fieldId >> 8) & 0xFF;
schemaId *= FNV1_PRIME;
schemaId ^= (fieldId >> 16) & 0xFF;
schemaId *= FNV1_PRIME;
schemaId ^= (fieldId >> 24) & 0xFF;
schemaId *= FNV1_PRIME;
}
}
Поля объектов#
Каждое поле — бинарный объект сложного или стандартного типа. Также могут встречаться и допускаются сложные объекты без полей. У каждого поля может быть или не быть названия. Для именованных полей в схеме задано смещение, с помощью которого их можно расположить в объекте без полной десериализации. Поля без названий всегда сохраняются после именованных полей и записываются в так называемом «сыром» (raw) режиме.
То есть доступ к полям, которые записаны в «сыром» режиме, возможен только с помощью последовательного чтения в том же порядке, в каком они были записаны. Именованные поля можно читать в случайном порядке.
Схема#
Схема — необязательное поле, которое может быть у любого сложного объекта. Если в объекте нет именованных полей или вообще нет полей, у него будет отсутствовать схема. Чтобы определить, есть ли схема у объекта, проверьте его флаг HAS_SCHEMA.
Основная цель схемы — обеспечить быстрый поиск полей объекта. Для этого схема содержит последовательность смещений полей объекта в полезной нагрузке объекта (object payload). Смещения полей могут быть разного размера — он определяется при записи по максимальному значению смещения. Если оно находится в диапазоне:
от 24 до 255 байт, используется смещение на 1 байт;
от 256 до 65535 байт — смещение на 2 байта;
во всех остальных случаях — смещения на 4 байта.
Чтобы определить размер смещений при чтении, проверьте флаги OFFSET_ONE_BYTE и OFFSET_TWO_BYTES:
Если установлен флаг
OFFSET_ONE_BYTE, длина смещения — 1 байт.Если установлен флаг
OFFSET_TWO_BYTES, длина смещения — 2 байта.В остальных случаях длина смещения — 4 байта.
Поддерживается два формата схемы:
Подход с использованием полной схемы — проще в реализации, но требует больше ресурсов.
Подход
CompactFooter— сложнее в реализации, но обеспечивает лучшую производительность и снижает потребление памяти. Новым пользователям рекомендуется внедрять этот подход.
Подробнее об обоих форматах написано в разделах ниже.
Внимание
Чтобы определить, какой подход используется в конкретном объекте, проверьте флаг
COMPACT_FOOTER.
Полная схема#
При использовании этого подхода флаг COMPACT_FOOTER не устанавливается, и вся схема объекта записывается в его нижний колонтитул. В этом случае для десериализации требуется только сам сложный объект — поле schema_id игнорируется и никаких дополнительных данных не требуется.
Структура поля schema сложного объекта для данного случая:
Поле |
Размер в байтах |
Описание |
|---|---|---|
|
4 |
Идентификатор поля с индексом |
|
Переменная, которая зависит от размера объекта: 1, 2 или 4 |
Целое число без знака, которое хранится в формате little-endian. Смещение поля в объекте, начиная с первого байта полного значения объекта (то есть с позиции |
|
4 |
Хеш длиной 4 байта, который хранится в формате little-endian. Идентификатор поля с индексом |
|
Переменная, которая зависит от размера объекта: 1, 2 или 4 |
Целое число без знака, которое хранится в формате little-endian. Смещение поля в объекте |
… |
… |
… |
|
4 |
Хеш длиной 4 байта, который хранится в формате little-endian. Идентификатор поля с индексом |
|
Переменная, которая зависит от размера объекта: 1, 2 или 4 |
Целое число без знака, которое хранится в формате little-endian. Смещение поля в объекте |
CompactFooter#
При таком подходе устанавливается флаг COMPACT_FOOTER, и в нижний колонтитул объекта записывается только последовательность смещений полей. В этом случае клиент использует поле schema_id для поиска схемы объектов в ранее сохраненном хранилище метаданных, чтобы определить порядок полей и связать поле с его смещением.
Если используется этот подход, держите схемы в специальном хранилище метаданных и отправляйте/восстанавливайте их на серверы DataGrid.
Структура схемы для данного случая:
Поле |
Размер в байтах |
Описание |
|---|---|---|
|
Переменная, которая зависит от размера объекта: 1, 2 или 4 |
Целое число без знака, которое хранится в формате little-endian. Смещение нулевого поля в объекте, начиная с первого байта полного значения объекта (то есть с позиции |
|
Переменная, которая зависит от размера объекта: 1, 2 или 4 |
Целое число без знака, которое хранится в формате little-endian. Смещение первого поля в объекте |
… |
… |
… |
|
Переменная, которая зависит от размера объекта: 1, 2 или 4 |
Целое число без знака, которое хранится в формате little-endian. Смещение |
Смещение сырых (raw) данных#
Необязательное поле. Присутствует только когда в объекте есть поля, которые были записаны в raw-режиме. В этом случае установлен флаг HAS_RAW_DATA и присутствует поле смещения «сырых» данных. Оно хранится в виде 4-байтового значения в формате little-endian, которое указывает на смещение необработанных данных в сложном объекте, начиная с самого первого байта заголовка. То есть это поле всегда больше, чем длина заголовка.
Поле используется для позиционирования потока, чтобы пользователь мог начать чтение в raw-режиме.
Особые типы#
Wrapped Data#
Код типа: 27.
В массив могут быть обернуты один или несколько бинарных объектов. Это позволяет эффективно считывать, хранить, передавать и записывать объекты без понимания их содержимого с помощью простого байтового копирования. Все операции кеширования возвращают сложные объекты внутри оболочки (примитивы не возвращаются).
Структура:
Поле |
Размер в байтах |
Описание |
|---|---|---|
|
4 |
Целое число без знака, которое хранится в формате little-endian. Размер обернутых данных в байтах |
|
|
Полезная нагрузка |
|
4 |
Целое число без знака, которое хранится в формате little-endian. Смещение объекта внутри массива. Массив может содержать граф объектов — такое смещение указывает на корневой объект |
Binary enum#
Код типа: 38.
Обернутый перечислимый тип, который движок может вернуть вместо обычного типа enum. При использовании Binary API перечисления должны быть записаны в таком виде.
Структура:
Поле |
Размер в байтах |
Описание |
|---|---|---|
|
4 |
Целое число без знака, которое хранится в формате little-endian. Подробнее написано выше в разделе «Идентификатор типа» |
|
4 |
Целое число без знака, которое хранится в формате little-endian. Порядковый номер значения перечисления. Его позиция в декларации |
Примеры сериализации и десериализации#
Чтение объектов#
Пример, как считывать данные различных типов из входного потока байтов:
private static Object readDataObject(DataInputStream in) throws IOException {
byte code = in.readByte();
switch (code) {
case 1:
return in.readByte();
case 2:
return readShortLittleEndian(in);
case 3:
return readIntLittleEndian(in);
case 4:
return readLongLittleEndian(in);
case 27: {
int len = readIntLittleEndian(in);
// Для простоты предполагается, что смещение равно `0`.
Object res = readDataObject(in);
int offset = readIntLittleEndian(in);
return res;
}
case 103:
byte ver = in.readByte();
assert ver == 1; // Версия.
short flags = readShortLittleEndian(in);
int typeId = readIntLittleEndian(in);
int hash = readIntLittleEndian(in);
int len = readIntLittleEndian(in);
int schemaId = readIntLittleEndian(in);
int schemaOffset = readIntLittleEndian(in);
byte[] data = new byte[len - 24];
in.read(data);
return "Binary Object: " + typeId;
default:
throw new Error("Unsupported type: " + code);
}
}
Int#
Пример, как записывать и считывать объект данных типа int с помощью потока вывода/ввода на основе сокета:
// Запись объекта данных типа `int`.
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
int val = 11;
writeByteLittleEndian(3, out); // Код типа `int`.
writeIntLittleEndian(val, out);
// Чтение объекта данных типа `int`.
DataInputStream in = new DataInputStream(socket.getInputStream());
int typeCode = readByteLittleEndian(in);
int val = readIntLittleEndian(in);
Пример, как будет выглядеть структура для типа String:
Тип |
Описание |
|---|---|
|
Код для типа |
|
Длина строки в кодировке UTF-8 (в байтах) |
|
Фактическая строка |
String#
Пример, как записывать и считывать значение типа String в соответствии с этим форматом:
private static void writeString (String str, DataOutputStream out) throws IOException {
writeByteLittleEndian(9, out); // Код типа `String`.
int strLen = str.getBytes("UTF-8").length; // Длина строки.
writeIntLittleEndian(strLen, out);
out.writeBytes(str);
}
private static String readString(DataInputStream in) throws IOException {
int type = readByteLittleEndian(in); // Код типа.
int strLen = readIntLittleEndian(in); // Длина строки.
byte[] buf = new byte[strLen];
readFully(in, buf, 0, strLen);
return new String(buf);
}