Использование обратных вызовов для вложенных и повторяющихся полей в protobuf с использованием nanopb в c
* Редактировать: обновлено * Мое сообщение определено как:
message Repeat {
int32 inum = 1;
float fnum = 2;
}
message NotSimpleMessage {
repeated Repeat repeat = 1;
}
Я пытаюсь написать декодер и кодировщик, используя опцию обратного вызова. Я думаю, что мое кодирование работает нормально, но мой декодер не работает. Мой код: определения:
typedef struct{
Repeat rep[MAX_NUMBERS];
int32_t numbers_count;
}Messer;
typedef struct{
Mess mess[MAX_NUMBERS];
int32_t numbers_count;
}MessList;
void mess_add_number(MessList * list, int32_t inum, float fnum)
{
if (list->numbers_count < MAX_NUMBERS)
{
(list->mess[list->numbers_count]).inumber = inum;
(list->mess[list->numbers_count]).fnumber = fnum;
list->numbers_count++;
}
}
void messer_add_number(Messer * list, int32_t inum, float fnum)
{
if (list->numbers_count < MAX_NUMBERS)
{
(list->rep[list->numbers_count]).inum = inum;
(list->rep[list->numbers_count]).fnum = fnum;
(list->rep[list->numbers_count]).has_inum = true;
(list->rep[list->numbers_count]).has_fnum = true;
list->numbers_count++;
}
}
Функции кодера / декодера:
bool NestedMessage_encode_numbers(pb_ostream_t *ostream, const pb_field_t *field, void * const *arg)
{
Messer * source = (Messer*)(*arg);
int i;
// encode all numbers
for ( i = 0; i < source->numbers_count; i++)
{
if (!pb_encode_tag_for_field(ostream, field))
{
const char * error = PB_GET_ERROR(ostream);
printf("SimpleMessage_encode_numbers error: %s\n", error);
return false;
}
if (!pb_encode_submessage(ostream, Repeat_fields, &(source->rep[i])))
{
const char * error = PB_GET_ERROR(ostream);
printf("SimpleMessage_encode_numbers error: %s\n", error);
return false;
}
}
return true;
c}
bool NestedMessage_decode_numbers(pb_istream_t *istream, const pb_field_t *field, void **arg)
{
MessList * dest = (MessList*)(*arg);
Repeat rep;
// decode single number
Mess decmess;
printf("decoding...\n");
if (!pb_decode(istream, Repeat_fields ,&rep))
{
const char * error = PB_GET_ERROR(istream);
printf("decode error: %s\n", error);
return false;
}
// add to destination list
mess_add_number(dest, rep.inum, rep.fnum);
return true;
}
и главное это:
int main(void) {
uint8_t buffer[128];
size_t total_bytes_encoded = 0;
// encoding
// prepare the actual "variable" array
Messer actualData = { 0 };
messer_add_number(&actualData, 123, 1.2);
messer_add_number(&actualData, 456, 2.3);
messer_add_number(&actualData, 789, 3.4);
printf("Size: %d\n",actualData.numbers_count);
printf("data to be encoded: %d - %f, %d-%f, %d-%f\n",actualData.rep[0].inum,actualData.rep[0].fnum,
actualData.rep[1].inum, actualData.rep[1].fnum,
actualData.rep[2].inum,actualData.rep[2].fnum);
// prepare the nanopb ENCODING callback
NotSimpleMessage msg = NotSimpleMessage_init_zero;
msg.repeat.arg = &actualData;
msg.repeat.funcs.encode = NestedMessage_encode_numbers;
// call nanopb
pb_ostream_t ostream = pb_ostream_from_buffer(buffer, sizeof(buffer));
if (!pb_encode(&ostream, NotSimpleMessage_fields, &msg))
{
const char * error = PB_GET_ERROR(&ostream);
printf("pb_encode error: %s\n", error);
return EXIT_FAILURE;
}
total_bytes_encoded = ostream.bytes_written;
printf("Encoded size: %d\n", total_bytes_encoded);
// decoding
// empty array for decoding
Messer decodedData = { 0 };
// prepare the nanopb DECODING callback
NotSimpleMessage msgdec = NotSimpleMessage_init_zero;
msgdec.repeat.arg = &decodedData;
msgdec.repeat.funcs.decode = NestedMessage_decode_numbers;
// call nanopb
pb_istream_t istream = pb_istream_from_buffer(buffer, total_bytes_encoded);
if (!pb_decode(&istream, NotSimpleMessage_fields, &msgdec))
{
const char * error = PB_GET_ERROR(&istream);
printf("pb_decode error: %s", error);
return EXIT_FAILURE;
}
printf("Bytes decoded: %d\n", total_bytes_encoded - istream.bytes_left);
printf("decoded data: %d - %f, %d-%f, %d-%f\n",decodedData.rep[0].inum,decodedData.rep[0].fnum,
decodedData.rep[1].inum, decodedData.rep[1].fnum,
decodedData.rep[2].inum,decodedData.rep[2].fnum);
}
вывод, который я получаю:
Размер: 3 данных для кодирования: 123 - 1.200000, 456-2.300000, 789-3.400000 Кодированный размер: 29 Байт: 1 декодированные данные: 0 - 0.000000, 0-0.000000, 0-0.000000
печать закодированного буфера:
0a07087b15ffffff9affffff99ffffff993f0a0808ffffffc80315333313400a0808ffffff950615ffffff9affffff995940
Я пробовал разные структуры внутри декодера, но он просто не работает. Я уверен, что это какая-то глупая маленькая вещь, по которой я скучаю, но я ничего не понимаю об этом.
2 ответа
Ах, есть небольшая ошибка в кодировании / декодировании подсообщений в обратных вызовах.
При декодировании, pb_decode()
работает нормально, потому что тег и длина submessage уже были проанализированы nanopb. Однако при кодировании длину сообщения необходимо рассчитывать и кодировать отдельно. Так что вместо pb_encode()
нужно использовать pb_encode_submessage()
Вот:
if (!pb_encode_submessage(ostream, Repeat_fields, &(source->rep[i])))
{
const char * error = PB_GET_ERROR(ostream);
printf("SimpleMessage_encode_numbers error: %s\n", error);
return false;
}
(Для справки, вот соответствующая часть примера.)
-
Что касается вашего обновления, это шестнадцатеричный текст:
0a07087b15ffffff9affffff99ffffff993f0a0808ffffffc80315333313400a0808ffffff950615ffffff9affffff995940
несколько поврежден, потому что ваша функция печати, кажется, печатает "ffffff9a" вместо просто "9a". Возможно, подписанный актерский состав неподписанного ведет себя неожиданно. Но это можно исправить с помощью простого поиска и замены, который дает:
0a07087b159a99993f0a0808c80315333313400a08089506159a995940
Расшифровка этого с помощью protoc:
echo 0a07087b159a99993f0a0808c80315333313400a08089506159a995940 | xxd -r -p | protoc --decode=NotSimpleMessage test.proto
дает:
repeat {
inum: 123
fnum: 1.2
}
repeat {
inum: 456
fnum: 2.3
}
repeat {
inum: 789
fnum: 3.4
}
Кажется, теперь ваша кодировка работает правильно.
Не уверен, что вызывает декодирование так рано (только 1 байт) без сообщения об ошибке. Может быть, попробуйте пройти через это с помощью отладчика и посмотреть, что происходит. Одной из причин может быть, если данные в буфере каким-то образом повредятся перед вызовом декодирования, но не может понять, почему это произойдет.
В этих определениях какая структура есть у Repeat и Mess? Важно попытаться скомпилировать.