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