blob: 6e295ed2f52eb1e625d643bff25dcc4fa69d812f [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
David Reiss0c7d38c2008-07-25 21:06:06 +0000262static void write_string(VALUE buf, char* str, size_t len) {
Kevin Clark4bd89162008-07-08 00:47:49 +0000263 write_i32(buf, len);
David Reiss0c7d38c2008-07-25 21:06:06 +0000264 rb_str_buf_cat(buf, str, len);
Kevin Clark4bd89162008-07-08 00:47:49 +0000265}
266
267// Some functions macro'd out because they're nops for the binary protocol
268// but placeholders were desired in case things change
269#define write_struct_begin(buf)
270#define write_struct_end(buf)
271
272static void write_field_begin(VALUE buf, char* name, int type, int fid) {
273#ifdef __DEBUG__
274 fprintf(stderr, "Writing field beginning: %s %d %d\n", name, type, fid);
275#endif
276
277 write_byte(buf, (int8_t)type);
278 write_i16(buf, (int16_t)fid);
279}
280
281#define write_field_end(buf)
282
283static void write_field_stop(VALUE buf) {
284 write_byte(buf, T_STOP);
285}
286
287static void write_map_begin(VALUE buf, int8_t ktype, int8_t vtype, int32_t sz) {
288 write_byte(buf, ktype);
289 write_byte(buf, vtype);
290 write_i32(buf, sz);
291}
292
293#define write_map_end(buf);
294
295static void write_list_begin(VALUE buf, int type, int sz) {
296 write_byte(buf, type);
297 write_i32(buf, sz);
298}
299
300#define write_list_end(buf)
301
302static void write_set_begin(VALUE buf, int type, int sz) {
303 write_byte(buf, type);
304 write_i32(buf, sz);
305}
306
307#define write_set_end(buf)
308
309static void binary_encoding(VALUE buf, VALUE obj, int type);
310
311// Handles container types: Map, Set, List
312static void write_container(VALUE buf, VALUE value, field_spec* spec) {
313 int sz, i;
314
315 switch(spec->type) {
316 case T_MAP: {
317 VALUE keys;
318 VALUE key;
319 VALUE val;
320
321 keys = rb_funcall(value, keys_id, 0);
322
323 sz = RARRAY(keys)->len;
324
325 write_map_begin(buf, spec->data.map->key->type, spec->data.map->value->type, sz);
326
327 for (i = 0; i < sz; i++) {
328 key = rb_ary_entry(keys, i);
329 val = rb_hash_aref(value, key);
330
331 if (IS_CONTAINER(spec->data.map->key->type)) {
332 write_container(buf, key, spec->data.map->key);
333 } else {
334 binary_encoding(buf, key, spec->data.map->key->type);
335 }
336
337 if (IS_CONTAINER(spec->data.map->value->type)) {
338 write_container(buf, val, spec->data.map->value);
339 } else {
340 binary_encoding(buf, val, spec->data.map->value->type);
341 }
342 }
343
344 write_map_end(buf);
345
346 break;
347 }
348
349 case T_LIST: {
350 sz = RARRAY(value)->len;
351
352 write_list_begin(buf, spec->data.element->type, sz);
353 for (i = 0; i < sz; ++i) {
354 if (IS_CONTAINER(spec->data.element->type)) {
355 write_container(buf, rb_ary_entry(value, i), spec->data.element);
356 } else {
357 binary_encoding(buf, rb_ary_entry(value, i), spec->data.element->type);
358 }
359 }
360 write_list_end(buf);
361 break;
362 }
363
364 case T_SET: {
365 VALUE items;
366
367 if (TYPE(value) == T_ARRAY) {
368 items = value;
369 } else {
370 if (rb_cSet == CLASS_OF(value)) {
371 items = rb_funcall(value, entries_id, 0);
372 } else {
373 Check_Type(value, T_HASH);
374 items = rb_funcall(value, keys_id, 0);
375 }
376 }
377
378 sz = RARRAY(items)->len;
379
380 write_set_begin(buf, spec->data.element->type, sz);
381
382 for (i = 0; i < sz; i++) {
383 if (IS_CONTAINER(spec->data.element->type)) {
384 write_container(buf, rb_ary_entry(items, i), spec->data.element);
385 } else {
386 binary_encoding(buf, rb_ary_entry(items, i), spec->data.element->type);
387 }
388 }
389
390 write_set_end(buf);
391 break;
392 }
393 }
394}
395
396// Takes the field id, data to be encoded, buffer and enclosing object
397// to be encoded. buf and obj passed as a ruby array for rb_hash_foreach.
398// TODO(kevinclark): See if they can be passed individually to avoid object
399// creation
400static int encode_field(VALUE fid, VALUE data, VALUE ary) {
401 field_spec *spec = parse_field_spec(data);
402
403 VALUE buf = rb_ary_entry(ary, 0);
404 VALUE obj = rb_ary_entry(ary, 1);
405 char name_buf[128];
406
407 name_buf[0] = '@';
408 strlcpy(&name_buf[1], spec->name, sizeof(name_buf) - 1);
409
410 VALUE value = rb_ivar_get(obj, rb_intern(name_buf));
411
412 if (Qnil == value) {
413 free_field_spec(spec);
414 return 0;
415 }
416
417 write_field_begin(buf, spec->name, spec->type, NUM2INT(fid));
418
419 if (IS_CONTAINER(spec->type)) {
420 write_container(buf, value, spec);
421 } else {
422 binary_encoding(buf, value, spec->type);
423 }
424 write_field_end(buf);
425
426 free_field_spec(spec);
427
428 return 0;
429}
430
431// -----------------------------------------------------------------------------
432// TFastBinaryProtocol's main encoding loop
433// -----------------------------------------------------------------------------
434
435static void binary_encoding(VALUE buf, VALUE obj, int type) {
436#ifdef __DEBUG__
437 rb_p(rb_str_new2("Encoding binary (buf, obj, type)"));
438 rb_p(rb_inspect(buf));
439 rb_p(rb_inspect(obj));
440 rb_p(rb_inspect(INT2FIX(type)));
441#endif
442
443 switch(type) {
444 case T_BOOL:
445 if RTEST(obj) {
446 write_byte(buf, 1);
447 }
448 else {
449 write_byte(buf, 0);
450 }
451
452 break;
453
454 case T_BYTE:
455 write_byte(buf, NUM2INT(obj));
456 break;
457
458 case T_I16:
459 write_i16(buf, NUM2INT(obj));
460 break;
461
462 case T_I32:
463 write_i32(buf, NUM2INT(obj));
464 break;
465
466 case T_I64:
467 write_i64(buf, rb_num2ll(obj));
468 break;
469
470 case T_DBL:
471 write_double(buf, NUM2DBL(obj));
472 break;
473
474 case T_STR:
David Reiss0c7d38c2008-07-25 21:06:06 +0000475 write_string(buf, StringValuePtr(obj), RSTRING(obj)->len);
Kevin Clark4bd89162008-07-08 00:47:49 +0000476 break;
477
478 case T_STRCT: {
479 // rb_hash_foreach has to take args as a ruby array
480 VALUE args = rb_ary_new3(2, buf, obj);
481 VALUE fields = rb_const_get(CLASS_OF(obj), fields_id);
482
483 write_struct_begin(buf);
484
485 rb_hash_foreach(fields, encode_field, args);
486
487 write_field_stop(buf);
488 write_struct_end(buf);
489 break;
490 }
491
492 default: {
493 rb_raise(rb_eNotImpError, "Unknown type for binary_encoding: %d", type);
494 }
495 }
496}
497
498// obj is always going to be a TSTRCT
499static VALUE tbpa_encode_binary(VALUE self, VALUE obj) {
500 VALUE buf = rb_str_buf_new(1024);
501 binary_encoding(buf, obj, T_STRCT);
502 return buf;
503}
504
505
506// -----------------------------------------------------------------------------
507// Read stuff
508// -----------------------------------------------------------------------------
509
510typedef struct {
511 char* name;
512 int8_t type;
513 int16_t id;
514} field_header;
515
516typedef struct {
517 int8_t key_type;
518 int8_t val_type;
519 int num_entries;
520} map_header;
521
522typedef struct {
523 int8_t type;
524 int num_elements;
525} list_header;
526
527typedef list_header set_header;
528
529typedef struct {
530 char* data;
531 int pos;
532 int len;
533 VALUE trans;
534} decode_buffer;
535
536typedef struct {
537 char* ptr;
538 int len;
539} thrift_string;
540
541#define read_struct_begin(buf)
542#define read_struct_end(buf)
543
544// This prototype is required to be able to run a call through rb_protect
545// which rescues from ruby exceptions
546static VALUE protectable_consume(VALUE args) {
547 VALUE trans = rb_ary_entry(args, 0);
548 VALUE size = rb_ary_entry(args, 1);
549
550 return rb_funcall(trans, consume_bang_id, 1, size);
551}
552
553// Clears size bytes from the transport's string buffer
554static bool consume(decode_buffer* buf, int32_t size) {
555 if (size != 0) {
556 VALUE ret;
557 VALUE args = rb_ary_new3(2, buf->trans, INT2FIX(size));
558 int status = 0;
559
560 ret = rb_protect(protectable_consume, args, &status);
561
562 if (status) {
563 return false;
564 } else {
565 return true;
566 }
567 }
568
569 // Nothing to consume, we're all good
570 return true;
571}
572
573// This prototype is required to be able to run a call through rb_protect
574// which rescues from ruby exceptions
575static VALUE protectable_borrow(VALUE args) {
576 VALUE trans = rb_ary_entry(args, 0);
577
578 switch(RARRAY(args)->len) {
579 case 1:
580 return rb_funcall(trans, borrow_id, 0);
581
582 case 2: {
583 VALUE size = rb_ary_entry(args, 1);
584 return rb_funcall(trans, borrow_id, 1, size);
585 }
586 }
587
588 return Qnil;
589}
590
591// Calls into the transport to get the available string buffer
592static bool borrow(decode_buffer* buf, int32_t size, VALUE* dst) {
593 int status = 0;
594 VALUE args;
595
596 if (size == 0) {
597 args = rb_ary_new3(1, buf->trans);
598 } else {
599 args = rb_ary_new3(2, buf->trans, INT2FIX(size));
600 }
601
602 *dst = rb_protect(protectable_borrow, args, &status);
603
604 return (status == 0);
605}
606
607// Refills the buffer by calling borrow. If buf->pos is nonzero that number of bytes
608// is cleared through consume.
609//
610// returns: 0 on success, non-zero on failure. On error buf is unchanged.
611static int fill_buffer(decode_buffer* buf, int32_t req_len) {
612 VALUE refill;
613
614 if (!consume(buf, buf->pos)) {
615 return -1;
616 }
617
618 if (!borrow(buf, req_len, &refill)) {
619 return -2;
620 }
621
622 buf->data = StringValuePtr(refill);
623 buf->len = RSTRING(refill)->len;
624 buf->pos = 0;
625
626 return 0;
627}
628
629
630// read_bytes pulls a number of bytes (size) from the buffer, refilling if needed,
631// and places them in dst. This should _always_ be used used when reading from the buffer
632// or buffered transports will be upset with you.
633static bool read_bytes(decode_buffer* buf, void* dst, size_t size) {
634 int avail = (buf->len - buf->pos);
635
636 if (size <= avail) {
637 memcpy(dst, buf->data + buf->pos, size);
638 buf->pos += size;
639 } else {
640
641 if (avail > 0) {
642 // Copy what we can
643 memcpy(dst, buf->data + buf->pos, avail);
644 buf->pos += avail;
645 }
646
647 if (fill_buffer(buf, size - avail) < 0) {
648 return false;
649 }
650
651 memcpy(dst + avail, buf->data, size - avail);
652 buf->pos += size - avail;
653 }
654
655 return true;
656}
657
658// -----------------------------------------------------------------------------
659// Helpers for grabbing specific types from the buffer
660// -----------------------------------------------------------------------------
661
662static bool read_byte(decode_buffer* buf, int8_t* data) {
663 return read_bytes(buf, data, sizeof(int8_t));
664}
665
666static bool read_int16(decode_buffer* buf, int16_t* data) {
667 bool success = read_bytes(buf, data, sizeof(int16_t));
668 *data = ntohs(*data);
669
670 return success;
671}
672
673static bool read_int32(decode_buffer* buf, int32_t* data) {
674 bool success = read_bytes(buf, data, sizeof(int32_t));
675 *data = ntohl(*data);
676
677 return success;
678}
679
680static bool read_int64(decode_buffer* buf, int64_t* data) {
681 bool success = read_bytes(buf, data, sizeof(int64_t));
682 *data = ntohll(*data);
683
684 return success;
685}
686
687static bool read_double(decode_buffer* buf, double* data) {
688 return read_int64(buf, (int64_t*)data);
689}
690
691static bool read_string(decode_buffer* buf, VALUE* data) {
692 int len;
693
694 if (!read_int32(buf, &len)) {
695 return false;
696 }
697
698 if (buf->len - buf->pos >= len) {
699 *data = rb_str_new(buf->data + buf->pos, len);
700 buf->pos += len;
701 }
702 else {
703 char* str;
704
705 if ((str = (char*) malloc(len)) == NULL) {
706 return false;
707 }
708
709 if (!read_bytes(buf, str, len)) {
710 free(str);
711 return false;
712 }
713
714 *data = rb_str_new(str, len);
715
716 free(str);
717 }
718
719 return true;
720}
721
722static bool read_field_begin(decode_buffer* buf, field_header* header) {
723#ifdef __DEBUG__ // No need for this in prod since I set all the fields
724 bzero(header, sizeof(field_header));
725#endif
726
727 header->name = NULL;
728
729 if (!read_byte(buf, &header->type)) {
730 return false;
731 }
732
733 if (header->type == T_STOP) {
734 header->id = 0;
735 } else {
736 if (!read_int16(buf, &header->id)) {
737 return false;
738 }
739 }
740
741 return true;
742}
743
744#define read_field_end(buf)
745
746static bool read_map_begin(decode_buffer* buf, map_header* header) {
747#ifdef __DEBUG__ // No need for this in prod since I set all the fields
748 bzero(header, sizeof(map_header));
749#endif
750
751 return (read_byte(buf, &header->key_type) &&
752 read_byte(buf, &header->val_type) &&
753 read_int32(buf, &header->num_entries));
754}
755
756#define read_map_end(buf)
757
758static bool read_list_begin(decode_buffer* buf, list_header* header) {
759#ifdef __DEBUG__ // No need for this in prod since I set all the fields
760 bzero(header, sizeof(list_header));
761#endif
762
763 if (!read_byte(buf, &header->type) || !read_int32(buf, &header->num_elements)) {
764 return false;
765 } else {
766 return true;
767 }
768}
769
770#define read_list_end(buf)
771
772#define read_set_begin read_list_begin
773#define read_set_end read_list_end
774
775
776// High level reader function with ruby type coercion
777static bool read_type(int type, decode_buffer* buf, VALUE* dst) {
778 switch(type) {
779 case T_BOOL: {
780 int8_t byte;
781
782 if (!read_byte(buf, &byte)) {
783 return false;
784 }
785
786 if (0 == byte) {
787 *dst = Qfalse;
788 } else {
789 *dst = Qtrue;
790 }
791
792 break;
793 }
794
795 case T_BYTE: {
796 int8_t byte;
797
798 if (!read_byte(buf, &byte)) {
799 return false;
800 }
801
802 *dst = INT2FIX(byte);
803 break;
804 }
805
806 case T_I16: {
807 int16_t i16;
808
809 if (!read_int16(buf, &i16)) {
810 return false;
811 }
812
813 *dst = INT2FIX(i16);
814 break;
815 }
816
817 case T_I32: {
818 int32_t i32;
819
820 if (!read_int32(buf, &i32)) {
821 return false;
822 }
823
824 *dst = INT2NUM(i32);
825 break;
826 }
827
828 case T_I64: {
829 int64_t i64;
830
831 if (!read_int64(buf, &i64)) {
832 return false;
833 }
834
835 *dst = rb_ll2inum(i64);
836 break;
837 }
838
839 case T_DBL: {
840 double dbl;
841
842 if (!read_double(buf, &dbl)) {
843 return false;
844 }
845
846 *dst = rb_float_new(dbl);
847 break;
848 }
849
850 case T_STR: {
851 VALUE str;
852
853 if (!read_string(buf, &str)) {
854 return false;
855 }
856
857 *dst = str;
858 break;
859 }
860 }
861
862 return true;
863}
864
865// TODO(kevinclark): Now that read_string does a malloc,
866// This maybe could be modified to avoid that, and the type coercion
867
868// Read the bytes but don't do anything with the value
869static bool skip_type(int type, decode_buffer* buf) {
870 switch (type) {
871 case T_STRCT:
872 read_struct_begin(buf);
873 while (true) {
874 field_header header;
875
876 if (!read_field_begin(buf, &header)) {
877 return false;
878 }
879
880 if (header.type == T_STOP) {
881 break;
882 }
883
884 if (!skip_type(header.type, buf)) {
885 return false;
886 }
887
888 read_field_end(buf);
889 }
890 read_struct_end(buf);
891
892 break;
893
894 case T_MAP: {
895 int i;
896 map_header header;
897
898 if (!read_map_begin(buf, &header)) {
899 return false;
900 }
901
902 for (i = 0; i < header.num_entries; ++i) {
903 if (!skip_type(header.key_type, buf)) {
904 return false;
905 }
906 if (!skip_type(header.val_type, buf)) {
907 return false;
908 }
909 }
910
911 read_map_end(buf);
912 break;
913 }
914
915 case T_SET: {
916 int i;
917 set_header header;
918
919 if (!read_set_begin(buf, &header)) {
920 return false;
921 }
922
923 for (i = 0; i < header.num_elements; ++i) {
924 if (!skip_type(header.type, buf)) {
925 return false;
926 }
927 }
928
929 read_set_end(buf);
930 break;
931 }
932
933 case T_LIST: {
934 int i;
935 list_header header;
936
937 if (!read_list_begin(buf, &header)) {
938 return false;
939 }
940
941 for (i = 0; i < header.num_elements; ++i) {
942 if (!skip_type(header.type, buf)) {
943 return false;
944 }
945 }
946
947 read_list_end(buf);
948 break;
949 }
950
951 default: {
952 VALUE v;
953 if (!read_type(type, buf, &v)) {
954 return false;
955 }
956 }
957 }
958
959 return true;
960}
961
962
963static VALUE read_struct(VALUE obj, decode_buffer* buf);
964
965// Read the right thing from the buffer given the field spec
966// and return the ruby object
967static bool read_field(decode_buffer* buf, field_spec* spec, VALUE* dst) {
968 switch (spec->type) {
969 case T_STRCT: {
970 VALUE obj = rb_class_new_instance(0, NULL, spec->data.class);
971
972 *dst = read_struct(obj, buf);
973 break;
974 }
975
976 case T_MAP: {
977 map_header hdr;
978 VALUE hsh;
979 int i;
980
981 read_map_begin(buf, &hdr);
982 hsh = rb_hash_new();
983
984 for (i = 0; i < hdr.num_entries; ++i) {
985 VALUE key, val;
986
987 if (!read_field(buf, spec->data.map->key, &key)) {
988 return false;
989 }
990
991 if (!read_field(buf, spec->data.map->value, &val)) {
992 return false;
993 }
994
995 rb_hash_aset(hsh, key, val);
996 }
997
998 read_map_end(buf);
999
1000 *dst = hsh;
1001 break;
1002 }
1003
1004 case T_LIST: {
1005 list_header hdr;
1006 VALUE arr, element;
1007 int i;
1008
1009 read_list_begin(buf, &hdr);
1010 arr = rb_ary_new2(hdr.num_elements);
1011
1012 for (i = 0; i < hdr.num_elements; ++i) {
1013 if (!read_field(buf, spec->data.element, &element)) {
1014 return false;
1015 }
1016
1017 rb_ary_push(arr, element);
1018 }
1019
1020 read_list_end(buf);
1021
1022 *dst = arr;
1023 break;
1024 }
1025
1026 case T_SET: {
1027 VALUE items, item;
1028 set_header hdr;
1029 int i;
1030
1031 read_set_begin(buf, &hdr);
1032 items = rb_ary_new2(hdr.num_elements);
1033
1034 for (i = 0; i < hdr.num_elements; ++i) {
1035 if (!read_field(buf, spec->data.element, &item)) {
1036 return false;
1037 }
1038
1039 rb_ary_push(items, item);
1040 }
1041
1042 *dst = rb_class_new_instance(1, &items, rb_cSet);
1043 break;
1044 }
1045
1046
1047 default:
1048 return read_type(spec->type, buf, dst);
1049 }
1050
1051 return true;
1052}
1053
1054static void handle_read_error() {
1055 // If it was an exception, reraise
1056 if (!NIL_P(ruby_errinfo)) {
1057 rb_exc_raise(ruby_errinfo);
1058 } else {
1059 // Something else went wrong, no idea what would call this yet
1060 // So far, the only thing to cause failures underneath is ruby
1061 // exceptions. Follow up on this regularly -- Kevin Clark (TODO)
1062 rb_raise(rb_eStandardError, "[BUG] Something went wrong in the field reading, but not a ruby exception");
1063 }
1064}
1065
1066// Fill in the instance variables in an object (thrift struct)
1067// from the decode buffer
1068static VALUE read_struct(VALUE obj, decode_buffer* buf) {
1069 VALUE field;
1070 field_header f_header;
1071 VALUE value = Qnil;
1072 VALUE fields = rb_const_get(CLASS_OF(obj), fields_id);
1073 field_spec* spec;
1074 char name_buf[128];
1075
1076 read_struct_begin(buf);
1077
1078 while(true) {
1079 if (!read_field_begin(buf, &f_header)) {
1080 handle_read_error();
1081 }
1082
1083 if (T_STOP == f_header.type) {
1084 break;
1085 }
1086
1087 field = rb_hash_aref(fields, INT2FIX(f_header.id));
1088
1089 if (NIL_P(field)) {
1090 if (!skip_type(f_header.type, buf)) {
1091 handle_read_error();
1092 return Qnil;
1093 }
1094 }
1095 else {
1096 spec = parse_field_spec(field);
1097
1098 if (spec->type != f_header.type) {
1099 if (!skip_type(spec->type, buf)) {
1100 free_field_spec(spec);
1101 handle_read_error();
1102 return Qnil;
1103 }
1104 } else {
1105 // Read busted somewhere (probably borrow/consume), bail
1106 if (!read_field(buf, spec, &value)) {
1107 free_field_spec(spec);
1108 handle_read_error();
1109 return Qnil;
1110 }
1111
1112 name_buf[0] = '@';
1113 strlcpy(&name_buf[1], spec->name, sizeof(name_buf) - 1);
1114
1115 rb_iv_set(obj, name_buf, value);
1116 }
1117
1118 free_field_spec(spec);
1119 }
1120
1121 read_field_end(buf);
1122 }
1123
1124 read_struct_end(buf);
1125
1126 return obj;
1127}
1128
1129
1130// Takes an object and transport, and decodes the values in the transport's
1131// buffer to fill the object.
1132static VALUE tbpa_decode_binary(VALUE self, VALUE obj, VALUE transport) {
1133 decode_buffer buf;
1134 VALUE ret_val;
1135
1136 buf.pos = 0; // This needs to be set so an arbitrary number of bytes isn't consumed
1137 buf.trans = transport; // We need to hold this so the buffer can be refilled
1138
1139 if (fill_buffer(&buf, 0) < 0) {
1140 handle_read_error();
1141 return Qnil;
1142 }
1143
1144#ifdef __DEBUG__
1145 rb_p(rb_str_new2("Running decode binary with data:"));
1146 rb_p(rb_inspect(rb_str_new2(buf.data)));
1147#endif
1148
1149 ret_val = read_struct(obj, &buf);
1150
1151 // Consume whatever was read
1152 consume(&buf, buf.pos);
1153
1154 return ret_val;
1155}
1156
1157// -----------------------------------------------------------------------------
1158// These methods are used by the thrift library and need to handled
1159// seperately from encode and decode
1160// -----------------------------------------------------------------------------
1161
1162// Read the message header and return it as a ruby array
1163static VALUE tbpa_read_message_begin(VALUE self) {
1164 decode_buffer buf;
1165 int32_t version, seqid;
1166 int8_t type;
1167 VALUE name;
1168
1169 VALUE trans = rb_iv_get(self, "@trans");
1170
1171 buf.pos = 0; // This needs to be set so fill_buffer doesn't consume
1172 buf.trans = trans; // We need to hold this so the buffer can be refilled
1173
1174
1175 if (fill_buffer(&buf, 0) < 0 || !read_int32(&buf, &version)) {
1176 // Consume whatever was read
1177 consume(&buf, buf.pos);
1178 handle_read_error();
1179 return Qnil;
1180 }
1181
1182 if ((version & VERSION_MASK) != VERSION_1) {
David Reiss38992482008-07-25 21:05:59 +00001183 VALUE tprotocol_exception = rb_const_get(m_thrift, rb_intern("ProtocolException"));
Kevin Clark4bd89162008-07-08 00:47:49 +00001184 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"));
David Reiss38992482008-07-25 21:05:59 +00001185 rb_exc_raise(exception);
Kevin Clark4bd89162008-07-08 00:47:49 +00001186 }
1187
1188 type = version & 0x000000ff;
1189
1190 if (!read_string(&buf, &name) || !read_int32(&buf, &seqid)) {
1191 // Consume whatever was read
1192 consume(&buf, buf.pos);
1193 handle_read_error();
1194 return Qnil;
1195 }
1196
1197 // Consume whatever was read
1198 if (consume(&buf, buf.pos) < 0) {
1199 handle_read_error();
1200 return Qnil;
1201 }
1202
1203 return rb_ary_new3(3, name, INT2FIX(type), INT2FIX(seqid));
1204}
1205
1206void Init_binaryprotocolaccelerated()
1207{
1208 m_thrift = rb_const_get(rb_cObject, rb_intern("Thrift"));
1209 VALUE class_tbinproto = rb_const_get(m_thrift, rb_intern("BinaryProtocol"));
1210 class_tbpa = rb_define_class_under(m_thrift, "BinaryProtocolAccelerated", class_tbinproto);
1211 type_sym = ID2SYM(rb_intern("type"));
1212 class_sym = ID2SYM(rb_intern("class"));
1213 key_sym = ID2SYM(rb_intern("key"));
1214 value_sym = ID2SYM(rb_intern("value"));
1215 name_sym = ID2SYM(rb_intern("name"));
1216 fields_id = rb_intern("FIELDS");
1217 element_sym = ID2SYM(rb_intern("element"));
1218 consume_bang_id = rb_intern("consume!");
1219 string_buffer_id = rb_intern("string_buffer");
1220 borrow_id = rb_intern("borrow");
1221 keys_id = rb_intern("keys");
1222 entries_id = rb_intern("entries");
1223 rb_cSet = rb_const_get(rb_cObject, rb_intern("Set"));
1224
1225 // For fast access
1226 rb_define_method(class_tbpa, "encode_binary", tbpa_encode_binary, 1);
1227 rb_define_method(class_tbpa, "decode_binary", tbpa_decode_binary, 2);
1228 rb_define_method(class_tbpa, "read_message_begin", tbpa_read_message_begin, 0);
1229
1230}