Exemplo n.º 1
0
/*!
 * 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;
  }
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);
  }
Exemplo n.º 3
0
/*!
 * Create a submessage within a message for a specific tag.
 *
 * \param[in,out] message Message
 * \param[in]     tag     Tag
 * \return                Submessage
 */
extern pb_message_t
pb_message_create_within(pb_message_t *message, pb_tag_t tag) {
  assert(message && tag);
  if (pb_message_valid(message)) {
    const pb_field_descriptor_t *descriptor =
      pb_descriptor_field_by_tag(message->descriptor, tag);
    assert(descriptor &&
      pb_field_descriptor_type(descriptor) == PB_TYPE_MESSAGE &&
      pb_field_descriptor_nested(descriptor));
    pb_message_t submessage = {
      .descriptor = pb_field_descriptor_nested(descriptor),
      .part       = pb_part_create(message, tag)
    };
    return submessage;
  }
  return pb_message_create_invalid();
}

/*!
 * Create a submessage within a nested message for a branch of tags.
 *
 * \warning All messages on the branch of tags except the leaf may not occur
 * repeatedly, but must be defined as optional or required.
 *
 * \param[in,out] message Message
 * \param[in]     tags[]  Tags
 * \param[in]     size    Tag count
 * \return                Submessage
 */
extern pb_message_t
pb_message_create_nested(
    pb_message_t *message, const pb_tag_t tags[], size_t size) {
  assert(message && tags && size);
  pb_message_t prev = pb_message_create_invalid(),
               this = pb_message_copy(message);
  for (size_t t = 0; t < size && pb_message_valid(&this); ++t) {
    pb_message_destroy(&prev);

#ifndef NDEBUG

    /* Assert non-repeated non-leaf messages */
    const pb_field_descriptor_t *descriptor =
      pb_descriptor_field_by_tag(this.descriptor, tags[t]);
    assert(descriptor && (
      pb_field_descriptor_type(descriptor)  == PB_TYPE_MESSAGE && (
      pb_field_descriptor_label(descriptor) != PB_LABEL_REPEATED ||
        t == size - 1)));

#endif /* NDEBUG */

    /* Swap messages and create a new one within */
    prev = this; this = pb_message_create_within(&prev, tags[t]);
  }
  pb_message_destroy(&prev);
  return this;
}
Exemplo n.º 4
0
/*!
 * 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
    };