использование обратных вызовов для вложенных и повторяющихся полей в 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

Я пробовал несколько разных структур внутри декодера, но это просто не работает. почти уверен, что я упускаю какую-то тупую мелочь, но я понятия не имею об этом.


person me and stuff    schedule 04.09.2017    source источник
comment
Вы пробовали отлаживать код? Вы можете использовать флаг -g в компиляторе gcc, если вы используете?   -  person danglingpointer    schedule 04.09.2017
comment
Я использую затмение. debug показывает, что !pb_decode(istream, Repeat_fields ,&rep) возвращает false. дальнейшая отладка не показывает, где именно происходит сбой.   -  person me and stuff    schedule 04.09.2017
comment
Что ж, трудно сказать, что происходит, но, похоже, это так далеко 0a 07 08 7b 15 ff ff ff 9a. Это единственный экземпляр правильно закодированного вложенного сообщения. Но тогда следующий байт 0xFF не имеет смысла.   -  person jpa    schedule 04.09.2017
comment
А, у вас, вероятно, ошибка в вашей шестнадцатеричной печати. Вместо 9a печатается ff ff ff 9a.   -  person jpa    schedule 04.09.2017


Ответы (1)


Ах, есть небольшая ошибка в кодировании / декодировании вложенных сообщений в обратных вызовах.

При декодировании pb_decode() работает нормально, потому что тег и длина вложенного сообщения уже были проанализированы 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

Расшифровывая это с помощью протокола:

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 байт) без сообщения об ошибке. Может быть, попробуйте пройти через это с помощью отладчика и посмотреть, что происходит. Одна из причин может заключаться в том, что данные в буфере каким-то образом будут повреждены перед вызовом декодирования, но невозможно понять, почему это могло произойти.

person jpa    schedule 04.09.2017
comment
Я перешел на pb_encode_submessage b, но он по-прежнему не кодируется. Я могу с уверенностью сказать, что он никогда не входит в обратный вызов декодирования, поскольку printf никогда не печатает ... - person me and stuff; 04.09.2017
comment
@ItayPupko Попробуйте распечатать сообщение в шестнадцатеричном формате и использовать protoc --decode_raw или yura415.github.io/ js-protobuf-encode-decode для его декодирования. - person jpa; 04.09.2017
comment
Хорошо, теперь я знаю, что моя проблема в кодировщике. онлайн-инструмент не может его расшифровать. так что я делаю не так? - person me and stuff; 04.09.2017
comment
@ItayPupko Можете ли вы обновить вопрос напечатанными данными? - person jpa; 04.09.2017
comment
Отредактировано. добавлены новые принты. - person me and stuff; 04.09.2017
comment
он не входит в мою функцию обратного вызова декодирования. в функции pb_decode_noinit первый if за время возвращает false и eof. так что он получает eof при первом чтении. Но почему? - person me and stuff; 04.09.2017