Формат данных#

Стандартные типы данных представлены в виде комбинации кода типа и значения.

Поле

Размер в байтах

Описание

type_code

1

Код в виде знакового однобайтового целого числа, который показывает тип значения

value

Переменная

Само значение. Его формат и размер зависят от type_code

Описание поддерживаемых типов данных и их форматы указаны ниже.

Примитивы#

Примитивы — базовые типы данных, например числа.

Byte#

Поле

Размер в байтах

Описание

type

1

1

value

1

Однобайтовое значение

Short#

Код типа: 2.

Знаковое двухбайтовое целое число в формате little-endian.

Структура:

Поле

Размер в байтах

Описание

value

2

Значение

Int#

Код типа: 3.

Знаковое четырехбайтовое целое число в формате little-endian.

Структура:

Поле

Размер в байтах

Описание

value

4

Значение

Long#

Код типа: 4.

Знаковое восьмибайтное целое число в формате little-endian.

Структура:

Поле

Размер в байтах

Описание

value

8

Значение

Float#

Код типа: 5.

Четырехбайтовое число с плавающей запятой по стандарту IEEE 754 в формате little-endian.

Структура:

Поле

Размер в байтах

Описание

value

4

Значение

Double#

Код типа: 6.

Восьмибайтное число с плавающей запятой по стандарту IEEE 754 в формате little-endian.

Структура:

Поле

Размер в байтах

Описание

value

8

Значение

Char#

Код типа: 7.

Единичный код в системе кодировки UTF-16 и формате little-endian.

Структура:

Поле

Размер в байтах

Описание

value

2

Один символ в кодировке UTF-16 в формате little-endian

Bool#

Код типа: 8.

Булевое значение. Ноль – false, не ноль — true.

Структура:

Поле

Размер в байтах

Описание

Value

1

Значение. Ноль – false, не ноль — true

Null#

Код типа: 101.

Null — не совсем тип данных: это значение null, которое может быть присвоено объекту любого типа. Не имеет полезной нагрузки, является только кодом типа.

Стандартные объекты#

String#

Код типа: 9.

Строка в кодировке UTF-8. Всегда должна быть в формате UTF-8.

Структура:

Поле

Размер в байтах

Описание

length

4

Знаковое целое число в формате little-endian. Длина строки в единицах кодировки UTF-8, то есть в байтах

data

length

Строка данных в кодировке UTF-8 без BOM

UUID (GUID)#

Код типа: 10.

Универсальный уникальный идентификатор (UUID) — 128-битное число, которое используется для идентификации информации в компьютерных системах.

Структура:

Поле

Размер в байтах

Описание

most_significant_bits

8

64-битное число в формате little-endian, которое представляет 64 наиболее значимых бита UUID

least_significant_bits

8

64-битное число в формате little-endian, которое представляет 64 наименее значимых бита UUID

Timestamp#

Код типа: 33.

Более точный тип данных, чем Date. Кроме миллисекунд со времен эпохи Unix, содержит наносекундную долю последней миллисекунды, которая может варьироваться от 0 до 999999. Полную временную метку можно получить с помощью выражения msecs_since_epoch * 1000000 + msec_fraction_in_nsecs.

Внимание

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

Структура:

Поле

Размер в байтах

Описание

msecs_since_epoch

8

Знаковое целое число в формате little-endian. Количество миллисекунд в формате UTC, которые прошли с 00:00:00 1 января 1970 года. Этот формат известен как Unix- или POSIX-время

msec_fraction_in_nsecs

4

Знаковое целое число в формате little-endian. Наносекундная доля миллисекунды

Date#

Код типа: 11.

Дата в виде количества миллисекунд в формате UTC, которые прошли с 00:00:00 1 января 1970 года. Этот формат известен как Unix- или POSIX-время.

Структура:

Поле

Размер в байтах

Описание

msecs_since_epoch

8

Значение. Знаковое целое число в формате little-endian

Time#

Код типа: 36.

Время в формате количества миллисекунд, которые прошли с полуночи, то есть с 00:00:00 UTC.

Структура:

Поле

Размер в байтах

Описание

value

8

Значение. Знаковое целое число в формате little-endian. Количество миллисекунд, которые прошли с полуночи, то есть с 00:00:00 UTC

Decimal#

Код типа: 30.

Числовое значение любой точности и масштаба.

Структура:

Поле

Размер в байтах

Описание

scale

4

Знаковое целое число в формате little-endian. Это степень числа 10, на которое следует разделить немасштабированное (unscaled) значение. Например, 42 с масштабом 3 — 0,042. 42 в масштабе -3 — 42000. 42 в масштабе 1 — 42

length

4

Знаковое целое число в формате little-endian. Длина строки в байтах

data

length

Первый бит — флаг отрицательности. Если он равен 1, значение будет отрицательным. Остальные биты формируют целое число переменной длины со знаком в формате big-endian

