Я наткнулся на nanopb и хочу использовать его в своем проекте. Я пишу код для встроенного устройства, поэтому ограничения памяти вызывают реальную озабоченность.
Моя цель — передавать элементы данных с устройства на устройство, каждый элемент данных имеет 32-битный идентификатор и значение. Значение может быть любым, от 1 символа до длинной строки. Мне интересно, что было бы наиболее эффективным способом объявления сообщений для этого типа проблемы.
Я думал примерно так:
message data_msg{
message data_item{
int32 id = 1;
oneof value{
int8 ival = 2;
float fval = 3;
string sval = 4;
}
}
repeated data_item;
}
Но, как я понял, это преобразуется в объединение C, которое соответствует размеру самого большого элемента. Скажем, я ограничиваю строку до 50 символов, тогда объединение всегда имеет длину 50 байт, даже если мне потребуется 4 байта для числа с плавающей запятой.
Я правильно понял это или есть какой-то другой способ сделать это?
Спасибо!
Вы правильно поняли, размер структуры в C будет равен размеру наибольшего члена oneof. Однако это только размер в памяти, размер сообщения после сериализации будет минимально необходимым для содержимого.
Если размер в памяти является проблемой, есть несколько доступных вариантов. По умолчанию выделяется максимально возможный необходимый размер, что упрощает управление памятью. Если вы хотите динамически выделять только необходимый объем памяти, вам придется решить, как вы хотите это сделать:
Используя опцию компиляции PB_ENABLE_MALLOC
, вы можете использовать тип поля FT_POINTER
для строки и других больших полей. Затем память будет выделена с помощью malloc()
из системной кучи.
С FT_CALLBACK
вместо выделения какой-либо памяти вы получите обратный вызов, в котором вы можете прочитать строку и обработать или сохранить ее любым удобным для вас способом. Например, если вы хотите записать строку на SD-карту, вы можете сделать это, даже не сохраняя ее полностью в памяти.
В общем дизайне системы статическое распределение для максимального требуемого размера часто проще всего проверить. Если данные подходят один раз, они будут соответствовать всегда. Если вы выберете динамическое распределение, вам нужно будет более тщательно проанализировать, каково максимально возможное использование памяти.
@OuTsei По умолчанию необязательные поля также непосредственно выделяются в структуре. Если они настроены указатели (тип = FT_POINTER), это означает, что память выделяется отдельно, как описано в этом ответе.
Так что, может быть, вместо этого иметь отдельную спецификацию data_item для всех типов данных (float, int string и т. д.) и иметь повторяющуюся структуру для всех отдельно?
@OuTsei И где вы собираетесь взять память для этой повторяющейся структуры, если ее размер непостоянен? Это всегда будет заканчиваться либо статическим выделением максимального размера, либо динамическим выделением. Вы можете структурировать динамическое размещение разными способами, но это не изменит его основных свойств, таких как более сложное тестирование.
Как насчет того, чтобы использовать только кучу необязательных полей вместо одного? Я предполагаю, что после сериализации это не будет иметь значения, но в памяти будет несколько ненужных нулевых указателей для каждого элемента данных, занимающего место, верно?