/*! * Create a cursor over a message for a specific tag. * * This is the normal way of creating a cursor. If the cursor is created for * an optional or required field, it is ensured that the cursor points to the * last occurrence, which is the active/visible value. This is exactly the way * it is demanded by the Protocol Buffers specification. * * Furthermore, if the tag is part of a oneof and the tag exists, it is ensured * that the tag is the currently active/visible part of the oneof. * * \warning After creating a cursor, it is mandatory to check its validity * with the macro pb_cursor_valid(). * * \param[in,out] message Message * \param[in] tag Tag * \return Cursor */ extern pb_cursor_t pb_cursor_create(pb_message_t *message, pb_tag_t tag) { assert(message && tag); pb_cursor_t cursor = pb_cursor_create_unsafe(message, tag); if (pb_cursor_valid(&cursor)) { const pb_field_descriptor_t *descriptor = cursor.current.descriptor; /* If the field is non-repeated, move the cursor to the last occurrence */ if (pb_field_descriptor_label(descriptor) != PB_LABEL_REPEATED) { pb_cursor_t temp = pb_cursor_copy(&cursor); while (pb_cursor_next(&temp)) { if (tag == pb_cursor_tag(&temp)) { pb_cursor_destroy(&cursor); cursor = pb_cursor_copy(&temp); } } pb_cursor_destroy(&temp); /* If the tag is part of a oneof ensure it is the active tag */ if (pb_field_descriptor_label(descriptor) == PB_LABEL_ONEOF) { pb_cursor_t temp = pb_cursor_copy(&cursor); temp.tag = 0; while (pb_cursor_next(&temp)) { int member = pb_field_descriptor_oneof(descriptor) == pb_field_descriptor_oneof(cursor.current.descriptor); if (member && (cursor.error = PB_ERROR_EOM)) break; } /* LCOV_EXCL_LINE */ pb_cursor_destroy(&temp); } } } return cursor; }
/*! * 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 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); }
} END_TEST /* * Create a part at the current position of a cursor. */ START_TEST(test_create_from_cursor) { pb_binary_t binary = pb_binary_create_empty(); pb_message_t message = pb_message_create(&descriptor, &binary); /* Create a hundred part siblings and write values to them */ for (size_t p = 1; p < 101; p++) { pb_part_t part = pb_part_create(&message, 10); ck_assert_uint_eq(PB_ERROR_NONE, pb_part_write(&part, (uint8_t *)&p, 1)); /* Free all allocated memory */ pb_part_destroy(&part); } /* Create cursor */ pb_cursor_t cursor = pb_cursor_create(&message, 10); /* Assert cursor validity and error */ fail_unless(pb_cursor_valid(&cursor)); ck_assert_uint_eq(PB_ERROR_NONE, pb_cursor_error(&cursor)); /* Walk through parts */ for (size_t p = 1; p < 101; p++, pb_cursor_next(&cursor)) { pb_part_t part = pb_part_create_from_cursor(&cursor); /* Assert part validity and error */ fail_unless(pb_part_valid(&part)); ck_assert_uint_eq(PB_ERROR_NONE, pb_part_error(&part)); /* Assert part size and version */ fail_if(pb_part_empty(&part)); ck_assert_uint_eq(1, pb_part_size(&part)); ck_assert_uint_eq(200, pb_part_version(&part)); /* Assert part offsets */ ck_assert_uint_eq(p * 2 - 1, pb_part_start(&part)); ck_assert_uint_eq(p * 2, pb_part_end(&part)); /* Free all allocated memory */ pb_part_destroy(&part); } /* Assert cursor validity and error */ fail_if(pb_cursor_valid(&cursor)); ck_assert_uint_eq(PB_ERROR_OFFSET, pb_cursor_error(&cursor)); /* Free all allocated memory */ pb_cursor_destroy(&cursor); pb_message_destroy(&message); pb_binary_destroy(&binary); } END_TEST