Enum#

Код типа: 28.

Перечисляемый тип, для которого задается конечное количество значений.

Структура:

Поле

Размер в байтах

Описание

type_id

4

Знаковое целое число в формате little-endian. Подробнее написано ниже в разделе «Идентификатор типа»

ordinal

4

Знаковое целое число, которое хранится в формате little-endian. Порядковое расположение (ordinal) внутри декларации перечисления. Первой константе присваивается нулевой порядковый номер

Массив примитивов#

Подобные массивы содержат только полезную нагрузку значений в виде элементов. У всех массивов примитивов одинаковый формат — подробнее описано в таблице ниже:

Важно

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

Поле

Размер в байтах

Описание

length

4

Знаковое целое число. Количество элементов в массиве

element_0_payload

Зависит от типа

Полезная нагрузка значения 0

element_1_payload

Зависит от типа

Полезная нагрузка значения 1

element_N_payload

Зависит от типа

Полезная нагрузка значения N

Byte-массив#

Код типа: 12.

Массив байтов. Может быть фрагментом «сырых» (raw) данных или массивом маленьких знаковых целых чисел.

Структура:

Поле

Размер в байтах

Описание

length

4

Знаковое целое число. Количество элементов в массиве

elements

length

Последовательность элементов. Каждый элемент является полезной нагрузкой типа byte

Short-массив#

Код типа: 13.

Массив знаковых двухбайтовых целых чисел.

Структура:

Поле

Размер в байтах

Описание

length

4

Знаковое целое число. Количество элементов в массиве

elements

length * 2

Последовательность элементов. Каждый элемент является полезной нагрузкой типа short

Int-массив#

Код типа: 14.

Массив целых чисел со знаком.

Структура:

Поле

Размер в байтах

Описание

length

4

Знаковое целое число. Количество элементов в массиве

elements

length * 4

Последовательность элементов. Каждый элемент является полезной нагрузкой типа int

Long-массив#

Код типа: 15.

Массив длинных целых чисел со знаком.

Структура:

Поле

Размер в байтах

Описание

length

4

Знаковое целое число. Количество элементов в массиве

elements

length * 8

Последовательность элементов. Каждый элемент является полезной нагрузкой типа long

Float-массив#

Код типа: 16.

Массив чисел с плавающей запятой.

Структура:

Поле

Размер в байтах

Описание

length

4

Знаковое целое число. Количество элементов в массиве

elements

length * 4

Последовательность элементов. Каждый элемент является полезной нагрузкой типа float

Double-массив#

Код типа: 17.

Массив чисел с плавающей запятой двойной точности (массив типа double).

Структура:

Поле

Размер в байтах

Описание

length

4

Знаковое целое число. Количество элементов в массиве

elements

length * 8

Последовательность элементов. Каждый элемент является полезной нагрузкой типа double

Char-массив#

Код типа: 18.

Массив кодовых единиц в системе кодировки UTF-16. В отличие от string, тип array не должен обязательно содержать допустимый текст в формате UTF-16.

Структура:

Поле

Размер в байтах

Описание

length

4

Знаковое целое число. Количество элементов в массиве

elements

length * 2UTF-16

Последовательность элементов. Каждый элемент является полезной нагрузкой типа char

Bool-массив#

Код типа: 19.

Массив булевых значений.

Структура:

Поле

Размер в байтах

Описание

length

4

Знаковое целое число. Количество элементов в массиве

elements

length

Последовательность элементов. Каждый элемент является полезной нагрузкой типа bool

Массивы стандартных объектов#

Массивы стандартных объектов содержат полные значения в виде элементов — то есть они содержат код типа и полезную нагрузку. Этот формат позволяет элементам принимать значения NULL, поэтому они называются «объектами». У всех массивов стандартных объектов одинаковый формат — подробнее описано в таблице:

Поле

Размер в байтах

Описание

length

4

Знаковое целое число. Количество элементов в массиве

element_0_full_value

Зависит от типа значения

Полное значение элемента 0. Содержит код типа и полезную нагрузку. Также может быть NULL

element_1_full_value

Зависит от типа значения

Полное значение элемента 1 или NULL

element_N_full_value

Зависит от типа значения

Полное значение элемента N или NULL

String-массив#

Код типа: 20.

Массив значений строк в кодировке UTF-8.

Структура:

Поле

Размер в байтах

Описание

length

4

Знаковое целое число. Количество элементов в массиве

elements

Переменная. Зависит от длины каждой строки. Размер каждого элемента: 5 + value_length для string; 1 для NULL

Последовательность элементов. Каждый элемент представляет собой полное значение типа string (с кодом типа) или NULL

UUID-массив (GUID-массив)#

Код типа: 21.

Массив идентификаторов UUID (GUID).

Структура:

