Существует ли портативная схема двоичной сериализации в FlatBuffers / Protobuf, которая поддерживает произвольные 24-битные целочисленные определения со знаком? - PullRequest
2 голосов
/ 16 января 2020

Мы отправляем данные через последовательный порт UART с высокой скоростью передачи данных, поэтому размер данных важен. Наиболее оптимальным форматом для наших данных является Int24, который может быть упрощен как C структура битового поля (компилятор G CC) в C / C ++, чтобы быть абсолютно оптимальным:

#pragma pack(push, 1)
struct Int24
{
    int32_t value : 24;
};
#pragma pack(pop)

typedef std::array<Int24,32> ArrayOfInt24;

Эти данные упакованы с другими данными и совместно используются устройствами и облачными инфраструктурами. По сути, нам нужна двоичная сериализация, которая отправляется между устройствами разной архитектуры и языков программирования. Мы хотели бы использовать двоичную сериализацию на основе схемы, такую ​​как ProtoBuffers или FlatBuffers, чтобы избежать того, чтобы клиентским кодам приходилось обрабатывать соответствующие сдвиги битов и восстанавливать обработку битов знака двойного дополнения. т. е. для чтения 24-битного значения на языке, отличном от C, требуется следующее:

bool isSigned = (_b2 & (byte)0x80) != 0; // Sign extend negative quantities 
int32_t value = _b0 | (_b1 << 8) | (_b2 << 16) | (isSigned ? 0xFF : 0x00) << 24;

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

Ответы [ 4 ]

2 голосов
/ 16 января 2020

В зависимости от различных факторов, вы можете посмотреть ASN.1 и невыровненные правила упакованного кодирования (uPER). Это двоичная сериализация, которая широко используется в телефонии, чтобы легко минимизировать количество передаваемых битов. Инструменты доступны для C, C ++, C#, Java, Python (я думаю, что они охватывают UPER). Хорошая отправная точка - Полезные старые технологии .

Одна из причин, по которой вы можете использовать его, заключается в том, что UPER, вероятно, в конечном итоге добивается большего успеха, чем все остальное. Другие преимущества - это ограничения (по значениям и размерам массивов). Вы можете express указать их в своей схеме, и сгенерированный код проверит данные по ним Это то, что может иметь реальное значение для проекта - автоматическая c очистка входящих данных - отличный способ противостоять атакам - и это то, чего не делает GPB.

Причины не использовать его являются то, что самые лучшие инструменты являются коммерческими и довольно дорогими. Хотя есть некоторые инструменты с открытым исходным кодом, которые довольно хороши, но не обязательно реализуют весь стандарт ASN.1 (который является обширным). Это также кривая обучения, хотя (на базовом уровне c) не так сильно отличается от буферов протокола Google. Фактически, на конференции, где Google анонсировал GPB, кто-то спросил: «Почему бы не использовать ASN.1?». Бод Google не слышал об этом; несколько иронично c, поисковая компания, которая не ищет в Интернете технологии двоичной сериализации, пошла дальше и изобрела свою собственную ...

2 голосов
/ 16 января 2020

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

В вашем случае, если есть какая-либо корреляция между последовательными значениями, вы могли бы есть поле repeated sint32 values. Затем в качестве первой записи в массиве запишите первое значение. Для всех последующих записей запишите разницу от предыдущего значения.

Таким образом, например, [100001, 100050, 100023, 95000] будет закодировано как [100001, 49, -27, -5023]. В качестве упакованного массива varint дельты будут занимать 3, 1, 1 и 2 байта, всего 7 байтов. По сравнению с фиксированным 24-битным кодированием, занимающим 12 байтов, или недельта-вариацией, также занимающей 12 байтов.

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

2 голосов
/ 16 января 2020

Буферы протокола используют целочисленную кодировку динамического размера, называемую varint , поэтому вы можете просто использовать uint32 или sint32, и закодированное значение будет четыре байта или меньше для всех значений и три байта или меньше для любого значения <2 ^ 21 (фактический размер для закодированного целого числа равен ⌈HB / 7⌉, где HB - старший бит, установленный в значении). </p>

Убедитесь, что int32 не используется как таковой использует очень неэффективную кодировку фиксированного размера (10 байт!) для отрицательных значений. Для повторяющихся значений просто пометьте их как повторяющиеся, чтобы несколько значений были эффективно отправлены упаковано .

syntax = "proto3";

message Test {
  repeated sint32 data = 1;
}
1 голос
/ 16 января 2020

FlatBuffers не поддерживает 24-битные целые числа. Единственный способ представить это будет что-то вроде:

struct Int24 { a:ubyte; b:ubyte; c:ubyte; }

, который, очевидно, не выполняет сдвиг битов для вас, но все же позволит вам упаковать несколько Int24 вместе в родительский вектор или структурировать эффективно. Это также сэкономит байт при хранении в таблице, хотя там вам, вероятно, будет лучше использовать только 32-разрядное целое число, поскольку издержки выше.

...