summaryrefslogtreecommitdiff
path: root/lib/rb/ext
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rb/ext')
-rw-r--r--lib/rb/ext/binary_protocol_accelerated.c474
-rw-r--r--lib/rb/ext/binary_protocol_accelerated.h20
-rw-r--r--lib/rb/ext/compact_protocol.c665
-rw-r--r--lib/rb/ext/compact_protocol.h20
-rw-r--r--lib/rb/ext/constants.h95
-rw-r--r--lib/rb/ext/extconf.rb26
-rw-r--r--lib/rb/ext/macros.h41
-rw-r--r--lib/rb/ext/memory_buffer.c72
-rw-r--r--lib/rb/ext/memory_buffer.h20
-rw-r--r--lib/rb/ext/protocol.c185
-rw-r--r--lib/rb/ext/protocol.h20
-rw-r--r--lib/rb/ext/struct.c605
-rw-r--r--lib/rb/ext/struct.h67
-rw-r--r--lib/rb/ext/thrift_native.c194
14 files changed, 2504 insertions, 0 deletions
diff --git a/lib/rb/ext/binary_protocol_accelerated.c b/lib/rb/ext/binary_protocol_accelerated.c
new file mode 100644
index 000000000..728a05725
--- /dev/null
+++ b/lib/rb/ext/binary_protocol_accelerated.c
@@ -0,0 +1,474 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <ruby.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <constants.h>
+#include <struct.h>
+#include "macros.h"
+
+VALUE rb_thrift_binary_proto_native_qmark(VALUE self) {
+ return Qtrue;
+}
+
+
+
+static int VERSION_1;
+static int VERSION_MASK;
+static int TYPE_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_LEN(str));
+ 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);
+ VALUE strict_write = GET_STRICT_WRITE(self);
+
+ if (strict_write == Qtrue) {
+ write_i32_direct(trans, VERSION_1 | FIX2INT(type));
+ write_string_direct(trans, name);
+ write_i32_direct(trans, FIX2INT(seqid));
+ } else {
+ write_string_direct(trans, name);
+ write_byte_direct(trans, FIX2INT(type));
+ 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_VALUE(rb_Float(dub));
+ 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_string_direct(trans, str);
+ return Qnil;
+}
+
+//---------------------------------------
+// interface reading methods
+//---------------------------------------
+
+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) {
+ VALUE buf = READ(self, 1);
+ return RSTRING_PTR(buf)[0];
+}
+
+static int16_t read_i16_direct(VALUE self) {
+ VALUE buf = READ(self, 2);
+ return (int16_t)(((uint8_t)(RSTRING_PTR(buf)[1])) | ((uint16_t)((RSTRING_PTR(buf)[0]) << 8)));
+}
+
+static int32_t read_i32_direct(VALUE self) {
+ VALUE buf = READ(self, 4);
+ return ((uint8_t)(RSTRING_PTR(buf)[3])) |
+ (((uint8_t)(RSTRING_PTR(buf)[2])) << 8) |
+ (((uint8_t)(RSTRING_PTR(buf)[1])) << 16) |
+ (((uint8_t)(RSTRING_PTR(buf)[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) {
+ VALUE strict_read = GET_STRICT_READ(self);
+ VALUE name, seqid;
+ int type;
+
+ int version = read_i32_direct(self);
+
+ if (version < 0) {
+ if ((version & VERSION_MASK) != VERSION_1) {
+ rb_exc_raise(get_protocol_exception(INT2FIX(BAD_VERSION), rb_str_new2("Missing version identifier")));
+ }
+ type = version & TYPE_MASK;
+ name = rb_thrift_binary_proto_read_string(self);
+ seqid = rb_thrift_binary_proto_read_i32(self);
+ } else {
+ if (strict_read == Qtrue) {
+ rb_exc_raise(get_protocol_exception(INT2FIX(BAD_VERSION), rb_str_new2("No version identifier, old protocol client?")));
+ }
+ name = READ(self, version);
+ type = read_byte_direct(self);
+ 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 != 0 ? 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")));
+ TYPE_MASK = rb_num2ll(rb_const_get(thrift_binary_protocol_class, rb_intern("TYPE_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);
+}
diff --git a/lib/rb/ext/binary_protocol_accelerated.h b/lib/rb/ext/binary_protocol_accelerated.h
new file mode 100644
index 000000000..37baf4142
--- /dev/null
+++ b/lib/rb/ext/binary_protocol_accelerated.h
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+void Init_binary_protocol_accelerated();
diff --git a/lib/rb/ext/compact_protocol.c b/lib/rb/ext/compact_protocol.c
new file mode 100644
index 000000000..7966d3e3f
--- /dev/null
+++ b/lib/rb/ext/compact_protocol.c
@@ -0,0 +1,665 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <ruby.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <constants.h>
+#include <struct.h>
+#include "macros.h"
+
+#define LAST_ID(obj) FIX2INT(rb_ary_pop(rb_ivar_get(obj, last_field_id)))
+#define SET_LAST_ID(obj, val) rb_ary_push(rb_ivar_get(obj, last_field_id), val)
+
+VALUE rb_thrift_compact_proto_native_qmark(VALUE self) {
+ return Qtrue;
+}
+
+static ID last_field_id;
+static ID boolean_field_id;
+static ID bool_value_id;
+
+static int VERSION;
+static int VERSION_MASK;
+static int TYPE_MASK;
+static int TYPE_SHIFT_AMOUNT;
+static int PROTOCOL_ID;
+
+static VALUE thrift_compact_protocol_class;
+
+static int CTYPE_BOOLEAN_TRUE = 0x01;
+static int CTYPE_BOOLEAN_FALSE = 0x02;
+static int CTYPE_BYTE = 0x03;
+static int CTYPE_I16 = 0x04;
+static int CTYPE_I32 = 0x05;
+static int CTYPE_I64 = 0x06;
+static int CTYPE_DOUBLE = 0x07;
+static int CTYPE_BINARY = 0x08;
+static int CTYPE_LIST = 0x09;
+static int CTYPE_SET = 0x0A;
+static int CTYPE_MAP = 0x0B;
+static int CTYPE_STRUCT = 0x0C;
+
+VALUE rb_thrift_compact_proto_write_i16(VALUE self, VALUE i16);
+
+// TODO: implement this
+static int get_compact_type(VALUE type_value) {
+ int type = FIX2INT(type_value);
+ if (type == TTYPE_BOOL) {
+ return CTYPE_BOOLEAN_TRUE;
+ } else if (type == TTYPE_BYTE) {
+ return CTYPE_BYTE;
+ } else if (type == TTYPE_I16) {
+ return CTYPE_I16;
+ } else if (type == TTYPE_I32) {
+ return CTYPE_I32;
+ } else if (type == TTYPE_I64) {
+ return CTYPE_I64;
+ } else if (type == TTYPE_DOUBLE) {
+ return CTYPE_DOUBLE;
+ } else if (type == TTYPE_STRING) {
+ return CTYPE_BINARY;
+ } else if (type == TTYPE_LIST) {
+ return CTYPE_LIST;
+ } else if (type == TTYPE_SET) {
+ return CTYPE_SET;
+ } else if (type == TTYPE_MAP) {
+ return CTYPE_MAP;
+ } else if (type == TTYPE_STRUCT) {
+ return CTYPE_STRUCT;
+ } else {
+ char str[50];
+ sprintf(str, "don't know what type: %d", type);
+ rb_raise(rb_eStandardError, str);
+ return 0;
+ }
+}
+
+static void write_byte_direct(VALUE transport, int8_t b) {
+ WRITE(transport, (char*)&b, 1);
+}
+
+static void write_field_begin_internal(VALUE self, VALUE type, VALUE id_value, VALUE type_override) {
+ int id = FIX2INT(id_value);
+ int last_id = LAST_ID(self);
+ VALUE transport = GET_TRANSPORT(self);
+
+ // if there's a type override, use that.
+ int8_t type_to_write = RTEST(type_override) ? FIX2INT(type_override) : get_compact_type(type);
+ // check if we can use delta encoding for the field id
+ int diff = id - last_id;
+ if (diff > 0 && diff <= 15) {
+ // write them together
+ write_byte_direct(transport, diff << 4 | (type_to_write & 0x0f));
+ } else {
+ // write them separate
+ write_byte_direct(transport, type_to_write & 0x0f);
+ rb_thrift_compact_proto_write_i16(self, id_value);
+ }
+
+ SET_LAST_ID(self, id_value);
+}
+
+static int32_t int_to_zig_zag(int32_t n) {
+ return (n << 1) ^ (n >> 31);
+}
+
+static uint64_t ll_to_zig_zag(int64_t n) {
+ return (n << 1) ^ (n >> 63);
+}
+
+static void write_varint32(VALUE transport, uint32_t n) {
+ while (true) {
+ if ((n & ~0x7F) == 0) {
+ write_byte_direct(transport, n & 0x7f);
+ break;
+ } else {
+ write_byte_direct(transport, (n & 0x7F) | 0x80);
+ n = n >> 7;
+ }
+ }
+}
+
+static void write_varint64(VALUE transport, uint64_t n) {
+ while (true) {
+ if ((n & ~0x7F) == 0) {
+ write_byte_direct(transport, n & 0x7f);
+ break;
+ } else {
+ write_byte_direct(transport, (n & 0x7F) | 0x80);
+ n = n >> 7;
+ }
+ }
+}
+
+static void write_collection_begin(VALUE transport, VALUE elem_type, VALUE size_value) {
+ int size = FIX2INT(size_value);
+ if (size <= 14) {
+ write_byte_direct(transport, size << 4 | get_compact_type(elem_type));
+ } else {
+ write_byte_direct(transport, 0xf0 | get_compact_type(elem_type));
+ write_varint32(transport, size);
+ }
+}
+
+
+//--------------------------------
+// interface writing methods
+//--------------------------------
+
+VALUE rb_thrift_compact_proto_write_i32(VALUE self, VALUE i32);
+VALUE rb_thrift_compact_proto_write_string(VALUE self, VALUE str);
+
+VALUE rb_thrift_compact_proto_write_message_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_write_struct_begin(VALUE self, VALUE name) {
+ rb_ary_push(rb_ivar_get(self, last_field_id), INT2FIX(0));
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_write_struct_end(VALUE self) {
+ rb_ary_pop(rb_ivar_get(self, last_field_id));
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_write_field_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_write_map_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_write_list_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_write_set_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_write_message_begin(VALUE self, VALUE name, VALUE type, VALUE seqid) {
+ VALUE transport = GET_TRANSPORT(self);
+ write_byte_direct(transport, PROTOCOL_ID);
+ write_byte_direct(transport, (VERSION & VERSION_MASK) | ((FIX2INT(type) << TYPE_SHIFT_AMOUNT) & TYPE_MASK));
+ write_varint32(transport, FIX2INT(seqid));
+ rb_thrift_compact_proto_write_string(self, name);
+
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_write_field_begin(VALUE self, VALUE name, VALUE type, VALUE id) {
+ if (FIX2INT(type) == TTYPE_BOOL) {
+ // we want to possibly include the value, so we'll wait.
+ rb_ivar_set(self, boolean_field_id, rb_ary_new3(2, type, id));
+ } else {
+ write_field_begin_internal(self, type, id, Qnil);
+ }
+
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_write_field_stop(VALUE self) {
+ write_byte_direct(GET_TRANSPORT(self), TTYPE_STOP);
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_write_map_begin(VALUE self, VALUE ktype, VALUE vtype, VALUE size_value) {
+ int size = FIX2INT(size_value);
+ VALUE transport = GET_TRANSPORT(self);
+ if (size == 0) {
+ write_byte_direct(transport, 0);
+ } else {
+ write_varint32(transport, size);
+ write_byte_direct(transport, get_compact_type(ktype) << 4 | get_compact_type(vtype));
+ }
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_write_list_begin(VALUE self, VALUE etype, VALUE size) {
+ write_collection_begin(GET_TRANSPORT(self), etype, size);
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_write_set_begin(VALUE self, VALUE etype, VALUE size) {
+ write_collection_begin(GET_TRANSPORT(self), etype, size);
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_write_bool(VALUE self, VALUE b) {
+ int8_t type = b == Qtrue ? CTYPE_BOOLEAN_TRUE : CTYPE_BOOLEAN_FALSE;
+ VALUE boolean_field = rb_ivar_get(self, boolean_field_id);
+ if (NIL_P(boolean_field)) {
+ // we're not part of a field, so just write the value.
+ write_byte_direct(GET_TRANSPORT(self), type);
+ } else {
+ // we haven't written the field header yet
+ write_field_begin_internal(self, rb_ary_entry(boolean_field, 0), rb_ary_entry(boolean_field, 1), INT2FIX(type));
+ rb_ivar_set(self, boolean_field_id, Qnil);
+ }
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_write_byte(VALUE self, VALUE byte) {
+ CHECK_NIL(byte);
+ write_byte_direct(GET_TRANSPORT(self), FIX2INT(byte));
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_write_i16(VALUE self, VALUE i16) {
+ rb_thrift_compact_proto_write_i32(self, i16);
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_write_i32(VALUE self, VALUE i32) {
+ CHECK_NIL(i32);
+ write_varint32(GET_TRANSPORT(self), int_to_zig_zag(NUM2INT(i32)));
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_write_i64(VALUE self, VALUE i64) {
+ CHECK_NIL(i64);
+ write_varint64(GET_TRANSPORT(self), ll_to_zig_zag(NUM2LL(i64)));
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_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 l;
+ } transfer;
+ transfer.f = RFLOAT_VALUE(rb_Float(dub));
+ char buf[8];
+ buf[0] = transfer.l & 0xff;
+ buf[1] = (transfer.l >> 8) & 0xff;
+ buf[2] = (transfer.l >> 16) & 0xff;
+ buf[3] = (transfer.l >> 24) & 0xff;
+ buf[4] = (transfer.l >> 32) & 0xff;
+ buf[5] = (transfer.l >> 40) & 0xff;
+ buf[6] = (transfer.l >> 48) & 0xff;
+ buf[7] = (transfer.l >> 56) & 0xff;
+ WRITE(GET_TRANSPORT(self), buf, 8);
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_write_string(VALUE self, VALUE str) {
+ VALUE transport = GET_TRANSPORT(self);
+ write_varint32(transport, RSTRING_LEN(str));
+ WRITE(transport, RSTRING_PTR(str), RSTRING_LEN(str));
+ return Qnil;
+}
+
+//---------------------------------------
+// interface reading methods
+//---------------------------------------
+
+#define is_bool_type(ctype) (((ctype) & 0x0F) == CTYPE_BOOLEAN_TRUE || ((ctype) & 0x0F) == CTYPE_BOOLEAN_FALSE)
+
+VALUE rb_thrift_compact_proto_read_string(VALUE self);
+VALUE rb_thrift_compact_proto_read_byte(VALUE self);
+VALUE rb_thrift_compact_proto_read_i32(VALUE self);
+VALUE rb_thrift_compact_proto_read_i16(VALUE self);
+
+static int8_t get_ttype(int8_t ctype) {
+ if (ctype == TTYPE_STOP) {
+ return TTYPE_STOP;
+ } else if (ctype == CTYPE_BOOLEAN_TRUE || ctype == CTYPE_BOOLEAN_FALSE) {
+ return TTYPE_BOOL;
+ } else if (ctype == CTYPE_BYTE) {
+ return TTYPE_BYTE;
+ } else if (ctype == CTYPE_I16) {
+ return TTYPE_I16;
+ } else if (ctype == CTYPE_I32) {
+ return TTYPE_I32;
+ } else if (ctype == CTYPE_I64) {
+ return TTYPE_I64;
+ } else if (ctype == CTYPE_DOUBLE) {
+ return TTYPE_DOUBLE;
+ } else if (ctype == CTYPE_BINARY) {
+ return TTYPE_STRING;
+ } else if (ctype == CTYPE_LIST) {
+ return TTYPE_LIST;
+ } else if (ctype == CTYPE_SET) {
+ return TTYPE_SET;
+ } else if (ctype == CTYPE_MAP) {
+ return TTYPE_MAP;
+ } else if (ctype == CTYPE_STRUCT) {
+ return TTYPE_STRUCT;
+ } else {
+ char str[50];
+ sprintf(str, "don't know what type: %d", ctype);
+ rb_raise(rb_eStandardError, str);
+ return 0;
+ }
+}
+
+static char read_byte_direct(VALUE self) {
+ VALUE buf = READ(self, 1);
+ return RSTRING_PTR(buf)[0];
+}
+
+static int64_t zig_zag_to_ll(int64_t n) {
+ return (((uint64_t)n) >> 1) ^ -(n & 1);
+}
+
+static int32_t zig_zag_to_int(int32_t n) {
+ return (((uint32_t)n) >> 1) ^ -(n & 1);
+}
+
+static int64_t read_varint64(VALUE self) {
+ int shift = 0;
+ int64_t result = 0;
+ while (true) {
+ int8_t b = read_byte_direct(self);
+ result = result | ((uint64_t)(b & 0x7f) << shift);
+ if ((b & 0x80) != 0x80) {
+ break;
+ }
+ shift += 7;
+ }
+ return result;
+}
+
+static int16_t read_i16(VALUE self) {
+ return zig_zag_to_int((int32_t)read_varint64(self));
+}
+
+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_compact_proto_read_message_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_read_struct_begin(VALUE self) {
+ rb_ary_push(rb_ivar_get(self, last_field_id), INT2FIX(0));
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_read_struct_end(VALUE self) {
+ rb_ary_pop(rb_ivar_get(self, last_field_id));
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_read_field_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_read_map_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_read_list_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_read_set_end(VALUE self) {
+ return Qnil;
+}
+
+VALUE rb_thrift_compact_proto_read_message_begin(VALUE self) {
+ int8_t protocol_id = read_byte_direct(self);
+ if (protocol_id != PROTOCOL_ID) {
+ char buf[100];
+ int len = sprintf(buf, "Expected protocol id %d but got %d", PROTOCOL_ID, protocol_id);
+ buf[len] = 0;
+ rb_exc_raise(get_protocol_exception(INT2FIX(-1), rb_str_new2(buf)));
+ }
+
+ int8_t version_and_type = read_byte_direct(self);
+ int8_t version = version_and_type & VERSION_MASK;
+ if (version != VERSION) {
+ char buf[100];
+ int len = sprintf(buf, "Expected version id %d but got %d", version, VERSION);
+ buf[len] = 0;
+ rb_exc_raise(get_protocol_exception(INT2FIX(-1), rb_str_new2(buf)));
+ }
+
+ int8_t type = (version_and_type >> TYPE_SHIFT_AMOUNT) & 0x03;
+ int32_t seqid = read_varint64(self);
+ VALUE messageName = rb_thrift_compact_proto_read_string(self);
+ return rb_ary_new3(3, messageName, INT2FIX(type), INT2NUM(seqid));
+}
+
+VALUE rb_thrift_compact_proto_read_field_begin(VALUE self) {
+ int8_t type = read_byte_direct(self);
+ // if it's a stop, then we can return immediately, as the struct is over.
+ if ((type & 0x0f) == TTYPE_STOP) {
+ return rb_ary_new3(3, Qnil, INT2FIX(0), INT2FIX(0));
+ } else {
+ int field_id = 0;
+
+ // mask off the 4 MSB of the type header. it could contain a field id delta.
+ uint8_t modifier = ((type & 0xf0) >> 4);
+
+ if (modifier == 0) {
+ // not a delta. look ahead for the zigzag varint field id.
+ field_id = read_i16(self);
+ } else {
+ // has a delta. add the delta to the last read field id.
+ field_id = LAST_ID(self) + modifier;
+ }
+
+ // if this happens to be a boolean field, the value is encoded in the type
+ if (is_bool_type(type)) {
+ // save the boolean value in a special instance variable.
+ rb_ivar_set(self, bool_value_id, (type & 0x0f) == CTYPE_BOOLEAN_TRUE ? Qtrue : Qfalse);
+ }
+
+ // push the new field onto the field stack so we can keep the deltas going.
+ SET_LAST_ID(self, INT2FIX(field_id));
+ return rb_ary_new3(3, Qnil, INT2FIX(get_ttype(type & 0x0f)), INT2FIX(field_id));
+ }
+}
+
+VALUE rb_thrift_compact_proto_read_map_begin(VALUE self) {
+ int32_t size = read_varint64(self);
+ uint8_t key_and_value_type = size == 0 ? 0 : read_byte_direct(self);
+ return rb_ary_new3(3, INT2FIX(get_ttype(key_and_value_type >> 4)), INT2FIX(get_ttype(key_and_value_type & 0xf)), INT2FIX(size));
+}
+
+VALUE rb_thrift_compact_proto_read_list_begin(VALUE self) {
+ uint8_t size_and_type = read_byte_direct(self);
+ int32_t size = (size_and_type >> 4) & 0x0f;
+ if (size == 15) {
+ size = read_varint64(self);
+ }
+ uint8_t type = get_ttype(size_and_type & 0x0f);
+ return rb_ary_new3(2, INT2FIX(type), INT2FIX(size));
+}
+
+VALUE rb_thrift_compact_proto_read_set_begin(VALUE self) {
+ return rb_thrift_compact_proto_read_list_begin(self);
+}
+
+VALUE rb_thrift_compact_proto_read_bool(VALUE self) {
+ VALUE bool_value = rb_ivar_get(self, bool_value_id);
+ if (NIL_P(bool_value)) {
+ return read_byte_direct(self) == CTYPE_BOOLEAN_TRUE ? Qtrue : Qfalse;
+ } else {
+ rb_ivar_set(self, bool_value_id, Qnil);
+ return bool_value;
+ }
+}
+
+VALUE rb_thrift_compact_proto_read_byte(VALUE self) {
+ return INT2FIX(read_byte_direct(self));
+}
+
+VALUE rb_thrift_compact_proto_read_i16(VALUE self) {
+ return INT2FIX(read_i16(self));
+}
+
+VALUE rb_thrift_compact_proto_read_i32(VALUE self) {
+ return INT2NUM(zig_zag_to_int(read_varint64(self)));
+}
+
+VALUE rb_thrift_compact_proto_read_i64(VALUE self) {
+ return LL2NUM(zig_zag_to_ll(read_varint64(self)));
+}
+
+VALUE rb_thrift_compact_proto_read_double(VALUE self) {
+ union {
+ double f;
+ int64_t l;
+ } transfer;
+ VALUE bytes = READ(self, 8);
+ uint32_t lo = ((uint8_t)(RSTRING_PTR(bytes)[0]))
+ | (((uint8_t)(RSTRING_PTR(bytes)[1])) << 8)
+ | (((uint8_t)(RSTRING_PTR(bytes)[2])) << 16)
+ | (((uint8_t)(RSTRING_PTR(bytes)[3])) << 24);
+ uint64_t hi = (((uint8_t)(RSTRING_PTR(bytes)[4])))
+ | (((uint8_t)(RSTRING_PTR(bytes)[5])) << 8)
+ | (((uint8_t)(RSTRING_PTR(bytes)[6])) << 16)
+ | (((uint8_t)(RSTRING_PTR(bytes)[7])) << 24);
+ transfer.l = (hi << 32) | lo;
+
+ return rb_float_new(transfer.f);
+}
+
+VALUE rb_thrift_compact_proto_read_string(VALUE self) {
+ int64_t size = read_varint64(self);
+ return READ(self, size);
+}
+
+static void Init_constants() {
+ thrift_compact_protocol_class = rb_const_get(thrift_module, rb_intern("CompactProtocol"));
+
+ VERSION = rb_num2ll(rb_const_get(thrift_compact_protocol_class, rb_intern("VERSION")));
+ VERSION_MASK = rb_num2ll(rb_const_get(thrift_compact_protocol_class, rb_intern("VERSION_MASK")));
+ TYPE_MASK = rb_num2ll(rb_const_get(thrift_compact_protocol_class, rb_intern("TYPE_MASK")));
+ TYPE_SHIFT_AMOUNT = FIX2INT(rb_const_get(thrift_compact_protocol_class, rb_intern("TYPE_SHIFT_AMOUNT")));
+ PROTOCOL_ID = FIX2INT(rb_const_get(thrift_compact_protocol_class, rb_intern("PROTOCOL_ID")));
+
+ last_field_id = rb_intern("@last_field");
+ boolean_field_id = rb_intern("@boolean_field");
+ bool_value_id = rb_intern("@bool_value");
+}
+
+static void Init_rb_methods() {
+ rb_define_method(thrift_compact_protocol_class, "native?", rb_thrift_compact_proto_native_qmark, 0);
+
+ rb_define_method(thrift_compact_protocol_class, "write_message_begin", rb_thrift_compact_proto_write_message_begin, 3);
+ rb_define_method(thrift_compact_protocol_class, "write_field_begin", rb_thrift_compact_proto_write_field_begin, 3);
+ rb_define_method(thrift_compact_protocol_class, "write_field_stop", rb_thrift_compact_proto_write_field_stop, 0);
+ rb_define_method(thrift_compact_protocol_class, "write_map_begin", rb_thrift_compact_proto_write_map_begin, 3);
+ rb_define_method(thrift_compact_protocol_class, "write_list_begin", rb_thrift_compact_proto_write_list_begin, 2);
+ rb_define_method(thrift_compact_protocol_class, "write_set_begin", rb_thrift_compact_proto_write_set_begin, 2);
+ rb_define_method(thrift_compact_protocol_class, "write_byte", rb_thrift_compact_proto_write_byte, 1);
+ rb_define_method(thrift_compact_protocol_class, "write_bool", rb_thrift_compact_proto_write_bool, 1);
+ rb_define_method(thrift_compact_protocol_class, "write_i16", rb_thrift_compact_proto_write_i16, 1);
+ rb_define_method(thrift_compact_protocol_class, "write_i32", rb_thrift_compact_proto_write_i32, 1);
+ rb_define_method(thrift_compact_protocol_class, "write_i64", rb_thrift_compact_proto_write_i64, 1);
+ rb_define_method(thrift_compact_protocol_class, "write_double", rb_thrift_compact_proto_write_double, 1);
+ rb_define_method(thrift_compact_protocol_class, "write_string", rb_thrift_compact_proto_write_string, 1);
+
+ rb_define_method(thrift_compact_protocol_class, "write_message_end", rb_thrift_compact_proto_write_message_end, 0);
+ rb_define_method(thrift_compact_protocol_class, "write_struct_begin", rb_thrift_compact_proto_write_struct_begin, 1);
+ rb_define_method(thrift_compact_protocol_class, "write_struct_end", rb_thrift_compact_proto_write_struct_end, 0);
+ rb_define_method(thrift_compact_protocol_class, "write_field_end", rb_thrift_compact_proto_write_field_end, 0);
+ rb_define_method(thrift_compact_protocol_class, "write_map_end", rb_thrift_compact_proto_write_map_end, 0);
+ rb_define_method(thrift_compact_protocol_class, "write_list_end", rb_thrift_compact_proto_write_list_end, 0);
+ rb_define_method(thrift_compact_protocol_class, "write_set_end", rb_thrift_compact_proto_write_set_end, 0);
+
+
+ rb_define_method(thrift_compact_protocol_class, "read_message_begin", rb_thrift_compact_proto_read_message_begin, 0);
+ rb_define_method(thrift_compact_protocol_class, "read_field_begin", rb_thrift_compact_proto_read_field_begin, 0);
+ rb_define_method(thrift_compact_protocol_class, "read_map_begin", rb_thrift_compact_proto_read_map_begin, 0);
+ rb_define_method(thrift_compact_protocol_class, "read_list_begin", rb_thrift_compact_proto_read_list_begin, 0);
+ rb_define_method(thrift_compact_protocol_class, "read_set_begin", rb_thrift_compact_proto_read_set_begin, 0);
+ rb_define_method(thrift_compact_protocol_class, "read_byte", rb_thrift_compact_proto_read_byte, 0);
+ rb_define_method(thrift_compact_protocol_class, "read_bool", rb_thrift_compact_proto_read_bool, 0);
+ rb_define_method(thrift_compact_protocol_class, "read_i16", rb_thrift_compact_proto_read_i16, 0);
+ rb_define_method(thrift_compact_protocol_class, "read_i32", rb_thrift_compact_proto_read_i32, 0);
+ rb_define_method(thrift_compact_protocol_class, "read_i64", rb_thrift_compact_proto_read_i64, 0);
+ rb_define_method(thrift_compact_protocol_class, "read_double", rb_thrift_compact_proto_read_double, 0);
+ rb_define_method(thrift_compact_protocol_class, "read_string", rb_thrift_compact_proto_read_string, 0);
+
+ rb_define_method(thrift_compact_protocol_class, "read_message_end", rb_thrift_compact_proto_read_message_end, 0);
+ rb_define_method(thrift_compact_protocol_class, "read_struct_begin", rb_thrift_compact_proto_read_struct_begin, 0);
+ rb_define_method(thrift_compact_protocol_class, "read_struct_end", rb_thrift_compact_proto_read_struct_end, 0);
+ rb_define_method(thrift_compact_protocol_class, "read_field_end", rb_thrift_compact_proto_read_field_end, 0);
+ rb_define_method(thrift_compact_protocol_class, "read_map_end", rb_thrift_compact_proto_read_map_end, 0);
+ rb_define_method(thrift_compact_protocol_class, "read_list_end", rb_thrift_compact_proto_read_list_end, 0);
+ rb_define_method(thrift_compact_protocol_class, "read_set_end", rb_thrift_compact_proto_read_set_end, 0);
+}
+
+static void Init_npmt() {
+ native_proto_method_table *npmt;
+ npmt = ALLOC(native_proto_method_table);
+
+ npmt->write_field_begin = rb_thrift_compact_proto_write_field_begin;
+ npmt->write_field_stop = rb_thrift_compact_proto_write_field_stop;
+ npmt->write_map_begin = rb_thrift_compact_proto_write_map_begin;
+ npmt->write_list_begin = rb_thrift_compact_proto_write_list_begin;
+ npmt->write_set_begin = rb_thrift_compact_proto_write_set_begin;
+ npmt->write_byte = rb_thrift_compact_proto_write_byte;
+ npmt->write_bool = rb_thrift_compact_proto_write_bool;
+ npmt->write_i16 = rb_thrift_compact_proto_write_i16;
+ npmt->write_i32 = rb_thrift_compact_proto_write_i32;
+ npmt->write_i64 = rb_thrift_compact_proto_write_i64;
+ npmt->write_double = rb_thrift_compact_proto_write_double;
+ npmt->write_string = rb_thrift_compact_proto_write_string;
+ npmt->write_message_end = rb_thrift_compact_proto_write_message_end;
+ npmt->write_struct_begin = rb_thrift_compact_proto_write_struct_begin;
+ npmt->write_struct_end = rb_thrift_compact_proto_write_struct_end;
+ npmt->write_field_end = rb_thrift_compact_proto_write_field_end;
+ npmt->write_map_end = rb_thrift_compact_proto_write_map_end;
+ npmt->write_list_end = rb_thrift_compact_proto_write_list_end;
+ npmt->write_set_end = rb_thrift_compact_proto_write_set_end;
+
+ npmt->read_message_begin = rb_thrift_compact_proto_read_message_begin;
+ npmt->read_field_begin = rb_thrift_compact_proto_read_field_begin;
+ npmt->read_map_begin = rb_thrift_compact_proto_read_map_begin;
+ npmt->read_list_begin = rb_thrift_compact_proto_read_list_begin;
+ npmt->read_set_begin = rb_thrift_compact_proto_read_set_begin;
+ npmt->read_byte = rb_thrift_compact_proto_read_byte;
+ npmt->read_bool = rb_thrift_compact_proto_read_bool;
+ npmt->read_i16 = rb_thrift_compact_proto_read_i16;
+ npmt->read_i32 = rb_thrift_compact_proto_read_i32;
+ npmt->read_i64 = rb_thrift_compact_proto_read_i64;
+ npmt->read_double = rb_thrift_compact_proto_read_double;
+ npmt->read_string = rb_thrift_compact_proto_read_string;
+ npmt->read_message_end = rb_thrift_compact_proto_read_message_end;
+ npmt->read_struct_begin = rb_thrift_compact_proto_read_struct_begin;
+ npmt->read_struct_end = rb_thrift_compact_proto_read_struct_end;
+ npmt->read_field_end = rb_thrift_compact_proto_read_field_end;
+ npmt->read_map_end = rb_thrift_compact_proto_read_map_end;
+ npmt->read_list_end = rb_thrift_compact_proto_read_list_end;
+ npmt->read_set_end = rb_thrift_compact_proto_read_set_end;
+
+ VALUE method_table_object = Data_Wrap_Struct(rb_cObject, 0, free, npmt);
+ rb_const_set(thrift_compact_protocol_class, rb_intern("@native_method_table"), method_table_object);
+}
+
+
+
+void Init_compact_protocol() {
+ Init_constants();
+ Init_rb_methods();
+ Init_npmt();
+}
diff --git a/lib/rb/ext/compact_protocol.h b/lib/rb/ext/compact_protocol.h
new file mode 100644
index 000000000..163915e94
--- /dev/null
+++ b/lib/rb/ext/compact_protocol.h
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+void Init_compact_protocol();
diff --git a/lib/rb/ext/constants.h b/lib/rb/ext/constants.h
new file mode 100644
index 000000000..57df544b5
--- /dev/null
+++ b/lib/rb/ext/constants.h
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+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_all_method_id;
+extern ID native_qmark_method_id;
+
+extern ID fields_const_id;
+extern ID transport_ivar_id;
+extern ID strict_read_ivar_id;
+extern ID strict_write_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;
diff --git a/lib/rb/ext/extconf.rb b/lib/rb/ext/extconf.rb
new file mode 100644
index 000000000..07558b8aa
--- /dev/null
+++ b/lib/rb/ext/extconf.rb
@@ -0,0 +1,26 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'mkmf'
+
+$CFLAGS = "-g -O2 -Wall -Werror"
+
+have_func("strlcpy", "string.h")
+
+create_makefile 'thrift_native'
diff --git a/lib/rb/ext/macros.h b/lib/rb/ext/macros.h
new file mode 100644
index 000000000..265f6930d
--- /dev/null
+++ b/lib/rb/ext/macros.h
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#define GET_TRANSPORT(obj) rb_ivar_get(obj, transport_ivar_id)
+#define GET_STRICT_READ(obj) rb_ivar_get(obj, strict_read_ivar_id)
+#define GET_STRICT_WRITE(obj) rb_ivar_get(obj, strict_write_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!");}
+#define READ(obj, length) rb_funcall(GET_TRANSPORT(obj), read_all_method_id, 1, INT2FIX(length))
+
+#ifndef RFLOAT_VALUE
+# define RFLOAT_VALUE(v) RFLOAT(rb_Float(v))->value
+#endif
+
+#ifndef RSTRING_LEN
+# define RSTRING_LEN(v) RSTRING(rb_String(v))->len
+#endif
+
+#ifndef RSTRING_PTR
+# define RSTRING_PTR(v) RSTRING(rb_String(v))->ptr
+#endif
+
+#ifndef RARRAY_LEN
+# define RARRAY_LEN(v) RARRAY(rb_Array(v))->len
+#endif
diff --git a/lib/rb/ext/memory_buffer.c b/lib/rb/ext/memory_buffer.c
new file mode 100644
index 000000000..624012d4b
--- /dev/null
+++ b/lib/rb/ext/memory_buffer.c
@@ -0,0 +1,72 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <ruby.h>
+#include <constants.h>
+#include "macros.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_PTR(str), RSTRING_LEN(str));
+ 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_LEN(buf)) {
+ index = RSTRING_LEN(buf);
+ }
+ if (index >= GARBAGE_BUFFER_SIZE) {
+ rb_ivar_set(self, buf_ivar_id, rb_funcall(buf, slice_method_id, 2, INT2FIX(index), INT2FIX(RSTRING_LEN(buf) - 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("MemoryBufferTransport"));
+ 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")));
+}
diff --git a/lib/rb/ext/memory_buffer.h b/lib/rb/ext/memory_buffer.h
new file mode 100644
index 000000000..b277fa6f6
--- /dev/null
+++ b/lib/rb/ext/memory_buffer.h
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+void Init_memory_buffer();
diff --git a/lib/rb/ext/protocol.c b/lib/rb/ext/protocol.c
new file mode 100644
index 000000000..c1876541c
--- /dev/null
+++ b/lib/rb/ext/protocol.c
@@ -0,0 +1,185 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#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("BaseProtocol"));
+
+ 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);
+}
diff --git a/lib/rb/ext/protocol.h b/lib/rb/ext/protocol.h
new file mode 100644
index 000000000..536953036
--- /dev/null
+++ b/lib/rb/ext/protocol.h
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+void Init_protocol();
diff --git a/lib/rb/ext/struct.c b/lib/rb/ext/struct.c
new file mode 100644
index 000000000..fee285e97
--- /dev/null
+++ b/lib/rb/ext/struct.c
@@ -0,0 +1,605 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <struct.h>
+#include <constants.h>
+#include "macros.h"
+
+#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
+
+static native_proto_method_table *mt;
+static native_proto_method_table *default_mt;
+static VALUE last_proto_class = Qnil;
+
+#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)
+
+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);
+}
+
+static void check_native_proto_method_table(VALUE protocol) {
+ VALUE protoclass = CLASS_OF(protocol);
+ if (protoclass != last_proto_class) {
+ last_proto_class = protoclass;
+ if (rb_funcall(protocol, native_qmark_method_id, 0) == Qtrue) {
+ set_native_proto_function_pointers(protocol);
+ } else {
+ mt = default_mt;
+ }
+ }
+}
+
+//-------------------------------------------
+// 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() {
+ default_mt = ALLOC(native_proto_method_table);
+
+ default_mt->write_field_begin = default_write_field_begin;
+ default_mt->write_field_stop = default_write_field_stop;
+ default_mt->write_map_begin = default_write_map_begin;
+ default_mt->write_map_end = default_write_map_end;
+ default_mt->write_list_begin = default_write_list_begin;
+ default_mt->write_list_end = default_write_list_end;
+ default_mt->write_set_begin = default_write_set_begin;
+ default_mt->write_set_end = default_write_set_end;
+ default_mt->write_byte = default_write_byte;
+ default_mt->write_bool = default_write_bool;
+ default_mt->write_i16 = default_write_i16;
+ default_mt->write_i32 = default_write_i32;
+ default_mt->write_i64 = default_write_i64;
+ default_mt->write_double = default_write_double;
+ default_mt->write_string = default_write_string;
+ default_mt->write_struct_begin = default_write_struct_begin;
+ default_mt->write_struct_end = default_write_struct_end;
+ default_mt->write_field_end = default_write_field_end;
+
+ default_mt->read_struct_begin = default_read_struct_begin;
+ default_mt->read_struct_end = default_read_struct_end;
+ default_mt->read_field_begin = default_read_field_begin;
+ default_mt->read_field_end = default_read_field_end;
+ default_mt->read_map_begin = default_read_map_begin;
+ default_mt->read_map_end = default_read_map_end;
+ default_mt->read_list_begin = default_read_list_begin;
+ default_mt->read_list_end = default_read_list_end;
+ default_mt->read_set_begin = default_read_set_begin;
+ default_mt->read_set_end = default_read_set_end;
+ default_mt->read_byte = default_read_byte;
+ default_mt->read_bool = default_read_bool;
+ default_mt->read_i16 = default_read_i16;
+ default_mt->read_i32 = default_read_i32;
+ default_mt->read_i64 = default_read_i64;
+ default_mt->read_double = default_read_double;
+ default_mt->read_string = default_read_string;
+}
+
+// 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_LEN(field_name) + 1];
+
+ name_buf[0] = '@';
+ strlcpy(&name_buf[1], RSTRING_PTR(field_name), 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_LEN(keys);
+
+ 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_LEN(value);
+
+ 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_LEN(items);
+
+ 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);
+
+ check_native_proto_method_table(protocol);
+
+ // 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_LEN(struct_field_ids_ordered); 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_LEN(field_name) + 1];
+
+ name_buf[0] = '@';
+ strlcpy(&name_buf[1], RSTRING_PTR(field_name), 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) {
+ check_native_proto_method_table(protocol);
+
+ // read struct begin
+ mt->read_struct_begin(protocol);
+
+ VALUE struct_fields = STRUCT_FIELDS(self);
+
+ // read each field
+ while (true) {
+ VALUE field_header = mt->read_field_begin(protocol);
+ 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();
+}
+
diff --git a/lib/rb/ext/struct.h b/lib/rb/ext/struct.h
new file mode 100644
index 000000000..37b1b35b2
--- /dev/null
+++ b/lib/rb/ext/struct.h
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#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();
diff --git a/lib/rb/ext/thrift_native.c b/lib/rb/ext/thrift_native.c
new file mode 100644
index 000000000..effa202c4
--- /dev/null
+++ b/lib/rb/ext/thrift_native.c
@@ -0,0 +1,194 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <ruby.h>
+#include <struct.h>
+#include <binary_protocol_accelerated.h>
+#include <compact_protocol.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_all_method_id;
+ID native_qmark_method_id;
+
+// constant ids
+ID fields_const_id;
+ID transport_ivar_id;
+ID strict_read_ivar_id;
+ID strict_write_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_all_method_id = rb_intern("read_all");
+ native_qmark_method_id = rb_intern("native?");
+
+ // constant ids
+ fields_const_id = rb_intern("FIELDS");
+ transport_ivar_id = rb_intern("@trans");
+ strict_read_ivar_id = rb_intern("@strict_read");
+ strict_write_ivar_id = rb_intern("@strict_write");
+
+ // 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_compact_protocol();
+ Init_memory_buffer();
+}