/*! * 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); }
/*! * Create a cursor over a message. * * The cursor will halt on every occurrence of a field, even though a field is * declared optional or required. Internally, the tag may also be initialized * to zero, in which case the cursor will halt on every field. This function is * only meant for internal use (e.g. by pb_message_erase()). * * \param[in,out] message Message * \param[in] tag Tag * \return Cursor */ extern pb_cursor_t pb_cursor_create_unsafe(pb_message_t *message, pb_tag_t tag) { assert(message); if (pb_message_valid(message) && !pb_message_align(message)) { const pb_field_descriptor_t *descriptor = tag ? pb_descriptor_field_by_tag(pb_message_descriptor(message), tag) : NULL; pb_cursor_t cursor = { .message = pb_message_copy(message), .tag = tag, .current = { .descriptor = descriptor, .offset = { .start = pb_message_start(message), .end = pb_message_start(message), .diff = { .origin = 0, .tag = 0, .length = 0 } } }, .pos = SIZE_MAX, /* = uninitialized */ .error = PB_ERROR_NONE }; if (!pb_cursor_next(&cursor)) cursor.pos = 0; return cursor; }
/*! * Create a cursor over a message. * * Internally, the tag may also be initialized to zero, in which case the * cursor will halt on every field. The cursor position is initialized with * the maximum offset for the subsequent call to pb_cursor_next(), so that * the offset will simply wrap around. * * \param[in,out] message Message * \param[in] tag Tag * \return Cursor */ extern pb_cursor_t pb_cursor_create_internal(pb_message_t *message, pb_tag_t tag) { assert(message); if (pb_message_valid(message) && !pb_message_align(message)) { pb_cursor_t cursor = { .message = pb_message_copy(message), .tag = tag, .current = { .tag = 0, .offset = { .start = pb_message_start(message), .end = pb_message_start(message), .diff = { .origin = 0, .tag = 0, .length = 0 } } }, .pos = SIZE_MAX, /* = uninitialized */ .error = PB_ERROR_NONE };