blob: 2725bfcb91b54da01346beaef660ab2840ab4618 [file] [log] [blame]
Kevin Clark4bd89162008-07-08 00:47:49 +00001// Half of this file comes from contributions from Nitay Joffe (nitay@powerset.com)
2// Much of the rest (almost) directly ported (or pulled) from thrift-py's fastbinary.c
3// Everything else via Kevin Clark (kevin@powerset.com)
4#include <stdint.h>
5#include <stdbool.h>
6
7#include <ruby.h>
8#include <st.h>
9#include <netinet/in.h>
10
11// #define __DEBUG__
12
13#ifndef HAVE_STRLCPY
14
15static
16size_t
17strlcpy (char *dst, const char *src, size_t dst_sz)
18{
19 size_t n;
20
21 for (n = 0; n < dst_sz; n++) {
22 if ((*dst++ = *src++) == '\0')
23 break;
24 }
25
26 if (n < dst_sz)
27 return n;
28 if (n > 0)
29 *(dst - 1) = '\0';
30 return n + strlen (src);
31}
32
33#endif
34
35#define dbg() fprintf(stderr, "%s:%d\n", __FUNCTION__, __LINE__)
36
37
38// TODO (kevinclark): This was here from the patch/python. Not sure
39// If it's actually that big a pain. Need to look into pulling
40// From the right place
41
42// Stolen out of TProtocol.h.
43// It would be a huge pain to have both get this from one place.
44
45enum TType {
46 T_STOP = 0,
47 T_BOOL = 2,
48 T_BYTE = 3,
49 T_I16 = 6,
50 T_I32 = 8,
51 T_I64 = 10,
52 T_DBL = 4,
53 T_STR = 11,
54 T_STRCT = 12,
55 T_MAP = 13,
56 T_SET = 14,
57 T_LIST = 15
58 // T_VOID = 1,
59 // T_I08 = 3,
60 // T_U64 = 9,
61 // T_UTF7 = 11,
62 // T_UTF8 = 16,
63 // T_UTF16 = 17
64};
65
66#define IS_CONTAINER(x) (x == T_MAP || x == T_SET || x == T_LIST)
67
68// Same comment as the enum. Sorry.
69#ifdef HAVE_ENDIAN_H
70#include <endian.h>
71#endif
72
73#ifndef __BYTE_ORDER
74# if defined(BYTE_ORDER) && defined(LITTLE_ENDIAN) && defined(BIG_ENDIAN)
75# define __BYTE_ORDER BYTE_ORDER
76# define __LITTLE_ENDIAN LITTLE_ENDIAN
77# define __BIG_ENDIAN BIG_ENDIAN
78# else
79# error "Cannot determine endianness"
80# endif
81#endif
82
83#if __BYTE_ORDER == __BIG_ENDIAN
84# define ntohll(n) (n)
85# define htonll(n) (n)
86#elif __BYTE_ORDER == __LITTLE_ENDIAN
87# if defined(__GNUC__) && defined(__GLIBC__)
88# include <byteswap.h>
89# define ntohll(n) bswap_64(n)
90# define htonll(n) bswap_64(n)
91# else /* GNUC & GLIBC */
92# define ntohll(n) ( (((unsigned long long)ntohl(n)) << 32) + ntohl(n >> 32) )
93# define htonll(n) ( (((unsigned long long)htonl(n)) << 32) + htonl(n >> 32) )
94# endif /* GNUC & GLIBC */
95#else /* __BYTE_ORDER */
96# error "Can't define htonll or ntohll!"
97#endif
98
99
100// -----------------------------------------------------------------------------
101// Cached interned strings and such
102// -----------------------------------------------------------------------------
103
104static VALUE class_tbpa;
105static VALUE m_thrift;
106static VALUE rb_cSet;
107static ID type_sym;
108static ID class_sym;
109static ID key_sym;
110static ID value_sym;
111static ID element_sym;
112static ID name_sym;
113static ID fields_id;
114static ID consume_bang_id;
115static ID string_buffer_id;
116static ID borrow_id;
117static ID keys_id;
118static ID entries_id;
119
120static const uint32_t VERSION_MASK = 0xffff0000;
121static const uint32_t VERSION_1 = 0x80010000;
122
123// -----------------------------------------------------------------------------
124// Structs so I don't have to keep calling rb_hash_aref
125// -----------------------------------------------------------------------------
126
127// { :type => field[:type],
128// :class => field[:class],
129// :key => field[:key],
130// :value => field[:value],
131// :element => field[:element] }
132
133struct _thrift_map;
134struct _field_spec;
135
136typedef union {
137 VALUE class;
138 struct _thrift_map* map;
139 struct _field_spec* element;
140} container_data;
141
142typedef struct _field_spec {
143 int type;
144 char* name;
145 container_data data;
146} field_spec;
147
148typedef struct _thrift_map {
149 field_spec* key;
150 field_spec* value;
151} thrift_map;
152
153
154static void free_field_spec(field_spec* spec) {
155 switch(spec->type) {
156 case T_LIST:
157 case T_SET:
158 free_field_spec(spec->data.element);
159 break;
160
161 case T_MAP:
162 free_field_spec(spec->data.map->key);
163 free_field_spec(spec->data.map->value);
164 free(spec->data.map);
165 break;
166 }
167
168 free(spec);
169}
170
171// Parses a ruby field spec into a C struct
172//
173// Simple fields look like:
174// { :name => .., :type => .. }
175// Structs add the :class attribute
176// Maps adds :key and :value attributes, field specs
177// Lists and Sets add an :element, a field spec
178static field_spec* parse_field_spec(VALUE field_data) {
179 int type = NUM2INT(rb_hash_aref(field_data, type_sym));
180 VALUE name = rb_hash_aref(field_data, name_sym);
181 field_spec* spec = (field_spec *) malloc(sizeof(field_spec));
182
183#ifdef __DEBUG__ // No need for this in prod since I set all the fields
184 bzero(spec, sizeof(field_spec));
185#endif
186
187 spec->type = type;
188
189 if (Qnil != name) {
190 spec->name = StringValuePtr(name);
191 } else {
192 spec->name = NULL;
193 }
194
195 switch(type) {
196 case T_STRCT: {
197 spec->data.class = rb_hash_aref(field_data, class_sym);
198 break;
199 }
200
201 case T_MAP: {
202 VALUE key_fields = rb_hash_aref(field_data, key_sym);
203 VALUE value_fields = rb_hash_aref(field_data, value_sym);
204 thrift_map* map = (thrift_map *) malloc(sizeof(thrift_map));
205
206 map->key = parse_field_spec(key_fields);
207 map->value = parse_field_spec(value_fields);
208 spec->data.map = map;
209
210 break;
211 }
212
213 case T_LIST:
214 case T_SET:
215 {
216 VALUE list_fields = rb_hash_aref(field_data, element_sym);
217 spec->data.element = parse_field_spec(list_fields);
218 break;
219 }
220 }
221
222 return spec;
223}
224
225
226// -----------------------------------------------------------------------------
227// Serialization routines
228// -----------------------------------------------------------------------------
229
230
231// write_*(VALUE buf, ...) takes a value and adds it to a Ruby string buffer,
232// in network order
233static void write_byte(VALUE buf, int8_t val) {
234 rb_str_buf_cat(buf, (char*)&val, sizeof(int8_t));
235}
236
237static void write_i16(VALUE buf, int16_t val) {
238 int16_t net = (int16_t)htons(val);
239 rb_str_buf_cat(buf, (char*)&net, sizeof(int16_t));
240}
241
242static void write_i32(VALUE buf, int32_t val) {
243 int32_t net = (int32_t)htonl(val);
244 rb_str_buf_cat(buf, (char*)&net, sizeof(int32_t));
245}
246
247static void write_i64(VALUE buf, int64_t val) {
248 int64_t net = (int64_t)htonll(val);
249 rb_str_buf_cat(buf, (char*)&net, sizeof(int64_t));
250}
251
252static void write_double(VALUE buf, double dub) {
253 // Unfortunately, bitwise_cast doesn't work in C. Bad C!
254 union {
255 double f;
256 int64_t t;
257 } transfer;
258 transfer.f = dub;
259 write_i64(buf, transfer.t);
260}
261
262static void write_string(VALUE buf, char* str) {
263 int32_t len = strlen(str);
264 write_i32(buf, len);
265 rb_str_buf_cat2(buf, str);
266}
267
268// Some functions macro'd out because they're nops for the binary protocol
269// but placeholders were desired in case things change
270#define write_struct_begin(buf)
271#define write_struct_end(buf)
272
273static void write_field_begin(VALUE buf, char* name, int type, int fid) {
274#ifdef __DEBUG__
275 fprintf(stderr, "Writing field beginning: %s %d %d\n", name, type, fid);
276#endif
277
278 write_byte(buf, (int8_t)type);
279 write_i16(buf, (int16_t)fid);
280}
281
282#define write_field_end(buf)
283
284static void write_field_stop(VALUE buf) {
285 write_byte(buf, T_STOP);
286}
287
288static void write_map_begin(VALUE buf, int8_t ktype, int8_t vtype, int32_t sz) {
289 write_byte(buf, ktype);
290 write_byte(buf, vtype);
291 write_i32(buf, sz);
292}
293
294#define write_map_end(buf);
295
296static void write_list_begin(VALUE buf, int type, int sz) {
297 write_byte(buf, type);
298 write_i32(buf, sz);
299}
300
301#define write_list_end(buf)
302
303static void write_set_begin(VALUE buf, int type, int sz) {
304 write_byte(buf, type);
305 write_i32(buf, sz);
306}
307
308#define write_set_end(buf)
309
310static void binary_encoding(VALUE buf, VALUE obj, int type);
311
312// Handles container types: Map, Set, List
313static void write_container(VALUE buf, VALUE value, field_spec* spec) {
314 int sz, i;
315
316 switch(spec->type) {
317 case T_MAP: {
318 VALUE keys;
319 VALUE key;
320 VALUE val;
321
322 keys = rb_funcall(value, keys_id, 0);
323
324 sz = RARRAY(keys)->len;
325
326 write_map_begin(buf, spec->data.map->key->type, spec->data.map->value->type, sz);
327
328 for (i = 0; i < sz; i++) {
329 key = rb_ary_entry(keys, i);
330 val = rb_hash_aref(value, key);
331
332 if (IS_CONTAINER(spec->data.map->key->type)) {
333 write_container(buf, key, spec->data.map->key);
334 } else {
335 binary_encoding(buf, key, spec->data.map->key->type);
336 }
337
338 if (IS_CONTAINER(spec->data.map->value->type)) {
339 write_container(buf, val, spec->data.map->value);
340 } else {
341 binary_encoding(buf, val, spec->data.map->value->type);
342 }
343 }
344
345 write_map_end(buf);
346
347 break;
348 }
349
350 case T_LIST: {
351 sz = RARRAY(value)->len;
352
353 write_list_begin(buf, spec->data.element->type, sz);
354 for (i = 0; i < sz; ++i) {
355 if (IS_CONTAINER(spec->data.element->type)) {
356 write_container(buf, rb_ary_entry(value, i), spec->data.element);
357 } else {
358 binary_encoding(buf, rb_ary_entry(value, i), spec->data.element->type);
359 }
360 }
361 write_list_end(buf);
362 break;
363 }
364
365 case T_SET: {
366 VALUE items;
367
368 if (TYPE(value) == T_ARRAY) {
369 items = value;
370 } else {
371 if (rb_cSet == CLASS_OF(value)) {
372 items = rb_funcall(value, entries_id, 0);
373 } else {
374 Check_Type(value, T_HASH);
375 items = rb_funcall(value, keys_id, 0);
376 }
377 }
378
379 sz = RARRAY(items)->len;
380
381 write_set_begin(buf, spec->data.element->type, sz);
382
383 for (i = 0; i < sz; i++) {
384 if (IS_CONTAINER(spec->data.element->type)) {
385 write_container(buf, rb_ary_entry(items, i), spec->data.element);
386 } else {
387 binary_encoding(buf, rb_ary_entry(items, i), spec->data.element->type);
388 }
389 }
390
391 write_set_end(buf);
392 break;
393 }
394 }
395}
396
397// Takes the field id, data to be encoded, buffer and enclosing object
398// to be encoded. buf and obj passed as a ruby array for rb_hash_foreach.
399// TODO(kevinclark): See if they can be passed individually to avoid object
400// creation
401static int encode_field(VALUE fid, VALUE data, VALUE ary) {
402 field_spec *spec = parse_field_spec(data);
403
404 VALUE buf = rb_ary_entry(ary, 0);
405 VALUE obj = rb_ary_entry(ary, 1);
406 char name_buf[128];
407
408 name_buf[0] = '@';
409 strlcpy(&name_buf[1], spec->name, sizeof(name_buf) - 1);
410
411 VALUE value = rb_ivar_get(obj, rb_intern(name_buf));
412
413 if (Qnil == value) {
414 free_field_spec(spec);
415 return 0;
416 }
417
418 write_field_begin(buf, spec->name, spec->type, NUM2INT(fid));
419
420 if (IS_CONTAINER(spec->type)) {
421 write_container(buf, value, spec);
422 } else {
423 binary_encoding(buf, value, spec->type);
424 }
425 write_field_end(buf);
426
427 free_field_spec(spec);
428
429 return 0;
430}
431
432// -----------------------------------------------------------------------------
433// TFastBinaryProtocol's main encoding loop
434// -----------------------------------------------------------------------------
435
436static void binary_encoding(VALUE buf, VALUE obj, int type) {
437#ifdef __DEBUG__
438 rb_p(rb_str_new2("Encoding binary (buf, obj, type)"));
439 rb_p(rb_inspect(buf));
440 rb_p(rb_inspect(obj));
441 rb_p(rb_inspect(INT2FIX(type)));
442#endif
443
444 switch(type) {
445 case T_BOOL:
446 if RTEST(obj) {
447 write_byte(buf, 1);
448 }
449 else {
450 write_byte(buf, 0);
451 }
452
453 break;
454
455 case T_BYTE:
456 write_byte(buf, NUM2INT(obj));
457 break;
458
459 case T_I16:
460 write_i16(buf, NUM2INT(obj));
461 break;
462
463 case T_I32:
464 write_i32(buf, NUM2INT(obj));
465 break;
466
467 case T_I64:
468 write_i64(buf, rb_num2ll(obj));
469 break;
470
471 case T_DBL:
472 write_double(buf, NUM2DBL(obj));
473 break;
474
475 case T_STR:
476 write_string(buf, StringValuePtr(obj));
477 break;
478
479 case T_STRCT: {
480 // rb_hash_foreach has to take args as a ruby array
481 VALUE args = rb_ary_new3(2, buf, obj);
482 VALUE fields = rb_const_get(CLASS_OF(obj), fields_id);
483
484 write_struct_begin(buf);
485
486 rb_hash_foreach(fields, encode_field, args);
487
488 write_field_stop(buf);
489 write_struct_end(buf);
490 break;
491 }
492
493 default: {
494 rb_raise(rb_eNotImpError, "Unknown type for binary_encoding: %d", type);
495 }
496 }
497}
498
499// obj is always going to be a TSTRCT
500static VALUE tbpa_encode_binary(VALUE self, VALUE obj) {
501 VALUE buf = rb_str_buf_new(1024);
502 binary_encoding(buf, obj, T_STRCT);
503 return buf;
504}
505
506
507// -----------------------------------------------------------------------------
508// Read stuff
509// -----------------------------------------------------------------------------
510
511typedef struct {
512 char* name;
513 int8_t type;
514 int16_t id;
515} field_header;
516
517typedef struct {
518 int8_t key_type;
519 int8_t val_type;
520 int num_entries;
521} map_header;
522
523typedef struct {
524 int8_t type;
525 int num_elements;
526} list_header;
527
528typedef list_header set_header;
529
530typedef struct {
531 char* data;
532 int pos;
533 int len;
534 VALUE trans;
535} decode_buffer;
536
537typedef struct {
538 char* ptr;
539 int len;
540} thrift_string;
541
542#define read_struct_begin(buf)
543#define read_struct_end(buf)
544
545// This prototype is required to be able to run a call through rb_protect
546// which rescues from ruby exceptions
547static VALUE protectable_consume(VALUE args) {
548 VALUE trans = rb_ary_entry(args, 0);
549 VALUE size = rb_ary_entry(args, 1);
550
551 return rb_funcall(trans, consume_bang_id, 1, size);
552}
553
554// Clears size bytes from the transport's string buffer
555static bool consume(decode_buffer* buf, int32_t size) {
556 if (size != 0) {
557 VALUE ret;
558 VALUE args = rb_ary_new3(2, buf->trans, INT2FIX(size));
559 int status = 0;
560
561 ret = rb_protect(protectable_consume, args, &status);
562
563 if (status) {
564 return false;
565 } else {
566 return true;
567 }
568 }
569
570 // Nothing to consume, we're all good
571 return true;
572}
573
574// This prototype is required to be able to run a call through rb_protect
575// which rescues from ruby exceptions
576static VALUE protectable_borrow(VALUE args) {
577 VALUE trans = rb_ary_entry(args, 0);
578
579 switch(RARRAY(args)->len) {
580 case 1:
581 return rb_funcall(trans, borrow_id, 0);
582
583 case 2: {
584 VALUE size = rb_ary_entry(args, 1);
585 return rb_funcall(trans, borrow_id, 1, size);
586 }
587 }
588
589 return Qnil;
590}
591
592// Calls into the transport to get the available string buffer
593static bool borrow(decode_buffer* buf, int32_t size, VALUE* dst) {
594 int status = 0;
595 VALUE args;
596
597 if (size == 0) {
598 args = rb_ary_new3(1, buf->trans);
599 } else {
600 args = rb_ary_new3(2, buf->trans, INT2FIX(size));
601 }
602
603 *dst = rb_protect(protectable_borrow, args, &status);
604
605 return (status == 0);
606}
607
608// Refills the buffer by calling borrow. If buf->pos is nonzero that number of bytes
609// is cleared through consume.
610//
611// returns: 0 on success, non-zero on failure. On error buf is unchanged.
612static int fill_buffer(decode_buffer* buf, int32_t req_len) {
613 VALUE refill;
614
615 if (!consume(buf, buf->pos)) {
616 return -1;
617 }
618
619 if (!borrow(buf, req_len, &refill)) {
620 return -2;
621 }
622
623 buf->data = StringValuePtr(refill);
624 buf->len = RSTRING(refill)->len;
625 buf->pos = 0;
626
627 return 0;
628}
629
630
631// read_bytes pulls a number of bytes (size) from the buffer, refilling if needed,
632// and places them in dst. This should _always_ be used used when reading from the buffer
633// or buffered transports will be upset with you.
634static bool read_bytes(decode_buffer* buf, void* dst, size_t size) {
635 int avail = (buf->len - buf->pos);
636
637 if (size <= avail) {
638 memcpy(dst, buf->data + buf->pos, size);
639 buf->pos += size;
640 } else {
641
642 if (avail > 0) {
643 // Copy what we can
644 memcpy(dst, buf->data + buf->pos, avail);
645 buf->pos += avail;
646 }
647
648 if (fill_buffer(buf, size - avail) < 0) {
649 return false;
650 }
651
652 memcpy(dst + avail, buf->data, size - avail);
653 buf->pos += size - avail;
654 }
655
656 return true;
657}
658
659// -----------------------------------------------------------------------------
660// Helpers for grabbing specific types from the buffer
661// -----------------------------------------------------------------------------
662
663static bool read_byte(decode_buffer* buf, int8_t* data) {
664 return read_bytes(buf, data, sizeof(int8_t));
665}
666
667static bool read_int16(decode_buffer* buf, int16_t* data) {
668 bool success = read_bytes(buf, data, sizeof(int16_t));
669 *data = ntohs(*data);
670
671 return success;
672}
673
674static bool read_int32(decode_buffer* buf, int32_t* data) {
675 bool success = read_bytes(buf, data, sizeof(int32_t));
676 *data = ntohl(*data);
677
678 return success;
679}
680
681static bool read_int64(decode_buffer* buf, int64_t* data) {
682 bool success = read_bytes(buf, data, sizeof(int64_t));
683 *data = ntohll(*data);
684
685 return success;
686}
687
688static bool read_double(decode_buffer* buf, double* data) {
689 return read_int64(buf, (int64_t*)data);
690}
691
692static bool read_string(decode_buffer* buf, VALUE* data) {
693 int len;
694
695 if (!read_int32(buf, &len)) {
696 return false;
697 }
698
699 if (buf->len - buf->pos >= len) {
700 *data = rb_str_new(buf->data + buf->pos, len);
701 buf->pos += len;
702 }
703 else {
704 char* str;
705
706 if ((str = (char*) malloc(len)) == NULL) {
707 return false;
708 }
709
710 if (!read_bytes(buf, str, len)) {
711 free(str);
712 return false;
713 }
714
715 *data = rb_str_new(str, len);
716
717 free(str);
718 }
719
720 return true;
721}
722
723static bool read_field_begin(decode_buffer* buf, field_header* header) {
724#ifdef __DEBUG__ // No need for this in prod since I set all the fields
725 bzero(header, sizeof(field_header));
726#endif
727
728 header->name = NULL;
729
730 if (!read_byte(buf, &header->type)) {
731 return false;
732 }
733
734 if (header->type == T_STOP) {
735 header->id = 0;
736 } else {
737 if (!read_int16(buf, &header->id)) {
738 return false;
739 }
740 }
741
742 return true;
743}
744
745#define read_field_end(buf)
746
747static bool read_map_begin(decode_buffer* buf, map_header* header) {
748#ifdef __DEBUG__ // No need for this in prod since I set all the fields
749 bzero(header, sizeof(map_header));
750#endif
751
752 return (read_byte(buf, &header->key_type) &&
753 read_byte(buf, &header->val_type) &&
754 read_int32(buf, &header->num_entries));
755}
756
757#define read_map_end(buf)
758
759static bool read_list_begin(decode_buffer* buf, list_header* header) {
760#ifdef __DEBUG__ // No need for this in prod since I set all the fields
761 bzero(header, sizeof(list_header));
762#endif
763
764 if (!read_byte(buf, &header->type) || !read_int32(buf, &header->num_elements)) {
765 return false;
766 } else {
767 return true;
768 }
769}
770
771#define read_list_end(buf)
772
773#define read_set_begin read_list_begin
774#define read_set_end read_list_end
775
776
777// High level reader function with ruby type coercion
778static bool read_type(int type, decode_buffer* buf, VALUE* dst) {
779 switch(type) {
780 case T_BOOL: {
781 int8_t byte;
782
783 if (!read_byte(buf, &byte)) {
784 return false;
785 }
786
787 if (0 == byte) {
788 *dst = Qfalse;
789 } else {
790 *dst = Qtrue;
791 }
792
793 break;
794 }
795
796 case T_BYTE: {
797 int8_t byte;
798
799 if (!read_byte(buf, &byte)) {
800 return false;
801 }
802
803 *dst = INT2FIX(byte);
804 break;
805 }
806
807 case T_I16: {
808 int16_t i16;
809
810 if (!read_int16(buf, &i16)) {
811 return false;
812 }
813
814 *dst = INT2FIX(i16);
815 break;
816 }
817
818 case T_I32: {
819 int32_t i32;
820
821 if (!read_int32(buf, &i32)) {
822 return false;
823 }
824
825 *dst = INT2NUM(i32);
826 break;
827 }
828
829 case T_I64: {
830 int64_t i64;
831
832 if (!read_int64(buf, &i64)) {
833 return false;
834 }
835
836 *dst = rb_ll2inum(i64);
837 break;
838 }
839
840 case T_DBL: {
841 double dbl;
842
843 if (!read_double(buf, &dbl)) {
844 return false;
845 }
846
847 *dst = rb_float_new(dbl);
848 break;
849 }
850
851 case T_STR: {
852 VALUE str;
853
854 if (!read_string(buf, &str)) {
855 return false;
856 }
857
858 *dst = str;
859 break;
860 }
861 }
862
863 return true;
864}
865
866// TODO(kevinclark): Now that read_string does a malloc,
867// This maybe could be modified to avoid that, and the type coercion
868
869// Read the bytes but don't do anything with the value
870static bool skip_type(int type, decode_buffer* buf) {
871 switch (type) {
872 case T_STRCT:
873 read_struct_begin(buf);
874 while (true) {
875 field_header header;
876
877 if (!read_field_begin(buf, &header)) {
878 return false;
879 }
880
881 if (header.type == T_STOP) {
882 break;
883 }
884
885 if (!skip_type(header.type, buf)) {
886 return false;
887 }
888
889 read_field_end(buf);
890 }
891 read_struct_end(buf);
892
893 break;
894
895 case T_MAP: {
896 int i;
897 map_header header;
898
899 if (!read_map_begin(buf, &header)) {
900 return false;
901 }
902
903 for (i = 0; i < header.num_entries; ++i) {
904 if (!skip_type(header.key_type, buf)) {
905 return false;
906 }
907 if (!skip_type(header.val_type, buf)) {
908 return false;
909 }
910 }
911
912 read_map_end(buf);
913 break;
914 }
915
916 case T_SET: {
917 int i;
918 set_header header;
919
920 if (!read_set_begin(buf, &header)) {
921 return false;
922 }
923
924 for (i = 0; i < header.num_elements; ++i) {
925 if (!skip_type(header.type, buf)) {
926 return false;
927 }
928 }
929
930 read_set_end(buf);
931 break;
932 }
933
934 case T_LIST: {
935 int i;
936 list_header header;
937
938 if (!read_list_begin(buf, &header)) {
939 return false;
940 }
941
942 for (i = 0; i < header.num_elements; ++i) {
943 if (!skip_type(header.type, buf)) {
944 return false;
945 }
946 }
947
948 read_list_end(buf);
949 break;
950 }
951
952 default: {
953 VALUE v;
954 if (!read_type(type, buf, &v)) {
955 return false;
956 }
957 }
958 }
959
960 return true;
961}
962
963
964static VALUE read_struct(VALUE obj, decode_buffer* buf);
965
966// Read the right thing from the buffer given the field spec
967// and return the ruby object
968static bool read_field(decode_buffer* buf, field_spec* spec, VALUE* dst) {
969 switch (spec->type) {
970 case T_STRCT: {
971 VALUE obj = rb_class_new_instance(0, NULL, spec->data.class);
972
973 *dst = read_struct(obj, buf);
974 break;
975 }
976
977 case T_MAP: {
978 map_header hdr;
979 VALUE hsh;
980 int i;
981
982 read_map_begin(buf, &hdr);
983 hsh = rb_hash_new();
984
985 for (i = 0; i < hdr.num_entries; ++i) {
986 VALUE key, val;
987
988 if (!read_field(buf, spec->data.map->key, &key)) {
989 return false;
990 }
991
992 if (!read_field(buf, spec->data.map->value, &val)) {
993 return false;
994 }
995
996 rb_hash_aset(hsh, key, val);
997 }
998
999 read_map_end(buf);
1000
1001 *dst = hsh;
1002 break;
1003 }
1004
1005 case T_LIST: {
1006 list_header hdr;
1007 VALUE arr, element;
1008 int i;
1009
1010 read_list_begin(buf, &hdr);
1011 arr = rb_ary_new2(hdr.num_elements);
1012
1013 for (i = 0; i < hdr.num_elements; ++i) {
1014 if (!read_field(buf, spec->data.element, &element)) {
1015 return false;
1016 }
1017
1018 rb_ary_push(arr, element);
1019 }
1020
1021 read_list_end(buf);
1022
1023 *dst = arr;
1024 break;
1025 }
1026
1027 case T_SET: {
1028 VALUE items, item;
1029 set_header hdr;
1030 int i;
1031
1032 read_set_begin(buf, &hdr);
1033 items = rb_ary_new2(hdr.num_elements);
1034
1035 for (i = 0; i < hdr.num_elements; ++i) {
1036 if (!read_field(buf, spec->data.element, &item)) {
1037 return false;
1038 }
1039
1040 rb_ary_push(items, item);
1041 }
1042
1043 *dst = rb_class_new_instance(1, &items, rb_cSet);
1044 break;
1045 }
1046
1047
1048 default:
1049 return read_type(spec->type, buf, dst);
1050 }
1051
1052 return true;
1053}
1054
1055static void handle_read_error() {
1056 // If it was an exception, reraise
1057 if (!NIL_P(ruby_errinfo)) {
1058 rb_exc_raise(ruby_errinfo);
1059 } else {
1060 // Something else went wrong, no idea what would call this yet
1061 // So far, the only thing to cause failures underneath is ruby
1062 // exceptions. Follow up on this regularly -- Kevin Clark (TODO)
1063 rb_raise(rb_eStandardError, "[BUG] Something went wrong in the field reading, but not a ruby exception");
1064 }
1065}
1066
1067// Fill in the instance variables in an object (thrift struct)
1068// from the decode buffer
1069static VALUE read_struct(VALUE obj, decode_buffer* buf) {
1070 VALUE field;
1071 field_header f_header;
1072 VALUE value = Qnil;
1073 VALUE fields = rb_const_get(CLASS_OF(obj), fields_id);
1074 field_spec* spec;
1075 char name_buf[128];
1076
1077 read_struct_begin(buf);
1078
1079 while(true) {
1080 if (!read_field_begin(buf, &f_header)) {
1081 handle_read_error();
1082 }
1083
1084 if (T_STOP == f_header.type) {
1085 break;
1086 }
1087
1088 field = rb_hash_aref(fields, INT2FIX(f_header.id));
1089
1090 if (NIL_P(field)) {
1091 if (!skip_type(f_header.type, buf)) {
1092 handle_read_error();
1093 return Qnil;
1094 }
1095 }
1096 else {
1097 spec = parse_field_spec(field);
1098
1099 if (spec->type != f_header.type) {
1100 if (!skip_type(spec->type, buf)) {
1101 free_field_spec(spec);
1102 handle_read_error();
1103 return Qnil;
1104 }
1105 } else {
1106 // Read busted somewhere (probably borrow/consume), bail
1107 if (!read_field(buf, spec, &value)) {
1108 free_field_spec(spec);
1109 handle_read_error();
1110 return Qnil;
1111 }
1112
1113 name_buf[0] = '@';
1114 strlcpy(&name_buf[1], spec->name, sizeof(name_buf) - 1);
1115
1116 rb_iv_set(obj, name_buf, value);
1117 }
1118
1119 free_field_spec(spec);
1120 }
1121
1122 read_field_end(buf);
1123 }
1124
1125 read_struct_end(buf);
1126
1127 return obj;
1128}
1129
1130
1131// Takes an object and transport, and decodes the values in the transport's
1132// buffer to fill the object.
1133static VALUE tbpa_decode_binary(VALUE self, VALUE obj, VALUE transport) {
1134 decode_buffer buf;
1135 VALUE ret_val;
1136
1137 buf.pos = 0; // This needs to be set so an arbitrary number of bytes isn't consumed
1138 buf.trans = transport; // We need to hold this so the buffer can be refilled
1139
1140 if (fill_buffer(&buf, 0) < 0) {
1141 handle_read_error();
1142 return Qnil;
1143 }
1144
1145#ifdef __DEBUG__
1146 rb_p(rb_str_new2("Running decode binary with data:"));
1147 rb_p(rb_inspect(rb_str_new2(buf.data)));
1148#endif
1149
1150 ret_val = read_struct(obj, &buf);
1151
1152 // Consume whatever was read
1153 consume(&buf, buf.pos);
1154
1155 return ret_val;
1156}
1157
1158// -----------------------------------------------------------------------------
1159// These methods are used by the thrift library and need to handled
1160// seperately from encode and decode
1161// -----------------------------------------------------------------------------
1162
1163// Read the message header and return it as a ruby array
1164static VALUE tbpa_read_message_begin(VALUE self) {
1165 decode_buffer buf;
1166 int32_t version, seqid;
1167 int8_t type;
1168 VALUE name;
1169
1170 VALUE trans = rb_iv_get(self, "@trans");
1171
1172 buf.pos = 0; // This needs to be set so fill_buffer doesn't consume
1173 buf.trans = trans; // We need to hold this so the buffer can be refilled
1174
1175
1176 if (fill_buffer(&buf, 0) < 0 || !read_int32(&buf, &version)) {
1177 // Consume whatever was read
1178 consume(&buf, buf.pos);
1179 handle_read_error();
1180 return Qnil;
1181 }
1182
1183 if ((version & VERSION_MASK) != VERSION_1) {
1184 VALUE tprotocol_exception = rb_const_get(rb_cObject, rb_intern("TProtocolException"));
1185 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"));
1186 rb_raise(exception, "");
1187 }
1188
1189 type = version & 0x000000ff;
1190
1191 if (!read_string(&buf, &name) || !read_int32(&buf, &seqid)) {
1192 // Consume whatever was read
1193 consume(&buf, buf.pos);
1194 handle_read_error();
1195 return Qnil;
1196 }
1197
1198 // Consume whatever was read
1199 if (consume(&buf, buf.pos) < 0) {
1200 handle_read_error();
1201 return Qnil;
1202 }
1203
1204 return rb_ary_new3(3, name, INT2FIX(type), INT2FIX(seqid));
1205}
1206
1207void Init_binaryprotocolaccelerated()
1208{
1209 m_thrift = rb_const_get(rb_cObject, rb_intern("Thrift"));
1210 VALUE class_tbinproto = rb_const_get(m_thrift, rb_intern("BinaryProtocol"));
1211 class_tbpa = rb_define_class_under(m_thrift, "BinaryProtocolAccelerated", class_tbinproto);
1212 type_sym = ID2SYM(rb_intern("type"));
1213 class_sym = ID2SYM(rb_intern("class"));
1214 key_sym = ID2SYM(rb_intern("key"));
1215 value_sym = ID2SYM(rb_intern("value"));
1216 name_sym = ID2SYM(rb_intern("name"));
1217 fields_id = rb_intern("FIELDS");
1218 element_sym = ID2SYM(rb_intern("element"));
1219 consume_bang_id = rb_intern("consume!");
1220 string_buffer_id = rb_intern("string_buffer");
1221 borrow_id = rb_intern("borrow");
1222 keys_id = rb_intern("keys");
1223 entries_id = rb_intern("entries");
1224 rb_cSet = rb_const_get(rb_cObject, rb_intern("Set"));
1225
1226 // For fast access
1227 rb_define_method(class_tbpa, "encode_binary", tbpa_encode_binary, 1);
1228 rb_define_method(class_tbpa, "decode_binary", tbpa_decode_binary, 2);
1229 rb_define_method(class_tbpa, "read_message_begin", tbpa_read_message_begin, 0);
1230
1231}