Payload
Payload
представляет собой структуру индексированных полей фиксированного типа.
Внутренний API Payload
представлен тремя классами верхнего уровня:
-
PayloadValue
— shared-копия структуры полей записи. Размер структуры равен сумме размера полей + 16 байт заголовка. -
PayloadType
— определение структурыPayload
. Содержит вектор полей (имена, типы, размеры, смещения). На один неймспейс существует только одна копияPayloadType
. Она изменяема и потокобезопасна. Изменяемость и потокобезопасность обеспечиваются за счёт COW-семантики. При изменении или добавлении полей создаётся новая копияPayloadType
. -
PayloadIface
— шаблон интерфейса для управленияPayload
. -
Выполняет всю низкоуровневую работу над
PayloadValue
. Содержит указатели наPayloadValue
иPayloadType
. Существует в двух экземплярах:Payload
— может изменятьPayloadValue
.ConstPayload
— интерфейс только для чтения, не может изменятьPayloadValue
.
API Payload
схож с обычным Reflection API
и содержит методы Set
и Get
для управления полями (см. payloadiface.h для получения подробной информации).
Структура PayloadValue
PayloadValue
в каждом объекте Payload
имеет следующую структуру:
Поле: | RefCount | Cap | LSN | Поле 1 | … | Поле N | Вложенные массивы |
---|---|---|---|---|---|---|---|
Размер в байтах: | 4 | 4 | 8 | Зависит от типа данных | Зависит от типа данных | Зависит от типа данных |
Формат хранения данных полей PayloadValue
Тривиальные типы данных, такие как int32
, int64
, double
, хранятся в PayloadValue
в нативном формате:
Поле | Поле с содержимым с типом значения int |
Поле с содержимым с типом значения int64 |
Поле с содержимым с типом значения double |
Поле с содержимым с типом значения string |
---|---|---|---|---|
Размер в байтах | 4 | 8 | 8 | 8 |
Для хранения строк используется 8-байтовый слабый указатель на строку reindexer::p_string
.
Важно, что само значение PayloadValue
не содержит строки.
p_string
p_string — это оболочка указателя. Она использует 59-й, 60-й и 61-й биты указателя в качестве тега строкового типа (предполагается, что размер адресного пространства x86_64 равен 2^48: https://en.wikipedia.org/wiki/X86-64#Virtual_address_space_details).
Тег | Объект, на который он указывает |
---|---|
0 | С-подобная null-terminated строка |
1 | 4-байтовый заголовок строки (len), за которым следует массив символов строки |
2 | std::string |
3 | Заголовок (varint len), за которым следует массив символов строки |
4 | std::string_view |
5 | reindexer::key_string |
6 | json-строка. Указывает на 3 байта длины. Если в 3-м байте старший бит == 0 , эти 3 байта и есть длина строки, а сама строка лежит в памяти непосредственно перед длиной (начало по адресу ptr - len). Это строки менее 8 Мб.Если в 3-м байте старший бит == 1 , то у этой строки длина закодирована 4 байтами (ptr-1, ptr, ptr+1 и ptr+2). Перед этими 4-мя байтами лежат 8 байт, где закодирован указатель на саму строку |
7 | msgpack-строка. Содержит указатель на l_msgpack_hdr (4 байта на длину + указатель на строку) |
key_string
reindexer::key_string
является производным от std::string
и, кроме того, содержит счетчик ссылок и экспортированный заголовок с полями указатель
+ размер
.
Размер в байтах | Поле |
---|---|
Меняется в зависимости от stl | std::string |
8 | Экспортированный указатель на массив символов строки |
4 | Экспортированная длина строки |
4 | Счетчик ссылок |
Экспортированный заголовок используется для прямого доступа к Payload
из приложения, написанного не на c++ (например, когда Reindexer — часть golang-приложения).
Данные в key_string
доступны для изменения.
Хранение массивов в PayloadValue
Массивы хранятся в виде заголовка массива (ArrayHeader
), который содержит 4-байтовое начальное смещение, и 4-байтового количества элементов в массиве.
Поле | Поле 1 Массив Смещение + количество элементов |
Поле N | Массив 1 элемент 1 |
… | Массив N элемент N |
---|---|---|---|---|---|
Размер в байтах | 4+4 | Зависит от типа данных | Зависит от типа данных | Зависит от типа данных |
Пример структуры:
struct
{
int64_t f1 = 5;
int f2[] = {6,7,8};
int f3 = 10;
string f4 = "abc";
}
PayloadValue
для данного примера:
Поле | Размер | Смещение | Значение |
---|---|---|---|
f1 | 8 | 0 | int64_t(5) |
f2.offset | 4 | 8 | uint32_t(28) |
f2.count | 4 | 12 | int32_t(3) |
f3 | 4 | 16 | int32_t(10) |
f4 | 8 | 20 | &string(abc) |
f2[0] | 4 | 28 | int32_t(6) |
f2[1] | 4 | 32 | int32_t(7) |
f2[2] | 4 | 36 | int32_t(8) |
Жизненный цикл и владение
Поведение PayloadValue
похоже на поведение умного указателя std::shared_ptr
. PayloadValue
увеличивает внутренний счетчик ссылок при копировании, уменьшает этот счетчик в деструкторе и удаляет хранимые данные, если значение счетчика ссылок равно 0
. Счетчик ссылок PayloadValue
потокобезопасен.
PayloadValue
может быть в одном из трех состояний:
Free
: структура данных не выделена.Non Shared
: данные структуры принадлежат исключительноPayloadValue
.Shared
: данные структуры совместно используются с другимиPayloadValue
.
PayloadValue
в состоянии Shared
не допускает модификации.
Перед любой модификацией код должен создать его копию путем вызова PayloadValue::Clone ()
.
Clone
проверит состояние PayloadValue
и при необходимости выделит новую структуру либо создаст эксклюзивную копию.
PayloadValue
не владеет своими строками по умолчанию.
Для контроля владения строками существуют 2 метода: PayloadIface::AddRefStrings ()
и PayloadIface::ReleaseStrings ()
.
AddRefStrings
иReleaseStrings
не работают по идиоме RAII, поэтому код должен сам контролировать баланс их вызовов. Если код не вызоветReleaseStrings
, возникнет утечка памяти.
Пример типового использования
PayloadType type = ns->payloadType_;
PayloadValue value = ns->items_[index];
// Create control object
ConstPayload payload(type, value);
VariantArray keyRefs;
// Dump all fields to stdout
for (int i = 0; i < payload.NumFields(); i++) {
auto &field = payload.Type().Field(i);
printf("\n%s=", field.Name().c_str());
for (auto &elem : payload.Get (i,keyRefs)) {
printf("%s", Variant(elem).toString().c_str());
}
}