module Tarantool::UnpackTuples

Public Instance Methods

_unpack_tuples(p1, p2, p3, p4) click to toggle source
static VALUE
unpack_tuples(VALUE self, VALUE data, VALUE fields, VALUE _tail, VALUE tuples_affected)
{
    uint32_t fieldsn = RARRAY_LEN(fields);
    uint32_t tuplesn = NUM2UINT(tuples_affected);
    uint32_t tail = NUM2UINT(_tail);
    const char *str = StringValuePtr(data);
    size_t len = RSTRING_LEN(data);
    rb_encoding *utf8 = rb_utf8_encoding(), *binary = rb_ascii8bit_encoding();

    VALUE tuples = rb_ary_new2(tuplesn);
    VALUE serializers = rb_ary_new2(fieldsn);

    for (;tuplesn > 0; tuplesn--) {
        uint32_t tuplen, i, realfield;
        const char *end;
        VALUE tuple;
        if (len < 8) {
            rb_raise(rb_eValueError, "Response too short");
        }
        end = str + 8 + *(uint32_t*)str;
        tuplen = *(uint32_t*)(str+4);
        tuple = rb_ary_new2(fieldsn);
        str += 8;
        len -= 8;
        for(i = 0; i < tuplen; i++) {
            size_t fieldsize = slice_ber((const uint8_t**)&str, &len);
            VALUE field, value;
            if (fieldsize == 0) {
                rb_ary_push(tuple, Qnil);
                continue;
            }
            if (fieldsize > len) {
                rb_raise(rb_eValueError, "Response mailformed at field #%u fieldsize: %zu tail len: %zu", i, fieldsize, len);
            }
            realfield = i;
            if (i >= fieldsn) {
                if (tail == 1) {
                    realfield = fieldsn - 1;
                } else {
                    realfield = fieldsn + (i - fieldsn) % tail - tail;
                }
            }
            field = RARRAY_CONST_PTR(fields)[realfield];

            if (field == sym_int || field == sym_integer) {
                if (fieldsize != 4) {
                    rb_raise(rb_eValueError, "Bad field size %zd for integer field #%u", fieldsize, i);
                }
                value = UINT2NUM(get_uint32(str));
            } else if (field == sym_str || field == sym_string) {
                if (*str == 0 && fieldsize > 0) {
                    str++; len--; fieldsize--;
                }
                value = rb_enc_str_new(str, fieldsize, utf8);
            } else if (field == sym_int64) {
                if (fieldsize != 8) {
                    rb_raise(rb_eValueError, "Bad field size %zd for 64bit integer field #%u", fieldsize, i);
                }
#if SIZEOF_LONG == 8
                value = ULONG2NUM(get_uint64(str));
#elif HAVE_LONG_LONG
                value = ULL2NUM(get_uint64(str));
#else
#error "Should have long long or sizeof(long) == 8"
#endif
            } else if (field == sym_bytes) {
                if (*str == 0 && fieldsize > 0) {
                    str++; len--; fieldsize--;
                }
                value = rb_enc_str_new(str, fieldsize, binary);
            } else if (field == sym_int16) {
                if (fieldsize != 2) {
                    rb_raise(rb_eValueError, "Bad field size %zd for 16bit integer field #%u", fieldsize, i);
                }
                value = UINT2NUM(get_uint16(str));
            } else if (field == sym_int8) {
                if (fieldsize != 1) {
                    rb_raise(rb_eValueError, "Bad field size %zd for 8bit integer field #%u", fieldsize, i);
                }
                value = UINT2NUM(*(uint8_t*)str);
            } else if (field == sym_sint) {
                if (fieldsize != 4) {
                    rb_raise(rb_eValueError, "Bad field size %zd for integer field #%u", fieldsize, i);
                }
                value = INT2NUM((int32_t)get_uint32(str));
            } else if (field == sym_sint64) {
                if (fieldsize != 8) {
                    rb_raise(rb_eValueError, "Bad field size %zd for 64bit integer field #%u", fieldsize, i);
                }
#if SIZEOF_LONG == 8
                value = LONG2NUM((int64_t)get_uint64(str));
#elif HAVE_LONG_LONG
                value = LL2NUM((int64_t)get_uint64(str));
#endif
            } else if (field == sym_sint16) {
                if (fieldsize != 2) {
                    rb_raise(rb_eValueError, "Bad field size %zd for 16bit integer field #%u", fieldsize, i);
                }
                value = INT2NUM((int16_t)get_uint16(str));
            } else if (field == sym_sint8) {
                if (fieldsize != 1) {
                    rb_raise(rb_eValueError, "Bad field size %zd for 8bit integer field #%u", fieldsize, i);
                }
                value = INT2NUM(*(int8_t*)str);
            } else if (field == sym_varint) {
                if (fieldsize == 4) {
                    value = UINT2NUM(get_uint32(str));
                } else if (fieldsize == 8) {
#if SIZEOF_LONG == 8
                    value = ULONG2NUM(get_uint64(str));
#elif HAVE_LONG_LONG
                    value = ULL2NUM(get_uint64(str));
#endif
                } else if (fieldsize == 2) {
                    value = UINT2NUM(get_uint16(str));
                } else {
                    rb_raise(rb_eValueError, "Bad field size %zd for integer field %d", fieldsize, i);
                }
            } else if (field == sym_auto) {
                value = rb_enc_str_new(str, fieldsize, utf8);
                if (fieldsize == 2 || fieldsize == 4 || fieldsize == 8) {
                    value = rb_class_new_instance(1, &value, rb_cAutoType);
                }
            } else {
                VALUE serializer = rb_ary_entry(serializers, realfield);
                VALUE substr = rb_enc_str_new(str, fieldsize, binary);
                if (!RTEST(serializer)) {
                    serializer = rb_funcall2(self, id_get_serializer, 1, &field);
                    rb_ary_store(serializers, realfield, serializer);
                }
                value = rb_funcall2(serializer, id_decode, 1, &substr);
            }
            str += fieldsize;
            len -= fieldsize;
            rb_ary_push(tuple, value);
        }
        if (end != str) {
            rb_raise(rb_eValueError, "Response mailformed");
        }
        rb_ary_push(tuples, tuple);
    }

    RB_GC_GUARD(data);
    return tuples;
}