Exemplo n.º 1
0
/*!
 * 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;
}
Exemplo n.º 2
0
/*!
 * 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);
  }