/*! * Create a part from a message for a specific tag. * * This method returns a part to read or alter a certain field or submessage of * a message. However, how and when a field or submessage is created in the * underlying binary depends on the label of the part, which is defined in the * message's descriptor. There are two cases: * * -# Required and optional parts are directly returned when they are already * contained in the message. Otherwise, they are created and returned empty. * * -# Repeated parts are always appended to the end of the list of already * contained parts with the same tag when created. Dedicated instances of * repeated parts can be accessed through cursors. * * At first we try to find the exact offset of the specified part. If there is * no exact offset, meaning the part does currently not exist, we record the * best matching offset, at which we would expect the part to reside. This is * just after the part with the previously smaller (or same) tag. * * There are a few possible scenarios to consider: * * -# The cursor is valid, the tag matches exactly and the message is not * repeated. In this case we found an existing part and can return it. * * -# The cursor is valid but the tag does not match exactly or the part is * repeated, so we create a new part. * * -# The cursor is not valid, in which case we're at the beginning of the * message, so we create a new part. * * When the second case is true, we know that the message is either empty, or * the part we're looking for does not exist. Therefore we create an empty part * and return it. * * \warning New parts always come without values, so only the tag (and length * prefix, if applicable) are present. However, empty fields are not valid and * not recognized by the parser (except empty strings), so the caller must * ensure that a value is set directly after creating a new part. * * \warning Merged messages are not implemented as demanded in the Protocol * Buffers standard. This would make it necessary to scan the entire binary * every time, as there may be overrides for required or optional fields. This * is orthogonal to the philosophy of this library, and is thus omitted. * * \warning The existence of the descriptor field is checked with an assertion, * as the tag is assumed to be specified at compile time. * * \param[in,out] message Message * \param[in] tag Tag * \return Part */ extern pb_part_t pb_part_create(pb_message_t *message, pb_tag_t tag) { assert(message && tag); if (pb_message_valid(message) && !pb_message_align(message)) { const pb_field_descriptor_t *descriptor = pb_message_descriptor_field_by_tag(pb_message_descriptor(message), tag); assert(descriptor); /* Determine exact or best matching field offset */ pb_cursor_t cursor = pb_cursor_create_without_tag(message); pb_cursor_t temp = pb_cursor_copy(&cursor); if (pb_cursor_valid(&temp)) { do { if (pb_cursor_tag(&temp) <= tag) { pb_cursor_destroy(&cursor); cursor = pb_cursor_copy(&temp); } } while (pb_cursor_next(&temp)); } /* Don't indicate an error, if the cursor just reached the end */ if (pb_cursor_valid(&temp) || pb_cursor_error(&temp) == PB_ERROR_OFFSET) { /* If the tag matches and the field is non-repeated, we're done */ size_t start = pb_message_start(message); if (pb_cursor_valid(&cursor)) { if (pb_cursor_tag(&cursor) == tag && pb_field_descriptor_label(descriptor) != PB_LABEL_REPEATED) { pb_part_t part = pb_part_create_from_cursor(&cursor); pb_cursor_destroy(&cursor); return part; } /* Otherwise correct insert vector in documented cases */ const pb_offset_t *offset = pb_cursor_current(&cursor); if (pb_cursor_pos(&cursor) || pb_cursor_tag(&cursor) <= tag) start = offset->end; } pb_cursor_destroy(&cursor); /* Create and return an empty part */ pb_part_t part = { .binary = pb_message_binary(message), .version = pb_message_version(message), .offset = { .start = start, .end = start, .diff = { .origin = pb_message_start(message) - start, .tag = 0, .length = 0 } } }; init(&part, descriptor); return part; } pb_cursor_destroy(&temp); }
/*! * Move a cursor to the next field. * * \param[in,out] cursor Cursor * \return Test result */ static int next(pb_cursor_t *cursor) { assert(cursor); pb_offset_t *offset = &(cursor->current.offset), *packed = &(cursor->current.packed); /* Create temporary buffer to read the next value */ pb_buffer_t buffer = pb_buffer_create_zero_copy_internal( pb_journal_data_from(pb_cursor_journal(cursor), 0), pb_message_end(&(cursor->message))); /* Create stream over temporary buffer */ pb_stream_t stream = pb_stream_create_at(&buffer, offset->end); while (pb_stream_left(&stream)) { /* Adjust offsets */ offset->start = offset->end; offset->diff.origin = pb_message_start(&(cursor->message)); offset->diff.tag = pb_stream_offset(&stream); /* Read tag from stream */ pb_tag_t tag; uint32_t length; if ((cursor->error = pb_stream_read(&stream, PB_TYPE_UINT32, &tag))) break; /* Extract wiretype and tag */ pb_wiretype_t wiretype = tag & 7; tag >>= 3; /* Skip field contents to determine length */ offset->diff.length = pb_stream_offset(&stream); if (wiretype == PB_WIRETYPE_LENGTH) { if ((cursor->error = pb_stream_read(&stream, PB_TYPE_UINT32, &length))) break; offset->start = pb_stream_offset(&stream); if ((cursor->error = pb_stream_advance(&stream, length))) break; } else { offset->start = pb_stream_offset(&stream); if ((cursor->error = pb_stream_skip(&stream, wiretype))) break; } /* Adjust offsets */ offset->end = pb_stream_offset(&stream); offset->diff.origin -= offset->start; offset->diff.tag -= offset->start; offset->diff.length -= offset->start; /* If a tag is set check if the tags match or continue */ if (cursor->tag && cursor->tag != tag) { continue; /* Otherwise try to load descriptor for current tag */ } else if (!cursor->current.descriptor || pb_field_descriptor_tag(cursor->current.descriptor) != tag) { if (!(cursor->current.descriptor = pb_descriptor_field_by_tag( pb_message_descriptor(&(cursor->message)), tag))) continue; } /* Switch to packed context in case of packed field */ if (wiretype != pb_field_descriptor_wiretype(cursor->current.descriptor) && wiretype == PB_WIRETYPE_LENGTH) { *packed = *offset; /* Prepare offsets for packed field members */ offset->end = offset->start; offset->diff.tag = 0; offset->diff.length = 0; } /* Cleanup and return */ pb_stream_destroy(&stream); pb_buffer_destroy(&buffer); return !packed->end; } /* Invalidate cursor if at end */ if (!(pb_stream_left(&stream) && cursor->error)) cursor->error = PB_ERROR_EOM; /* Cleanup and return */ pb_stream_destroy(&stream); pb_buffer_destroy(&buffer); return 0; }