Поле

Размер в байтах

Описание

length

4

Знаковое целое число. Количество элементов в массиве

elements

Переменная. Размер каждого элемента: 17 для UUID; 1 для NULL

Последовательность элементов. Каждый элемент представляет собой полное значение типа UUID (с кодом типа) или NULL

Timestamp-массив#

Код типа: 34.

Массив значений временных меток.

Структура:

Поле

Размер в байтах

Описание

length

4

Знаковое целое число. Количество элементов в массиве

elements

Переменная. Размер каждого элемента: 13 для timestamp и 1 для NULL

Последовательность элементов. Каждый элемент представляет собой полное значение типа timestamp (с кодом типа) или NULL

Date-массив#

Код типа: 22.

Массив дат.

Структура:

Поле

Размер в байтах

Описание

length

4

Знаковое целое число. Количество элементов в массиве

elements

Переменная. Размер каждого элемента: 9 для date; 1 для NULL

Последовательность элементов. Каждый элемент представляет собой полное значение типа date (с кодом типа) или NULL

Time-массив#

Код типа: 37.

Массив значений времени.

Структура:

Поле

Размер в байтах

Описание

length

4

Знаковое целое число. Количество элементов в массиве

elements

Переменная. Размер каждого элемента: 9 для time; 1 для NULL

Последовательность элементов. Каждый элемент представляет собой полное значение типа time (с кодом типа) или NULL

Decimal-массив#

Код типа: 31.

Массив десятичных значений.

Структура:

Поле

Размер в байтах

Описание

length

4

Знаковое целое число. Количество элементов в массиве

elements

Переменная. Размер каждого элемента: 9 + value_length для decimal; 1 для NULL

Последовательность элементов. Каждый элемент представляет собой полное значение типа decimal (с кодом типа) или NULL

Коллекции объектов#

Массив объектов#

Код типа: 23.

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

Структура:

Поле

Размер в байтах

Описание

type_id

4

Идентификатор типа объектов, которые содержатся в нем. Например, в Java этот тип используется для десериализации в Type[]. Все значения в массиве должны иметь тип, как у родителя — то есть это родительский тип для любого объекта. Например, в Java это всегда java.lang.Object. Идентификатор типа родительского объекта — -1.

Подробнее написано ниже в разделе «Идентификатор типа»

length

4

Знаковое целое число. Количество элементов в массиве

elements

Переменная. Зависит от размера объектов

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

Коллекция#

Код типа: 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) для десериализации коллекции в наиболее подходящий тип. Например, в Java HASH_SET десериализован в java.util.HashSet, а LINKED_HASH_SET — в java.util.LinkedHashSet. Для реализации на тонком клиенте рекомендуется использовать наиболее подходящий тип коллекции при сериализации и десериализации. Подсказку можно игнорировать, если она неактуальна или неприменима для платформы.

Структура:

Поле

Размер в байтах

Описание

length

4

Знаковое целое число. Количество элементов в коллекции

type

1

Тип коллекции. Подробнее о типах можно прочесть в описании выше

Отображение (map)#

Код типа: 25.

Коллекция, подобная отображению (map-like). Содержит пары объектов «ключ-значение». Объекты ключей и значений могут быть различного типа: стандартные и сложные объекты различных типов и любые комбинации. Есть подсказка (hint) для десериализации отображений определенного типа. Типы отображений:

  • HASH_MAP = 1. Базовое хеш-отображение (hash-map).

  • LINKED_HASH_MAP = 2. Хеш-отображение, которое поддерживает порядок элементов.

Внимание

Тип отображения byte используется определенными платформами как подсказка для десериализации коллекции в наиболее подходящий тип. Для реализации на тонком клиенте рекомендуется использовать наиболее подходящий тип отображения при сериализации и десериализации. Подсказку можно игнорировать, если она неактуальна или неприменима для платформы.

Структура:

Поле

Размер в байтах

Описание

length

4

Знаковое целое число. Количество элементов в коллекции

type

1

Тип коллекции. Подробнее о типах можно прочесть в описании выше

elements

Переменная. Зависит от размера объектов

Последовательность элементов. Элементы в данном случае — ключи и значения, которые попарно следуют один за другим. Каждый элемент представляет собой полное значение любого типа или NULL

Массив перечислений (Enum array)#

Код типа: 29.

Массив значений перечислимого типа. Элемент массива может быть перечисляемым значением или NULL, то есть любой элемент занимает или 9 байтов, или 1 байт.

Структура:

Поле

Размер в байтах

Описание

type_id

4

Идентификатор типа объектов, которые содержатся в нем. Например, в Java этот тип используется для десериализации в EnumType[]. У всех значений в массиве должен быть EnumType в качестве родительского — то есть это родительский тип любого перечисляемого типа объекта .

Подробнее написано ниже в разделе «Идентификатор типа»

