Пример #1
0
SRL_STATIC_INLINE void
srl_merge_binary_utf8(pTHX_ srl_merger_t *mrg, ptable_entry_ptr ptable_entry)
{
    int ok;
    UV length, total_length;
    strtable_entry_ptr strtable_entry;
    srl_reader_char_ptr tag_ptr = mrg->ibuf.pos;

    DEBUG_ASSERT_RDR_SANE(mrg->pibuf);
    DEBUG_ASSERT_BUF_SANE(&mrg->obuf);

    mrg->ibuf.pos++; // skip tag in input buffer
    length = srl_read_varint_uv_length(aTHX_ mrg->pibuf, " while reading BINARY or STR_UTF8");

    assert((mrg->ibuf.pos - tag_ptr) > 0);
    assert((mrg->ibuf.pos - tag_ptr) <= SRL_MAX_VARINT_LENGTH);
    total_length = length + (mrg->ibuf.pos - tag_ptr);

    strtable_entry = srl_lookup_string(aTHX_ mrg, tag_ptr, total_length, &ok);

    if (ok) {
        // issue COPY tag
        srl_buf_cat_varint(aTHX_ &mrg->obuf, SRL_HDR_COPY, strtable_entry->offset);
        mrg->ibuf.pos += length;

        if (expect_false(ptable_entry)) {
            // update value in ptable entry
            // This is needed because if any of following tags will reffer to
            // this one as COPY we need to point them to original string.
            // By Sereal spec a COPY tag cannot reffer to another COPY tag.
            ptable_entry->value = INT2PTR(void *, strtable_entry->offset);
        }
    } else if (strtable_entry) {
Пример #2
0
SRL_STATIC_INLINE void
srl_set_input_buffer(pTHX_ srl_merger_t *mrg, SV *src)
{
    STRLEN len;
    UV header_len;
    U8 encoding_flags;
    U8 protocol_version;
    srl_buffer_char *tmp;
    IV proto_version_and_encoding_flags_int;

    SRL_RDR_CLEAR(&mrg->ibuf);

    tmp = (srl_buffer_char*) SvPV(src, len);
    mrg->ibuf.start = mrg->ibuf.pos = tmp;
    mrg->ibuf.end = mrg->ibuf.start + len;

    proto_version_and_encoding_flags_int = srl_validate_header_version(aTHX_ (srl_reader_char_ptr) mrg->ibuf.start, len);

    if (proto_version_and_encoding_flags_int < 1) {
        if (proto_version_and_encoding_flags_int == 0)
            SRL_RDR_ERROR(mrg->pibuf, "Bad Sereal header: It seems your document was accidentally UTF-8 encoded");
        else
            SRL_RDR_ERROR(mrg->pibuf, "Bad Sereal header: Not a valid Sereal document.");
    }

    mrg->ibuf.pos += 5;
    encoding_flags = (U8) (proto_version_and_encoding_flags_int & SRL_PROTOCOL_ENCODING_MASK);
    protocol_version = (U8) (proto_version_and_encoding_flags_int & SRL_PROTOCOL_VERSION_MASK);

    if (expect_false(protocol_version > 3 || protocol_version < 1)) {
        SRL_RDR_ERRORf1(mrg->pibuf, "Unsupported Sereal protocol version %u", (unsigned int) protocol_version);
    }

    // skip header in any case
    header_len = srl_read_varint_uv_length(aTHX_ mrg->pibuf, " while reading header");
    mrg->ibuf.pos += header_len;

    if (encoding_flags == SRL_PROTOCOL_ENCODING_RAW) {
        /* no op */
    } else if (   encoding_flags == SRL_PROTOCOL_ENCODING_SNAPPY
               || encoding_flags == SRL_PROTOCOL_ENCODING_SNAPPY_INCREMENTAL)
    {
        srl_decompress_body_snappy(aTHX_ mrg->pibuf, encoding_flags, NULL);
    } else if (encoding_flags == SRL_PROTOCOL_ENCODING_ZLIB) {
        srl_decompress_body_zlib(aTHX_ mrg->pibuf, NULL);
    } else {
        SRL_RDR_ERROR(mrg->pibuf, "Sereal document encoded in an unknown format");
    }

    /* this functions *MUST* be called after srl_decompress_body* */
    SRL_RDR_UPDATE_BODY_POS(mrg->pibuf, protocol_version);
    DEBUG_ASSERT_RDR_SANE(mrg->pibuf);
}
Пример #3
0
SRL_STATIC_INLINE void
srl_read_header(pTHX_ srl_decoder_t *dec)
{
    UV header_len;

    /* 4 byte magic string + version/flags + hdr len at least */
    ASSERT_BUF_SPACE(dec, 4 + 1 + 1," while reading header");
    if (strnEQ((char*)dec->pos, SRL_MAGIC_STRING, 4)) {
        dec->pos += 4;
        dec->proto_version_and_flags = *dec->pos++;

        if (expect_false( (dec->proto_version_and_flags & SRL_PROTOCOL_VERSION_MASK) != 1 ))
            SRL_ERRORf1("Unsupported Sereal protocol version %u",
                    dec->proto_version_and_flags & SRL_PROTOCOL_VERSION_MASK);
        if ((dec->proto_version_and_flags & SRL_PROTOCOL_ENCODING_MASK) == SRL_PROTOCOL_ENCODING_RAW) {
            /* no op */
        }
        else
        if (
                ( dec->proto_version_and_flags & SRL_PROTOCOL_ENCODING_MASK ) == SRL_PROTOCOL_ENCODING_SNAPPY
                ||
                ( dec->proto_version_and_flags & SRL_PROTOCOL_ENCODING_MASK ) == SRL_PROTOCOL_ENCODING_SNAPPY_INCREMENTAL
        ) {
            if (expect_false( SRL_DEC_HAVE_OPTION(dec, SRL_F_DECODER_REFUSE_SNAPPY) )) {
                SRL_ERROR("Sereal document is compressed with Snappy, "
                      "but this decoder is configured to refuse Snappy-compressed input.");
            }
            dec->flags |= SRL_F_DECODER_DECOMPRESS_SNAPPY;
        }
        else
        {
            SRL_ERRORf1( "Sereal document encoded in an unknown format '%d'",
                     (dec->proto_version_and_flags & SRL_PROTOCOL_ENCODING_MASK) >> 4 );
        }

        /* Must do this via a temporary as it modifes dec->pos itself */
        header_len= srl_read_varint_uv_length(aTHX_ dec, " while reading header");
        /* Skip header since we don't have any defined header-content in this
         * protocol version. */
        dec->pos += header_len;
    } else {
Пример #4
0
IV
srl_iterator_hash_exists(pTHX_ srl_iterator_t *iter, const char *name, STRLEN name_len)
{
    U8 tag;
    UV length, offset;
    const char *key_ptr;

    IV stack_depth = iter->stack.depth;
    srl_iterator_stack_ptr stack_ptr = iter->stack.ptr;

    DEBUG_ASSERT_RDR_SANE(iter->pbuf);
    SRL_ITER_ASSERT_EOF(iter, "stringish");
    SRL_ITER_ASSERT_STACK(iter);
    SRL_ITER_ASSERT_HASH_ON_STACK(iter);

    SRL_ITER_TRACE("name=%.*s", (int) name_len, name);
    SRL_ITER_REPORT_STACK_STATE(iter);

    while (stack_ptr->idx) {
        stack_ptr->idx--; // do not make it be part of while clause
        SRL_ITER_ASSERT_STACK(iter);
        assert(stack_ptr->idx % 2 == 1);
        assert(iter->stack.depth == stack_depth);
        DEBUG_ASSERT_RDR_SANE(iter->pbuf);

        tag = *iter->buf.pos & ~SRL_HDR_TRACK_FLAG;
        SRL_ITER_REPORT_TAG(iter, tag);
        iter->buf.pos++;

        switch (tag) {
            CASE_SRL_HDR_SHORT_BINARY:
                length = SRL_HDR_SHORT_BINARY_LEN_FROM_TAG(tag);
                key_ptr = (const char *) iter->buf.pos;
                iter->buf.pos += length;
                break;

            case SRL_HDR_BINARY:
                length = srl_read_varint_uv_length(aTHX_ iter->pbuf, " while reading BINARY");
                key_ptr = (const char *) iter->buf.pos;
                iter->buf.pos += length;
                break;

            case SRL_HDR_STR_UTF8:      
                // TODO deal with UTF8
                length = srl_read_varint_uv_length(aTHX_ iter->pbuf, " while reading STR_UTF8");
                key_ptr = (const char *) iter->buf.pos;
                iter->buf.pos += length;
                break;

            case SRL_HDR_COPY:
                offset = srl_read_varint_uv_offset(aTHX_ iter->pbuf, " while reading COPY tag");
                key_ptr = (const char *) iter->buf.body_pos + offset;
                tag = *key_ptr & ~SRL_HDR_TRACK_FLAG;
                key_ptr++;

                /* Note we do NOT validate these items, as we have already read them
                 * and if they were a problem we would not be here to process them! */

                switch (tag) {
                    CASE_SRL_HDR_SHORT_BINARY:
                        length = SRL_HDR_SHORT_BINARY_LEN_FROM_TAG(tag);
                        break;

                    case SRL_HDR_BINARY:
                        SET_UV_FROM_VARINT(iter->pbuf, length, key_ptr);
                        break;

                    case SRL_HDR_STR_UTF8:
                        // TODO deal with UTF8
                        SET_UV_FROM_VARINT(iter->pbuf, length, key_ptr);
                        break;

                    default:
                        SRL_RDR_ERROR_BAD_COPY(iter->pbuf, SRL_HDR_HASH);
                }

                break;

            default:
                SRL_RDR_ERROR_UNEXPECTED(iter->pbuf, tag, "stringish");
        }

        if (expect_false((srl_reader_char_ptr) key_ptr >= iter->buf.end)) {
            SRL_RDR_ERROR_EOF(iter->pbuf, "string content");
        }

        if (   length == name_len
            && memcmp(name, key_ptr, name_len) == 0)
        {
            SRL_ITER_TRACE("found key '%.*s' at offset %"UVuf,
                         (int) name_len, name, SRL_RDR_BODY_POS_OFS(iter->pbuf));
            return SRL_RDR_BODY_POS_OFS(iter->pbuf);
        }

        // srl_iterator_next garantee that we remans on current stack
        srl_iterator_next(aTHX_ iter, 1);
        stack_ptr = iter->stack.ptr;
    }

    SRL_ITER_TRACE("didn't found key '%.*s'", (int) name_len, name);
    return SRL_ITER_NOT_FOUND;
}
Пример #5
0
const char *
srl_iterator_hash_key(pTHX_ srl_iterator_t *iter, STRLEN *len_out)
{
    U8 tag;
    UV length, offset;
    const char *result = NULL;
    srl_reader_char_ptr orig_pos = iter->buf.pos;
    *len_out = 0;

    DEBUG_ASSERT_RDR_SANE(iter->pbuf);
    SRL_ITER_ASSERT_EOF(iter, "stringish");
    SRL_ITER_ASSERT_STACK(iter);
    SRL_ITER_ASSERT_HASH_ON_STACK(iter);

    tag = *iter->buf.pos & ~SRL_HDR_TRACK_FLAG;
    SRL_ITER_REPORT_TAG(iter, tag);
    iter->buf.pos++;

    switch (tag) {
        CASE_SRL_HDR_SHORT_BINARY:
            length = SRL_HDR_SHORT_BINARY_LEN_FROM_TAG(tag);
            break;

        case SRL_HDR_BINARY:
            length = srl_read_varint_uv_length(aTHX_ iter->pbuf, " while reading BINARY");
            break;

        case SRL_HDR_STR_UTF8:      
            // TODO deal with UTF8
            length = srl_read_varint_uv_length(aTHX_ iter->pbuf, " while reading STR_UTF8");
            break;

        case SRL_HDR_COPY:
            offset = srl_read_varint_uv_offset(aTHX_ iter->pbuf, " while reading COPY tag");
            iter->buf.pos = iter->buf.body_pos + offset;

            /* Note we do NOT validate these items, as we have already read them
             * and if they were a problem we would not be here to process them! */

            tag = *iter->buf.pos & ~SRL_HDR_TRACK_FLAG;
            SRL_ITER_REPORT_TAG(iter, tag);
            iter->buf.pos++;

            switch (tag) {
                CASE_SRL_HDR_SHORT_BINARY:
                    length = SRL_HDR_SHORT_BINARY_LEN_FROM_TAG(tag);
                    break;

                case SRL_HDR_BINARY:
                    SET_UV_FROM_VARINT(iter->pbuf, length, iter->buf.pos);
                    break;

                case SRL_HDR_STR_UTF8:
                    // TODO deal with UTF8
                    SET_UV_FROM_VARINT(iter->pbuf, length, iter->buf.pos);
                    break;

                default:
                    SRL_RDR_ERROR_BAD_COPY(iter->pbuf, SRL_HDR_HASH);
            }

            break;

        default:
            SRL_RDR_ERROR_UNEXPECTED(iter->pbuf, tag, "stringish");
    }

    if (expect_false(iter->buf.pos + length >= iter->buf.end)) {
        SRL_RDR_ERROR_EOF(iter->pbuf, "string content");
    }

    *len_out = length;
    result = (const char *) iter->buf.pos;
    iter->buf.pos = orig_pos; // restore original position
    DEBUG_ASSERT_RDR_SANE(iter->pbuf);
    return result;
}
Пример #6
0
void
srl_iterator_set(pTHX_ srl_iterator_t *iter, SV *src)
{
    SV *sv;
    STRLEN len;
    UV header_len;
    U8 encoding_flags;
    U8 protocol_version;
    srl_reader_char_ptr tmp;
    IV proto_version_and_encoding_flags_int;
    srl_iterator_stack_ptr stack_ptr = NULL;

    if (iter->document) {
        SvREFCNT_dec(iter->document);
        iter->document = NULL;
    }

    iter->document = src;
    SvREFCNT_inc(iter->document);

    tmp = (srl_reader_char_ptr) SvPV(src, len);
    iter->buf.start = iter->buf.pos = tmp;
    iter->buf.end = iter->buf.start + len;

    proto_version_and_encoding_flags_int = srl_validate_header_version(aTHX_ iter->buf.start, len);

    if (proto_version_and_encoding_flags_int < 1) {
        if (proto_version_and_encoding_flags_int == 0)
            SRL_RDR_ERROR(iter->pbuf, "Bad Sereal header: It seems your document was accidentally UTF-8 encoded");
        else
            SRL_RDR_ERROR(iter->pbuf, "Bad Sereal header: Not a valid Sereal document.");
    }

    iter->buf.pos += 5;
    encoding_flags = (U8) (proto_version_and_encoding_flags_int & SRL_PROTOCOL_ENCODING_MASK);
    protocol_version = (U8) (proto_version_and_encoding_flags_int & SRL_PROTOCOL_VERSION_MASK);

    if (expect_false(protocol_version > 3 || protocol_version < 1)) {
        SRL_RDR_ERRORf1(iter->pbuf, "Unsupported Sereal protocol version %u", (unsigned int) protocol_version);
    }

    // skip header in any case
    header_len = srl_read_varint_uv_length(aTHX_ iter->pbuf, " while reading header");
    iter->buf.pos += header_len;

    if (encoding_flags == SRL_PROTOCOL_ENCODING_RAW) {
        /* no op */
    } else if (   encoding_flags == SRL_PROTOCOL_ENCODING_SNAPPY
               || encoding_flags == SRL_PROTOCOL_ENCODING_SNAPPY_INCREMENTAL)
    {
        srl_decompress_body_snappy(aTHX_ iter->pbuf, encoding_flags, &sv);
        SvREFCNT_dec(iter->document);
        SvREFCNT_inc(sv);
        iter->document = sv;
    } else if (encoding_flags == SRL_PROTOCOL_ENCODING_ZLIB) {
        srl_decompress_body_zlib(aTHX_ iter->pbuf, &sv);
        SvREFCNT_dec(iter->document);
        SvREFCNT_inc(sv);
        iter->document = sv;
    } else {
        SRL_RDR_ERROR(iter->pbuf, "Sereal document encoded in an unknown format");
    }

    /* this function *MUST* be called after calling srl_decompress_body* */
    SRL_RDR_UPDATE_BODY_POS(iter->pbuf, protocol_version);
    DEBUG_ASSERT_RDR_SANE(iter->pbuf);

    srl_stack_push_and_set(iter, SRL_ITER_STACK_ROOT_TAG, 1, stack_ptr);
    srl_iterator_reset(aTHX_ iter);
}
Пример #7
0
/* Main routine. Caller must ensure that EOF is NOT reached */
SRL_STATIC_INLINE void
srl_iterator_step_internal(pTHX_ srl_iterator_t *iter)
{
    U8 tag;
    UV length;
    srl_stack_t *stack = iter->stack;

    DEBUG_ASSERT_RDR_SANE(iter->pbuf);

    srl_iterator_wrap_stack(aTHX_ iter, -1);
    if (srl_stack_empty(stack)) return;

    SRL_ITER_ASSERT_STACK(iter);

    stack->ptr->idx--;
    SRL_ITER_TRACE("stack->ptr: idx=%d depth=%d",
                   stack->ptr->idx, (int) SRL_STACK_DEPTH(stack));

    SRL_ITER_ASSERT_STACK(iter);

read_again:
    tag = *iter->buf.pos & ~SRL_HDR_TRACK_FLAG;
    SRL_ITER_REPORT_TAG(iter, tag);
    iter->buf.pos++;

    /* No code which decrease step, next or stack's counters should be added here.
     * Otherwise the counters will be decreased twicer for tags like REFN, ALIAS, etc. */

    switch (tag) {
        CASE_SRL_HDR_SHORT_BINARY:
            iter->buf.pos += SRL_HDR_SHORT_BINARY_LEN_FROM_TAG(tag);
            break;

        case SRL_HDR_HASH:
            length = srl_read_varint_uv_count(aTHX_ iter->pbuf, " while reading HASH");
            if (length > 0) srl_stack_push_and_set(iter, tag, length * 2);
            break;

        case SRL_HDR_ARRAY:
            length = srl_read_varint_uv_count(aTHX_ iter->pbuf, " while reading ARRAY");
            if (length > 0) srl_stack_push_and_set(iter, tag, length);
            break;

        CASE_SRL_HDR_HASHREF:
            length = SRL_HDR_HASHREF_LEN_FROM_TAG(tag);
            if (length > 0) srl_stack_push_and_set(iter, tag, length * 2);
            break;

        CASE_SRL_HDR_ARRAYREF:
            length = SRL_HDR_ARRAYREF_LEN_FROM_TAG(tag);
            if (length > 0) srl_stack_push_and_set(iter, tag, length);
            break;

        CASE_SRL_HDR_POS:
        CASE_SRL_HDR_NEG:
            break;

        case SRL_HDR_VARINT:
        case SRL_HDR_ZIGZAG:
            srl_skip_varint(aTHX_ iter->pbuf);
            break;

        case SRL_HDR_FLOAT:         iter->buf.pos += 4;      break;
        case SRL_HDR_DOUBLE:        iter->buf.pos += 8;      break;
        case SRL_HDR_LONG_DOUBLE:   iter->buf.pos += 16;     break;

        case SRL_HDR_TRUE:
        case SRL_HDR_FALSE:
        case SRL_HDR_UNDEF:
        case SRL_HDR_CANONICAL_UNDEF:
            break;

        case SRL_HDR_REFN:
        case SRL_HDR_ALIAS:
        case SRL_HDR_WEAKEN:
            goto read_again;

        case SRL_HDR_PAD:
            while (SRL_RDR_NOT_DONE(iter->pbuf) && *iter->buf.pos++ == SRL_HDR_PAD) {};
            goto read_again;

        case SRL_HDR_BINARY:
        case SRL_HDR_STR_UTF8:
            length = srl_read_varint_uv_length(aTHX_ iter->pbuf, " while reading BINARY or STR_UTF8");
            iter->buf.pos += length;
            break;

        case SRL_HDR_COPY:
        case SRL_HDR_REFP:
            srl_skip_varint(aTHX_ iter->pbuf);
            break;

        /* case SRL_HDR_OBJECTV: */
        /* case SRL_HDR_OBJECTV_FREEZE: */
        /* case SRL_HDR_REGEXP: */
        /* case SRL_HDR_OBJECT: */
        /* case SRL_HDR_OBJECT_FREEZE: */

        default:
            SRL_RDR_ERROR_UNIMPLEMENTED(iter->pbuf, tag, "");
            break;
    }

    DEBUG_ASSERT_RDR_SANE(iter->pbuf);
}
Пример #8
0
/* This is the main routine to deserialize a structure.
 * It rolls up all the other "top level" routines into one
 */
SV *
srl_decode_into(pTHX_ srl_decoder_t *dec, SV *src, SV* into, UV start_offset)
{
    assert(dec != NULL);
    if (SvUTF8(src))
        sv_utf8_downgrade(src, 0);
    srl_begin_decoding(aTHX_ dec, src, start_offset);
    srl_read_header(aTHX_ dec);
    if (SRL_DEC_HAVE_OPTION(dec, SRL_F_DECODER_DECOMPRESS_SNAPPY)) {
        /* uncompress */
        uint32_t dest_len;
        SV *buf_sv;
        unsigned char *buf;
        unsigned char *old_pos;
        const ptrdiff_t sereal_header_len = dec->pos - dec->buf_start;
        const STRLEN compressed_packet_len =
                ( dec->proto_version_and_flags & SRL_PROTOCOL_ENCODING_MASK ) == SRL_PROTOCOL_ENCODING_SNAPPY_INCREMENTAL
                ? (STRLEN)srl_read_varint_uv_length(aTHX_ dec, " while reading compressed packet size")
                : (STRLEN)(dec->buf_end - dec->pos);
        int decompress_ok;
        int header_len;

        /* all decl's above here, or we break C89 compilers */

        dec->bytes_consumed= compressed_packet_len + (dec->pos - dec->buf_start);

        header_len = csnappy_get_uncompressed_length(
                            (char *)dec->pos,
                            compressed_packet_len,
                            &dest_len
                         );
        if (header_len == CSNAPPY_E_HEADER_BAD)
            SRL_ERROR("Invalid Snappy header in Snappy-compressed Sereal packet");

        /* Let perl clean this up. Yes, it's not the most efficient thing
         * ever, but it's just one mortal per full decompression, so not
         * a bottle-neck. */
        buf_sv = sv_2mortal( newSV(sereal_header_len + dest_len + 1 ));
        buf = (unsigned char *)SvPVX(buf_sv);

        /* FIXME probably unnecessary to copy the Sereal header! */
        Copy(dec->buf_start, buf, sereal_header_len, unsigned char);

        old_pos = dec->pos;
        dec->buf_start = buf;
        dec->pos = buf + sereal_header_len;
        dec->buf_end = dec->pos + dest_len;
        dec->buf_len = dest_len + sereal_header_len;

        decompress_ok = csnappy_decompress_noheader((char *)(old_pos + header_len),
                                                    compressed_packet_len - header_len,
                                                    (char *)dec->pos,
                                                    &dest_len);
        if (expect_false( decompress_ok != 0 ))
        {
            SRL_ERRORf1("Snappy decompression of Sereal packet payload failed with error %i!", decompress_ok);
        }
    }

    if (expect_true(!into)) {
        into= sv_2mortal(newSV_type(SVt_NULL));
    }
    srl_read_single_value(aTHX_ dec, into);
    /* assert(dec->pos == dec->buf_end); For now we disable this */
    if (expect_false(SRL_DEC_HAVE_OPTION(dec, SRL_F_DECODER_NEEDS_FINALIZE))) {
        srl_finalize_structure(aTHX_ dec);
    }

    /* If we aren't reading from a decompressed buffer we have to remember the number
     * of bytes used for the user to query. */
    if (dec->bytes_consumed == 0)
        dec->bytes_consumed = dec->pos - dec->buf_start;

    if (SRL_DEC_HAVE_OPTION(dec, SRL_F_DECODER_DESTRUCTIVE_INCREMENTAL)) {
        STRLEN len;
        char *pv= SvPV(src,len);
        /* check the length here? do something different if the string is now exhausted? */
        sv_chop(src, pv + dec->bytes_consumed);
    }

    srl_clear_decoder(aTHX_ dec);
    return into;
}
Пример #9
0
SRL_STATIC_INLINE void
srl_build_track_table(pTHX_ srl_merger_t *mrg)
{
    U8 tag;
    UV offset, length;

    DEBUG_ASSERT_RDR_SANE(mrg->pibuf);

    if (mrg->tracked_offsets)
        srl_stack_clear(mrg->tracked_offsets);

    while (expect_true(BUF_NOT_DONE(mrg->pibuf))) {
        /* since we're doing full pass, it's not necessary to
         * add items into tracked_offsets here. They will be added
         * by corresponding REFP/ALIAS/COPY and other tags */

        tag = *mrg->ibuf.pos & ~SRL_HDR_TRACK_FLAG;
        SRL_REPORT_CURRENT_TAG(mrg, tag);
        mrg->ibuf.pos++;

        if (tag >= SRL_HDR_SHORT_BINARY_LOW) {
            mrg->ibuf.pos += SRL_HDR_SHORT_BINARY_LEN_FROM_TAG(tag);
        } else if (tag > SRL_HDR_NEG_HIGH && tag < SRL_HDR_ARRAYREF_LOW) {
            switch (tag) {
                case SRL_HDR_VARINT:
                case SRL_HDR_ZIGZAG:
                    srl_read_varint_uv(aTHX_ mrg->pibuf); // TODO test/implement srl_skip_varint()
                    break;

                case SRL_HDR_FLOAT:         mrg->ibuf.pos += 4;     break;
                case SRL_HDR_DOUBLE:        mrg->ibuf.pos += 8;     break;
                case SRL_HDR_LONG_DOUBLE:   mrg->ibuf.pos += 16;    break;

                case SRL_HDR_BINARY:
                case SRL_HDR_STR_UTF8:
                    length = srl_read_varint_uv_length(aTHX_ mrg->pibuf, " while reading BINARY or STR_UTF8");
                    mrg->ibuf.pos += length;
                    break;

                case SRL_HDR_HASH:
                case SRL_HDR_ARRAY:
                    srl_read_varint_uv_count(aTHX_ mrg->pibuf, " while reading ARRAY or HASH");
                    break;

                case SRL_HDR_TRUE:
                case SRL_HDR_FALSE:
                case SRL_HDR_UNDEF:
                case SRL_HDR_CANONICAL_UNDEF:
                    // noop
                    break;

                default:
                    switch (tag) {
                        case SRL_HDR_COPY:
                        case SRL_HDR_REFP:
                        case SRL_HDR_ALIAS:
                        case SRL_HDR_OBJECTV:
                        case SRL_HDR_OBJECTV_FREEZE:
                            offset = srl_read_varint_uv_offset(aTHX_ mrg->pibuf, " while reading COPY, OBJECTV or OBJECTV_FREEZE");
                            srl_stack_push_val(SRL_GET_TRACKED_OFFSETS(mrg), offset);
                            break;

                        case SRL_HDR_PAD:
                        case SRL_HDR_REFN:
                        case SRL_HDR_WEAKEN:
                        case SRL_HDR_EXTEND:
                        case SRL_HDR_REGEXP:
                        case SRL_HDR_OBJECT:
                        case SRL_HDR_OBJECT_FREEZE:
                            // noop
                            break;

                        default:
                            SRL_RDR_ERROR_UNIMPLEMENTED(mrg->pibuf, tag, "");
                            break;
                    }
            }
        }
    }

    if (mrg->tracked_offsets && !srl_stack_empty(mrg->tracked_offsets)) {
        srl_stack_rsort(aTHX_ mrg->tracked_offsets);
        srl_stack_dedupe(aTHX_ mrg->tracked_offsets);

        //int i = 0;
        //SRL_STACK_TYPE *ptr = mrg->tracked_offsets->begin;
        //while (ptr <= mrg->tracked_offsets->ptr) {
        //    warn("tracked_offsets: offset dedups idx %d offset %d\n", i, (int) *ptr);
        //    i++; ptr++;
        //}
    }

    DEBUG_ASSERT_RDR_SANE(mrg->pibuf);
}