示例#1
0
static void advance(unsigned char **cursor, qd_buffer_t **buffer, int consume, buffer_process_t handler, void *context)
{
    unsigned char *local_cursor = *cursor;
    qd_buffer_t   *local_buffer = *buffer;

    int remaining = qd_buffer_size(local_buffer) - (local_cursor - qd_buffer_base(local_buffer));
    while (consume > 0) {
        if (consume < remaining) {
            if (handler)
                handler(context, local_cursor, consume);
            local_cursor += consume;
            consume = 0;
        } else {
            if (handler)
                handler(context, local_cursor, remaining);
            consume -= remaining;
            local_buffer = local_buffer->next;
            if (local_buffer == 0){
                local_cursor = 0;
                break;
            }
            local_cursor = qd_buffer_base(local_buffer);
            remaining = qd_buffer_size(local_buffer) - (local_cursor - qd_buffer_base(local_buffer));
        }
    }

    *cursor = local_cursor;
    *buffer = local_buffer;
}
示例#2
0
ssize_t qd_message_field_copy(qd_message_t *msg, qd_message_field_t field, void *buffer, size_t *hdr_length)
{
    qd_field_location_t *loc = qd_message_field_location(msg, field);
    if (!loc)
        return -1;

    qd_buffer_t *buf       = loc->buffer;
    size_t       bufsize   = qd_buffer_size(buf) - loc->offset;
    void        *base      = qd_buffer_base(buf) + loc->offset;
    size_t       remaining = loc->length + loc->hdr_length;
    *hdr_length = loc->hdr_length;

    while (remaining > 0) {
        if (bufsize > remaining)
            bufsize = remaining;
        memcpy(buffer, base, bufsize);
        buffer    += bufsize;
        remaining -= bufsize;
        if (remaining > 0) {
            buf     = buf->next;
            base    = qd_buffer_base(buf);
            bufsize = qd_buffer_size(buf);
        }
    }

    return loc->length + loc->hdr_length;
}
示例#3
0
qd_iovec_t *qd_iterator_iovec(const qd_iterator_t *iter)
{
    if (!iter)
        return 0;

    //
    // Count the number of buffers this field straddles
    //
    pointer_t    pointer   = iter->view_start_pointer;
    int          bufcnt    = 1;
    qd_buffer_t *buf       = pointer.buffer;
    size_t       bufsize   = qd_buffer_size(buf) - (pointer.cursor - qd_buffer_base(pointer.buffer));
    ssize_t      remaining = pointer.remaining - bufsize;

    while (remaining > 0) {
        bufcnt++;
        buf = buf->next;
        if (!buf)
            return 0;
        remaining -= qd_buffer_size(buf);
    }

    //
    // Allocate an iovec object big enough to hold the number of buffers
    //
    qd_iovec_t *iov = qd_iovec(bufcnt);
    if (!iov)
        return 0;

    //
    // Build out the io vectors with pointers to the segments of the field in buffers
    //
    bufcnt     = 0;
    buf        = pointer.buffer;
    bufsize    = qd_buffer_size(buf) - (pointer.cursor - qd_buffer_base(pointer.buffer));
    void *base = pointer.cursor;
    remaining  = pointer.remaining;

    while (remaining > 0) {
        if (bufsize > remaining)
            bufsize = remaining;
        qd_iovec_array(iov)[bufcnt].iov_base = base;
        qd_iovec_array(iov)[bufcnt].iov_len  = bufsize;
        bufcnt++;
        remaining -= bufsize;
        if (remaining > 0) {
            buf     = buf->next;
            base    = qd_buffer_base(buf);
            bufsize = qd_buffer_size(buf);
        }
    }

    return iov;
}
示例#4
0
qd_field_iterator_t *qd_message_field_iterator(qd_message_t *msg, qd_message_field_t field)
{
    qd_field_location_t *loc = qd_message_field_location(msg, field);
    if (!loc)
        return 0;

    if (loc->tag == QD_AMQP_NULL)
        return 0;

    qd_buffer_t   *buffer = loc->buffer;
    unsigned char *cursor = qd_buffer_base(loc->buffer) + loc->offset;
    advance(&cursor, &buffer, loc->hdr_length, 0, 0);

    return qd_field_iterator_buffer(buffer, cursor - qd_buffer_base(buffer), loc->length);
}
示例#5
0
unsigned int qd_buffer_list_clone(qd_buffer_list_t *dst, const qd_buffer_list_t *src)
{
    uint32_t len = 0;
    DEQ_INIT(*dst);
    qd_buffer_t *buf = DEQ_HEAD(*src);
    while (buf) {
        size_t to_copy = qd_buffer_size(buf);
        unsigned char *src = qd_buffer_base(buf);
        len += to_copy;
        while (to_copy) {
            qd_buffer_t *newbuf = qd_buffer();
            size_t count = qd_buffer_capacity(newbuf);
            // default buffer capacity may have changed,
            // so don't assume it will fit:
            if (count > to_copy) count = to_copy;
            memcpy(qd_buffer_cursor(newbuf), src, count);
            qd_buffer_insert(newbuf, count);
            DEQ_INSERT_TAIL(*dst, newbuf);
            src += count;
            to_copy -= count;
        }
        buf = DEQ_NEXT(buf);
    }
    return len;
}
示例#6
0
static inline void field_iterator_move_cursor(qd_iterator_t *iter, uint32_t length)
{
    // Only safe to call this help method if the cursor is parsing the data,
    // i.e. if iter is an address iterator, the cursor must be 'past' the
    // prefix
    assert(iter->state == STATE_IN_BODY);
    uint32_t count = (length > iter->view_pointer.remaining) ? iter->view_pointer.remaining : length;

    if (iter->view_pointer.buffer) {
        while (count) {
            uint32_t remaining = qd_buffer_cursor(iter->view_pointer.buffer) - iter->view_pointer.cursor;
            remaining = (remaining > count) ? count : remaining;
            iter->view_pointer.cursor += remaining;
            iter->view_pointer.remaining -= remaining;
            count -= remaining;
            if (iter->view_pointer.cursor == qd_buffer_cursor(iter->view_pointer.buffer)) {
                iter->view_pointer.buffer = iter->view_pointer.buffer->next;
                if (iter->view_pointer.buffer == 0) {
                    iter->view_pointer.remaining = 0;
                    iter->view_pointer.cursor = 0;
                    break;
                } else {
                    iter->view_pointer.cursor = qd_buffer_base(iter->view_pointer.buffer);
                }
            }
        }
    } else {    // string/binary data
        iter->view_pointer.cursor    += count;
        iter->view_pointer.remaining -= count;
    }
}
示例#7
0
static int traverse_field(unsigned char **cursor, qd_buffer_t **buffer, qd_field_location_t *field)
{
    qd_buffer_t   *start_buffer = *buffer;
    unsigned char *start_cursor = *cursor;

    unsigned char tag = next_octet(cursor, buffer);
    if (!(*cursor)) return 0;

    int    consume    = 0;
    size_t hdr_length = 1;

    switch (tag & 0xF0) {
    case 0x40 : consume = 0;  break;
    case 0x50 : consume = 1;  break;
    case 0x60 : consume = 2;  break;
    case 0x70 : consume = 4;  break;
    case 0x80 : consume = 8;  break;
    case 0x90 : consume = 16; break;

    case 0xB0 :
    case 0xD0 :
    case 0xF0 :
        hdr_length += 3;
        consume |= ((int) next_octet(cursor, buffer)) << 24;
        if (!(*cursor)) return 0;
        consume |= ((int) next_octet(cursor, buffer)) << 16;
        if (!(*cursor)) return 0;
        consume |= ((int) next_octet(cursor, buffer)) << 8;
        if (!(*cursor)) return 0;
        // Fall through to the next case...

    case 0xA0 :
    case 0xC0 :
    case 0xE0 :
        hdr_length++;
        consume |= (int) next_octet(cursor, buffer);
        if (!(*cursor)) return 0;
        break;
    }

    if (field && !field->parsed) {
        field->buffer     = start_buffer;
        field->offset     = start_cursor - qd_buffer_base(start_buffer);
        field->length     = consume;
        field->hdr_length = hdr_length;
        field->parsed     = true;
        field->tag        = tag;
    }

    advance(cursor, buffer, consume, 0, 0);
    return 1;
}
示例#8
0
// get the field location of a field in the message properties (if it exists,
// else 0).
static qd_field_location_t *qd_message_properties_field(qd_message_t *msg, qd_message_field_t field)
{
    static const intptr_t offsets[] = {
        // position of the field's qd_field_location_t in the message content
        // object
        (intptr_t) &((qd_message_content_t *)0)->field_message_id,
        (intptr_t) &((qd_message_content_t *)0)->field_user_id,
        (intptr_t) &((qd_message_content_t *)0)->field_to,
        (intptr_t) &((qd_message_content_t *)0)->field_subject,
        (intptr_t) &((qd_message_content_t *)0)->field_reply_to,
        (intptr_t) &((qd_message_content_t *)0)->field_correlation_id
    };
    // update table above if new fields need to be accessed:
    assert(QD_FIELD_MESSAGE_ID <= field && field <= QD_FIELD_CORRELATION_ID);

    qd_message_content_t *content = MSG_CONTENT(msg);
    if (!content->section_message_properties.parsed) {
        if (!qd_message_check(msg, QD_DEPTH_PROPERTIES) || !content->section_message_properties.parsed)
            return 0;
    }
    if (field == QD_FIELD_PROPERTIES) return &content->section_message_properties;

    const int index = field - QD_FIELD_MESSAGE_ID;
    qd_field_location_t *const location = (qd_field_location_t *)((char *)content + offsets[index]);
    if (location->parsed)
        return location;

    // requested field not parsed out.  Need to parse out up to the requested field:
    qd_buffer_t   *buffer = content->section_message_properties.buffer;
    unsigned char *cursor = qd_buffer_base(buffer) + content->section_message_properties.offset;
    advance(&cursor, &buffer, content->section_message_properties.hdr_length, 0, 0);
    if (index >= start_list(&cursor, &buffer)) return 0;  // properties list too short

    int position = 0;
    while (position < index) {
        qd_field_location_t *f = (qd_field_location_t *)((char *)content + offsets[position]);
        if (f->parsed)
            advance(&cursor, &buffer, f->hdr_length + f->length, 0, 0);
        else // parse it out
            if (!traverse_field(&cursor, &buffer, f)) return 0;
        position++;
    }

    // all fields previous to the target have now been parsed and cursor/buffer
    // are in the correct position, parse out the field:
    if (traverse_field(&cursor, &buffer, location))
        return location;

    return 0;
}
示例#9
0
qd_iterator_t *qd_iterator_buffer(qd_buffer_t *buffer, int offset, int length, qd_iterator_view_t view)
{
    qd_iterator_t *iter = new_qd_iterator_t();
    if (!iter)
        return 0;

    ZERO(iter);
    iter->start_pointer.buffer    = buffer;
    iter->start_pointer.cursor    = qd_buffer_base(buffer) + offset;
    iter->start_pointer.remaining = length;
    iter->phase                   = '0';

    qd_iterator_reset_view(iter, view);

    return iter;
}
示例#10
0
qd_field_iterator_t *qd_address_iterator_buffer(qd_buffer_t *buffer, int offset, int length, qd_iterator_view_t view)
{
    qd_field_iterator_t *iter = new_qd_field_iterator_t();
    if (!iter)
        return 0;

    iter->start_pointer.buffer = buffer;
    iter->start_pointer.cursor = qd_buffer_base(buffer) + offset;
    iter->start_pointer.length = length;
    iter->phase                = '0';
    iter->prefix_override      = '\0';

    DEQ_INIT(iter->hash_segments);

    qd_address_iterator_reset_view(iter, view);

    return iter;
}
示例#11
0
const char *qd_parse_annotations_v1(
    bool                   strip_anno_in,
    qd_iterator_t         *ma_iter_in,
    qd_parsed_field_t    **ma_ingress,
    qd_parsed_field_t    **ma_phase,
    qd_parsed_field_t    **ma_to_override,
    qd_parsed_field_t    **ma_trace,
    qd_iterator_pointer_t *blob_pointer,
    uint32_t              *blob_item_count)
{
    // Do full parse
    qd_iterator_reset(ma_iter_in);

    qd_parsed_turbo_list_t annos;
    uint32_t               user_entries;
    uint32_t               user_bytes;
    const char * parse_error = qd_parse_turbo(ma_iter_in, &annos, &user_entries, &user_bytes);
    if (parse_error) {
        return parse_error;
    }

    qd_parsed_turbo_t *anno;
    if (!strip_anno_in) {
        anno = DEQ_HEAD(annos);
        while (anno) {
            qd_iterator_t *key_iter =
                qd_iterator_buffer(anno->bufptr.buffer,
                                anno->bufptr.cursor - qd_buffer_base(anno->bufptr.buffer),
                                anno->size,
                                ITER_VIEW_ALL);
            assert(key_iter);

            qd_parsed_field_t *key_field = qd_parse(key_iter);
            assert(key_field);

            qd_iterator_t *iter = qd_parse_raw(key_field);
            assert(iter);

            qd_parsed_turbo_t *anno_val = DEQ_NEXT(anno);
            assert(anno_val);

            qd_iterator_t *val_iter =
                qd_iterator_buffer(anno_val->bufptr.buffer,
                                anno_val->bufptr.cursor - qd_buffer_base(anno_val->bufptr.buffer),
                                anno_val->size,
                                ITER_VIEW_ALL);
            assert(val_iter);

            qd_parsed_field_t *val_field = qd_parse(val_iter);
            assert(val_field);

            // Hoist the key name out of the buffers into a normal char array
            char key_name[QD_MA_MAX_KEY_LEN + 1];
            (void)qd_iterator_strncpy(iter, key_name, QD_MA_MAX_KEY_LEN + 1);

            // transfer ownership of the extracted value to the message
            if        (!strcmp(key_name, QD_MA_TRACE)) {
                *ma_trace = val_field;
            } else if (!strcmp(key_name, QD_MA_INGRESS)) {
                *ma_ingress = val_field;
            } else if (!strcmp(key_name, QD_MA_TO)) {
                *ma_to_override = val_field;
            } else if (!strcmp(key_name, QD_MA_PHASE)) {
                *ma_phase = val_field;
            } else {
                // TODO: this key had the QD_MA_PREFIX but it does not match
                //       one of the actual fields. 
                qd_parse_free(val_field);
            }

            qd_iterator_free(key_iter);
            qd_parse_free(key_field);
            qd_iterator_free(val_iter);
            // val_field is usually handed over to message_private and is freed 

            anno = DEQ_NEXT(anno_val);
        }
    }

    anno = DEQ_HEAD(annos);
    while (anno) {
        DEQ_REMOVE_HEAD(annos);
        free_qd_parsed_turbo_t(anno);
        anno = DEQ_HEAD(annos);
    }

    // Adjust size of user annotation blob by the size of the router
    // annotations
    blob_pointer->remaining = user_bytes;
    assert(blob_pointer->remaining >= 0);

    *blob_item_count = user_entries;
    assert(*blob_item_count >= 0);
    return 0;
}
示例#12
0
static bool qd_message_check_LH(qd_message_content_t *content, qd_message_depth_t depth)
{
    qd_error_clear();
    qd_buffer_t *buffer  = DEQ_HEAD(content->buffers);

    if (!buffer) {
        qd_error(QD_ERROR_MESSAGE, "No data");
        return false;
    }

    if (depth <= content->parse_depth)
        return true; // We've already parsed at least this deep

    if (content->parse_buffer == 0) {
        content->parse_buffer = buffer;
        content->parse_cursor = qd_buffer_base(content->parse_buffer);
    }

    if (depth == QD_DEPTH_NONE)
        return true;

    //
    // MESSAGE HEADER
    //
    if (0 == qd_check_field_LH(content, QD_DEPTH_HEADER,
                               MSG_HDR_LONG, MSG_HDR_SHORT, TAGS_LIST, &content->section_message_header, 0)) {
        qd_error(QD_ERROR_MESSAGE, "Invalid header");
        return false;
    }
    if (depth == QD_DEPTH_HEADER)
        return true;

    //
    // DELIVERY ANNOTATION
    //
    if (0 == qd_check_field_LH(content, QD_DEPTH_DELIVERY_ANNOTATIONS,
                               DELIVERY_ANNOTATION_LONG, DELIVERY_ANNOTATION_SHORT, TAGS_MAP, &content->section_delivery_annotation, 0)) {
        qd_error(QD_ERROR_MESSAGE, "Invalid delivery-annotations");
        return false;
    }
    if (depth == QD_DEPTH_DELIVERY_ANNOTATIONS)
        return true;

    //
    // MESSAGE ANNOTATION
    //
    if (0 == qd_check_field_LH(content, QD_DEPTH_MESSAGE_ANNOTATIONS,
                               MESSAGE_ANNOTATION_LONG, MESSAGE_ANNOTATION_SHORT, TAGS_MAP, &content->section_message_annotation, 0)) {
        qd_error(QD_ERROR_MESSAGE, "Invalid annotations");
        return false;
    }
    if (depth == QD_DEPTH_MESSAGE_ANNOTATIONS)
        return true;

    //
    // PROPERTIES
    //
    if (0 == qd_check_field_LH(content, QD_DEPTH_PROPERTIES,
                               PROPERTIES_LONG, PROPERTIES_SHORT, TAGS_LIST, &content->section_message_properties, 0)) {
        qd_error(QD_ERROR_MESSAGE, "Invalid message properties");
        return false;
    }
    if (depth == QD_DEPTH_PROPERTIES)
        return true;

    //
    // APPLICATION PROPERTIES
    //
    if (0 == qd_check_field_LH(content, QD_DEPTH_APPLICATION_PROPERTIES,
                               APPLICATION_PROPERTIES_LONG, APPLICATION_PROPERTIES_SHORT, TAGS_MAP, &content->section_application_properties, 0)) {
        qd_error(QD_ERROR_MESSAGE, "Invalid application-properties");
        return false;
    }
    if (depth == QD_DEPTH_APPLICATION_PROPERTIES)
        return true;

    //
    // BODY
    // Note that this function expects a limited set of types in a VALUE section.  This is
    // not a problem for messages passing through Dispatch because through-only messages won't
    // be parsed to BODY-depth.
    //
    if (0 == qd_check_field_LH(content, QD_DEPTH_BODY,
                               BODY_DATA_LONG, BODY_DATA_SHORT, TAGS_BINARY, &content->section_body, 1)) {
        qd_error(QD_ERROR_MESSAGE, "Invalid body data");
        return false;
    }
    if (0 == qd_check_field_LH(content, QD_DEPTH_BODY,
                               BODY_SEQUENCE_LONG, BODY_SEQUENCE_SHORT, TAGS_LIST, &content->section_body, 1)) {
        qd_error(QD_ERROR_MESSAGE, "Invalid body sequence");
        return false;
    }
    if (0 == qd_check_field_LH(content, QD_DEPTH_BODY,
                               BODY_VALUE_LONG, BODY_VALUE_SHORT, TAGS_ANY, &content->section_body, 0)) {
        qd_error(QD_ERROR_MESSAGE, "Invalid body value");
        return false;
    }
    if (depth == QD_DEPTH_BODY)
        return true;

    //
    // FOOTER
    //
    if (0 == qd_check_field_LH(content, QD_DEPTH_ALL,
                               FOOTER_LONG, FOOTER_SHORT, TAGS_MAP, &content->section_footer, 0)) {

        qd_error(QD_ERROR_MESSAGE, "Invalid footer");
        return false;
    }

    return true;
}
示例#13
0
void qd_message_send(qd_message_t *in_msg,
					qd_link_t *link,
					bool strip_annotations)
{
    qd_message_pvt_t     *msg     = (qd_message_pvt_t*) in_msg;
    qd_message_content_t *content = msg->content;
    qd_buffer_t          *buf     = DEQ_HEAD(content->buffers);
    unsigned char        *cursor;
    pn_link_t            *pnl     = qd_link_pn(link);

    char repr[qd_message_repr_len()];
    qd_log(log_source, QD_LOG_TRACE, "Sending %s on link %s",
           qd_message_repr(in_msg, repr, sizeof(repr)),
           pn_link_name(pnl));

    qd_buffer_list_t new_ma;
    DEQ_INIT(new_ma);

    if (strip_annotations || compose_message_annotations(msg, &new_ma)) {
        //
        // This is the case where the message annotations have been modified.
        // The message send must be divided into sections:  The existing header;
        // the new message annotations; the rest of the existing message.
        // Note that the original message annotations that are still in the
        // buffer chain must not be sent.
        //
        // Start by making sure that we've parsed the message sections through
        // the message annotations
        //
        // ??? NO LONGER NECESSARY???
        if (!qd_message_check(in_msg, QD_DEPTH_MESSAGE_ANNOTATIONS)) {
            qd_log(log_source, QD_LOG_ERROR, "Cannot send: %s", qd_error_message);
            return;
        }

        //
        // Send header if present
        //
        cursor = qd_buffer_base(buf);
        if (content->section_message_header.length > 0) {
            buf    = content->section_message_header.buffer;
            cursor = content->section_message_header.offset + qd_buffer_base(buf);
            advance(&cursor, &buf,
                    content->section_message_header.length + content->section_message_header.hdr_length,
                    send_handler, (void*) pnl);
        }

        //
        // Send new message annotations
        //
        qd_buffer_t *da_buf = DEQ_HEAD(new_ma);
        while (da_buf) {
            pn_link_send(pnl, (char*) qd_buffer_base(da_buf), qd_buffer_size(da_buf));
            da_buf = DEQ_NEXT(da_buf);
        }
        qd_buffer_list_free_buffers(&new_ma);

        //
        // Skip over replaced message annotations
        //
        if (content->section_message_annotation.length > 0)
            advance(&cursor, &buf,
                    content->section_message_annotation.hdr_length + content->section_message_annotation.length,
                    0, 0);

        //
        // Send remaining partial buffer
        //
        if (buf) {
            size_t len = qd_buffer_size(buf) - (cursor - qd_buffer_base(buf));
            advance(&cursor, &buf, len, send_handler, (void*) pnl);
        }

        // Fall through to process the remaining buffers normally
        // Note that 'advance' will have moved us to the next buffer in the chain.
    }

    while (buf) {
        pn_link_send(pnl, (char*) qd_buffer_base(buf), qd_buffer_size(buf));
        buf = DEQ_NEXT(buf);
    }
}
示例#14
0
//
// Check the buffer chain, starting at cursor to see if it matches the pattern.
// If the pattern matches, check the next tag to see if it's in the set of expected
// tags.  If not, return zero.  If so, set the location descriptor to the good
// tag and advance the cursor (and buffer, if needed) to the end of the matched section.
//
// If there is no match, don't advance the cursor.
//
// Return 0 if the pattern matches but the following tag is unexpected
// Return 0 if the pattern matches and the location already has a pointer (duplicate section)
// Return 1 if the pattern matches and we've advanced the cursor/buffer
// Return 1 if the pattern does not match
//
static int qd_check_and_advance(qd_buffer_t         **buffer,
                                unsigned char       **cursor,
                                const unsigned char  *pattern,
                                int                   pattern_length,
                                const unsigned char  *expected_tags,
                                qd_field_location_t  *location)
{
    qd_buffer_t   *test_buffer = *buffer;
    unsigned char *test_cursor = *cursor;

    if (!test_cursor)
        return 1; // no match

    unsigned char *end_of_buffer = qd_buffer_base(test_buffer) + qd_buffer_size(test_buffer);
    int idx = 0;

    while (idx < pattern_length && *test_cursor == pattern[idx]) {
        idx++;
        test_cursor++;
        if (test_cursor == end_of_buffer) {
            test_buffer = test_buffer->next;
            if (test_buffer == 0)
                return 1; // Pattern didn't match
            test_cursor = qd_buffer_base(test_buffer);
            end_of_buffer = test_cursor + qd_buffer_size(test_buffer);
        }
    }

    if (idx < pattern_length)
        return 1; // Pattern didn't match

    //
    // Pattern matched, check the tag
    //
    while (*expected_tags && *test_cursor != *expected_tags)
        expected_tags++;
    if (*expected_tags == 0)
        return 0;  // Unexpected tag

    if (location->parsed)
        return 0;  // Duplicate section

    //
    // Pattern matched and tag is expected.  Mark the beginning of the section.
    //
    location->parsed     = 1;
    location->buffer     = *buffer;
    location->offset     = *cursor - qd_buffer_base(*buffer);
    location->length     = 0;
    location->hdr_length = pattern_length;

    //
    // Advance the pointers to consume the whole section.
    //
    int pre_consume = 1;  // Count the already extracted tag
    int consume     = 0;
    unsigned char tag = next_octet(&test_cursor, &test_buffer);
    if (!test_cursor) return 0;
    switch (tag) {
    case 0x45 : // list0
        break;

    case 0xd0 : // list32
    case 0xd1 : // map32
    case 0xb0 : // vbin32
        pre_consume += 3;
        consume |= ((int) next_octet(&test_cursor, &test_buffer)) << 24;
        if (!test_cursor) return 0;
        consume |= ((int) next_octet(&test_cursor, &test_buffer)) << 16;
        if (!test_cursor) return 0;
        consume |= ((int) next_octet(&test_cursor, &test_buffer)) << 8;
        if (!test_cursor) return 0;
        // Fall through to the next case...

    case 0xc0 : // list8
    case 0xc1 : // map8
    case 0xa0 : // vbin8
        pre_consume += 1;
        consume |= (int) next_octet(&test_cursor, &test_buffer);
        if (!test_cursor) return 0;
        break;
    }

    location->length = pre_consume + consume;
    if (consume)
        advance(&test_cursor, &test_buffer, consume, 0, 0);

    *cursor = test_cursor;
    *buffer = test_buffer;
    return 1;
}