thrift 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. data/InstalledFiles +1 -0
  2. data/Makefile +512 -0
  3. data/Makefile.am +3 -1
  4. data/Makefile.in +117 -45
  5. data/Manifest +17 -0
  6. data/Rakefile +6 -8
  7. data/benchmark/gen-rb/benchmark_constants.rb +10 -0
  8. data/benchmark/gen-rb/benchmark_service.rb +80 -0
  9. data/benchmark/gen-rb/benchmark_types.rb +9 -0
  10. data/debug_proto_test/gen-rb/debug_proto_test_constants.rb +273 -0
  11. data/debug_proto_test/gen-rb/debug_proto_test_types.rb +705 -0
  12. data/debug_proto_test/gen-rb/empty_service.rb +24 -0
  13. data/debug_proto_test/gen-rb/inherited.rb +79 -0
  14. data/debug_proto_test/gen-rb/reverse_order_service.rb +82 -0
  15. data/debug_proto_test/gen-rb/service_for_exception_with_a_map.rb +81 -0
  16. data/debug_proto_test/gen-rb/srv.rb +330 -0
  17. data/ext/binary_protocol_accelerated.c +24 -11
  18. data/ext/compact_protocol.c +14 -11
  19. data/ext/constants.h +1 -0
  20. data/ext/memory_buffer.c +56 -1
  21. data/ext/struct.c +76 -19
  22. data/ext/thrift_native.c +2 -0
  23. data/lib/thrift/exceptions.rb +3 -1
  24. data/lib/thrift/protocol/binary_protocol.rb +14 -10
  25. data/lib/thrift/protocol/compact_protocol.rb +7 -4
  26. data/lib/thrift/server/nonblocking_server.rb +15 -5
  27. data/lib/thrift/struct.rb +9 -6
  28. data/lib/thrift/struct_union.rb +47 -14
  29. data/lib/thrift/transport/base_transport.rb +39 -2
  30. data/lib/thrift/transport/buffered_transport.rb +31 -0
  31. data/lib/thrift/transport/framed_transport.rb +26 -0
  32. data/lib/thrift/transport/memory_buffer_transport.rb +29 -0
  33. data/spec/binary_protocol_spec.rb +2 -4
  34. data/spec/gen-rb/nonblocking_service.rb +272 -0
  35. data/spec/gen-rb/thrift_spec_constants.rb +10 -0
  36. data/spec/gen-rb/thrift_spec_types.rb +345 -0
  37. data/spec/spec_helper.rb +1 -3
  38. data/thrift.gemspec +10 -11
  39. data/tmp/thrift-0.7.0.gem +0 -0
  40. metadata +22 -8
data/ext/constants.h CHANGED
@@ -74,6 +74,7 @@ extern ID write_field_stop_method_id;
74
74
  extern ID skip_method_id;
75
75
  extern ID write_method_id;
76
76
  extern ID read_all_method_id;
77
+ extern ID read_into_buffer_method_id;
77
78
  extern ID native_qmark_method_id;
78
79
 
79
80
  extern ID fields_const_id;
data/ext/memory_buffer.c CHANGED
@@ -30,6 +30,11 @@ int GARBAGE_BUFFER_SIZE;
30
30
 
31
31
  #define GET_BUF(self) rb_ivar_get(self, buf_ivar_id)
32
32
 
33
+ VALUE rb_thrift_memory_buffer_write(VALUE self, VALUE str);
34
+ VALUE rb_thrift_memory_buffer_read(VALUE self, VALUE length_value);
35
+ VALUE rb_thrift_memory_buffer_read_byte(VALUE self);
36
+ VALUE rb_thrift_memory_buffer_read_into_buffer(VALUE self, VALUE buffer_value, VALUE size_value);
37
+
33
38
  VALUE rb_thrift_memory_buffer_write(VALUE self, VALUE str) {
34
39
  VALUE buf = GET_BUF(self);
35
40
  rb_str_buf_cat(buf, RSTRING_PTR(str), RSTRING_LEN(str));
@@ -53,19 +58,69 @@ VALUE rb_thrift_memory_buffer_read(VALUE self, VALUE length_value) {
53
58
  rb_ivar_set(self, buf_ivar_id, rb_funcall(buf, slice_method_id, 2, INT2FIX(index), INT2FIX(RSTRING_LEN(buf) - 1)));
54
59
  index = 0;
55
60
  }
61
+ rb_ivar_set(self, index_ivar_id, INT2FIX(index));
56
62
 
57
63
  if (RSTRING_LEN(data) < length) {
58
64
  rb_raise(rb_eEOFError, "Not enough bytes remain in memory buffer");
59
65
  }
60
66
 
61
- rb_ivar_set(self, index_ivar_id, INT2FIX(index));
62
67
  return data;
63
68
  }
