benchmark/thin_server.rb
CHANGELOG
COPYING
-ext/binaryprotocolaccelerated.c
+ext/binary_protocol_accelerated.c
+ext/binary_protocol_accelerated.h
+ext/struct.c
+ext/struct.h
+ext/protocol.h
+ext/protocol.c
+ext/constants.h
+ext/memory_buffer.h
+ext/memory_buffer.c
+ext/rope_transport.c
+ext/rope_transport.h
+ext/thrift_native.c
ext/extconf.rb
lib/thrift/client.rb
lib/thrift/deprecation.rb
--- /dev/null
+#include <ruby.h>
+#include <stdbool.h>
+#include <constants.h>
+#include <struct.h>
+
+#define GET_TRANSPORT(obj) rb_ivar_get(obj, transport_ivar_id)
+#define WRITE(obj, data, length) rb_funcall(obj, write_method_id, 1, rb_str_new(data, length))
+#define CHECK_NIL(obj) if (NIL_P(obj)) { rb_raise(rb_eStandardError, "nil argument not allowed!");}
+
+VALUE rb_thrift_binary_proto_native_qmark(VALUE self) {
+ return Qtrue;
+}
+
+
+
+static int VERSION_1;
+static int VERSION_MASK;
+static int BAD_VERSION;
+
+static void write_byte_direct(VALUE trans, int8_t b) {
+ WRITE(trans, (char*)&b, 1);
+}
+
+static void write_i16_direct(VALUE trans, int16_t value) {
+ char data[2];
+
+ data[1] = value;
+ data[0] = (value >> 8);
+
+ WRITE(trans, data, 2);
+}
+
+static void write_i32_direct(VALUE trans, int32_t value) {
+ char data[4];
+
+ data[3] = value;
+ data[2] = (value >> 8);
+ data[1] = (value >> 16);
+ data[0] = (value >> 24);
+
+ WRITE(trans, data, 4);
+}
+
+
+static void write_i64_direct(VALUE trans, int64_t value) {
+ char data[8];
+
+ data[7] = value;
+ data[6] = (value >> 8);
+ data[5] = (value >> 16);
+ data[4] = (value >> 24);
+ data[3] = (value >> 32);
+ data[2] = (value >> 40);
+ data[1] = (value >> 48);
+ data[0] = (value >> 56);
+
+ WRITE(trans, data, 8);
+}
+
+static void write_string_direct(VALUE trans, VALUE str) {
+ write_i32_direct(trans, RSTRING(str)->len);
+ rb_funcall(trans, write_method_id, 1, str);
+}
+
+//--------------------------------
+// interface writing methods
+//--------------------------------
+
+VALUE rb_thrift_binary_proto_write_message_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thrift_binary_proto_write_struct_begin(VALUE self, VALUE name) {
+ return Qnil;
+}
+
+VALUE rb_thrift_binary_proto_write_struct_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thrift_binary_proto_write_field_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thrift_binary_proto_write_map_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thrift_binary_proto_write_list_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thrift_binary_proto_write_set_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thrift_binary_proto_write_message_begin(VALUE self, VALUE name, VALUE type, VALUE seqid) {
+ VALUE trans = GET_TRANSPORT(self);
+ write_i32_direct(trans, VERSION_1 | FIX2INT(type));
+ write_string_direct(trans, name);
+ write_i32_direct(trans, FIX2INT(seqid));
+
+ return Qnil;
+}
+
+VALUE rb_thrift_binary_proto_write_field_begin(VALUE self, VALUE name, VALUE type, VALUE id) {
+ VALUE trans = GET_TRANSPORT(self);
+ write_byte_direct(trans, FIX2INT(type));
+ write_i16_direct(trans, FIX2INT(id));
+
+ return Qnil;
+}
+
+VALUE rb_thrift_binary_proto_write_field_stop(VALUE self) {
+ write_byte_direct(GET_TRANSPORT(self), TTYPE_STOP);
+ return Qnil;
+}
+
+VALUE rb_thrift_binary_proto_write_map_begin(VALUE self, VALUE ktype, VALUE vtype, VALUE size) {
+ VALUE trans = GET_TRANSPORT(self);
+ write_byte_direct(trans, FIX2INT(ktype));
+ write_byte_direct(trans, FIX2INT(vtype));
+ write_i32_direct(trans, FIX2INT(size));
+
+ return Qnil;
+}
+
+VALUE rb_thrift_binary_proto_write_list_begin(VALUE self, VALUE etype, VALUE size) {
+ VALUE trans = GET_TRANSPORT(self);
+ write_byte_direct(trans, FIX2INT(etype));
+ write_i32_direct(trans, FIX2INT(size));
+
+ return Qnil;
+}
+
+VALUE rb_thrift_binary_proto_write_set_begin(VALUE self, VALUE etype, VALUE size) {
+ rb_thrift_binary_proto_write_list_begin(self, etype, size);
+ return Qnil;
+}
+
+VALUE rb_thrift_binary_proto_write_bool(VALUE self, VALUE b) {
+ write_byte_direct(GET_TRANSPORT(self), RTEST(b) ? 1 : 0);
+ return Qnil;
+}
+
+VALUE rb_thrift_binary_proto_write_byte(VALUE self, VALUE byte) {
+ CHECK_NIL(byte);
+ write_byte_direct(GET_TRANSPORT(self), NUM2INT(byte));
+ return Qnil;
+}
+
+VALUE rb_thrift_binary_proto_write_i16(VALUE self, VALUE i16) {
+ CHECK_NIL(i16);
+ write_i16_direct(GET_TRANSPORT(self), FIX2INT(i16));
+ return Qnil;
+}
+
+VALUE rb_thrift_binary_proto_write_i32(VALUE self, VALUE i32) {
+ CHECK_NIL(i32);
+ write_i32_direct(GET_TRANSPORT(self), NUM2INT(i32));
+ return Qnil;
+}
+
+VALUE rb_thrift_binary_proto_write_i64(VALUE self, VALUE i64) {
+ CHECK_NIL(i64);
+ write_i64_direct(GET_TRANSPORT(self), NUM2LL(i64));
+ return Qnil;
+}
+
+VALUE rb_thrift_binary_proto_write_double(VALUE self, VALUE dub) {
+ CHECK_NIL(dub);
+ // Unfortunately, bitwise_cast doesn't work in C. Bad C!
+ union {
+ double f;
+ int64_t t;
+ } transfer;
+ transfer.f = RFLOAT(rb_Float(dub))->value;
+ write_i64_direct(GET_TRANSPORT(self), transfer.t);
+
+ return Qnil;
+}
+
+VALUE rb_thrift_binary_proto_write_string(VALUE self, VALUE str) {
+ CHECK_NIL(str);
+ VALUE trans = GET_TRANSPORT(self);
+ // write_i32_direct(trans, RSTRING(str)->len);
+ // rb_funcall(trans, write_method_id, 1, str);
+ write_string_direct(trans, str);
+ return Qnil;
+}
+
+//---------------------------------------
+// interface reading methods
+//---------------------------------------
+
+#define READ(obj, length) rb_funcall(GET_TRANSPORT(obj), read_method_id, 1, INT2FIX(length))
+
+VALUE rb_thrift_binary_proto_read_string(VALUE self);
+VALUE rb_thrift_binary_proto_read_byte(VALUE self);
+VALUE rb_thrift_binary_proto_read_i32(VALUE self);
+VALUE rb_thrift_binary_proto_read_i16(VALUE self);
+
+static char read_byte_direct(VALUE self) {
+ return (RSTRING(READ(self, 1))->ptr)[0];
+}
+
+static int16_t read_i16_direct(VALUE self) {
+ VALUE buf = READ(self, 2);
+ return (int16_t)(((uint8_t)(RSTRING(buf)->ptr[1])) | ((uint16_t)((RSTRING(buf)->ptr[0]) << 8)));
+}
+
+static int32_t read_i32_direct(VALUE self) {
+ VALUE buf = READ(self, 4);
+ return ((uint8_t)(RSTRING(buf)->ptr[3])) |
+ (((uint8_t)(RSTRING(buf)->ptr[2])) << 8) |
+ (((uint8_t)(RSTRING(buf)->ptr[1])) << 16) |
+ (((uint8_t)(RSTRING(buf)->ptr[0])) << 24);
+}
+
+static int64_t read_i64_direct(VALUE self) {
+ uint64_t hi = read_i32_direct(self);
+ uint32_t lo = read_i32_direct(self);
+ return (hi << 32) | lo;
+}
+
+static VALUE get_protocol_exception(VALUE code, VALUE message) {
+ VALUE args[2];
+ args[0] = code;
+ args[1] = message;
+ return rb_class_new_instance(2, (VALUE*)&args, protocol_exception_class);
+}
+
+VALUE rb_thrift_binary_proto_read_message_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thift_binary_proto_read_struct_begin(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thift_binary_proto_read_struct_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thift_binary_proto_read_field_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thift_binary_proto_read_map_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thift_binary_proto_read_list_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thift_binary_proto_read_set_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thrift_binary_proto_read_message_begin(VALUE self) {
+ int version = read_i32_direct(self);
+ if ((version & VERSION_MASK) != VERSION_1) {
+ rb_exc_raise(get_protocol_exception(INT2FIX(BAD_VERSION), rb_str_new2("Missing version identifier")));
+ }
+
+ int type = version & 0x000000ff;
+ VALUE name = rb_thrift_binary_proto_read_string(self);
+ VALUE seqid = rb_thrift_binary_proto_read_i32(self);
+
+ return rb_ary_new3(3, name, INT2FIX(type), seqid);
+}
+
+VALUE rb_thrift_binary_proto_read_field_begin(VALUE self) {
+ int type = read_byte_direct(self);
+ if (type == TTYPE_STOP) {
+ return rb_ary_new3(3, Qnil, INT2FIX(type), INT2FIX(0));
+ } else {
+ VALUE id = rb_thrift_binary_proto_read_i16(self);
+ return rb_ary_new3(3, Qnil, INT2FIX(type), id);
+ }
+}
+
+VALUE rb_thrift_binary_proto_read_map_begin(VALUE self) {
+ VALUE ktype = rb_thrift_binary_proto_read_byte(self);
+ VALUE vtype = rb_thrift_binary_proto_read_byte(self);
+ VALUE size = rb_thrift_binary_proto_read_i32(self);
+ return rb_ary_new3(3, ktype, vtype, size);
+}
+
+VALUE rb_thrift_binary_proto_read_list_begin(VALUE self) {
+ VALUE etype = rb_thrift_binary_proto_read_byte(self);
+ VALUE size = rb_thrift_binary_proto_read_i32(self);
+ return rb_ary_new3(2, etype, size);
+}
+
+VALUE rb_thrift_binary_proto_read_set_begin(VALUE self) {
+ return rb_thrift_binary_proto_read_list_begin(self);
+}
+
+VALUE rb_thrift_binary_proto_read_bool(VALUE self) {
+ char byte = read_byte_direct(self);
+ return byte == 1 ? Qtrue : Qfalse;
+}
+
+VALUE rb_thrift_binary_proto_read_byte(VALUE self) {
+ return INT2FIX(read_byte_direct(self));
+}
+
+VALUE rb_thrift_binary_proto_read_i16(VALUE self) {
+ return INT2FIX(read_i16_direct(self));
+}
+
+VALUE rb_thrift_binary_proto_read_i32(VALUE self) {
+ return INT2NUM(read_i32_direct(self));
+}
+
+VALUE rb_thrift_binary_proto_read_i64(VALUE self) {
+ return LL2NUM(read_i64_direct(self));
+}
+
+VALUE rb_thrift_binary_proto_read_double(VALUE self) {
+ union {
+ double f;
+ int64_t t;
+ } transfer;
+ transfer.t = read_i64_direct(self);
+ return rb_float_new(transfer.f);
+}
+
+VALUE rb_thrift_binary_proto_read_string(VALUE self) {
+ int size = read_i32_direct(self);
+ return READ(self, size);
+}
+
+void Init_binary_protocol_accelerated() {
+ VALUE thrift_binary_protocol_class = rb_const_get(thrift_module, rb_intern("BinaryProtocol"));
+
+ VERSION_1 = rb_num2ll(rb_const_get(thrift_binary_protocol_class, rb_intern("VERSION_1")));
+ VERSION_MASK = rb_num2ll(rb_const_get(thrift_binary_protocol_class, rb_intern("VERSION_MASK")));
+
+ VALUE bpa_class = rb_define_class_under(thrift_module, "BinaryProtocolAccelerated", thrift_binary_protocol_class);
+
+ rb_define_method(bpa_class, "native?", rb_thrift_binary_proto_native_qmark, 0);
+
+ rb_define_method(bpa_class, "write_message_begin", rb_thrift_binary_proto_write_message_begin, 3);
+ rb_define_method(bpa_class, "write_field_begin", rb_thrift_binary_proto_write_field_begin, 3);
+ rb_define_method(bpa_class, "write_field_stop", rb_thrift_binary_proto_write_field_stop, 0);
+ rb_define_method(bpa_class, "write_map_begin", rb_thrift_binary_proto_write_map_begin, 3);
+ rb_define_method(bpa_class, "write_list_begin", rb_thrift_binary_proto_write_list_begin, 2);
+ rb_define_method(bpa_class, "write_set_begin", rb_thrift_binary_proto_write_set_begin, 2);
+ rb_define_method(bpa_class, "write_byte", rb_thrift_binary_proto_write_byte, 1);
+ rb_define_method(bpa_class, "write_bool", rb_thrift_binary_proto_write_bool, 1);
+ rb_define_method(bpa_class, "write_i16", rb_thrift_binary_proto_write_i16, 1);
+ rb_define_method(bpa_class, "write_i32", rb_thrift_binary_proto_write_i32, 1);
+ rb_define_method(bpa_class, "write_i64", rb_thrift_binary_proto_write_i64, 1);
+ rb_define_method(bpa_class, "write_double", rb_thrift_binary_proto_write_double, 1);
+ rb_define_method(bpa_class, "write_string", rb_thrift_binary_proto_write_string, 1);
+ // unused methods
+ rb_define_method(bpa_class, "write_message_end", rb_thrift_binary_proto_write_message_end, 0);
+ rb_define_method(bpa_class, "write_struct_begin", rb_thrift_binary_proto_write_struct_begin, 1);
+ rb_define_method(bpa_class, "write_struct_end", rb_thrift_binary_proto_write_struct_end, 0);
+ rb_define_method(bpa_class, "write_field_end", rb_thrift_binary_proto_write_field_end, 0);
+ rb_define_method(bpa_class, "write_map_end", rb_thrift_binary_proto_write_map_end, 0);
+ rb_define_method(bpa_class, "write_list_end", rb_thrift_binary_proto_write_list_end, 0);
+ rb_define_method(bpa_class, "write_set_end", rb_thrift_binary_proto_write_set_end, 0);
+
+
+
+ rb_define_method(bpa_class, "read_message_begin", rb_thrift_binary_proto_read_message_begin, 0);
+ rb_define_method(bpa_class, "read_field_begin", rb_thrift_binary_proto_read_field_begin, 0);
+ rb_define_method(bpa_class, "read_map_begin", rb_thrift_binary_proto_read_map_begin, 0);
+ rb_define_method(bpa_class, "read_list_begin", rb_thrift_binary_proto_read_list_begin, 0);
+ rb_define_method(bpa_class, "read_set_begin", rb_thrift_binary_proto_read_set_begin, 0);
+ rb_define_method(bpa_class, "read_byte", rb_thrift_binary_proto_read_byte, 0);
+ rb_define_method(bpa_class, "read_bool", rb_thrift_binary_proto_read_bool, 0);
+ rb_define_method(bpa_class, "read_i16", rb_thrift_binary_proto_read_i16, 0);
+ rb_define_method(bpa_class, "read_i32", rb_thrift_binary_proto_read_i32, 0);
+ rb_define_method(bpa_class, "read_i64", rb_thrift_binary_proto_read_i64, 0);
+ rb_define_method(bpa_class, "read_double", rb_thrift_binary_proto_read_double, 0);
+ rb_define_method(bpa_class, "read_string", rb_thrift_binary_proto_read_string, 0);
+ // unused methods
+ rb_define_method(bpa_class, "read_message_end", rb_thrift_binary_proto_read_message_end, 0);
+ rb_define_method(bpa_class, "read_struct_begin", rb_thift_binary_proto_read_struct_begin, 0);
+ rb_define_method(bpa_class, "read_struct_end", rb_thift_binary_proto_read_struct_end, 0);
+ rb_define_method(bpa_class, "read_field_end", rb_thift_binary_proto_read_field_end, 0);
+ rb_define_method(bpa_class, "read_map_end", rb_thift_binary_proto_read_map_end, 0);
+ rb_define_method(bpa_class, "read_list_end", rb_thift_binary_proto_read_list_end, 0);
+ rb_define_method(bpa_class, "read_set_end", rb_thift_binary_proto_read_set_end, 0);
+
+ // set up native method table
+ native_proto_method_table *npmt;
+ npmt = ALLOC(native_proto_method_table);
+
+ npmt->write_field_begin = rb_thrift_binary_proto_write_field_begin;
+ npmt->write_field_stop = rb_thrift_binary_proto_write_field_stop;
+ npmt->write_map_begin = rb_thrift_binary_proto_write_map_begin;
+ npmt->write_list_begin = rb_thrift_binary_proto_write_list_begin;
+ npmt->write_set_begin = rb_thrift_binary_proto_write_set_begin;
+ npmt->write_byte = rb_thrift_binary_proto_write_byte;
+ npmt->write_bool = rb_thrift_binary_proto_write_bool;
+ npmt->write_i16 = rb_thrift_binary_proto_write_i16;
+ npmt->write_i32 = rb_thrift_binary_proto_write_i32;
+ npmt->write_i64 = rb_thrift_binary_proto_write_i64;
+ npmt->write_double = rb_thrift_binary_proto_write_double;
+ npmt->write_string = rb_thrift_binary_proto_write_string;
+ npmt->write_message_end = rb_thrift_binary_proto_write_message_end;
+ npmt->write_struct_begin = rb_thrift_binary_proto_write_struct_begin;
+ npmt->write_struct_end = rb_thrift_binary_proto_write_struct_end;
+ npmt->write_field_end = rb_thrift_binary_proto_write_field_end;
+ npmt->write_map_end = rb_thrift_binary_proto_write_map_end;
+ npmt->write_list_end = rb_thrift_binary_proto_write_list_end;
+ npmt->write_set_end = rb_thrift_binary_proto_write_set_end;
+
+ npmt->read_message_begin = rb_thrift_binary_proto_read_message_begin;
+ npmt->read_field_begin = rb_thrift_binary_proto_read_field_begin;
+ npmt->read_map_begin = rb_thrift_binary_proto_read_map_begin;
+ npmt->read_list_begin = rb_thrift_binary_proto_read_list_begin;
+ npmt->read_set_begin = rb_thrift_binary_proto_read_set_begin;
+ npmt->read_byte = rb_thrift_binary_proto_read_byte;
+ npmt->read_bool = rb_thrift_binary_proto_read_bool;
+ npmt->read_i16 = rb_thrift_binary_proto_read_i16;
+ npmt->read_i32 = rb_thrift_binary_proto_read_i32;
+ npmt->read_i64 = rb_thrift_binary_proto_read_i64;
+ npmt->read_double = rb_thrift_binary_proto_read_double;
+ npmt->read_string = rb_thrift_binary_proto_read_string;
+ npmt->read_message_end = rb_thrift_binary_proto_read_message_end;
+ npmt->read_struct_begin = rb_thift_binary_proto_read_struct_begin;
+ npmt->read_struct_end = rb_thift_binary_proto_read_struct_end;
+ npmt->read_field_end = rb_thift_binary_proto_read_field_end;
+ npmt->read_map_end = rb_thift_binary_proto_read_map_end;
+ npmt->read_list_end = rb_thift_binary_proto_read_list_end;
+ npmt->read_set_end = rb_thift_binary_proto_read_set_end;
+
+ VALUE method_table_object = Data_Wrap_Struct(rb_cObject, 0, free, npmt);
+ rb_const_set(bpa_class, rb_intern("@native_method_table"), method_table_object);
+}
\ No newline at end of file
--- /dev/null
+
+void Init_binary_protocol_accelerated();
\ No newline at end of file
+++ /dev/null
-// Half of this file comes from contributions from Nitay Joffe (nitay@powerset.com)
-// Much of the rest (almost) directly ported (or pulled) from thrift-py's fastbinary.c
-// Everything else via Kevin Clark (kevin@powerset.com)
-#include <stdint.h>
-#include <stdbool.h>
-
-#include <ruby.h>
-#include <st.h>
-#include <netinet/in.h>
-
-// #define __DEBUG__
-
-#ifndef HAVE_STRLCPY
-
-static
-size_t
-strlcpy (char *dst, const char *src, size_t dst_sz)
-{
- size_t n;
-
- for (n = 0; n < dst_sz; n++) {
- if ((*dst++ = *src++) == '\0')
- break;
- }
-
- if (n < dst_sz)
- return n;
- if (n > 0)
- *(dst - 1) = '\0';
- return n + strlen (src);
-}
-
-#endif
-
-#define dbg() fprintf(stderr, "%s:%d\n", __FUNCTION__, __LINE__)
-
-
-// TODO (kevinclark): This was here from the patch/python. Not sure
-// If it's actually that big a pain. Need to look into pulling
-// From the right place
-
-// Stolen out of TProtocol.h.
-// It would be a huge pain to have both get this from one place.
-
-enum TType {
- T_STOP = 0,
- T_BOOL = 2,
- T_BYTE = 3,
- T_I16 = 6,
- T_I32 = 8,
- T_I64 = 10,
- T_DBL = 4,
- T_STR = 11,
- T_STRCT = 12,
- T_MAP = 13,
- T_SET = 14,
- T_LIST = 15
- // T_VOID = 1,
- // T_I08 = 3,
- // T_U64 = 9,
- // T_UTF7 = 11,
- // T_UTF8 = 16,
- // T_UTF16 = 17
-};
-
-#define IS_CONTAINER(x) (x == T_MAP || x == T_SET || x == T_LIST)
-
-// Same comment as the enum. Sorry.
-#ifdef HAVE_ENDIAN_H
-#include <endian.h>
-#endif
-
-#ifndef __BYTE_ORDER
-# if defined(BYTE_ORDER) && defined(LITTLE_ENDIAN) && defined(BIG_ENDIAN)
-# define __BYTE_ORDER BYTE_ORDER
-# define __LITTLE_ENDIAN LITTLE_ENDIAN
-# define __BIG_ENDIAN BIG_ENDIAN
-# else
-# error "Cannot determine endianness"
-# endif
-#endif
-
-#if __BYTE_ORDER == __BIG_ENDIAN
-# define ntohll(n) (n)
-# define htonll(n) (n)
-#elif __BYTE_ORDER == __LITTLE_ENDIAN
-# if defined(__GNUC__) && defined(__GLIBC__)
-# include <byteswap.h>
-# define ntohll(n) bswap_64(n)
-# define htonll(n) bswap_64(n)
-# else /* GNUC & GLIBC */
-# define ntohll(n) ( (((unsigned long long)ntohl(n)) << 32) + ntohl(n >> 32) )
-# define htonll(n) ( (((unsigned long long)htonl(n)) << 32) + htonl(n >> 32) )
-# endif /* GNUC & GLIBC */
-#else /* __BYTE_ORDER */
-# error "Can't define htonll or ntohll!"
-#endif
-
-
-// -----------------------------------------------------------------------------
-// Cached interned strings and such
-// -----------------------------------------------------------------------------
-
-static VALUE class_tbpa;
-static VALUE m_thrift;
-static VALUE rb_cSet;
-static ID type_sym;
-static ID class_sym;
-static ID key_sym;
-static ID value_sym;
-static ID element_sym;
-static ID name_sym;
-static ID fields_id;
-static ID consume_bang_id;
-static ID string_buffer_id;
-static ID borrow_id;
-static ID keys_id;
-static ID entries_id;
-
-static const uint32_t VERSION_MASK = 0xffff0000;
-static const uint32_t VERSION_1 = 0x80010000;
-
-// -----------------------------------------------------------------------------
-// Structs so I don't have to keep calling rb_hash_aref
-// -----------------------------------------------------------------------------
-
-// { :type => field[:type],
-// :class => field[:class],
-// :key => field[:key],
-// :value => field[:value],
-// :element => field[:element] }
-
-struct _thrift_map;
-struct _field_spec;
-
-typedef union {
- VALUE class;
- struct _thrift_map* map;
- struct _field_spec* element;
-} container_data;
-
-typedef struct _field_spec {
- int type;
- char* name;
- container_data data;
-} field_spec;
-
-typedef struct _thrift_map {
- field_spec* key;
- field_spec* value;
-} thrift_map;
-
-
-static void free_field_spec(field_spec* spec) {
- switch(spec->type) {
- case T_LIST:
- case T_SET:
- free_field_spec(spec->data.element);
- break;
-
- case T_MAP:
- free_field_spec(spec->data.map->key);
- free_field_spec(spec->data.map->value);
- free(spec->data.map);
- break;
- }
-
- free(spec);
-}
-
-// Parses a ruby field spec into a C struct
-//
-// Simple fields look like:
-// { :name => .., :type => .. }
-// Structs add the :class attribute
-// Maps adds :key and :value attributes, field specs
-// Lists and Sets add an :element, a field spec
-static field_spec* parse_field_spec(VALUE field_data) {
- int type = NUM2INT(rb_hash_aref(field_data, type_sym));
- VALUE name = rb_hash_aref(field_data, name_sym);
- field_spec* spec = (field_spec *) malloc(sizeof(field_spec));
-
-#ifdef __DEBUG__ // No need for this in prod since I set all the fields
- bzero(spec, sizeof(field_spec));
-#endif
-
- spec->type = type;
-
- if (!NIL_P(name)) {
- spec->name = StringValuePtr(name);
- } else {
- spec->name = NULL;
- }
-
- switch(type) {
- case T_STRCT: {
- spec->data.class = rb_hash_aref(field_data, class_sym);
- break;
- }
-
- case T_MAP: {
- VALUE key_fields = rb_hash_aref(field_data, key_sym);
- VALUE value_fields = rb_hash_aref(field_data, value_sym);
- thrift_map* map = (thrift_map *) malloc(sizeof(thrift_map));
-
- map->key = parse_field_spec(key_fields);
- map->value = parse_field_spec(value_fields);
- spec->data.map = map;
-
- break;
- }
-
- case T_LIST:
- case T_SET:
- {
- VALUE list_fields = rb_hash_aref(field_data, element_sym);
- spec->data.element = parse_field_spec(list_fields);
- break;
- }
- }
-
- return spec;
-}
-
-
-// -----------------------------------------------------------------------------
-// Serialization routines
-// -----------------------------------------------------------------------------
-
-
-// write_*(VALUE buf, ...) takes a value and adds it to a Ruby string buffer,
-// in network order
-static void write_byte(VALUE buf, int8_t val) {
- rb_str_buf_cat(buf, (char*)&val, sizeof(int8_t));
-}
-
-static void write_i16(VALUE buf, int16_t val) {
- int16_t net = (int16_t)htons(val);
- rb_str_buf_cat(buf, (char*)&net, sizeof(int16_t));
-}
-
-static void write_i32(VALUE buf, int32_t val) {
- int32_t net = (int32_t)htonl(val);
- rb_str_buf_cat(buf, (char*)&net, sizeof(int32_t));
-}
-
-static void write_i64(VALUE buf, int64_t val) {
- int64_t net = (int64_t)htonll(val);
- rb_str_buf_cat(buf, (char*)&net, sizeof(int64_t));
-}
-
-static void write_double(VALUE buf, double dub) {
- // Unfortunately, bitwise_cast doesn't work in C. Bad C!
- union {
- double f;
- int64_t t;
- } transfer;
- transfer.f = dub;
- write_i64(buf, transfer.t);
-}
-
-static void write_string(VALUE buf, char* str, size_t len) {
- write_i32(buf, len);
- rb_str_buf_cat(buf, str, len);
-}
-
-// Some functions macro'd out because they're nops for the binary protocol
-// but placeholders were desired in case things change
-#define write_struct_begin(buf)
-#define write_struct_end(buf)
-
-static void write_field_begin(VALUE buf, char* name, int type, int fid) {
-#ifdef __DEBUG__
- fprintf(stderr, "Writing field beginning: %s %d %d\n", name, type, fid);
-#endif
-
- write_byte(buf, (int8_t)type);
- write_i16(buf, (int16_t)fid);
-}
-
-#define write_field_end(buf)
-
-static void write_field_stop(VALUE buf) {
- write_byte(buf, T_STOP);
-}
-
-static void write_map_begin(VALUE buf, int8_t ktype, int8_t vtype, int32_t sz) {
- write_byte(buf, ktype);
- write_byte(buf, vtype);
- write_i32(buf, sz);
-}
-
-#define write_map_end(buf);
-
-static void write_list_begin(VALUE buf, int type, int sz) {
- write_byte(buf, type);
- write_i32(buf, sz);
-}
-
-#define write_list_end(buf)
-
-static void write_set_begin(VALUE buf, int type, int sz) {
- write_byte(buf, type);
- write_i32(buf, sz);
-}
-
-#define write_set_end(buf)
-
-static void binary_encoding(VALUE buf, VALUE obj, int type);
-
-// Handles container types: Map, Set, List
-static void write_container(VALUE buf, VALUE value, field_spec* spec) {
- int sz, i;
-
- switch(spec->type) {
- case T_MAP: {
- VALUE keys;
- VALUE key;
- VALUE val;
-
- Check_Type(value, T_HASH);
-
- keys = rb_funcall(value, keys_id, 0);
-
- sz = RARRAY(keys)->len;
-
- write_map_begin(buf, spec->data.map->key->type, spec->data.map->value->type, sz);
-
- for (i = 0; i < sz; i++) {
- key = rb_ary_entry(keys, i);
- val = rb_hash_aref(value, key);
-
- if (IS_CONTAINER(spec->data.map->key->type)) {
- write_container(buf, key, spec->data.map->key);
- } else {
- binary_encoding(buf, key, spec->data.map->key->type);
- }
-
- if (IS_CONTAINER(spec->data.map->value->type)) {
- write_container(buf, val, spec->data.map->value);
- } else {
- binary_encoding(buf, val, spec->data.map->value->type);
- }
- }
-
- write_map_end(buf);
-
- break;
- }
-
- case T_LIST: {
- Check_Type(value, T_ARRAY);
-
- sz = RARRAY(value)->len;
-
- write_list_begin(buf, spec->data.element->type, sz);
- for (i = 0; i < sz; ++i) {
- VALUE val = rb_ary_entry(value, i);
- if (IS_CONTAINER(spec->data.element->type)) {
- write_container(buf, val, spec->data.element);
- } else {
- binary_encoding(buf, val, spec->data.element->type);
- }
- }
- write_list_end(buf);
- break;
- }
-
- case T_SET: {
- VALUE items;
-
- if (TYPE(value) == T_ARRAY) {
- items = value;
- } else {
- if (rb_cSet == CLASS_OF(value)) {
- items = rb_funcall(value, entries_id, 0);
- } else {
- Check_Type(value, T_HASH);
- items = rb_funcall(value, keys_id, 0);
- }
- }
-
- sz = RARRAY(items)->len;
-
- write_set_begin(buf, spec->data.element->type, sz);
-
- for (i = 0; i < sz; i++) {
- VALUE val = rb_ary_entry(items, i);
- if (IS_CONTAINER(spec->data.element->type)) {
- write_container(buf, val, spec->data.element);
- } else {
- binary_encoding(buf, val, spec->data.element->type);
- }
- }
-
- write_set_end(buf);
- break;
- }
- }
-}
-
-// Takes the field id, data to be encoded, buffer and enclosing object
-// to be encoded. buf and obj passed as a ruby array for rb_hash_foreach.
-// TODO(kevinclark): See if they can be passed individually to avoid object
-// creation
-static int encode_field(VALUE fid, VALUE data, VALUE ary) {
- field_spec *spec = parse_field_spec(data);
-
- VALUE buf = rb_ary_entry(ary, 0);
- VALUE obj = rb_ary_entry(ary, 1);
- char name_buf[128];
-
- name_buf[0] = '@';
- strlcpy(&name_buf[1], spec->name, sizeof(name_buf) - 1);
-
- VALUE value = rb_ivar_get(obj, rb_intern(name_buf));
-
- if (NIL_P(value)) {
- free_field_spec(spec);
- return 0;
- }
-
- write_field_begin(buf, spec->name, spec->type, NUM2INT(fid));
-
- if (IS_CONTAINER(spec->type)) {
- write_container(buf, value, spec);
- } else {
- binary_encoding(buf, value, spec->type);
- }
- write_field_end(buf);
-
- free_field_spec(spec);
-
- return 0;
-}
-
-// -----------------------------------------------------------------------------
-// TFastBinaryProtocol's main encoding loop
-// -----------------------------------------------------------------------------
-
-static void binary_encoding(VALUE buf, VALUE obj, int type) {
-#ifdef __DEBUG__
- rb_p(rb_str_new2("Encoding binary (buf, obj, type)"));
- rb_p(rb_inspect(buf));
- rb_p(rb_inspect(obj));
- rb_p(rb_inspect(INT2FIX(type)));
-#endif
-
- switch(type) {
- case T_BOOL:
- if RTEST(obj) {
- write_byte(buf, 1);
- }
- else {
- write_byte(buf, 0);
- }
-
- break;
-
- case T_BYTE:
- write_byte(buf, NUM2INT(obj));
- break;
-
- case T_I16:
- write_i16(buf, NUM2INT(obj));
- break;
-
- case T_I32:
- write_i32(buf, NUM2INT(obj));
- break;
-
- case T_I64:
- write_i64(buf, rb_num2ll(obj));
- break;
-
- case T_DBL:
- write_double(buf, NUM2DBL(obj));
- break;
-
- case T_STR: {
- // make sure to call StringValuePtr before calling RSTRING
- char *ptr = StringValuePtr(obj);
- write_string(buf, ptr, RSTRING(obj)->len);
- break;
- }
-
- case T_STRCT: {
- // rb_hash_foreach has to take args as a ruby array
- VALUE args = rb_ary_new3(2, buf, obj);
- VALUE fields = rb_const_get(CLASS_OF(obj), fields_id);
-
- write_struct_begin(buf);
-
- rb_hash_foreach(fields, encode_field, args);
-
- write_field_stop(buf);
- write_struct_end(buf);
- break;
- }
-
- default: {
- rb_raise(rb_eNotImpError, "Unknown type for binary_encoding: %d", type);
- }
- }
-}
-
-// obj is always going to be a TSTRCT
-static VALUE tbpa_encode_binary(VALUE self, VALUE obj) {
- VALUE buf = rb_str_buf_new(1024);
- binary_encoding(buf, obj, T_STRCT);
- return buf;
-}
-
-
-// -----------------------------------------------------------------------------
-// Read stuff
-// -----------------------------------------------------------------------------
-
-typedef struct {
- char* name;
- int8_t type;
- int16_t id;
-} field_header;
-
-typedef struct {
- int8_t key_type;
- int8_t val_type;
- int num_entries;
-} map_header;
-
-typedef struct {
- int8_t type;
- int num_elements;
-} list_header;
-
-typedef list_header set_header;
-
-typedef struct {
- char* data;
- int pos;
- int len;
- VALUE trans;
-} decode_buffer;
-
-typedef struct {
- char* ptr;
- int len;
-} thrift_string;
-
-#define read_struct_begin(buf)
-#define read_struct_end(buf)
-
-// This prototype is required to be able to run a call through rb_protect
-// which rescues from ruby exceptions
-static VALUE protectable_consume(VALUE args) {
- VALUE trans = rb_ary_entry(args, 0);
- VALUE size = rb_ary_entry(args, 1);
-
- return rb_funcall(trans, consume_bang_id, 1, size);
-}
-
-// Clears size bytes from the transport's string buffer
-static bool consume(decode_buffer* buf, int32_t size) {
- if (size != 0) {
- VALUE ret;
- VALUE args = rb_ary_new3(2, buf->trans, INT2FIX(size));
- int status = 0;
-
- ret = rb_protect(protectable_consume, args, &status);
-
- if (status) {
- return false;
- } else {
- return true;
- }
- }
-
- // Nothing to consume, we're all good
- return true;
-}
-
-// This prototype is required to be able to run a call through rb_protect
-// which rescues from ruby exceptions
-static VALUE protectable_borrow(VALUE args) {
- VALUE trans = rb_ary_entry(args, 0);
-
- switch(RARRAY(args)->len) {
- case 1:
- return rb_funcall(trans, borrow_id, 0);
-
- case 2: {
- VALUE size = rb_ary_entry(args, 1);
- return rb_funcall(trans, borrow_id, 1, size);
- }
- }
-
- return Qnil;
-}
-
-// Calls into the transport to get the available string buffer
-static bool borrow(decode_buffer* buf, int32_t size, VALUE* dst) {
- int status = 0;
- VALUE args;
-
- if (size == 0) {
- args = rb_ary_new3(1, buf->trans);
- } else {
- args = rb_ary_new3(2, buf->trans, INT2FIX(size));
- }
-
- *dst = rb_protect(protectable_borrow, args, &status);
-
- return (status == 0);
-}
-
-// Refills the buffer by calling borrow. If buf->pos is nonzero that number of bytes
-// is cleared through consume.
-//
-// returns: 0 on success, non-zero on failure. On error buf is unchanged.
-static int fill_buffer(decode_buffer* buf, int32_t req_len) {
- VALUE refill;
-
- if (!consume(buf, buf->pos)) {
- return -1;
- }
-
- if (!borrow(buf, req_len, &refill)) {
- return -2;
- }
-
- buf->data = StringValuePtr(refill);
- buf->len = RSTRING(refill)->len;
- buf->pos = 0;
-
- return 0;
-}
-
-
-// read_bytes pulls a number of bytes (size) from the buffer, refilling if needed,
-// and places them in dst. This should _always_ be used used when reading from the buffer
-// or buffered transports will be upset with you.
-static bool read_bytes(decode_buffer* buf, void* dst, size_t size) {
- int avail = (buf->len - buf->pos);
-
- if (size <= avail) {
- memcpy(dst, buf->data + buf->pos, size);
- buf->pos += size;
- } else {
-
- if (avail > 0) {
- // Copy what we can
- memcpy(dst, buf->data + buf->pos, avail);
- buf->pos += avail;
- }
-
- if (fill_buffer(buf, size - avail) < 0) {
- return false;
- }
-
- memcpy(dst + avail, buf->data, size - avail);
- buf->pos += size - avail;
- }
-
- return true;
-}
-
-// -----------------------------------------------------------------------------
-// Helpers for grabbing specific types from the buffer
-// -----------------------------------------------------------------------------
-
-static bool read_byte(decode_buffer* buf, int8_t* data) {
- return read_bytes(buf, data, sizeof(int8_t));
-}
-
-static bool read_int16(decode_buffer* buf, int16_t* data) {
- bool success = read_bytes(buf, data, sizeof(int16_t));
- *data = ntohs(*data);
-
- return success;
-}
-
-static bool read_int32(decode_buffer* buf, int32_t* data) {
- bool success = read_bytes(buf, data, sizeof(int32_t));
- *data = ntohl(*data);
-
- return success;
-}
-
-static bool read_int64(decode_buffer* buf, int64_t* data) {
- bool success = read_bytes(buf, data, sizeof(int64_t));
- *data = ntohll(*data);
-
- return success;
-}
-
-static bool read_double(decode_buffer* buf, double* data) {
- return read_int64(buf, (int64_t*)data);
-}
-
-static bool read_string(decode_buffer* buf, VALUE* data) {
- int len;
-
- if (!read_int32(buf, &len)) {
- return false;
- }
-
- if (buf->len - buf->pos >= len) {
- *data = rb_str_new(buf->data + buf->pos, len);
- buf->pos += len;
- }
- else {
- char* str;
-
- if ((str = (char*) malloc(len)) == NULL) {
- return false;
- }
-
- if (!read_bytes(buf, str, len)) {
- free(str);
- return false;
- }
-
- *data = rb_str_new(str, len);
-
- free(str);
- }
-
- return true;
-}
-
-static bool read_field_begin(decode_buffer* buf, field_header* header) {
-#ifdef __DEBUG__ // No need for this in prod since I set all the fields
- bzero(header, sizeof(field_header));
-#endif
-
- header->name = NULL;
-
- if (!read_byte(buf, &header->type)) {
- return false;
- }
-
- if (header->type == T_STOP) {
- header->id = 0;
- } else {
- if (!read_int16(buf, &header->id)) {
- return false;
- }
- }
-
- return true;
-}
-
-#define read_field_end(buf)
-
-static bool read_map_begin(decode_buffer* buf, map_header* header) {
-#ifdef __DEBUG__ // No need for this in prod since I set all the fields
- bzero(header, sizeof(map_header));
-#endif
-
- return (read_byte(buf, &header->key_type) &&
- read_byte(buf, &header->val_type) &&
- read_int32(buf, &header->num_entries));
-}
-
-#define read_map_end(buf)
-
-static bool read_list_begin(decode_buffer* buf, list_header* header) {
-#ifdef __DEBUG__ // No need for this in prod since I set all the fields
- bzero(header, sizeof(list_header));
-#endif
-
- if (!read_byte(buf, &header->type) || !read_int32(buf, &header->num_elements)) {
- return false;
- } else {
- return true;
- }
-}
-
-#define read_list_end(buf)
-
-#define read_set_begin read_list_begin
-#define read_set_end read_list_end
-
-
-// High level reader function with ruby type coercion
-static bool read_type(int type, decode_buffer* buf, VALUE* dst) {
- switch(type) {
- case T_BOOL: {
- int8_t byte;
-
- if (!read_byte(buf, &byte)) {
- return false;
- }
-
- if (0 == byte) {
- *dst = Qfalse;
- } else {
- *dst = Qtrue;
- }
-
- break;
- }
-
- case T_BYTE: {
- int8_t byte;
-
- if (!read_byte(buf, &byte)) {
- return false;
- }
-
- *dst = INT2FIX(byte);
- break;
- }
-
- case T_I16: {
- int16_t i16;
-
- if (!read_int16(buf, &i16)) {
- return false;
- }
-
- *dst = INT2FIX(i16);
- break;
- }
-
- case T_I32: {
- int32_t i32;
-
- if (!read_int32(buf, &i32)) {
- return false;
- }
-
- *dst = INT2NUM(i32);
- break;
- }
-
- case T_I64: {
- int64_t i64;
-
- if (!read_int64(buf, &i64)) {
- return false;
- }
-
- *dst = rb_ll2inum(i64);
- break;
- }
-
- case T_DBL: {
- double dbl;
-
- if (!read_double(buf, &dbl)) {
- return false;
- }
-
- *dst = rb_float_new(dbl);
- break;
- }
-
- case T_STR: {
- VALUE str;
-
- if (!read_string(buf, &str)) {
- return false;
- }
-
- *dst = str;
- break;
- }
- }
-
- return true;
-}
-
-// TODO(kevinclark): Now that read_string does a malloc,
-// This maybe could be modified to avoid that, and the type coercion
-
-// Read the bytes but don't do anything with the value
-static bool skip_type(int type, decode_buffer* buf) {
- switch (type) {
- case T_STRCT:
- read_struct_begin(buf);
- while (true) {
- field_header header;
-
- if (!read_field_begin(buf, &header)) {
- return false;
- }
-
- if (header.type == T_STOP) {
- break;
- }
-
- if (!skip_type(header.type, buf)) {
- return false;
- }
-
- read_field_end(buf);
- }
- read_struct_end(buf);
-
- break;
-
- case T_MAP: {
- int i;
- map_header header;
-
- if (!read_map_begin(buf, &header)) {
- return false;
- }
-
- for (i = 0; i < header.num_entries; ++i) {
- if (!skip_type(header.key_type, buf)) {
- return false;
- }
- if (!skip_type(header.val_type, buf)) {
- return false;
- }
- }
-
- read_map_end(buf);
- break;
- }
-
- case T_SET: {
- int i;
- set_header header;
-
- if (!read_set_begin(buf, &header)) {
- return false;
- }
-
- for (i = 0; i < header.num_elements; ++i) {
- if (!skip_type(header.type, buf)) {
- return false;
- }
- }
-
- read_set_end(buf);
- break;
- }
-
- case T_LIST: {
- int i;
- list_header header;
-
- if (!read_list_begin(buf, &header)) {
- return false;
- }
-
- for (i = 0; i < header.num_elements; ++i) {
- if (!skip_type(header.type, buf)) {
- return false;
- }
- }
-
- read_list_end(buf);
- break;
- }
-
- default: {
- VALUE v;
- if (!read_type(type, buf, &v)) {
- return false;
- }
- }
- }
-
- return true;
-}
-
-
-static VALUE read_struct(VALUE obj, decode_buffer* buf);
-
-// Read the right thing from the buffer given the field spec
-// and return the ruby object
-static bool read_field(decode_buffer* buf, field_spec* spec, VALUE* dst) {
- switch (spec->type) {
- case T_STRCT: {
- VALUE obj = rb_class_new_instance(0, NULL, spec->data.class);
-
- *dst = read_struct(obj, buf);
- break;
- }
-
- case T_MAP: {
- map_header hdr;
- VALUE hsh;
- int i;
-
- read_map_begin(buf, &hdr);
- hsh = rb_hash_new();
-
- for (i = 0; i < hdr.num_entries; ++i) {
- VALUE key, val;
-
- if (!read_field(buf, spec->data.map->key, &key)) {
- return false;
- }
-
- if (!read_field(buf, spec->data.map->value, &val)) {
- return false;
- }
-
- rb_hash_aset(hsh, key, val);
- }
-
- read_map_end(buf);
-
- *dst = hsh;
- break;
- }
-
- case T_LIST: {
- list_header hdr;
- VALUE arr, element;
- int i;
-
- read_list_begin(buf, &hdr);
- arr = rb_ary_new2(hdr.num_elements);
-
- for (i = 0; i < hdr.num_elements; ++i) {
- if (!read_field(buf, spec->data.element, &element)) {
- return false;
- }
-
- rb_ary_push(arr, element);
- }
-
- read_list_end(buf);
-
- *dst = arr;
- break;
- }
-
- case T_SET: {
- VALUE items, item;
- set_header hdr;
- int i;
-
- read_set_begin(buf, &hdr);
- items = rb_ary_new2(hdr.num_elements);
-
- for (i = 0; i < hdr.num_elements; ++i) {
- if (!read_field(buf, spec->data.element, &item)) {
- return false;
- }
-
- rb_ary_push(items, item);
- }
-
- *dst = rb_class_new_instance(1, &items, rb_cSet);
- break;
- }
-
-
- default:
- return read_type(spec->type, buf, dst);
- }
-
- return true;
-}
-
-static void handle_read_error() {
- // If it was an exception, reraise
- if (!NIL_P(ruby_errinfo)) {
- rb_exc_raise(ruby_errinfo);
- } else {
- // Something else went wrong, no idea what would call this yet
- // So far, the only thing to cause failures underneath is ruby
- // exceptions. Follow up on this regularly -- Kevin Clark (TODO)
- rb_raise(rb_eStandardError, "[BUG] Something went wrong in the field reading, but not a ruby exception");
- }
-}
-
-// Fill in the instance variables in an object (thrift struct)
-// from the decode buffer
-static VALUE read_struct(VALUE obj, decode_buffer* buf) {
- VALUE field;
- field_header f_header;
- VALUE value = Qnil;
- VALUE fields = rb_const_get(CLASS_OF(obj), fields_id);
- field_spec* spec;
- char name_buf[128];
-
- read_struct_begin(buf);
-
- while(true) {
- if (!read_field_begin(buf, &f_header)) {
- handle_read_error();
- }
-
- if (T_STOP == f_header.type) {
- break;
- }
-
- field = rb_hash_aref(fields, INT2FIX(f_header.id));
-
- if (NIL_P(field)) {
- if (!skip_type(f_header.type, buf)) {
- handle_read_error();
- return Qnil;
- }
- }
- else {
- spec = parse_field_spec(field);
-
- if (spec->type != f_header.type) {
- if (!skip_type(spec->type, buf)) {
- free_field_spec(spec);
- handle_read_error();
- return Qnil;
- }
- } else {
- // Read busted somewhere (probably borrow/consume), bail
- if (!read_field(buf, spec, &value)) {
- free_field_spec(spec);
- handle_read_error();
- return Qnil;
- }
-
- name_buf[0] = '@';
- strlcpy(&name_buf[1], spec->name, sizeof(name_buf) - 1);
-
- rb_iv_set(obj, name_buf, value);
- }
-
- free_field_spec(spec);
- }
-
- read_field_end(buf);
- }
-
- read_struct_end(buf);
-
- return obj;
-}
-
-
-// Takes an object and transport, and decodes the values in the transport's
-// buffer to fill the object.
-static VALUE tbpa_decode_binary(VALUE self, VALUE obj, VALUE transport) {
- decode_buffer buf;
- VALUE ret_val;
-
- buf.pos = 0; // This needs to be set so an arbitrary number of bytes isn't consumed
- buf.trans = transport; // We need to hold this so the buffer can be refilled
-
- if (fill_buffer(&buf, 0) < 0) {
- handle_read_error();
- return Qnil;
- }
-
-#ifdef __DEBUG__
- rb_p(rb_str_new2("Running decode binary with data:"));
- rb_p(rb_inspect(rb_str_new2(buf.data)));
-#endif
-
- ret_val = read_struct(obj, &buf);
-
- // Consume whatever was read
- consume(&buf, buf.pos);
-
- return ret_val;
-}
-
-// -----------------------------------------------------------------------------
-// These methods are used by the thrift library and need to handled
-// seperately from encode and decode
-// -----------------------------------------------------------------------------
-
-// Read the message header and return it as a ruby array
-static VALUE tbpa_read_message_begin(VALUE self) {
- decode_buffer buf;
- int32_t version, seqid;
- int8_t type;
- VALUE name;
-
- VALUE trans = rb_iv_get(self, "@trans");
-
- buf.pos = 0; // This needs to be set so fill_buffer doesn't consume
- buf.trans = trans; // We need to hold this so the buffer can be refilled
-
-
- if (fill_buffer(&buf, 0) < 0 || !read_int32(&buf, &version)) {
- // Consume whatever was read
- consume(&buf, buf.pos);
- handle_read_error();
- return Qnil;
- }
-
- if ((version & VERSION_MASK) != VERSION_1) {
- VALUE tprotocol_exception = rb_const_get(m_thrift, rb_intern("ProtocolException"));
- VALUE exception = rb_funcall(tprotocol_exception, rb_intern("new"), 2, rb_const_get(tprotocol_exception, rb_intern("BAD_VERSION")), rb_str_new2("Missing version identifier"));
- rb_exc_raise(exception);
- }
-
- type = version & 0x000000ff;
-
- if (!read_string(&buf, &name) || !read_int32(&buf, &seqid)) {
- // Consume whatever was read
- consume(&buf, buf.pos);
- handle_read_error();
- return Qnil;
- }
-
- // Consume whatever was read
- if (consume(&buf, buf.pos) < 0) {
- handle_read_error();
- return Qnil;
- }
-
- return rb_ary_new3(3, name, INT2FIX(type), INT2FIX(seqid));
-}
-
-void Init_binaryprotocolaccelerated()
-{
- m_thrift = rb_const_get(rb_cObject, rb_intern("Thrift"));
- VALUE class_tbinproto = rb_const_get(m_thrift, rb_intern("BinaryProtocol"));
- class_tbpa = rb_define_class_under(m_thrift, "BinaryProtocolAccelerated", class_tbinproto);
- type_sym = ID2SYM(rb_intern("type"));
- class_sym = ID2SYM(rb_intern("class"));
- key_sym = ID2SYM(rb_intern("key"));
- value_sym = ID2SYM(rb_intern("value"));
- name_sym = ID2SYM(rb_intern("name"));
- fields_id = rb_intern("FIELDS");
- element_sym = ID2SYM(rb_intern("element"));
- consume_bang_id = rb_intern("consume!");
- string_buffer_id = rb_intern("string_buffer");
- borrow_id = rb_intern("borrow");
- keys_id = rb_intern("keys");
- entries_id = rb_intern("entries");
- rb_cSet = rb_const_get(rb_cObject, rb_intern("Set"));
-
- // For fast access
- rb_define_method(class_tbpa, "encode_binary", tbpa_encode_binary, 1);
- rb_define_method(class_tbpa, "decode_binary", tbpa_decode_binary, 2);
- rb_define_method(class_tbpa, "read_message_begin", tbpa_read_message_begin, 0);
-
-}
--- /dev/null
+
+extern int TTYPE_STOP;
+extern int TTYPE_BOOL;
+extern int TTYPE_BYTE;
+extern int TTYPE_I16;
+extern int TTYPE_I32;
+extern int TTYPE_I64;
+extern int TTYPE_DOUBLE;
+extern int TTYPE_STRING;
+extern int TTYPE_MAP;
+extern int TTYPE_SET;
+extern int TTYPE_LIST;
+extern int TTYPE_STRUCT;
+
+extern ID validate_method_id;
+extern ID write_struct_begin_method_id;
+extern ID write_struct_end_method_id;
+extern ID write_field_begin_method_id;
+extern ID write_field_end_method_id;
+extern ID write_boolean_method_id;
+extern ID write_byte_method_id;
+extern ID write_i16_method_id;
+extern ID write_i32_method_id;
+extern ID write_i64_method_id;
+extern ID write_double_method_id;
+extern ID write_string_method_id;
+extern ID write_map_begin_method_id;
+extern ID write_map_end_method_id;
+extern ID write_list_begin_method_id;
+extern ID write_list_end_method_id;
+extern ID write_set_begin_method_id;
+extern ID write_set_end_method_id;
+extern ID size_method_id;
+extern ID read_bool_method_id;
+extern ID read_byte_method_id;
+extern ID read_i16_method_id;
+extern ID read_i32_method_id;
+extern ID read_i64_method_id;
+extern ID read_string_method_id;
+extern ID read_double_method_id;
+extern ID read_map_begin_method_id;
+extern ID read_map_end_method_id;
+extern ID read_list_begin_method_id;
+extern ID read_list_end_method_id;
+extern ID read_set_begin_method_id;
+extern ID read_set_end_method_id;
+extern ID read_struct_begin_method_id;
+extern ID read_struct_end_method_id;
+extern ID read_field_begin_method_id;
+extern ID read_field_end_method_id;
+extern ID keys_method_id;
+extern ID entries_method_id;
+extern ID name_method_id;
+extern ID sort_method_id;
+extern ID write_field_stop_method_id;
+extern ID skip_method_id;
+extern ID write_method_id;
+extern ID read_method_id;
+extern ID native_qmark_method_id;
+
+extern ID fields_const_id;
+extern ID transport_ivar_id;
+
+extern VALUE type_sym;
+extern VALUE name_sym;
+extern VALUE key_sym;
+extern VALUE value_sym;
+extern VALUE element_sym;
+extern VALUE class_sym;
+
+extern VALUE rb_cSet;
+extern VALUE thrift_module;
+extern VALUE thrift_types_module;
+extern VALUE class_thrift_protocol;
+extern VALUE protocol_exception_class;
\ No newline at end of file
have_func("strlcpy", "string.h")
-create_makefile 'binaryprotocolaccelerated'
+create_makefile 'thrift_native'
--- /dev/null
+#include <ruby.h>
+#include <constants.h>
+
+ID buf_ivar_id;
+ID index_ivar_id;
+
+ID slice_method_id;
+
+int GARBAGE_BUFFER_SIZE;
+
+#define GET_BUF(self) rb_ivar_get(self, buf_ivar_id)
+
+VALUE rb_thrift_memory_buffer_write(VALUE self, VALUE str) {
+ VALUE buf = GET_BUF(self);
+ rb_str_buf_cat(buf, RSTRING(str)->ptr, RSTRING(str)->len);
+ return Qnil;
+}
+
+VALUE rb_thrift_memory_buffer_read(VALUE self, VALUE length_value) {
+ int length = FIX2INT(length_value);
+
+ VALUE index_value = rb_ivar_get(self, index_ivar_id);
+ int index = FIX2INT(index_value);
+
+ VALUE buf = GET_BUF(self);
+ VALUE data = rb_funcall(buf, slice_method_id, 2, index_value, length_value);
+
+ index += length;
+ if (index > RSTRING(buf)->len) {
+ index = RSTRING(buf)->len;
+ }
+ if (index >= GARBAGE_BUFFER_SIZE) {
+ rb_ivar_set(self, buf_ivar_id, rb_funcall(buf, slice_method_id, 2, INT2FIX(index), INT2FIX(-1)));
+ index = 0;
+ }
+
+ rb_ivar_set(self, index_ivar_id, INT2FIX(index));
+ return data;
+}
+
+void Init_memory_buffer() {
+ VALUE thrift_memory_buffer_class = rb_const_get(thrift_module, rb_intern("MemoryBuffer"));
+ rb_define_method(thrift_memory_buffer_class, "write", rb_thrift_memory_buffer_write, 1);
+ rb_define_method(thrift_memory_buffer_class, "read", rb_thrift_memory_buffer_read, 1);
+
+ buf_ivar_id = rb_intern("@buf");
+ index_ivar_id = rb_intern("@index");
+
+ slice_method_id = rb_intern("slice");
+
+ GARBAGE_BUFFER_SIZE = FIX2INT(rb_const_get(thrift_memory_buffer_class, rb_intern("GARBAGE_BUFFER_SIZE")));
+}
\ No newline at end of file
--- /dev/null
+
+void Init_memory_buffer();
\ No newline at end of file
--- /dev/null
+#include <ruby.h>
+#include <protocol.h>
+#include <stdbool.h>
+#include <constants.h>
+#include <struct.h>
+
+static VALUE skip(VALUE self, int ttype) {
+ if (ttype == TTYPE_STOP) {
+ return Qnil;
+ } else if (ttype == TTYPE_BOOL) {
+ rb_funcall(self, read_bool_method_id, 0);
+ } else if (ttype == TTYPE_BYTE) {
+ rb_funcall(self, read_byte_method_id, 0);
+ } else if (ttype == TTYPE_I16) {
+ rb_funcall(self, read_i16_method_id, 0);
+ } else if (ttype == TTYPE_I32) {
+ rb_funcall(self, read_i32_method_id, 0);
+ } else if (ttype == TTYPE_I64) {
+ rb_funcall(self, read_i64_method_id, 0);
+ } else if (ttype == TTYPE_DOUBLE) {
+ rb_funcall(self, read_double_method_id, 0);
+ } else if (ttype == TTYPE_STRING) {
+ rb_funcall(self, read_string_method_id, 0);
+ } else if (ttype == TTYPE_STRUCT) {
+ rb_funcall(self, read_struct_begin_method_id, 0);
+ while (true) {
+ VALUE field_header = rb_funcall(self, read_field_begin_method_id, 0);
+ if (NIL_P(field_header) || FIX2INT(rb_ary_entry(field_header, 1)) == TTYPE_STOP ) {
+ break;
+ }
+ skip(self, FIX2INT(rb_ary_entry(field_header, 1)));
+ rb_funcall(self, read_field_end_method_id, 0);
+ }
+ rb_funcall(self, read_struct_end_method_id, 0);
+ } else if (ttype == TTYPE_MAP) {
+ int i;
+ VALUE map_header = rb_funcall(self, read_map_begin_method_id, 0);
+ int ktype = FIX2INT(rb_ary_entry(map_header, 0));
+ int vtype = FIX2INT(rb_ary_entry(map_header, 1));
+ int size = FIX2INT(rb_ary_entry(map_header, 2));
+
+ for (i = 0; i < size; i++) {
+ skip(self, ktype);
+ skip(self, vtype);
+ }
+ rb_funcall(self, read_map_end_method_id, 0);
+ } else if (ttype == TTYPE_LIST || ttype == TTYPE_SET) {
+ int i;
+ VALUE collection_header = rb_funcall(self, ttype == TTYPE_LIST ? read_list_begin_method_id : read_set_begin_method_id, 0);
+ int etype = FIX2INT(rb_ary_entry(collection_header, 0));
+ int size = FIX2INT(rb_ary_entry(collection_header, 1));
+ for (i = 0; i < size; i++) {
+ skip(self, etype);
+ }
+ rb_funcall(self, ttype == TTYPE_LIST ? read_list_end_method_id : read_set_end_method_id, 0);
+ } else {
+ rb_raise(rb_eNotImpError, "don't know how to skip type %d", ttype);
+ }
+
+ return Qnil;
+}
+
+VALUE rb_thrift_protocol_native_qmark(VALUE self) {
+ return Qfalse;
+}
+
+VALUE rb_thrift_protocol_skip(VALUE protocol, VALUE ttype) {
+ return skip(protocol, FIX2INT(ttype));
+}
+
+VALUE rb_thrift_write_message_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thrift_write_struct_begin(VALUE self, VALUE name) {
+ return Qnil;
+}
+
+VALUE rb_thrift_write_struct_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thrift_write_field_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thrift_write_map_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thrift_write_list_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thrift_write_set_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thrift_read_message_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thift_read_struct_begin(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thift_read_struct_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thift_read_field_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thift_read_map_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thift_read_list_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thift_read_set_end(VALUE self) {
+ return Qnil;
+}
+
+void Init_protocol() {
+ VALUE c_protocol = rb_const_get(thrift_module, rb_intern("Protocol"));
+
+ rb_define_method(c_protocol, "skip", rb_thrift_protocol_skip, 1);
+ rb_define_method(c_protocol, "write_message_end", rb_thrift_write_message_end, 0);
+ rb_define_method(c_protocol, "write_struct_begin", rb_thrift_write_struct_begin, 1);
+ rb_define_method(c_protocol, "write_struct_end", rb_thrift_write_struct_end, 0);
+ rb_define_method(c_protocol, "write_field_end", rb_thrift_write_field_end, 0);
+ rb_define_method(c_protocol, "write_map_end", rb_thrift_write_map_end, 0);
+ rb_define_method(c_protocol, "write_list_end", rb_thrift_write_list_end, 0);
+ rb_define_method(c_protocol, "write_set_end", rb_thrift_write_set_end, 0);
+ rb_define_method(c_protocol, "read_message_end", rb_thrift_read_message_end, 0);
+ rb_define_method(c_protocol, "read_struct_begin", rb_thift_read_struct_begin, 0);
+ rb_define_method(c_protocol, "read_struct_end", rb_thift_read_struct_end, 0);
+ rb_define_method(c_protocol, "read_field_end", rb_thift_read_field_end, 0);
+ rb_define_method(c_protocol, "read_map_end", rb_thift_read_map_end, 0);
+ rb_define_method(c_protocol, "read_list_end", rb_thift_read_list_end, 0);
+ rb_define_method(c_protocol, "read_set_end", rb_thift_read_set_end, 0);
+ rb_define_method(c_protocol, "native?", rb_thrift_protocol_native_qmark, 0);
+
+ // native_proto_method_table *npmt;
+ // npmt = ALLOC(native_proto_method_table);
+ // npmt->write_message_end = rb_thrift_write_message_end;
+ // npmt->write_struct_begin = rb_thrift_write_struct_begin;
+ // npmt->write_struct_end = rb_thrift_write_struct_end;
+ // npmt->write_field_end = rb_thrift_write_field_end;
+ // npmt->write_map_end = rb_thrift_write_map_end;
+ // npmt->write_list_end = rb_thrift_write_list_end;
+ // npmt->write_set_end = rb_thrift_write_set_end;
+ // npmt->read_message_end = rb_thrift_read_message_end;
+ // npmt->read_struct_begin = rb_thift_read_struct_begin;
+ // npmt->read_struct_end = rb_thift_read_struct_end;
+ // npmt->read_field_end = rb_thift_read_field_end;
+ // npmt->read_map_end = rb_thift_read_map_end;
+ // npmt->read_list_end = rb_thift_read_list_end;
+ // npmt->read_set_end = rb_thift_read_set_end;
+ //
+ // VALUE method_table_object = Data_Wrap_Struct(rb_cObject, 0, free, npmt);
+ // rb_const_set(c_protocol, rb_intern("@native_method_table"), method_table_object);
+}
\ No newline at end of file
--- /dev/null
+
+void Init_protocol();
\ No newline at end of file
--- /dev/null
+
+#include <struct.h>
+#include <constants.h>
+
+static native_proto_method_table *mt;
+
+#define IS_CONTAINER(ttype) ((ttype) == TTYPE_MAP || (ttype) == TTYPE_LIST || (ttype) == TTYPE_SET)
+#define STRUCT_FIELDS(obj) rb_const_get(CLASS_OF(obj), fields_const_id)
+
+//-------------------------------------------
+// Writing section
+//-------------------------------------------
+
+// default fn pointers for protocol stuff here
+
+VALUE default_write_bool(VALUE protocol, VALUE value) {
+ rb_funcall(protocol, write_boolean_method_id, 1, value);
+ return Qnil;
+}
+
+VALUE default_write_byte(VALUE protocol, VALUE value) {
+ rb_funcall(protocol, write_byte_method_id, 1, value);
+ return Qnil;
+}
+
+VALUE default_write_i16(VALUE protocol, VALUE value) {
+ rb_funcall(protocol, write_i16_method_id, 1, value);
+ return Qnil;
+}
+
+VALUE default_write_i32(VALUE protocol, VALUE value) {
+ rb_funcall(protocol, write_i32_method_id, 1, value);
+ return Qnil;
+}
+
+VALUE default_write_i64(VALUE protocol, VALUE value) {
+ rb_funcall(protocol, write_i64_method_id, 1, value);
+ return Qnil;
+}
+
+VALUE default_write_double(VALUE protocol, VALUE value) {
+ rb_funcall(protocol, write_double_method_id, 1, value);
+ return Qnil;
+}
+
+VALUE default_write_string(VALUE protocol, VALUE value) {
+ rb_funcall(protocol, write_string_method_id, 1, value);
+ return Qnil;
+}
+
+VALUE default_write_list_begin(VALUE protocol, VALUE etype, VALUE length) {
+ rb_funcall(protocol, write_list_begin_method_id, 2, etype, length);
+ return Qnil;
+}
+
+VALUE default_write_list_end(VALUE protocol) {
+ rb_funcall(protocol, write_list_end_method_id, 0);
+ return Qnil;
+}
+
+VALUE default_write_set_begin(VALUE protocol, VALUE etype, VALUE length) {
+ rb_funcall(protocol, write_set_begin_method_id, 2, etype, length);
+ return Qnil;
+}
+
+VALUE default_write_set_end(VALUE protocol) {
+ rb_funcall(protocol, write_set_end_method_id, 0);
+ return Qnil;
+}
+
+VALUE default_write_map_begin(VALUE protocol, VALUE ktype, VALUE vtype, VALUE length) {
+ rb_funcall(protocol, write_map_begin_method_id, 3, ktype, vtype, length);
+ return Qnil;
+}
+
+VALUE default_write_map_end(VALUE protocol) {
+ rb_funcall(protocol, write_map_end_method_id, 0);
+ return Qnil;
+}
+
+VALUE default_write_struct_begin(VALUE protocol, VALUE struct_name) {
+ rb_funcall(protocol, write_struct_begin_method_id, 1, struct_name);
+ return Qnil;
+}
+
+VALUE default_write_struct_end(VALUE protocol) {
+ rb_funcall(protocol, write_struct_end_method_id, 0);
+ return Qnil;
+}
+
+VALUE default_write_field_begin(VALUE protocol, VALUE name, VALUE type, VALUE id) {
+ rb_funcall(protocol, write_field_begin_method_id, 3, name, type, id);
+ return Qnil;
+}
+
+VALUE default_write_field_end(VALUE protocol) {
+ rb_funcall(protocol, write_field_end_method_id, 0);
+ return Qnil;
+}
+
+VALUE default_write_field_stop(VALUE protocol) {
+ rb_funcall(protocol, write_field_stop_method_id, 0);
+ return Qnil;
+}
+
+VALUE default_read_field_begin(VALUE protocol) {
+ return rb_funcall(protocol, read_field_begin_method_id, 0);
+}
+
+VALUE default_read_field_end(VALUE protocol) {
+ return rb_funcall(protocol, read_field_end_method_id, 0);
+}
+
+VALUE default_read_map_begin(VALUE protocol) {
+ return rb_funcall(protocol, read_map_begin_method_id, 0);
+}
+
+VALUE default_read_map_end(VALUE protocol) {
+ return rb_funcall(protocol, read_map_end_method_id, 0);
+}
+
+VALUE default_read_list_begin(VALUE protocol) {
+ return rb_funcall(protocol, read_list_begin_method_id, 0);
+}
+
+VALUE default_read_list_end(VALUE protocol) {
+ return rb_funcall(protocol, read_list_end_method_id, 0);
+}
+
+VALUE default_read_set_begin(VALUE protocol) {
+ return rb_funcall(protocol, read_set_begin_method_id, 0);
+}
+
+VALUE default_read_set_end(VALUE protocol) {
+ return rb_funcall(protocol, read_set_end_method_id, 0);
+}
+
+VALUE default_read_byte(VALUE protocol) {
+ return rb_funcall(protocol, read_byte_method_id, 0);
+}
+
+VALUE default_read_bool(VALUE protocol) {
+ return rb_funcall(protocol, read_bool_method_id, 0);
+}
+
+VALUE default_read_i16(VALUE protocol) {
+ return rb_funcall(protocol, read_i16_method_id, 0);
+}
+
+VALUE default_read_i32(VALUE protocol) {
+ return rb_funcall(protocol, read_i32_method_id, 0);
+}
+
+VALUE default_read_i64(VALUE protocol) {
+ return rb_funcall(protocol, read_i64_method_id, 0);
+}
+
+VALUE default_read_double(VALUE protocol) {
+ return rb_funcall(protocol, read_double_method_id, 0);
+}
+
+VALUE default_read_string(VALUE protocol) {
+ return rb_funcall(protocol, read_string_method_id, 0);
+}
+
+VALUE default_read_struct_begin(VALUE protocol) {
+ return rb_funcall(protocol, read_struct_begin_method_id, 0);
+}
+
+VALUE default_read_struct_end(VALUE protocol) {
+ return rb_funcall(protocol, read_struct_end_method_id, 0);
+}
+
+static void set_default_proto_function_pointers() {
+ mt = ALLOC(native_proto_method_table);
+
+ mt->write_field_begin = default_write_field_begin;
+ mt->write_field_stop = default_write_field_stop;
+ mt->write_map_begin = default_write_map_begin;
+ mt->write_map_end = default_write_map_end;
+ mt->write_list_begin = default_write_list_begin;
+ mt->write_list_end = default_write_list_end;
+ mt->write_set_begin = default_write_set_begin;
+ mt->write_set_end = default_write_set_end;
+ mt->write_byte = default_write_byte;
+ mt->write_bool = default_write_bool;
+ mt->write_i16 = default_write_i16;
+ mt->write_i32 = default_write_i32;
+ mt->write_i64 = default_write_i64;
+ mt->write_double = default_write_double;
+ mt->write_string = default_write_string;
+ mt->write_struct_begin = default_write_struct_begin;
+ mt->write_struct_end = default_write_struct_end;
+ mt->write_field_end = default_write_field_end;
+
+ mt->read_struct_begin = default_read_struct_begin;
+ mt->read_struct_end = default_read_struct_end;
+ mt->read_field_begin = default_read_field_begin;
+ mt->read_field_end = default_read_field_end;
+ mt->read_map_begin = default_read_map_begin;
+ mt->read_map_end = default_read_map_end;
+ mt->read_list_begin = default_read_list_begin;
+ mt->read_list_end = default_read_list_end;
+ mt->read_set_begin = default_read_set_begin;
+ mt->read_set_end = default_read_set_end;
+ mt->read_byte = default_read_byte;
+ mt->read_bool = default_read_bool;
+ mt->read_i16 = default_read_i16;
+ mt->read_i32 = default_read_i32;
+ mt->read_i64 = default_read_i64;
+ mt->read_double = default_read_double;
+ mt->read_string = default_read_string;
+
+}
+
+static void set_native_proto_function_pointers(VALUE protocol) {
+ VALUE method_table_object = rb_const_get(CLASS_OF(protocol), rb_intern("@native_method_table"));
+ // TODO: check nil?
+ Data_Get_Struct(method_table_object, native_proto_method_table, mt);
+}
+
+// end default protocol methods
+
+
+static VALUE rb_thrift_struct_write(VALUE self, VALUE protocol);
+static void write_anything(int ttype, VALUE value, VALUE protocol, VALUE field_info);
+
+VALUE get_field_value(VALUE obj, VALUE field_name) {
+ char name_buf[RSTRING(field_name)->len + 1];
+
+ name_buf[0] = '@';
+ strlcpy(&name_buf[1], RSTRING(field_name)->ptr, sizeof(name_buf));
+
+ VALUE value = rb_ivar_get(obj, rb_intern(name_buf));
+
+ return value;
+}
+
+static void write_container(int ttype, VALUE field_info, VALUE value, VALUE protocol) {
+ int sz, i;
+
+ if (ttype == TTYPE_MAP) {
+ VALUE keys;
+ VALUE key;
+ VALUE val;
+
+ Check_Type(value, T_HASH);
+
+ VALUE key_info = rb_hash_aref(field_info, key_sym);
+ VALUE keytype_value = rb_hash_aref(key_info, type_sym);
+ int keytype = FIX2INT(keytype_value);
+
+ VALUE value_info = rb_hash_aref(field_info, value_sym);
+ VALUE valuetype_value = rb_hash_aref(value_info, type_sym);
+ int valuetype = FIX2INT(valuetype_value);
+
+ keys = rb_funcall(value, keys_method_id, 0);
+
+ sz = RARRAY(keys)->len;
+
+ mt->write_map_begin(protocol, keytype_value, valuetype_value, INT2FIX(sz));
+
+ for (i = 0; i < sz; i++) {
+ key = rb_ary_entry(keys, i);
+ val = rb_hash_aref(value, key);
+
+ if (IS_CONTAINER(keytype)) {
+ write_container(keytype, key_info, key, protocol);
+ } else {
+ write_anything(keytype, key, protocol, key_info);
+ }
+
+ if (IS_CONTAINER(valuetype)) {
+ write_container(valuetype, value_info, val, protocol);
+ } else {
+ write_anything(valuetype, val, protocol, value_info);
+ }
+ }
+
+ mt->write_map_end(protocol);
+ } else if (ttype == TTYPE_LIST) {
+ Check_Type(value, T_ARRAY);
+
+ sz = RARRAY(value)->len;
+
+ VALUE element_type_info = rb_hash_aref(field_info, element_sym);
+ VALUE element_type_value = rb_hash_aref(element_type_info, type_sym);
+ int element_type = FIX2INT(element_type_value);
+
+ mt->write_list_begin(protocol, element_type_value, INT2FIX(sz));
+ for (i = 0; i < sz; ++i) {
+ VALUE val = rb_ary_entry(value, i);
+ if (IS_CONTAINER(element_type)) {
+ write_container(element_type, element_type_info, val, protocol);
+ } else {
+ write_anything(element_type, val, protocol, element_type_info);
+ }
+ }
+ mt->write_list_end(protocol);
+ } else if (ttype == TTYPE_SET) {
+ VALUE items;
+
+ if (TYPE(value) == T_ARRAY) {
+ items = value;
+ } else {
+ if (rb_cSet == CLASS_OF(value)) {
+ items = rb_funcall(value, entries_method_id, 0);
+ } else {
+ Check_Type(value, T_HASH);
+ items = rb_funcall(value, keys_method_id, 0);
+ }
+ }
+
+ sz = RARRAY(items)->len;
+
+ VALUE element_type_info = rb_hash_aref(field_info, element_sym);
+ VALUE element_type_value = rb_hash_aref(element_type_info, type_sym);
+ int element_type = FIX2INT(element_type_value);
+
+ mt->write_set_begin(protocol, element_type_value, INT2FIX(sz));
+
+ for (i = 0; i < sz; i++) {
+ VALUE val = rb_ary_entry(items, i);
+ if (IS_CONTAINER(element_type)) {
+ write_container(element_type, element_type_info, val, protocol);
+ } else {
+ write_anything(element_type, val, protocol, element_type_info);
+ }
+ }
+
+ mt->write_set_end(protocol);
+ } else {
+ rb_raise(rb_eNotImpError, "can't write container of type: %d", ttype);
+ }
+}
+
+static void write_anything(int ttype, VALUE value, VALUE protocol, VALUE field_info) {
+ if (ttype == TTYPE_BOOL) {
+ mt->write_bool(protocol, value);
+ } else if (ttype == TTYPE_BYTE) {
+ mt->write_byte(protocol, value);
+ } else if (ttype == TTYPE_I16) {
+ mt->write_i16(protocol, value);
+ } else if (ttype == TTYPE_I32) {
+ mt->write_i32(protocol, value);
+ } else if (ttype == TTYPE_I64) {
+ mt->write_i64(protocol, value);
+ } else if (ttype == TTYPE_DOUBLE) {
+ mt->write_double(protocol, value);
+ } else if (ttype == TTYPE_STRING) {
+ mt->write_string(protocol, value);
+ } else if (IS_CONTAINER(ttype)) {
+ write_container(ttype, field_info, value, protocol);
+ } else if (ttype == TTYPE_STRUCT) {
+ rb_thrift_struct_write(value, protocol);
+ } else {
+ rb_raise(rb_eNotImpError, "Unknown type for binary_encoding: %d", ttype);
+ }
+}
+
+static VALUE rb_thrift_struct_write(VALUE self, VALUE protocol) {
+ // call validate
+ rb_funcall(self, validate_method_id, 0);
+
+ if (RTEST(rb_funcall(protocol, native_qmark_method_id, 0))) {
+ set_native_proto_function_pointers(protocol);
+ } else {
+ set_default_proto_function_pointers();
+ }
+
+ // write struct begin
+ mt->write_struct_begin(protocol, rb_class_name(CLASS_OF(self)));
+
+ // iterate through all the fields here
+ VALUE struct_fields = STRUCT_FIELDS(self);
+ VALUE struct_field_ids_unordered = rb_funcall(struct_fields, keys_method_id, 0);
+ VALUE struct_field_ids_ordered = rb_funcall(struct_field_ids_unordered, sort_method_id, 0);
+
+ int i = 0;
+ for (i=0; i < RARRAY(struct_field_ids_ordered)->len; i++) {
+ VALUE field_id = rb_ary_entry(struct_field_ids_ordered, i);
+ VALUE field_info = rb_hash_aref(struct_fields, field_id);
+
+ VALUE ttype_value = rb_hash_aref(field_info, type_sym);
+ int ttype = FIX2INT(ttype_value);
+ VALUE field_name = rb_hash_aref(field_info, name_sym);
+ VALUE field_value = get_field_value(self, field_name);
+
+ if (!NIL_P(field_value)) {
+ mt->write_field_begin(protocol, field_name, ttype_value, field_id);
+
+ write_anything(ttype, field_value, protocol, field_info);
+
+ mt->write_field_end(protocol);
+ }
+ }
+
+ mt->write_field_stop(protocol);
+
+ // write struct end
+ mt->write_struct_end(protocol);
+
+ return Qnil;
+}
+
+//-------------------------------------------
+// Reading section
+//-------------------------------------------
+
+static VALUE rb_thrift_struct_read(VALUE self, VALUE protocol);
+
+static void set_field_value(VALUE obj, VALUE field_name, VALUE value) {
+ char name_buf[RSTRING(field_name)->len + 1];
+
+ name_buf[0] = '@';
+ strlcpy(&name_buf[1], RSTRING(field_name)->ptr, sizeof(name_buf));
+
+ rb_ivar_set(obj, rb_intern(name_buf), value);
+}
+
+static VALUE read_anything(VALUE protocol, int ttype, VALUE field_info) {
+ VALUE result = Qnil;
+
+ if (ttype == TTYPE_BOOL) {
+ result = mt->read_bool(protocol);
+ } else if (ttype == TTYPE_BYTE) {
+ result = mt->read_byte(protocol);
+ } else if (ttype == TTYPE_I16) {
+ result = mt->read_i16(protocol);
+ } else if (ttype == TTYPE_I32) {
+ result = mt->read_i32(protocol);
+ } else if (ttype == TTYPE_I64) {
+ result = mt->read_i64(protocol);
+ } else if (ttype == TTYPE_STRING) {
+ result = mt->read_string(protocol);
+ } else if (ttype == TTYPE_DOUBLE) {
+ result = mt->read_double(protocol);
+ } else if (ttype == TTYPE_STRUCT) {
+ VALUE klass = rb_hash_aref(field_info, class_sym);
+ result = rb_class_new_instance(0, NULL, klass);
+ rb_thrift_struct_read(result, protocol);
+ } else if (ttype == TTYPE_MAP) {
+ int i;
+
+ VALUE map_header = mt->read_map_begin(protocol);
+ int key_ttype = FIX2INT(rb_ary_entry(map_header, 0));
+ int value_ttype = FIX2INT(rb_ary_entry(map_header, 1));
+ int num_entries = FIX2INT(rb_ary_entry(map_header, 2));
+
+ VALUE key_info = rb_hash_aref(field_info, key_sym);
+ VALUE value_info = rb_hash_aref(field_info, value_sym);
+
+ result = rb_hash_new();
+
+ for (i = 0; i < num_entries; ++i) {
+ VALUE key, val;
+
+ key = read_anything(protocol, key_ttype, key_info);
+ val = read_anything(protocol, value_ttype, value_info);
+
+ rb_hash_aset(result, key, val);
+ }
+
+ mt->read_map_end(protocol);
+ } else if (ttype == TTYPE_LIST) {
+ int i;
+
+ VALUE list_header = mt->read_list_begin(protocol);
+ int element_ttype = FIX2INT(rb_ary_entry(list_header, 0));
+ int num_elements = FIX2INT(rb_ary_entry(list_header, 1));
+ result = rb_ary_new2(num_elements);
+
+ for (i = 0; i < num_elements; ++i) {
+ rb_ary_push(result, read_anything(protocol, element_ttype, rb_hash_aref(field_info, element_sym)));
+ }
+
+
+ mt->read_list_end(protocol);
+ } else if (ttype == TTYPE_SET) {
+ VALUE items;
+ int i;
+
+ VALUE set_header = mt->read_set_begin(protocol);
+ int element_ttype = FIX2INT(rb_ary_entry(set_header, 0));
+ int num_elements = FIX2INT(rb_ary_entry(set_header, 1));
+ items = rb_ary_new2(num_elements);
+
+ for (i = 0; i < num_elements; ++i) {
+ rb_ary_push(items, read_anything(protocol, element_ttype, rb_hash_aref(field_info, element_sym)));
+ }
+
+
+ mt->read_set_end(protocol);
+
+ result = rb_class_new_instance(1, &items, rb_cSet);
+ } else {
+ rb_raise(rb_eNotImpError, "read_anything not implemented for type %d!", ttype);
+ }
+
+ return result;
+}
+
+static VALUE rb_thrift_struct_read(VALUE self, VALUE protocol) {
+ // read struct begin
+ mt->read_struct_begin(protocol);
+
+ VALUE struct_fields = STRUCT_FIELDS(self);
+
+ // read each field
+ while (true) {
+ VALUE field_header = rb_funcall(protocol, read_field_begin_method_id, 0);
+ VALUE field_type_value = rb_ary_entry(field_header, 1);
+ int field_type = FIX2INT(field_type_value);
+
+ if (field_type == TTYPE_STOP) {
+ break;
+ }
+
+ // make sure we got a type we expected
+ VALUE field_info = rb_hash_aref(struct_fields, rb_ary_entry(field_header, 2));
+
+ if (!NIL_P(field_info)) {
+ int specified_type = FIX2INT(rb_hash_aref(field_info, type_sym));
+ if (field_type == specified_type) {
+ // read the value
+ VALUE name = rb_hash_aref(field_info, name_sym);
+ set_field_value(self, name, read_anything(protocol, field_type, field_info));
+ } else {
+ rb_funcall(protocol, skip_method_id, 1, field_type_value);
+ }
+ } else {
+ rb_funcall(protocol, skip_method_id, 1, field_type_value);
+ }
+
+ // read field end
+ mt->read_field_end(protocol);
+ }
+
+ // read struct end
+ mt->read_struct_end(protocol);
+
+ return Qnil;
+}
+
+void Init_struct() {
+ VALUE struct_module = rb_const_get(thrift_module, rb_intern("Struct"));
+
+ rb_define_method(struct_module, "write", rb_thrift_struct_write, 1);
+ rb_define_method(struct_module, "read", rb_thrift_struct_read, 1);
+
+ set_default_proto_function_pointers();
+}
+
--- /dev/null
+#include <stdbool.h>
+#include <ruby.h>
+
+typedef struct native_proto_method_table {
+ VALUE (*write_bool)(VALUE, VALUE);
+ VALUE (*write_byte)(VALUE, VALUE);
+ VALUE (*write_i16)(VALUE, VALUE);
+ VALUE (*write_i32)(VALUE, VALUE);
+ VALUE (*write_i64)(VALUE, VALUE);
+ VALUE (*write_double)(VALUE, VALUE);
+ VALUE (*write_string)(VALUE, VALUE);
+ VALUE (*write_list_begin)(VALUE, VALUE, VALUE);
+ VALUE (*write_list_end)(VALUE);
+ VALUE (*write_set_begin)(VALUE, VALUE, VALUE);
+ VALUE (*write_set_end)(VALUE);
+ VALUE (*write_map_begin)(VALUE, VALUE, VALUE, VALUE);
+ VALUE (*write_map_end)(VALUE);
+ VALUE (*write_struct_begin)(VALUE, VALUE);
+ VALUE (*write_struct_end)(VALUE);
+ VALUE (*write_field_begin)(VALUE, VALUE, VALUE, VALUE);
+ VALUE (*write_field_end)(VALUE);
+ VALUE (*write_field_stop)(VALUE);
+ VALUE (*write_message_begin)(VALUE, VALUE, VALUE, VALUE);
+ VALUE (*write_message_end)(VALUE);
+
+ VALUE (*read_message_begin)(VALUE);
+ VALUE (*read_message_end)(VALUE);
+ VALUE (*read_field_begin)(VALUE);
+ VALUE (*read_field_end)(VALUE);
+ VALUE (*read_map_begin)(VALUE);
+ VALUE (*read_map_end)(VALUE);
+ VALUE (*read_list_begin)(VALUE);
+ VALUE (*read_list_end)(VALUE);
+ VALUE (*read_set_begin)(VALUE);
+ VALUE (*read_set_end)(VALUE);
+ VALUE (*read_byte)(VALUE);
+ VALUE (*read_bool)(VALUE);
+ VALUE (*read_i16)(VALUE);
+ VALUE (*read_i32)(VALUE);
+ VALUE (*read_i64)(VALUE);
+ VALUE (*read_double)(VALUE);
+ VALUE (*read_string)(VALUE);
+ VALUE (*read_struct_begin)(VALUE);
+ VALUE (*read_struct_end)(VALUE);
+
+} native_proto_method_table;
+
+void Init_struct();
\ No newline at end of file
--- /dev/null
+#include <ruby.h>
+#include <struct.h>
+#include <binary_protocol_accelerated.h>
+#include <protocol.h>
+#include <memory_buffer.h>
+
+// cached classes/modules
+VALUE rb_cSet;
+VALUE thrift_module;
+VALUE thrift_types_module;
+
+// TType constants
+int TTYPE_STOP;
+int TTYPE_BOOL;
+int TTYPE_BYTE;
+int TTYPE_I16;
+int TTYPE_I32;
+int TTYPE_I64;
+int TTYPE_DOUBLE;
+int TTYPE_STRING;
+int TTYPE_MAP;
+int TTYPE_SET;
+int TTYPE_LIST;
+int TTYPE_STRUCT;
+
+// method ids
+ID validate_method_id;
+ID write_struct_begin_method_id;
+ID write_struct_end_method_id;
+ID write_field_begin_method_id;
+ID write_field_end_method_id;
+ID write_boolean_method_id;
+ID write_byte_method_id;
+ID write_i16_method_id;
+ID write_i32_method_id;
+ID write_i64_method_id;
+ID write_double_method_id;
+ID write_string_method_id;
+ID write_map_begin_method_id;
+ID write_map_end_method_id;
+ID write_list_begin_method_id;
+ID write_list_end_method_id;
+ID write_set_begin_method_id;
+ID write_set_end_method_id;
+ID size_method_id;
+ID read_bool_method_id;
+ID read_byte_method_id;
+ID read_i16_method_id;
+ID read_i32_method_id;
+ID read_i64_method_id;
+ID read_string_method_id;
+ID read_double_method_id;
+ID read_map_begin_method_id;
+ID read_map_end_method_id;
+ID read_list_begin_method_id;
+ID read_list_end_method_id;
+ID read_set_begin_method_id;
+ID read_set_end_method_id;
+ID read_struct_begin_method_id;
+ID read_struct_end_method_id;
+ID read_field_begin_method_id;
+ID read_field_end_method_id;
+ID keys_method_id;
+ID entries_method_id;
+ID name_method_id;
+ID sort_method_id;
+ID write_field_stop_method_id;
+ID skip_method_id;
+ID write_method_id;
+ID read_method_id;
+ID native_qmark_method_id;
+
+// constant ids
+ID fields_const_id;
+ID transport_ivar_id;
+
+// cached symbols
+VALUE type_sym;
+VALUE name_sym;
+VALUE key_sym;
+VALUE value_sym;
+VALUE element_sym;
+VALUE class_sym;
+VALUE protocol_exception_class;
+
+void Init_thrift_native() {
+ // cached classes
+ thrift_module = rb_const_get(rb_cObject, rb_intern("Thrift"));
+ thrift_types_module = rb_const_get(thrift_module, rb_intern("Types"));
+ rb_cSet = rb_const_get(rb_cObject, rb_intern("Set"));
+ protocol_exception_class = rb_const_get(thrift_module, rb_intern("ProtocolException"));
+
+ // Init ttype constants
+ TTYPE_BOOL = FIX2INT(rb_const_get(thrift_types_module, rb_intern("BOOL")));
+ TTYPE_BYTE = FIX2INT(rb_const_get(thrift_types_module, rb_intern("BYTE")));
+ TTYPE_I16 = FIX2INT(rb_const_get(thrift_types_module, rb_intern("I16")));
+ TTYPE_I32 = FIX2INT(rb_const_get(thrift_types_module, rb_intern("I32")));
+ TTYPE_I64 = FIX2INT(rb_const_get(thrift_types_module, rb_intern("I64")));
+ TTYPE_DOUBLE = FIX2INT(rb_const_get(thrift_types_module, rb_intern("DOUBLE")));
+ TTYPE_STRING = FIX2INT(rb_const_get(thrift_types_module, rb_intern("STRING")));
+ TTYPE_MAP = FIX2INT(rb_const_get(thrift_types_module, rb_intern("MAP")));
+ TTYPE_SET = FIX2INT(rb_const_get(thrift_types_module, rb_intern("SET")));
+ TTYPE_LIST = FIX2INT(rb_const_get(thrift_types_module, rb_intern("LIST")));
+ TTYPE_STRUCT = FIX2INT(rb_const_get(thrift_types_module, rb_intern("STRUCT")));
+
+ // method ids
+ validate_method_id = rb_intern("validate");
+ write_struct_begin_method_id = rb_intern("write_struct_begin");
+ write_struct_end_method_id = rb_intern("write_struct_end");
+ write_field_begin_method_id = rb_intern("write_field_begin");
+ write_field_end_method_id = rb_intern("write_field_end");
+ write_boolean_method_id = rb_intern("write_bool");
+ write_byte_method_id = rb_intern("write_byte");
+ write_i16_method_id = rb_intern("write_i16");
+ write_i32_method_id = rb_intern("write_i32");
+ write_i64_method_id = rb_intern("write_i64");
+ write_double_method_id = rb_intern("write_double");
+ write_string_method_id = rb_intern("write_string");
+ write_map_begin_method_id = rb_intern("write_map_begin");
+ write_map_end_method_id = rb_intern("write_map_end");
+ write_list_begin_method_id = rb_intern("write_list_begin");
+ write_list_end_method_id = rb_intern("write_list_end");
+ write_set_begin_method_id = rb_intern("write_set_begin");
+ write_set_end_method_id = rb_intern("write_set_end");
+ size_method_id = rb_intern("size");
+ read_bool_method_id = rb_intern("read_bool");
+ read_byte_method_id = rb_intern("read_byte");
+ read_i16_method_id = rb_intern("read_i16");
+ read_i32_method_id = rb_intern("read_i32");
+ read_i64_method_id = rb_intern("read_i64");
+ read_string_method_id = rb_intern("read_string");
+ read_double_method_id = rb_intern("read_double");
+ read_map_begin_method_id = rb_intern("read_map_begin");
+ read_map_end_method_id = rb_intern("read_map_end");
+ read_list_begin_method_id = rb_intern("read_list_begin");
+ read_list_end_method_id = rb_intern("read_list_end");
+ read_set_begin_method_id = rb_intern("read_set_begin");
+ read_set_end_method_id = rb_intern("read_set_end");
+ read_struct_begin_method_id = rb_intern("read_struct_begin");
+ read_struct_end_method_id = rb_intern("read_struct_end");
+ read_field_begin_method_id = rb_intern("read_field_begin");
+ read_field_end_method_id = rb_intern("read_field_end");
+ keys_method_id = rb_intern("keys");
+ entries_method_id = rb_intern("entries");
+ name_method_id = rb_intern("name");
+ sort_method_id = rb_intern("sort");
+ write_field_stop_method_id = rb_intern("write_field_stop");
+ skip_method_id = rb_intern("skip");
+ write_method_id = rb_intern("write");
+ read_method_id = rb_intern("read");
+ native_qmark_method_id = rb_intern("native?");
+
+ // constant ids
+ fields_const_id = rb_intern("FIELDS");
+ transport_ivar_id = rb_intern("@trans");
+
+ // cached symbols
+ type_sym = ID2SYM(rb_intern("type"));
+ name_sym = ID2SYM(rb_intern("name"));
+ key_sym = ID2SYM(rb_intern("key"));
+ value_sym = ID2SYM(rb_intern("value"));
+ element_sym = ID2SYM(rb_intern("element"));
+ class_sym = ID2SYM(rb_intern("class"));
+
+ Init_protocol();
+ Init_struct();
+ Init_binary_protocol_accelerated();
+ Init_memory_buffer();
+}
\ No newline at end of file
@trans = trans
end
+ def native?
+ puts "wrong method is being called!"
+ false
+ end
+
def write_message_begin(name, type, seqid); nil; end
deprecate! :writeMessageBegin => :write_message_begin
VERSION_1 = 0x80010000
def write_message_begin(name, type, seqid)
- write_i32(VERSION_1 | type)
+ # this is necessary because we added (needed) bounds checking to
+ # write_i32, and 0x80010000 is too big for that.
+ write_i16(VERSION_1 >> 16)
+ write_i16(type)
write_string(name)
write_i32(seqid)
end
end
def write_i32(i32)
+ raise RangeError if i32 < -2**31 || i32 >= 2**31
trans.write([i32].pack('N'))
end
require 'thrift/protocol/binaryprotocol'
-require 'binaryprotocolaccelerated'
+require 'thrift_native'
=begin
The only change required for a transport to support TBinaryProtocolAccelerated is to implement 2 methods:
def write(oprot)
validate
- if oprot.respond_to?(:encode_binary)
- # TODO(kevinclark): Clean this so I don't have to access the transport.
- oprot.trans.write oprot.encode_binary(self)
- else
+ # if oprot.respond_to?(:encode_binary)
+ # # TODO(kevinclark): Clean this so I don't have to access the transport.
+ # oprot.trans.write oprot.encode_binary(self)
+ # else
oprot.write_struct_begin(self.class.name)
each_field do |fid, type, name|
unless (value = instance_variable_get("@#{name}")).nil?
end
oprot.write_field_stop
oprot.write_struct_end
- end
+ # end
end
def ==(other)
require 'thrift/exceptions'
require 'thrift/client'
require 'thrift/struct'
+begin
+ require "thrift_native"
+rescue
+ puts "Could not load thrift_native libraries. Using pure ruby version."
+end
\ No newline at end of file
shared_examples_for 'a binary protocol' do
before(:each) do
- @trans = mock("MockTransport")
+ @trans = Thrift::MemoryBuffer.new
@prot = protocol_class.new(@trans)
end
end
it "should write the message header" do
- @prot.should_receive(:write_i32).with(protocol_class.const_get(:VERSION_1) | Thrift::MessageTypes::CALL).ordered
- @prot.should_receive(:write_string).with('testMessage').ordered
- @prot.should_receive(:write_i32).with(17).ordered
@prot.write_message_begin('testMessage', Thrift::MessageTypes::CALL, 17)
+ @trans.read(1000).should == [protocol_class.const_get(:VERSION_1) | Thrift::MessageTypes::CALL, "testMessage".size, "testMessage", 17].pack("NNa11N")
end
# message footer is a noop
it "should write the field header" do
- @prot.should_receive(:write_byte).with(Thrift::Types::DOUBLE).ordered
- @prot.should_receive(:write_i16).with(3).ordered
@prot.write_field_begin('foo', Thrift::Types::DOUBLE, 3)
+ @trans.read(1000).should == [Thrift::Types::DOUBLE, 3].pack("cn")
end
-
+
# field footer is a noop
-
+
it "should write the STOP field" do
- @prot.should_receive(:write_byte).with(Thrift::Types::STOP)
@prot.write_field_stop
+ @trans.read(1).should == "\000"
end
-
+
it "should write the map header" do
- @prot.should_receive(:write_byte).with(Thrift::Types::STRING).ordered
- @prot.should_receive(:write_byte).with(Thrift::Types::LIST).ordered
- @prot.should_receive(:write_i32).with(17).ordered
@prot.write_map_begin(Thrift::Types::STRING, Thrift::Types::LIST, 17)
+ @trans.read(1000).should == [Thrift::Types::STRING, Thrift::Types::LIST, 17].pack("ccN");
end
-
+
# map footer is a noop
-
+
it "should write the list header" do
- @prot.should_receive(:write_byte).with(Thrift::Types::I16).ordered
- @prot.should_receive(:write_i32).with(42).ordered
@prot.write_list_begin(Thrift::Types::I16, 42)
+ @trans.read(1000).should == [Thrift::Types::I16, 42].pack("cN")
end
-
+
# list footer is a noop
-
+
it "should write the set header" do
- @prot.should_receive(:write_byte).with(Thrift::Types::BOOL).ordered
- @prot.should_receive(:write_i32).with(2).ordered
- @prot.write_set_begin(Thrift::Types::BOOL, 2)
+ @prot.write_set_begin(Thrift::Types::I16, 42)
+ @trans.read(1000).should == [Thrift::Types::I16, 42].pack("cN")
end
-
+
it "should write a bool" do
- @prot.should_receive(:write_byte).with(1).ordered
@prot.write_bool(true)
- @prot.should_receive(:write_byte).with(0).ordered
@prot.write_bool(false)
+ @trans.read(1000).should == "\001\000"
end
-
+
it "should treat a nil bool as false" do
- @prot.should_receive(:write_byte).with(0)
@prot.write_bool(nil)
+ @trans.read(1).should == "\000"
end
-
+
it "should write a byte" do
# byte is small enough, let's check -128..127
(-128..127).each do |i|
- @trans.should_receive(:write).with([i].pack('c')).ordered
@prot.write_byte(i)
+ @trans.read(1).should == [i].pack('c')
end
(-128..127).each do |i|
end
# handing it numbers out of signed range should clip
@trans.rspec_verify
(128..255).each do |i|
- @trans.should_receive(:write).with([i].pack('c')).ordered
@prot.write_byte(i)
+ @trans.read(1).should == [i].pack('c')
end
# and lastly, a Bignum is going to error out
lambda { @prot.write_byte(2**65) }.should raise_error(RangeError)
end
-
+
it "should error gracefully when trying to write a nil byte" do
lambda { @prot.write_byte(nil) }.should raise_error
end
-
+
it "should write an i16" do
# try a random scattering of values
# include the signed i16 minimum/maximum
- @trans.should_receive(:write).with("\200\000").ordered
- @trans.should_receive(:write).with("\374\000").ordered
- @trans.should_receive(:write).with("\000\021").ordered
- @trans.should_receive(:write).with("\000\000").ordered
- @trans.should_receive(:write).with("\330\360").ordered
- @trans.should_receive(:write).with("\006\273").ordered
- @trans.should_receive(:write).with("\177\377").ordered
[-2**15, -1024, 17, 0, -10000, 1723, 2**15-1].each do |i|
@prot.write_i16(i)
end
# and try something out of signed range, it should clip
- @trans.should_receive(:write).with("\200\005").ordered
@prot.write_i16(2**15 + 5)
+
+ @trans.read(1000).should == "\200\000\374\000\000\021\000\000\330\360\006\273\177\377\200\005"
+
# a Bignum should error
# lambda { @prot.write_i16(2**65) }.should raise_error(RangeError)
end
-
+
it "should error gracefully when trying to write a nil i16" do
lambda { @prot.write_i16(nil) }.should raise_error
end
-
+
it "should write an i32" do
# try a random scattering of values
# include the signed i32 minimum/maximum
- @trans.should_receive(:write).with("\200\000\000\000").ordered
- @trans.should_receive(:write).with("\377\376\037\r").ordered
- @trans.should_receive(:write).with("\377\377\366\034").ordered
- @trans.should_receive(:write).with("\377\377\377\375").ordered
- @trans.should_receive(:write).with("\000\000\000\000").ordered
- @trans.should_receive(:write).with("\000#\340\203").ordered
- @trans.should_receive(:write).with("\000\0000+").ordered
- @trans.should_receive(:write).with("\177\377\377\377").ordered
[-2**31, -123123, -2532, -3, 0, 2351235, 12331, 2**31-1].each do |i|
@prot.write_i32(i)
end
# try something out of signed range, it should clip
- @trans.should_receive(:write).with("\200\000\000\005").ordered
- @prot.write_i32(2 ** 31 + 5)
- # lambda { @prot.write_i32(2 ** 65 + 5) }.should raise_error(RangeError)
+ @trans.read(1000).should == "\200\000\000\000" + "\377\376\037\r" + "\377\377\366\034" + "\377\377\377\375" + "\000\000\000\000" + "\000#\340\203" + "\000\0000+" + "\177\377\377\377"
+ [2 ** 31 + 5, 2 ** 65 + 5].each do |i|
+ lambda { @prot.write_i32(i) }.should raise_error(RangeError)
+ end
end
-
+
it "should error gracefully when trying to write a nil i32" do
lambda { @prot.write_i32(nil) }.should raise_error
end
-
+
it "should write an i64" do
# try a random scattering of values
# try the signed i64 minimum/maximum
- @trans.should_receive(:write).with("\200\000\000\000\000\000\000\000").ordered
- @trans.should_receive(:write).with("\377\377\364\303\035\244+]").ordered
- @trans.should_receive(:write).with("\377\377\377\377\376\231:\341").ordered
- @trans.should_receive(:write).with("\377\377\377\377\377\377\377\026").ordered
- @trans.should_receive(:write).with("\000\000\000\000\000\000\000\000").ordered
- @trans.should_receive(:write).with("\000\000\000\000\000\000\004\317").ordered
- @trans.should_receive(:write).with("\000\000\000\000\000#\340\204").ordered
- @trans.should_receive(:write).with("\000\000\000\002\340\311~\365").ordered
- @trans.should_receive(:write).with("\177\377\377\377\377\377\377\377").ordered
[-2**63, -12356123612323, -23512351, -234, 0, 1231, 2351236, 12361236213, 2**63-1].each do |i|
@prot.write_i64(i)
end
# try something out of signed range, it should clip
- @trans.should_receive(:write).with("\200\000\000\000\000\000\000\005").ordered
- @prot.write_i64(2**63 + 5)
- # lambda { @prot.write_i64(2 ** 65 + 5) }.should raise_error(RangeError)
- end
-
+ @trans.read(1000).should == ["\200\000\000\000\000\000\000\000",
+ "\377\377\364\303\035\244+]",
+ "\377\377\377\377\376\231:\341",
+ "\377\377\377\377\377\377\377\026",
+ "\000\000\000\000\000\000\000\000",
+ "\000\000\000\000\000\000\004\317",
+ "\000\000\000\000\000#\340\204",
+ "\000\000\000\002\340\311~\365",
+ "\177\377\377\377\377\377\377\377"].join("")
+ lambda { @prot.write_i64(2 ** 65 + 5) }.should raise_error(RangeError)
+ end
+
it "should error gracefully when trying to write a nil i64" do
lambda { @prot.write_i64(nil) }.should raise_error
end
-
+
it "should write a double" do
# try a random scattering of values, including min/max
- @trans.should_receive(:write).with([Float::MIN].pack('G')).ordered
- @trans.should_receive(:write).with("\300\223<\234\355\221hs").ordered
- @trans.should_receive(:write).with("\300\376\0173\256\024z\341").ordered
- @trans.should_receive(:write).with("\3007<2\336\372v\324").ordered
- @trans.should_receive(:write).with("\000\000\000\000\000\000\000\000").ordered
- @trans.should_receive(:write).with("@\310\037\220\365\302\217\\").ordered
- @trans.should_receive(:write).with("@\200Y\327\n=p\244").ordered
- @trans.should_receive(:write).with([Float::MAX].pack('G')).ordered
- [Float::MIN, -1231.15325, -123123.23, -23.23515123, 0, 12351.1325, 523.23, Float::MAX].each do |f|
+ values = [Float::MIN,-1231.15325, -123123.23, -23.23515123, 0, 12351.1325, 523.23, Float::MAX]
+ values.each do |f|
@prot.write_double(f)
+ @trans.read(1000).should == [f].pack("G")
end
end
-
+
it "should error gracefully when trying to write a nil double" do
lambda { @prot.write_double(nil) }.should raise_error
end
-
+
it "should write a string" do
str = "hello world"
- @prot.should_receive(:write_i32).with(str.length).ordered
- @trans.should_receive(:write).with(str).ordered
@prot.write_string(str)
+ @trans.read(1000).should == [str.size].pack("N") + str
end
-
+
it "should error gracefully when trying to write a nil string" do
lambda { @prot.write_string(nil) }.should raise_error
end
-
+
# message footer is a noop
-
+
it "should read a field header" do
- @prot.should_receive(:read_byte).ordered.and_return(Thrift::Types::STRING)
- @prot.should_receive(:read_i16).ordered.and_return(3)
+ @trans.write([Thrift::Types::STRING, 3].pack("cn"))
@prot.read_field_begin.should == [nil, Thrift::Types::STRING, 3]
end
-
+
# field footer is a noop
-
+
it "should read a stop field" do
- @prot.should_receive(:read_byte).and_return(Thrift::Types::STOP)
- @prot.should_not_receive(:read_i16)
+ @trans.write([Thrift::Types::STOP].pack("c"));
@prot.read_field_begin.should == [nil, Thrift::Types::STOP, 0]
end
it "should read a map header" do
- @prot.should_receive(:read_byte).and_return(Thrift::Types::DOUBLE, Thrift::Types::I64)
- @prot.should_receive(:read_i32).and_return(42)
+ @trans.write([Thrift::Types::DOUBLE, Thrift::Types::I64, 42].pack("ccN"))
@prot.read_map_begin.should == [Thrift::Types::DOUBLE, Thrift::Types::I64, 42]
end
-
+
# map footer is a noop
-
+
it "should read a list header" do
- @prot.should_receive(:read_byte).ordered.and_return(Thrift::Types::STRING)
- @prot.should_receive(:read_i32).and_return(17)
+ @trans.write([Thrift::Types::STRING, 17].pack("cN"))
@prot.read_list_begin.should == [Thrift::Types::STRING, 17]
end
-
+
# list footer is a noop
-
+
it "should read a set header" do
- @prot.should_receive(:read_byte).ordered.and_return(Thrift::Types::MAP)
- @prot.should_receive(:read_i32).ordered.and_return(42)
- @prot.read_set_begin.should == [Thrift::Types::MAP, 42]
+ @trans.write([Thrift::Types::STRING, 17].pack("cN"))
+ @prot.read_set_begin.should == [Thrift::Types::STRING, 17]
end
-
+
# set footer is a noop
-
+
it "should read a bool" do
- @prot.should_receive(:read_byte).and_return(1, 0)
+ @trans.write("\001\000");
@prot.read_bool.should == true
@prot.read_bool.should == false
end
-
+
it "should read a byte" do
- # try a scattering of values, including min/max
- @trans.should_receive(:read_all).with(1).and_return(
- "\200", "\307", "\375",
- "\000", "\021", "\030", "\177"
- )
[-128, -57, -3, 0, 17, 24, 127].each do |i|
+ @trans.write([i].pack("c"))
@prot.read_byte.should == i
end
end
-
+
it "should read an i16" do
# try a scattering of values, including min/max
- @trans.should_receive(:read_all).with(2).and_return(
- "\200\000", "\353\213", "\376\237",
- "\000\000", "\005\367", "\b\272", "\177\377"
- )
[-2**15, -5237, -353, 0, 1527, 2234, 2**15-1].each do |i|
+ @trans.write([i].pack("n"));
@prot.read_i16.should == i
end
end
-
+
it "should read an i32" do
# try a scattering of values, including min/max
- @trans.should_receive(:read_all).with(4).and_return(
- "\200\000\000\000", "\377\374i\213", "\377\377\347\244",
- "\000\000\000\000", "\000\000\t/", "\000\001\340\363", "\177\377\377\377"
- )
[-2**31, -235125, -6236, 0, 2351, 123123, 2**31-1].each do |i|
+ @trans.write([i].pack("N"))
@prot.read_i32.should == i
end
end
-
+
it "should read an i64" do
# try a scattering of values, including min/max
- @trans.should_receive(:read_all).with(8).and_return(
- "\200\000\000\000\000\000\000\000", "\377\377\377\377\370\243Z\b",
- "\377\377\377\377\377\377\3476", "\000\000\000\000\000\000\000\000",
- "\000\000\000\000\000\000\000 ", "\000\000\000\000\213\332\t\223",
- "\177\377\377\377\377\377\377\377"
- )
[-2**63, -123512312, -6346, 0, 32, 2346322323, 2**63-1].each do |i|
+ @trans.write([i >> 32, i & 0xFFFFFFFF].pack("NN"))
@prot.read_i64.should == i
end
end
-
+
it "should read a double" do
# try a random scattering of values, including min/max
- @trans.should_receive(:read_all).with(8).and_return(
- [Float::MIN].pack('G'), "\301\f9\370\374\362\317\226",
- "\300t3\274x \243\016", "\000\000\000\000\000\000\000\000", "@^\317\fCo\301Y",
- "AA\360A\217\317@\260", [Float::MAX].pack('G')
- )
[Float::MIN, -231231.12351, -323.233513, 0, 123.2351235, 2351235.12351235, Float::MAX].each do |f|
+ @trans.write([f].pack("G"));
@prot.read_double.should == f
end
end
-
+
it "should read a string" do
str = "hello world"
- @prot.should_receive(:read_i32).and_return(str.length)
- @trans.should_receive(:read_all).with(str.length).and_return(str)
+ @trans.write([str.size].pack("N") + str)
@prot.read_string.should == str
end
end
class ThriftBinaryProtocolAcceleratedSpec < Spec::ExampleGroup
include Thrift
- describe BinaryProtocolAccelerated do
- # given that BinaryProtocolAccelerated only actually overrides read_message_begin
- # this shared spec isn't going to do much, but it's still worth including
- # for future-proofing in case we start overriding individual methods
+ describe Thrift::BinaryProtocolAccelerated do
+ # since BinaryProtocolAccelerated should be directly equivalent to
+ # BinaryProtocol, we don't need any custom specs!
it_should_behave_like 'a binary protocol'
def protocol_class
BinaryProtocolAccelerated
end
- before(:each) do
- @buffer = ""
- @trans.stub!(:borrow).and_return { @buffer }
- @trans.stub!(:consume!).and_return do |*args|
- n = args.first || 0
- @buffer.slice!(0,n)
- end
- end
-
- it "should read a message header" do
- @buffer = "\200\001\000\002\000\000\000\vtestMessage\000\000\000*"
- # @prot.should_receive(:read_i32).and_return(protocol_class.const_get(:VERSION_1) | Thrift::MessageTypes::REPLY, 42)
- # @prot.should_receive(:read_string).and_return('testMessage')
- @prot.read_message_begin.should == ['testMessage', Thrift::MessageTypes::REPLY, 42]
- end
-
- it "should raise an exception if the message header has the wrong version" do
- @buffer = "\000\000\000\v"
- # @prot.should_receive(:read_i32).and_return(42)
- lambda { @prot.read_message_begin }.should raise_error(Thrift::ProtocolException, 'Missing version identifier') do |e|
- e.type == Thrift::ProtocolException::BAD_VERSION
- end
- end
-
- it "should encode a struct with all fields set identically to Thrift::BinaryProtocol" do
- foo = SpecNamespace::Foo.new(:complex => {5 => {"foo" => 1.2}, 17 => {"bar" => 3.14159, "baz" => 5.8}})
- @prot.encode_binary(foo).should == "\r\000\005\b\r\000\000\000\002\000\000\000\005\v\004\000\000\000\001\000\
-\000\000\003foo?\363333333\000\000\000\021\v\004\000\000\000\002\000\000\000\003baz@\027333333\000\000\000\003bar@\
-\t!\371\360\e\206n\016\000\006\006\000\000\000\003\000\005\000\021\000\357\b\000\001\000\000\0005\v\000\002\000\000\
-\000\005words\f\000\003\v\000\001\000\000\000\rhello, world!\000\017\000\004\b\000\000\000\004\000\000\000\001\000\
-\000\000\002\000\000\000\002\000\000\000\003\000"
- end
-
- it "should encode a struct with missing fields identically to Thrift::BinaryProtocol" do
- foo = SpecNamespace::Foo.new(:simple => nil, :ints => nil)
- @prot.encode_binary(foo).should == "\016\000\006\006\000\000\000\003\000\005\000\021\000\357\v\000\002\000\000\
-\000\005words\f\000\003\v\000\001\000\000\000\rhello, world!\000\000"
- end
-
- it "should decode a struct with all fields set identically to Thrift::BinaryProtocol" do
- foo = SpecNamespace::Foo.new(:complex => {5 => {"foo" => 1.2}, 17 => {"bar" => 3.14159, "baz" => 5.8}})
- trans = Thrift::MemoryBuffer.new("\r\000\005\b\r\000\000\000\002\000\000\000\005\v\004\000\000\000\001\000\
-\000\000\003foo?\363333333\000\000\000\021\v\004\000\000\000\002\000\000\000\003baz@\027333333\000\000\000\003bar@\
-\t!\371\360\e\206n\016\000\006\006\000\000\000\003\000\005\000\021\000\357\b\000\001\000\000\0005\v\000\002\000\000\
-\000\005words\f\000\003\v\000\001\000\000\000\rhello, world!\000\017\000\004\b\000\000\000\004\000\000\000\001\000\
-\000\000\002\000\000\000\002\000\000\000\003\000")
- @prot.decode_binary(SpecNamespace::Foo.new, trans).should == foo
- end
-
- it "should decode a struct with missing fields identically to Thrift::BinaryProtocol" do
- trans = Thrift::MemoryBuffer.new("\016\000\006\006\000\000\000\003\000\005\000\021\000\357\v\000\002\000\000\
-\000\005words\f\000\003\v\000\001\000\000\000\rhello, world!\000\000")
- @prot.decode_binary(SpecNamespace::Foo.new, trans).should == SpecNamespace::Foo.new
- end
-
- it "should encode a string with null bytes in it" do
- foo = SpecNamespace::Hello.new(:greeting => "Hello\000World!")
- @prot.encode_binary(foo).should == "\v\000\001\000\000\000\fHello\000World!\000"
- end
-
- it "should decode a string with null bytes in it" do
- trans = Thrift::MemoryBuffer.new("\v\000\001\000\000\000\fHello\000World!\000")
- @prot.decode_binary(SpecNamespace::Hello.new, trans).should == SpecNamespace::Hello.new(:greeting => "Hello\000World!")
- end
-
- it "should error when encoding a struct with a nil value in a list" do
- Thrift.type_checking = false
- sl = SpecNamespace::SimpleList
- hello = SpecNamespace::Hello
- # nil counts as false for bools
- # lambda { @prot.encode_binary(sl.new(:bools => [true, false, nil, false])) }.should raise_error
- lambda { @prot.encode_binary(sl.new(:bytes => [1, 2, nil, 3])) }.should raise_error
- lambda { @prot.encode_binary(sl.new(:i16s => [1, 2, nil, 3])) }.should raise_error
- lambda { @prot.encode_binary(sl.new(:i32s => [1, 2, nil, 3])) }.should raise_error
- lambda { @prot.encode_binary(sl.new(:i64s => [1, 2, nil, 3])) }.should raise_error
- lambda { @prot.encode_binary(sl.new(:doubles => [1.0, 2.0, nil, 3.0])) }.should raise_error
- lambda { @prot.encode_binary(sl.new(:strings => ["one", "two", nil, "three"])) }.should raise_error
- lambda { @prot.encode_binary(sl.new(:lists => [[1, 2], nil, [3, 4]])) }.should raise_error
- lambda { @prot.encode_binary(sl.new(:maps => [{1 => 2}, nil, {3 => 4}])) }.should raise_error
- lambda { @prot.encode_binary(sl.new(:sets => [Set.new([1, 2]), nil, Set.new([3, 4])])) }.should raise_error
- lambda { @prot.encode_binary(sl.new(:structs => [hello.new, nil, hello.new(:greeting => "hi")])) }.should raise_error
- end
-
- it "should error when encoding a non-nil, non-correctly-typed value in a list" do
- Thrift.type_checking = false
- sl = SpecNamespace::SimpleList
- hello = SpecNamespace::Hello
- # bool should accept any value
- # lambda { @prot.encode_binary(sl.new(:bools => [true, false, 3])) }.should raise_error
- lambda { @prot.encode_binary(sl.new(:bytes => [1, 2, "3", 5])) }.should raise_error
- lambda { @prot.encode_binary(sl.new(:i16s => ["one", 2, 3])) }.should raise_error
- lambda { @prot.encode_binary(sl.new(:i32s => [[1,2], 3, 4])) }.should raise_error
- lambda { @prot.encode_binary(sl.new(:i64s => [{1 => 2}, 3, 4])) }.should raise_error
- lambda { @prot.encode_binary(sl.new(:doubles => ["one", 2.3, 3.4])) }.should raise_error
- lambda { @prot.encode_binary(sl.new(:strings => ["one", "two", 3, 4])) }.should raise_error
- lambda { @prot.encode_binary(sl.new(:lists => [{1 => 2}, [3, 4]])) }.should raise_error
- lambda { @prot.encode_binary(sl.new(:maps => [{1 => 2}, [3, 4]])) }.should raise_error
- lambda { @prot.encode_binary(sl.new(:sets => [Set.new([1, 2]), 3, 4])) }.should raise_error
- lambda { @prot.encode_binary(sl.new(:structs => [3, "four"])) }.should raise_error
- end
-
- it "should error when given nil to encode" do
- lambda { @prot.encode_binary(nil) }.should raise_error
- end
-
- it "should error when encoding an improper object where a container is expected" do
- Thrift.type_checking = false
- sl = SpecNamespace::SimpleList
- lambda { @prot.encode_binary(sl.new(:strings => {"one" => "two", nil => "three"})) }.should raise_error
- lambda { @prot.encode_binary(sl.new(:maps => [[1, 2]])) }.should raise_error
- end
-
- it "should accept arrays and hashes as sets" do
- Thrift.type_checking = false
- sl = SpecNamespace::SimpleList
- lambda { @prot.encode_binary(sl.new(:sets => [[1, 2], {3 => true, 4 => true}])) }.should_not raise_error
- end
+# before(:each) do
+# @buffer = ""
+# @trans.stub!(:borrow).and_return { @buffer }
+# @trans.stub!(:consume!).and_return do |*args|
+# n = args.first || 0
+# @buffer.slice!(0,n)
+# end
+# end
+#
+#
+# it "should raise an exception if the message header has the wrong version" do
+# @buffer = "\000\000\000\v"
+# # @prot.should_receive(:read_i32).and_return(42)
+# lambda { @prot.read_message_begin }.should raise_error(Thrift::ProtocolException, 'Missing version identifier') do |e|
+# e.type == Thrift::ProtocolException::BAD_VERSION
+# end
+# end
+#
+# it "should encode a string with null bytes in it" do
+# foo = SpecNamespace::Hello.new(:greeting => "Hello\000World!")
+# @prot.encode_binary(foo).should == "\v\000\001\000\000\000\fHello\000World!\000"
+# end
+#
+# it "should decode a string with null bytes in it" do
+# trans = Thrift::MemoryBuffer.new("\v\000\001\000\000\000\fHello\000World!\000")
+# @prot.decode_binary(SpecNamespace::Hello.new, trans).should == SpecNamespace::Hello.new(:greeting => "Hello\000World!")
+# end
+#
+# it "should error when encoding a struct with a nil value in a list" do
+# Thrift.type_checking = false
+# sl = SpecNamespace::SimpleList
+# hello = SpecNamespace::Hello
+# # nil counts as false for bools
+# # lambda { @prot.encode_binary(sl.new(:bools => [true, false, nil, false])) }.should raise_error
+# lambda { @prot.encode_binary(sl.new(:bytes => [1, 2, nil, 3])) }.should raise_error
+# lambda { @prot.encode_binary(sl.new(:i16s => [1, 2, nil, 3])) }.should raise_error
+# lambda { @prot.encode_binary(sl.new(:i32s => [1, 2, nil, 3])) }.should raise_error
+# lambda { @prot.encode_binary(sl.new(:i64s => [1, 2, nil, 3])) }.should raise_error
+# lambda { @prot.encode_binary(sl.new(:doubles => [1.0, 2.0, nil, 3.0])) }.should raise_error
+# lambda { @prot.encode_binary(sl.new(:strings => ["one", "two", nil, "three"])) }.should raise_error
+# lambda { @prot.encode_binary(sl.new(:lists => [[1, 2], nil, [3, 4]])) }.should raise_error
+# lambda { @prot.encode_binary(sl.new(:maps => [{1 => 2}, nil, {3 => 4}])) }.should raise_error
+# lambda { @prot.encode_binary(sl.new(:sets => [Set.new([1, 2]), nil, Set.new([3, 4])])) }.should raise_error
+# lambda { @prot.encode_binary(sl.new(:structs => [hello.new, nil, hello.new(:greeting => "hi")])) }.should raise_error
+# end
+#
+# it "should error when encoding a non-nil, non-correctly-typed value in a list" do
+# Thrift.type_checking = false
+# sl = SpecNamespace::SimpleList
+# hello = SpecNamespace::Hello
+# # bool should accept any value
+# # lambda { @prot.encode_binary(sl.new(:bools => [true, false, 3])) }.should raise_error
+# lambda { @prot.encode_binary(sl.new(:bytes => [1, 2, "3", 5])) }.should raise_error
+# lambda { @prot.encode_binary(sl.new(:i16s => ["one", 2, 3])) }.should raise_error
+# lambda { @prot.encode_binary(sl.new(:i32s => [[1,2], 3, 4])) }.should raise_error
+# lambda { @prot.encode_binary(sl.new(:i64s => [{1 => 2}, 3, 4])) }.should raise_error
+# lambda { @prot.encode_binary(sl.new(:doubles => ["one", 2.3, 3.4])) }.should raise_error
+# lambda { @prot.encode_binary(sl.new(:strings => ["one", "two", 3, 4])) }.should raise_error
+# lambda { @prot.encode_binary(sl.new(:lists => [{1 => 2}, [3, 4]])) }.should raise_error
+# lambda { @prot.encode_binary(sl.new(:maps => [{1 => 2}, [3, 4]])) }.should raise_error
+# lambda { @prot.encode_binary(sl.new(:sets => [Set.new([1, 2]), 3, 4])) }.should raise_error
+# lambda { @prot.encode_binary(sl.new(:structs => [3, "four"])) }.should raise_error
+# end
+#
+# it "should error when encoding an improper object where a container is expected" do
+# Thrift.type_checking = false
+# sl = SpecNamespace::SimpleList
+# lambda { @prot.encode_binary(sl.new(:strings => {"one" => "two", nil => "three"})) }.should raise_error
+# lambda { @prot.encode_binary(sl.new(:maps => [[1, 2]])) }.should raise_error
+# end
+#
+# it "should accept arrays and hashes as sets" do
+# Thrift.type_checking = false
+# sl = SpecNamespace::SimpleList
+# lambda { @prot.encode_binary(sl.new(:sets => [[1, 2], {3 => true, 4 => true}])) }.should_not raise_error
+# end
end
describe BinaryProtocolAcceleratedFactory do
require File.dirname(__FILE__) + '/spec_helper'
+require "thrift_native"
class ThriftProtocolSpec < Spec::ExampleGroup
include Thrift
['field 3', Types::MAP, 3],
[nil, Types::STOP, 0]
)
- @prot.should_receive(:skip).with(Types::STRING).ordered
- @prot.should_receive(:skip).with(Types::I32).ordered
- @prot.should_receive(:skip).with(Types::MAP).ordered
@prot.should_receive(:read_field_end).exactly(3).times
+ @prot.should_receive(:read_string).exactly(3).times
+ @prot.should_receive(:read_i32).ordered
+ @prot.should_receive(:read_map_begin).ordered.and_return([Types::STRING, Types::STRING, 1])
+ # @prot.should_receive(:read_string).exactly(2).times
+ @prot.should_receive(:read_map_end).ordered
@prot.should_receive(:read_struct_end).ordered
real_skip.call(Types::STRUCT)
end
it "should skip maps" do
real_skip = @prot.method(:skip)
- @prot.should_receive(:read_map_begin).ordered.and_return([Types::STRING, Types::STRUCT, 7])
- @prot.should_receive(:skip).ordered.exactly(14).times # once per key and once per value
+ @prot.should_receive(:read_map_begin).ordered.and_return([Types::STRING, Types::STRUCT, 1])
+ @prot.should_receive(:read_string).ordered
+ @prot.should_receive(:read_struct_begin).ordered.and_return(["some_struct"])
+ @prot.should_receive(:read_field_begin).ordered.and_return([nil, Types::STOP, nil]);
+ @prot.should_receive(:read_struct_end).ordered
@prot.should_receive(:read_map_end).ordered
real_skip.call(Types::MAP)
end
it "should skip sets" do
real_skip = @prot.method(:skip)
@prot.should_receive(:read_set_begin).ordered.and_return([Types::I64, 9])
- @prot.should_receive(:skip).with(Types::I64).ordered.exactly(9).times
+ @prot.should_receive(:read_i64).ordered.exactly(9).times
@prot.should_receive(:read_set_end)
real_skip.call(Types::SET)
end
it "should skip lists" do
real_skip = @prot.method(:skip)
@prot.should_receive(:read_list_begin).ordered.and_return([Types::DOUBLE, 11])
- @prot.should_receive(:skip).with(Types::DOUBLE).ordered.exactly(11).times
+ @prot.should_receive(:read_double).ordered.exactly(11).times
@prot.should_receive(:read_list_end)
real_skip.call(Types::LIST)
end
describe Serializer do
it "should serialize structs to binary by default" do
- serializer = Serializer.new
+ serializer = Serializer.new(Thrift::BinaryProtocolAcceleratedFactory.new)
data = serializer.serialize(Hello.new(:greeting => "'Ello guv'nor!"))
data.should == "\x0B\x00\x01\x00\x00\x00\x0E'Ello guv'nor!\x00"
end
it "should serialize structs to the given protocol" do
- protocol = mock("Protocol")
+ protocol = Protocol.new(mock("transport"))
protocol.should_receive(:write_struct_begin).with("SpecNamespace::Hello")
- protocol.should_receive(:write_field).with("greeting", Types::STRING, 1, "Good day")
+ protocol.should_receive(:write_field_begin).with("greeting", Types::STRING, 1)
+ protocol.should_receive(:write_string).with("Good day")
+ protocol.should_receive(:write_field_end)
protocol.should_receive(:write_field_stop)
protocol.should_receive(:write_struct_end)
protocolFactory = mock("ProtocolFactory")
end
it "should deserialize structs from the given protocol" do
- protocol = mock("Protocol")
+ protocol = Protocol.new(mock("transport"))
protocol.should_receive(:read_struct_begin).and_return("SpecNamespace::Hello")
protocol.should_receive(:read_field_begin).and_return(["greeting", Types::STRING, 1],
[nil, Types::STOP, 0])
- protocol.should_receive(:read_type).with(Types::STRING).and_return("Good day")
+ protocol.should_receive(:read_string).and_return("Good day")
protocol.should_receive(:read_field_end)
protocol.should_receive(:read_struct_end)
protocolFactory = mock("ProtocolFactory")
Thrift.type_checking = true
end
end
+
+require "thrift_native"
\ No newline at end of file
require File.dirname(__FILE__) + '/spec_helper'
require File.dirname(__FILE__) + '/gen-rb/ThriftSpec_types'
+# require "binaryprotocolaccelerated"
+
class ThriftStructSpec < Spec::ExampleGroup
include Thrift
include SpecNamespace
it "should read itself off the wire" do
struct = Foo.new
- prot = mock("Protocol")
+ prot = Protocol.new(mock("transport"))
prot.should_receive(:read_struct_begin).twice
prot.should_receive(:read_struct_end).twice
prot.should_receive(:read_field_begin).and_return(
prot.should_receive(:read_list_end)
prot.should_receive(:read_set_begin).and_return([Types::I16, 2])
prot.should_receive(:read_set_end)
- prot.should_receive(:read_type).with(Types::I32).and_return(
+ prot.should_receive(:read_i32).and_return(
1, 14, # complex keys
42, # simple
4, 23, 4, 29 # ints
)
- prot.should_receive(:read_type).with(Types::STRING).and_return("pi", "e", "feigenbaum", "apple banana", "what's up?")
- prot.should_receive(:read_type).with(Types::DOUBLE).and_return(Math::PI, Math::E, 4.669201609)
- prot.should_receive(:read_type).with(Types::I16).and_return(2, 3)
+ prot.should_receive(:read_string).and_return("pi", "e", "feigenbaum", "apple banana", "what's up?")
+ prot.should_receive(:read_double).and_return(Math::PI, Math::E, 4.669201609)
+ prot.should_receive(:read_i16).and_return(2, 3)
prot.should_not_receive(:skip)
struct.read(prot)
it "should skip unexpected fields in structs and use default values" do
struct = Foo.new
- prot = mock("Protocol")
+ prot = Protocol.new(mock("transport"))
prot.should_receive(:read_struct_begin)
prot.should_receive(:read_struct_end)
prot.should_receive(:read_field_begin).and_return(
[nil, Types::STOP, 0]
)
prot.should_receive(:read_field_end).exactly(5).times
- prot.should_receive(:read_type).with(Types::I32).and_return(42)
- prot.should_receive(:read_type).with(Types::STRING).and_return("foobar")
+ prot.should_receive(:read_i32).and_return(42)
+ prot.should_receive(:read_string).and_return("foobar")
prot.should_receive(:skip).with(Types::STRUCT)
prot.should_receive(:skip).with(Types::MAP)
+ # prot.should_receive(:read_map_begin).and_return([Types::I32, Types::I32, 0])
+ # prot.should_receive(:read_map_end)
prot.should_receive(:skip).with(Types::I32)
struct.read(prot)
end
it "should write itself to the wire" do
- prot = mock("Protocol")
+ prot = Protocol.new(mock("transport")) #mock("Protocol")
prot.should_receive(:write_struct_begin).with("SpecNamespace::Foo")
- prot.should_receive(:write_struct_end)
+ prot.should_receive(:write_struct_begin).with("SpecNamespace::Hello")
+ prot.should_receive(:write_struct_end).twice
prot.should_receive(:write_field_begin).with('ints', Types::LIST, 4)
+ prot.should_receive(:write_i32).with(1)
+ prot.should_receive(:write_i32).with(2).twice
+ prot.should_receive(:write_i32).with(3)
prot.should_receive(:write_field_begin).with('complex', Types::MAP, 5)
+ prot.should_receive(:write_i32).with(5)
+ prot.should_receive(:write_string).with('foo')
+ prot.should_receive(:write_double).with(1.23)
prot.should_receive(:write_field_begin).with('shorts', Types::SET, 6)
- prot.should_receive(:write_field_stop)
- prot.should_receive(:write_field_end).exactly(3).times
- prot.should_receive(:write_field).with('simple', Types::I32, 1, 53)
- prot.should_receive(:write_field).with('hello', Types::STRUCT, 3, Hello.new(:greeting => 'hello, world!'))
+ prot.should_receive(:write_i16).with(5)
+ prot.should_receive(:write_i16).with(17)
+ prot.should_receive(:write_i16).with(239)
+ prot.should_receive(:write_field_stop).twice
+ prot.should_receive(:write_field_end).exactly(6).times
+ prot.should_receive(:write_field_begin).with('simple', Types::I32, 1)
+ prot.should_receive(:write_i32).with(53)
+ prot.should_receive(:write_field_begin).with('hello', Types::STRUCT, 3)
+ prot.should_receive(:write_field_begin).with('greeting', Types::STRING, 1)
+ prot.should_receive(:write_string).with('hello, world!')
prot.should_receive(:write_map_begin).with(Types::I32, Types::MAP, 1)
prot.should_receive(:write_map_begin).with(Types::STRING, Types::DOUBLE, 1)
- prot.should_receive(:write_type).with(Types::I32, 5) # complex/1/key
- prot.should_receive(:write_type).with(Types::STRING, "foo") # complex/1/value/1/key
- prot.should_receive(:write_type).with(Types::DOUBLE, 1.23) # complex/1/value/1/value
prot.should_receive(:write_map_end).twice
prot.should_receive(:write_list_begin).with(Types::I32, 4)
- prot.should_receive(:write_type).with(Types::I32, 1)
- prot.should_receive(:write_type).with(Types::I32, 2).twice
- prot.should_receive(:write_type).with(Types::I32, 3)
prot.should_receive(:write_list_end)
prot.should_receive(:write_set_begin).with(Types::I16, 3)
- prot.should_receive(:write_type).with(Types::I16, 5)
- prot.should_receive(:write_type).with(Types::I16, 17)
- prot.should_receive(:write_type).with(Types::I16, 239)
prot.should_receive(:write_set_end)
struct = Foo.new
e.message.should == "something happened"
e.code.should == 1
# ensure it gets serialized properly, this is the really important part
- prot = mock("Protocol")
+ prot = Protocol.new(mock("trans"))
prot.should_receive(:write_struct_begin).with("SpecNamespace::Xception")
prot.should_receive(:write_struct_end)
- prot.should_receive(:write_field).with('message', Types::STRING, 1, "something happened")
- prot.should_receive(:write_field).with('code', Types::I32, 2, 1)
+ prot.should_receive(:write_field_begin).with('message', Types::STRING, 1)#, "something happened")
+ prot.should_receive(:write_string).with("something happened")
+ prot.should_receive(:write_field_begin).with('code', Types::I32, 2)#, 1)
+ prot.should_receive(:write_i32).with(1)
prot.should_receive(:write_field_stop)
+ prot.should_receive(:write_field_end).twice
e.write(prot)
end
rescue Thrift::Exception => e
e.message.should == "something happened"
e.code.should == 5
- prot = mock("Protocol")
+ prot = Protocol.new(mock("trans"))
prot.should_receive(:write_struct_begin).with("SpecNamespace::Xception")
prot.should_receive(:write_struct_end)
- prot.should_receive(:write_field).with('message', Types::STRING, 1, "something happened")
- prot.should_receive(:write_field).with('code', Types::I32, 2, 5)
+ prot.should_receive(:write_field_begin).with('message', Types::STRING, 1)
+ prot.should_receive(:write_string).with("something happened")
+ prot.should_receive(:write_field_begin).with('code', Types::I32, 2)
+ prot.should_receive(:write_i32).with(5)
prot.should_receive(:write_field_stop)
+ prot.should_receive(:write_field_end).twice
e.write(prot)
end