length

4

Знаковое целое число. Количество элементов в коллекции

elements

Переменная. Зависит от размера объектов

Последовательность элементов. Каждый элемент представляет собой полное значение типа enum или NULL

Сложные объекты#

Код типа: 103.

Сложный объект состоит из 24-байтового заголовка, набора полей (объекты данных) и схемы (идентификаторы полей и позиции). В зависимости от операции и модели данных объект может быть примитивного или сложного типа (набор полей).

Структура:

Поле

Размер в байтах

Опциональность

version

1

Обязательное

flags

2

Обязательное

type_id

4

Обязательное

hash_code

4

Обязательное

length

4

Обязательное

schema_id

4

Обязательное

object_fields

Переменная

length. Опциональное

schema

Переменная

length. Опциональное

raw_data_offset

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 сложного объекта для данного случая:

Поле

Размер в байтах

Описание

field_id_0

4

Идентификатор поля с индексом 0. Хеш длиной 4 байта, который хранится в формате little-endian. Идентификатор поля вычисляется с использованием названия поля так же, как идентификатора типа

field_offset_0

Переменная, которая зависит от размера объекта: 1, 2 или 4

Целое число без знака, которое хранится в формате little-endian. Смещение поля в объекте, начиная с первого байта полного значения объекта (то есть с позиции type_code)

field_id_1

4

Хеш длиной 4 байта, который хранится в формате little-endian. Идентификатор поля с индексом 1

field_offset_1

Переменная, которая зависит от размера объекта: 1, 2 или 4

Целое число без знака, которое хранится в формате little-endian. Смещение поля в объекте

field_id_N

4

Хеш длиной 4 байта, который хранится в формате little-endian. Идентификатор поля с индексом N

field_offset_N

Переменная, которая зависит от размера объекта: 1, 2 или 4

Целое число без знака, которое хранится в формате little-endian. Смещение поля в объекте

CompactFooter#

При таком подходе устанавливается флаг COMPACT_FOOTER, и в нижний колонтитул объекта записывается только последовательность смещений полей. В этом случае клиент использует поле schema_id для поиска схемы объектов в ранее сохраненном хранилище метаданных, чтобы определить порядок полей и связать поле с его смещением.

Если используется этот подход, держите схемы в специальном хранилище метаданных и отправляйте/восстанавливайте их на серверы DataGrid.

Структура схемы для данного случая:

Поле

Размер в байтах

Описание

field_offset_0

Переменная, которая зависит от размера объекта: 1, 2 или 4

Целое число без знака, которое хранится в формате little-endian. Смещение нулевого поля в объекте, начиная с первого байта полного значения объекта (то есть с позиции type_code)

field_offset_1

Переменная, которая зависит от размера объекта: 1, 2 или 4

Целое число без знака, которое хранится в формате little-endian. Смещение первого поля в объекте

field_offset_N

Переменная, которая зависит от размера объекта: 1, 2 или 4

Целое число без знака, которое хранится в формате little-endian. Смещение N-го поля в объекте

Смещение сырых (raw) данных#

Необязательное поле. Присутствует только когда в объекте есть поля, которые были записаны в raw-режиме. В этом случае установлен флаг HAS_RAW_DATA и присутствует поле смещения «сырых» данных. Оно хранится в виде 4-байтового значения в формате little-endian, которое указывает на смещение необработанных данных в сложном объекте, начиная с самого первого байта заголовка. То есть это поле всегда больше, чем длина заголовка.

Поле используется для позиционирования потока, чтобы пользователь мог начать чтение в raw-режиме.

Особые типы#

Wrapped Data#

Код типа: 27.

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

Структура:

Поле

Размер в байтах

Описание

length

4

Целое число без знака, которое хранится в формате little-endian. Размер обернутых данных в байтах

payload

length

Полезная нагрузка

offset

4

Целое число без знака, которое хранится в формате little-endian. Смещение объекта внутри массива. Массив может содержать граф объектов — такое смещение указывает на корневой объект

Binary enum#

Код типа: 38.

Обернутый перечислимый тип, который движок может вернуть вместо обычного типа enum. При использовании Binary API перечисления должны быть записаны в таком виде.

Структура:

Поле

Размер в байтах

Описание

type_id

4

Целое число без знака, которое хранится в формате little-endian. Подробнее написано выше в разделе «Идентификатор типа»

ordinal

4

Целое число без знака, которое хранится в формате little-endian. Порядковый номер значения перечисления. Его позиция в декларации enum, где начальной константе присваивается нулевой порядковый номер

Примеры сериализации и десериализации#

Чтение объектов#

Пример, как считывать данные различных типов из входного потока байтов:

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:

Тип

Описание

byte

Код для типа String, 9

int

Длина строки в кодировке UTF-8 (в байтах)

bytes

Фактическая строка

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);
}