64
69
 
70
+ VALUE rb_thrift_memory_buffer_read_byte(VALUE self) {
71
+ VALUE index_value = rb_ivar_get(self, index_ivar_id);
72
+ int index = FIX2INT(index_value);
73
+
74
+ VALUE buf = GET_BUF(self);
75
+ if (index >= RSTRING_LEN(buf)) {
76
+ rb_raise(rb_eEOFError, "Not enough bytes remain in memory buffer");
77
+ }
78
+ char byte = RSTRING_PTR(buf)[index++];
79
+
80
+ if (index >= GARBAGE_BUFFER_SIZE) {
81
+ rb_ivar_set(self, buf_ivar_id, rb_funcall(buf, slice_method_id, 2, INT2FIX(index), INT2FIX(RSTRING_LEN(buf) - 1)));
82
+ index = 0;
83
+ }
84
+ rb_ivar_set(self, index_ivar_id, INT2FIX(index));
85
+
86
+ int result = (int) byte;
87
+ return INT2FIX(result);
88
+ }
89
+
90
+ VALUE rb_thrift_memory_buffer_read_into_buffer(VALUE self, VALUE buffer_value, VALUE size_value) {
91
+ int i = 0;
92
+ int size = FIX2INT(size_value);
93
+ int index;
94
+ VALUE buf = GET_BUF(self);
95
+
96
+ while (i < size) {
97
+ index = FIX2INT(rb_ivar_get(self, index_ivar_id));
98
+ if (index >= RSTRING_LEN(buf)) {
99
+ rb_raise(rb_eEOFError, "Not enough bytes remain in memory buffer");
100
+ }
101
+ char byte = RSTRING_PTR(buf)[index++];
102
+
103
+ if (index >= GARBAGE_BUFFER_SIZE) {
104
+ rb_ivar_set(self, buf_ivar_id, rb_funcall(buf, slice_method_id, 2, INT2FIX(index), INT2FIX(RSTRING_LEN(buf) - 1)));
105
+ index = 0;
106
+ }
107
+ rb_ivar_set(self, index_ivar_id, INT2FIX(index));
108
+
109
+ if (i >= RSTRING_LEN(buffer_value)) {
110
+ rb_raise(rb_eIndexError, "index %d out of string", i);
111
+ }
112
+ ((char*)RSTRING_PTR(buffer_value))[i] = byte;
113
+ i++;
114
+ }
115
+ return INT2FIX(i);
116
+ }
117
+
65
118
  void Init_memory_buffer() {
66
119
  VALUE thrift_memory_buffer_class = rb_const_get(thrift_module, rb_intern("MemoryBufferTransport"));
67
120
  rb_define_method(thrift_memory_buffer_class, "write", rb_thrift_memory_buffer_write, 1);
68
121
  rb_define_method(thrift_memory_buffer_class, "read", rb_thrift_memory_buffer_read, 1);
122
+ rb_define_method(thrift_memory_buffer_class, "read_byte", rb_thrift_memory_buffer_read_byte, 0);
123
+ rb_define_method(thrift_memory_buffer_class, "read_into_buffer", rb_thrift_memory_buffer_read_into_buffer, 2);
69
124
 
70
125
  buf_ivar_id = rb_intern("@buf");
71
126
  index_ivar_id = rb_intern("@index");
data/ext/struct.c CHANGED
@@ -55,6 +55,7 @@ ID setvalue_id;
55
55
 
56
56
  ID to_s_method_id;
57
57
  ID name_to_id_method_id;
58
+ static ID sorted_field_ids_method_id;
58
59
 
59
60
  #define IS_CONTAINER(ttype) ((ttype) == TTYPE_MAP || (ttype) == TTYPE_LIST || (ttype) == TTYPE_SET)
60
61
  #define STRUCT_FIELDS(obj) rb_const_get(CLASS_OF(obj), fields_const_id)
@@ -375,13 +376,11 @@ static VALUE rb_thrift_struct_write(VALUE self, VALUE protocol) {
375
376
 
376
377
  // iterate through all the fields here
377
378
  VALUE struct_fields = STRUCT_FIELDS(self);
378
-
379
- VALUE struct_field_ids_unordered = rb_funcall(struct_fields, keys_method_id, 0);
380
- VALUE struct_field_ids_ordered = rb_funcall(struct_field_ids_unordered, sort_method_id, 0);
379
+ VALUE sorted_field_ids = rb_funcall(self, sorted_field_ids_method_id, 0);
381
380
 
382
381
  int i = 0;
383
- for (i=0; i < RARRAY_LEN(struct_field_ids_ordered); i++) {
384
- VALUE field_id = rb_ary_entry(struct_field_ids_ordered, i);
382
+ for (i=0; i < RARRAY_LEN(sorted_field_ids); i++) {
383
+ VALUE field_id = rb_ary_entry(sorted_field_ids, i);
385
384
 
386
385
  VALUE field_info = rb_hash_aref(struct_fields, field_id);
387
386
 
@@ -414,6 +413,8 @@ static VALUE rb_thrift_struct_write(VALUE self, VALUE protocol) {
414
413
 
415
414
  static VALUE rb_thrift_union_read(VALUE self, VALUE protocol);
416
415
  static VALUE rb_thrift_struct_read(VALUE self, VALUE protocol);
416
+ static void skip_map_contents(VALUE protocol, VALUE key_type_value, VALUE value_type_value, int size);
417
+ static void skip_list_or_set_contents(VALUE protocol, VALUE element_type_value, int size);
417
418
 
418
419
  static void set_field_value(VALUE obj, VALUE field_name, VALUE value) {
419
420
  char name_buf[RSTRING_LEN(field_name) + 1];
@@ -424,6 +425,23 @@ static void set_field_value(VALUE obj, VALUE field_name, VALUE value) {
424
425
  rb_ivar_set(obj, rb_intern(name_buf), value);
425
426
  }
426
427
 
428
+ // Helper method to skip the contents of a map (assumes the map header has been read).
429
+ static void skip_map_contents(VALUE protocol, VALUE key_type_value, VALUE value_type_value, int size) {
430
+ int i;
431
+ for (i = 0; i < size; i++) {
432
+ rb_funcall(protocol, skip_method_id, 1, key_type_value);
433
+ rb_funcall(protocol, skip_method_id, 1, value_type_value);
434
+ }
435
+ }
436
+
437
+ // Helper method to skip the contents of a list or set (assumes the list/set header has been read).
438
+ static void skip_list_or_set_contents(VALUE protocol, VALUE element_type_value, int size) {
439
+ int i;
440
+ for (i = 0; i < size; i++) {
441
+ rb_funcall(protocol, skip_method_id, 1, element_type_value);
442
+ }
443
+ }
444
+
427
445
  static VALUE read_anything(VALUE protocol, int ttype, VALUE field_info) {
428
446
  VALUE result = Qnil;
429
447
 
@@ -458,18 +476,30 @@ static VALUE read_anything(VALUE protocol, int ttype, VALUE field_info) {
458
476
  int value_ttype = FIX2INT(rb_ary_entry(map_header, 1));
459
477
  int num_entries = FIX2INT(rb_ary_entry(map_header, 2));
460
478
 
479
+ // Check the declared key and value types against the expected ones and skip the map contents
480
+ // if the types don't match.
461
481
  VALUE key_info = rb_hash_aref(field_info, key_sym);
462
482
  VALUE value_info = rb_hash_aref(field_info, value_sym);
463
483
 
464
- result = rb_hash_new();
484
+ if (!NIL_P(key_info) && !NIL_P(value_info)) {
485
+ int specified_key_type = FIX2INT(rb_hash_aref(key_info, type_sym));
486
+ int specified_value_type = FIX2INT(rb_hash_aref(value_info, type_sym));
487
+ if (specified_key_type == key_ttype && specified_value_type == value_ttype) {
488
+ result = rb_hash_new();
465
489
 
466
- for (i = 0; i < num_entries; ++i) {
467
- VALUE key, val;
490
+ for (i = 0; i < num_entries; ++i) {
491
+ VALUE key, val;
468
492
 
469
- key = read_anything(protocol, key_ttype, key_info);
470
- val = read_anything(protocol, value_ttype, value_info);
493
+ key = read_anything(protocol, key_ttype, key_info);
494
+ val = read_anything(protocol, value_ttype, value_info);
471
495
 
472
- rb_hash_aset(result, key, val);
496
+ rb_hash_aset(result, key, val);
497
+ }
498
+ } else {
499
+ skip_map_contents(protocol, INT2FIX(key_ttype), INT2FIX(value_ttype), num_entries);
500
+ }
501
+ } else {
502
+ skip_map_contents(protocol, INT2FIX(key_ttype), INT2FIX(value_ttype), num_entries);
473
503
  }
474
504
 
475
505
  default_read_map_end(protocol);
@@ -479,10 +509,23 @@ static VALUE read_anything(VALUE protocol, int ttype, VALUE field_info) {
479
509
  VALUE list_header = default_read_list_begin(protocol);
480
510
  int element_ttype = FIX2INT(rb_ary_entry(list_header, 0));
481
511
  int num_elements = FIX2INT(rb_ary_entry(list_header, 1));
482
- result = rb_ary_new2(num_elements);
483
512
 
484
- for (i = 0; i < num_elements; ++i) {
485
- rb_ary_push(result, read_anything(protocol, element_ttype, rb_hash_aref(field_info, element_sym)));
513
+ // Check the declared element type against the expected one and skip the list contents
514
+ // if the types don't match.
515
+ VALUE element_info = rb_hash_aref(field_info, element_sym);
516
+ if (!NIL_P(element_info)) {
517
+ int specified_element_type = FIX2INT(rb_hash_aref(element_info, type_sym));
518
+ if (specified_element_type == element_ttype) {
519
+ result = rb_ary_new2(num_elements);
520
+
521
+ for (i = 0; i < num_elements; ++i) {
522
+ rb_ary_push(result, read_anything(protocol, element_ttype, rb_hash_aref(field_info, element_sym)));
523
+ }
524
+ } else {
525
+ skip_list_or_set_contents(protocol, INT2FIX(element_ttype), num_elements);
526
+ }
527
+ } else {
528
+ skip_list_or_set_contents(protocol, INT2FIX(element_ttype), num_elements);
486
529
  }
487
530
 
488
531
  default_read_list_end(protocol);
@@ -493,15 +536,28 @@ static VALUE read_anything(VALUE protocol, int ttype, VALUE field_info) {
493
536
  VALUE set_header = default_read_set_begin(protocol);
494
537
  int element_ttype = FIX2INT(rb_ary_entry(set_header, 0));
495
538
  int num_elements = FIX2INT(rb_ary_entry(set_header, 1));
496
- items = rb_ary_new2(num_elements);
497
539
 
498
- for (i = 0; i < num_elements; ++i) {
499
- rb_ary_push(items, read_anything(protocol, element_ttype, rb_hash_aref(field_info, element_sym)));
540
+ // Check the declared element type against the expected one and skip the set contents
541
+ // if the types don't match.
542
+ VALUE element_info = rb_hash_aref(field_info, element_sym);
543
+ if (!NIL_P(element_info)) {
544
+ int specified_element_type = FIX2INT(rb_hash_aref(element_info, type_sym));
545
+ if (specified_element_type == element_ttype) {
546
+ items = rb_ary_new2(num_elements);
547
+
548
+ for (i = 0; i < num_elements; ++i) {
549
+ rb_ary_push(items, read_anything(protocol, element_ttype, rb_hash_aref(field_info, element_sym)));
550
+ }
551
+
552
+ result = rb_class_new_instance(1, &items, rb_cSet);
553
+ } else {
554
+ skip_list_or_set_contents(protocol, INT2FIX(element_ttype), num_elements);
555
+ }
556
+ } else {
557
+ skip_list_or_set_contents(protocol, INT2FIX(element_ttype), num_elements);
500
558
  }
501
559
 
502
560
  default_read_set_end(protocol);
503
-
504
- result = rb_class_new_instance(1, &items, rb_cSet);
505
561
  } else {
506
562
  rb_raise(rb_eNotImpError, "read_anything not implemented for type %d!", ttype);
507
563
  }
@@ -657,4 +713,5 @@ void Init_struct() {
657
713
 
658
714
  to_s_method_id = rb_intern("to_s");
659
715
  name_to_id_method_id = rb_intern("name_to_id");
716
+ sorted_field_ids_method_id = rb_intern("sorted_field_ids");
660
717
  }
data/ext/thrift_native.c CHANGED
@@ -88,6 +88,7 @@ ID write_field_stop_method_id;
88
88
  ID skip_method_id;
89
89
  ID write_method_id;
90
90
  ID read_all_method_id;
91
+ ID read_into_buffer_method_id;
91
92
  ID native_qmark_method_id;
92
93
 
93
94
  // constant ids
@@ -170,6 +171,7 @@ void Init_thrift_native() {
170
171
  skip_method_id = rb_intern("skip");
171
172
  write_method_id = rb_intern("write");
172
173
  read_all_method_id = rb_intern("read_all");
174
+ read_into_buffer_method_id = rb_intern("read_into_buffer");
173
175
  native_qmark_method_id = rb_intern("native?");
174
176
 
175
177
  // constant ids
@@ -35,6 +35,8 @@ module Thrift
35
35
  WRONG_METHOD_NAME = 3
36
36
  BAD_SEQUENCE_ID = 4
37
37
  MISSING_RESULT = 5
38
+ INTERNAL_ERROR = 6
39
+ PROTOCOL_ERROR = 7
38
40
 
39
41
  attr_reader :type
40
42
 
@@ -79,4 +81,4 @@ module Thrift
79
81
  end
80
82
 
81
83
  end
82
- end
84
+ end
@@ -29,6 +29,11 @@ module Thrift
29
29
  super(trans)
30
30
  @strict_read = strict_read
31
31
  @strict_write = strict_write
32
+
33
+ # Pre-allocated read buffer for fixed-size read methods. Needs to be at least 8 bytes long for
34
+ # read_i64() and read_double().
35
+ @rbuf = "\0" * 8
36
+ @rbuf.force_encoding("BINARY") if @rbuf.respond_to?(:force_encoding)
32
37
  end
33
38
 
34
39
  def write_message_begin(name, type, seqid)
@@ -165,8 +170,7 @@ module Thrift
165
170
  end
166
171
 
167
172
  def read_byte
168
- dat = trans.read_all(1)
169
- val = dat[0].ord
173
+ val = trans.read_byte
170
174
  if (val > 0x7f)
171
175
  val = 0 - ((val - 1) ^ 0xff)
172
176
  end
@@ -174,8 +178,8 @@ module Thrift
174
178
  end
175
179
 
176
180
  def read_i16
177
- dat = trans.read_all(2)
178
- val, = dat.unpack('n')
181
+ trans.read_into_buffer(@rbuf, 2)
182
+ val, = @rbuf.unpack('n')
179
183
  if (val > 0x7fff)
180
184
  val = 0 - ((val - 1) ^ 0xffff)
181
185
  end
@@ -183,8 +187,8 @@ module Thrift
183
187
  end
184
188
 
185
189
  def read_i32
186
- dat = trans.read_all(4)
187
- val, = dat.unpack('N')
190
+ trans.read_into_buffer(@rbuf, 4)
191
+ val, = @rbuf.unpack('N')
188
192
  if (val > 0x7fffffff)
189
193
  val = 0 - ((val - 1) ^ 0xffffffff)
190
194
  end
@@ -192,8 +196,8 @@ module Thrift
192
196
  end
193
197
 
194
198
  def read_i64
195
- dat = trans.read_all(8)
196
- hi, lo = dat.unpack('N2')
199
+ trans.read_into_buffer(@rbuf, 8)
200
+ hi, lo = @rbuf.unpack('N2')
197
201
  if (hi > 0x7fffffff)
198
202
  hi ^= 0xffffffff
199
203
  lo ^= 0xffffffff
@@ -204,8 +208,8 @@ module Thrift
204
208
  end
205
209
 
206
210
  def read_double
207
- dat = trans.read_all(8)
208
- val = dat.unpack('G').first
211
+ trans.read_into_buffer(@rbuf, 8)
212
+ val = @rbuf.unpack('G').first
209
213
  val
210
214
  end
211
215
 
@@ -98,6 +98,10 @@ module Thrift
98
98
 
99
99
  @last_field = [0]
100
100
  @boolean_value = nil
101
+
102
+ # Pre-allocated read buffer for read_double().
103
+ @rbuf = "\0" * 8
104
+ @rbuf.force_encoding("BINARY") if @rbuf.respond_to?(:force_encoding)
101
105
  end
102
106
 
103
107
  def write_message_begin(name, type, seqid)
@@ -302,8 +306,7 @@ module Thrift
302
306
  end
303
307
 
304
308
  def read_byte
305
- dat = trans.read_all(1)
306
- val = dat[0]
309
+ val = trans.read_byte
307
310
  if (val > 0x7f)
308
311
  val = 0 - ((val - 1) ^ 0xff)
309
312
  end
@@ -323,8 +326,8 @@ module Thrift
323
326
  end
324
327
 
325
328
  def read_double
326
- dat = trans.read_all(8)
327
- val = dat.reverse.unpack('G').first
329
+ trans.read_into_buffer(@rbuf, 8)
330
+ val = @rbuf.reverse.unpack('G').first
328
331
  val
329
332
  end
330
333
 
@@ -44,7 +44,13 @@ module Thrift
44
44
  begin
45
45
  loop do
46
46
  break if @server_transport.closed?
47
- rd, = select([@server_transport], nil, nil, 0.1)
47
+ begin
48
+ rd, = select([@server_transport], nil, nil, 0.1)
49
+ rescue Errno::EBADF => e
50
+ # In Ruby 1.9, calling @server_transport.close in shutdown paths causes the select() to raise an
51
+ # Errno::EBADF. If this happens, ignore it and retry the loop.
52
+ break
53
+ end
48
54
  next if rd.nil?
49
55
  socket = @server_transport.accept
50
56
  @logger.debug "Accepted socket: #{socket.inspect}"
@@ -146,10 +152,14 @@ module Thrift
146
152
  break if read_signals == :shutdown
147
153
  end
148
154
  rd.each do |fd|
149
- if fd.handle.eof?
155
+ begin
156
+ if fd.handle.eof?
157
+ remove_connection fd
158
+ else
159
+ read_connection fd
160
+ end
161
+ rescue Errno::ECONNRESET
150
162
  remove_connection fd
151
- else
152
- read_connection fd
153
163
  end
154
164
  end
155
165
  end
@@ -292,4 +302,4 @@ module Thrift
292
302
  end
293
303
  end
294
304
  end
295
- end
305
+ end
data/lib/thrift/struct.rb CHANGED
@@ -55,7 +55,7 @@ module Thrift
55
55
  end
56
56
 
57
57
  def fields_with_default_values
58
- fields_with_default_values = self.class.instance_variable_get("@fields_with_default_values")
58
+ fields_with_default_values = self.class.instance_variable_get(:@fields_with_default_values)
59
59
  unless fields_with_default_values
60
60
  fields_with_default_values = {}
61
61
  struct_fields.each do |fid, field_def|
@@ -63,7 +63,7 @@ module Thrift
63
63
  fields_with_default_values[field_def[:name]] = field_def[:default]
64
64
  end
65
65
  end
66
- self.class.instance_variable_set("@fields_with_default_values", fields_with_default_values)
66
+ self.class.instance_variable_set(:@fields_with_default_values, fields_with_default_values)
67
67
  end
68
68
  fields_with_default_values
69
69
  end
@@ -114,9 +114,10 @@ module Thrift
114
114
  end
115
115
 
116
116
  def ==(other)
117
+ return false if other.nil?
117
118
  each_field do |fid, field_info|
118
119
  name = field_info[:name]
119
- return false unless self.instance_variable_get("@#{name}") == other.instance_variable_get("@#{name}")
120
+ return false unless other.respond_to?(name) && self.send(name) == other.send(name)
120
121
  end
121
122
  true
122
123
  end
@@ -125,13 +126,15 @@ module Thrift
125
126
  self.class == other.class && self == other
126
127
  end
127
128
 
129
+ # This implementation of hash() is inspired by Apache's Java HashCodeBuilder class.
128
130
  def hash
129
- field_values = []
131
+ total = 17
130
132
  each_field do |fid, field_info|
131
133
  name = field_info[:name]
132
- field_values << self.instance_variable_get("@#{name}")
134
+ value = self.send(name)
135
+ total = (total * 37 + value.hash) & 0xffffffff
133
136
  end
134
- field_values.hash
137
+ total
135
138
  end
136
139
 
137
140
  def differences(other)
@@ -21,19 +21,28 @@ require 'set'
21
21
  module Thrift
22
22
  module Struct_Union
23
23
  def name_to_id(name)
24
- names_to_ids = self.class.instance_variable_get("@names_to_ids")
24
+ names_to_ids = self.class.instance_variable_get(:@names_to_ids)
25
25
  unless names_to_ids
26
26
  names_to_ids = {}
27
27
  struct_fields.each do |fid, field_def|
28
28
  names_to_ids[field_def[:name]] = fid
29
29
  end
30
- self.class.instance_variable_set("@names_to_ids", names_to_ids)
30
+ self.class.instance_variable_set(:@names_to_ids, names_to_ids)
31
31
  end
32
32
  names_to_ids[name]
33
33
  end
34
34
 
35
+ def sorted_field_ids
36
+ sorted_field_ids = self.class.instance_variable_get(:@sorted_field_ids)
37
+ unless sorted_field_ids
38
+ sorted_field_ids = struct_fields.keys.sort
39
+ self.class.instance_variable_set(:@sorted_field_ids, sorted_field_ids)
40
+ end
41
+ sorted_field_ids
42
+ end
43
+
35
44
  def each_field
36
- struct_fields.keys.sort.each do |fid|
45
+ sorted_field_ids.each do |fid|
37
46
  data = struct_fields[fid]
38
47
  yield fid, data
39
48
  end
@@ -46,25 +55,49 @@ module Thrift
46
55
  value.read(iprot)
47
56
  when Types::MAP
48
57
  key_type, val_type, size = iprot.read_map_begin
49
- value = {}
50
- size.times do
51
- k = read_field(iprot, field_info(field[:key]))
52
- v = read_field(iprot, field_info(field[:value]))
53
- value[k] = v
58
+ # Skip the map contents if the declared key or value types don't match the expected ones.
59
+ if (key_type != field[:key][:type] || val_type != field[:value][:type])
60
+ size.times do
61
+ iprot.skip(key_type)
62
+ iprot.skip(val_type)
63
+ end
64
+ value = nil
65
+ else
66
+ value = {}
67
+ size.times do
68
+ k = read_field(iprot, field_info(field[:key]))
69
+ v = read_field(iprot, field_info(field[:value]))
70
+ value[k] = v
71
+ end
54
72
  end
55
73
  iprot.read_map_end
56
74
  when Types::LIST
57
75
  e_type, size = iprot.read_list_begin
58
- value = Array.new(size) do |n|
59
- read_field(iprot, field_info(field[:element]))
76
+ # Skip the list contents if the declared element type doesn't match the expected one.
77
+ if (e_type != field[:element][:type])
78
+ size.times do
79
+ iprot.skip(e_type)
80
+ end
81
+ value = nil
82
+ else
83
+ value = Array.new(size) do |n|
84
+ read_field(iprot, field_info(field[:element]))
85
+ end
60
86
  end
61
87
  iprot.read_list_end
62
88
  when Types::SET
63
89
  e_type, size = iprot.read_set_begin
64
- value = Set.new
65
- size.times do
66
- element = read_field(iprot, field_info(field[:element]))
67
- value << element
90
+ # Skip the set contents if the declared element type doesn't match the expected one.
91
+ if (e_type != field[:element][:type])
92
+ size.times do
93
+ iprot.skip(e_type)
94
+ end
95
+ else
96
+ value = Set.new
97
+ size.times do
98
+ element = read_field(iprot, field_info(field[:element]))
99
+ value << element
100
+ end
68
101
  end
69
102
  iprot.read_set_end
70
103
  else
@@ -34,6 +34,26 @@ module Thrift
34
34
  end
35
35
  end
36
36
 
37
+ module TransportUtils
38
+ if RUBY_VERSION >= '1.9'
39
+ def self.get_string_byte(string, index)
40
+ string.getbyte(index)
41
+ end
42
+
43
+ def self.set_string_byte(string, index, byte)
44
+ string.setbyte(index, byte)
45
+ end
46
+ else
47
+ def self.get_string_byte(string, index)
48
+ string[index]
49
+ end
50
+
51
+ def self.set_string_byte(string, index, byte)
52
+ string[index] = byte
53
+ end
54
+ end
55
+ end
56
+
37
57
  class BaseTransport
38
58
  def open?; end
39
59
 
@@ -45,9 +65,26 @@ module Thrift
45
65
  raise NotImplementedError
46
66
  end
47
67
 
68
+ # Returns an unsigned byte as a Fixnum in the range (0..255).
69
+ def read_byte
70
+ buf = read_all(1)
71
+ return ::Thrift::TransportUtils.get_string_byte(buf, 0)
72
+ end
73
+
74
+ # Reads size bytes and copies them into buffer[0..size].
75
+ def read_into_buffer(buffer, size)
76
+ tmp = read_all(size)
77
+ i = 0
78
+ tmp.each_byte do |byte|
79
+ ::Thrift::TransportUtils.set_string_byte(buffer, i, byte)
80
+ i += 1
81
+ end
82
+ i
83
+ end
84
+
48
85
  def read_all(size)
49
- buf = ''
50
-
86
+ return '' if size <= 0
87
+ buf = read(size)
51
88
  while (buf.length < size)
52
89
  chunk = read(size - buf.length)
53
90
  buf << chunk
@@ -55,6 +55,37 @@ module Thrift
55
55
  ret
56
56
  end
57
57
 
58
+ def read_byte
59
+ # If the read buffer is exhausted, try to read up to DEFAULT_BUFFER more bytes into it.
60
+ if @index >= @rbuf.size
61
+ @rbuf = @transport.read(DEFAULT_BUFFER)
62
+ @index = 0
63
+ end
64
+
65
+ # The read buffer has some data now, read a single byte. Using get_string_byte() avoids
66
+ # allocating a temp string of size 1 unnecessarily.
67
+ @index += 1
68
+ return ::Thrift::TransportUtils.get_string_byte(@rbuf, @index - 1)
69
+ end
70
+
71
+ def read_into_buffer(buffer, size)
72
+ i = 0
73
+ while i < size
74
+ # If the read buffer is exhausted, try to read up to DEFAULT_BUFFER more bytes into it.
75
+ if @index >= @rbuf.size
76
+ @rbuf = @transport.read(DEFAULT_BUFFER)
77
+ @index = 0
78
+ end
79
+
80
+ # The read buffer has some data now, so copy bytes over to the output buffer.
81
+ byte = ::Thrift::TransportUtils.get_string_byte(@rbuf, @index)
82
+ ::Thrift::TransportUtils.set_string_byte(buffer, i, byte)
83
+ @index += 1
84
+ i += 1
85
+ end
86
+ i
87
+ end
88
+
58
89
  def write(buf)
59
90
  @wbuf << buf
60
91
  end