sparsam 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3ab4bfb28dc0761f1ef66ea93e239408e4bdb308
4
+ data.tar.gz: 78cd96b406e6260832478236d53e7c20a166d628
5
+ SHA512:
6
+ metadata.gz: 9c5cfdaed8ebc55ded0e937139d21d3133459be09b3787c6fc0625fdf09be2452d615969dbcd9aa7f7eb9510ec2bd1dd65c2314b9d979cabb043349f113cbc94
7
+ data.tar.gz: 4a72dbf9b165aff8c419beaa7f870ed3cdb00e1d858201325707cdd5248d7e6ce6126896e9e4ae1e381c5e055003f4c8d348f95297e8143c270e86fe2bd198eb
@@ -0,0 +1,19 @@
1
+ # Sparsam [![Build Status](https://travis-ci.org/airbnb/sparsam.svg?branch=master)](https://travis-ci.org/airbnb/sparsam) [![Coverage Status](https://coveralls.io/repos/github/airbnb/sparsam/badge.svg?branch=master)](https://coveralls.io/github/airbnb/sparsam?branch=master)
2
+ New Thrift bindings and generator for Ruby!
3
+
4
+ ## Super basic Example
5
+ See the docs folder for more detailed information
6
+ ```
7
+ $ sparsam-gen my_struct.thrift
8
+ $ bundle exec irb
9
+ irb(main):001:0> require './gen-ruby/my_struct_types'
10
+ => true
11
+ irb(main):002:0> require 'sparsam'
12
+ => true
13
+ irb(main):003:0> obj = MyStruct.new
14
+ => #<MyStruct:0x007fa70d924148>
15
+ irb(main):004:0> serialized = obj.serialize # turn object into string
16
+ => "\x00"
17
+ irb(main):005:0> obj2 = Sparsam::Deserializer.deserialize( MyStruct, serialized ) # deserialize string into obj
18
+ => #<MyStruct:0x007fa70e3ee998>
19
+ ```
@@ -0,0 +1,18 @@
1
+ require 'rubygems'
2
+ require 'mkmf'
3
+
4
+ $CFLAGS = " -fsigned-char -O3 -ggdb3 -mtune=native "
5
+ $CXXFLAGS = " -std=c++0x -O3 -ggdb3 -mtune=native -I./third-party/sparsepp "
6
+ if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new('2.0.0')
7
+ $CPPFLAGS += $CXXFLAGS
8
+ end
9
+
10
+ have_func("strlcpy", "string.h")
11
+ have_library("thrift")
12
+ libs = ['-lthrift']
13
+
14
+ libs.each do |lib|
15
+ $LOCAL_LIBS << "#{lib} "
16
+ end
17
+
18
+ create_makefile 'sparsam_native'
@@ -0,0 +1,96 @@
1
+ #include "serializer.h"
2
+ #include "stdio.h"
3
+ #include <ruby.h>
4
+
5
+ VALUE Sparsam = Qnil;
6
+ VALUE static_zero_array;
7
+ VALUE SparsamNativeError = Qnil;
8
+
9
+ ID intern_for_DEFAULT_VALUES;
10
+ ID intern_for_assign_defaults;
11
+ ID intern_for_assign_from_arg;
12
+
13
+ static void deallocate(void *data) { serializer_free(data); }
14
+
15
+ static VALUE allocate(VALUE klass) {
16
+ void *data = serializer_create();
17
+ return Data_Wrap_Struct(klass, NULL, deallocate, data);
18
+ }
19
+
20
+ static VALUE sparsam_init_bang(VALUE self) {
21
+ initialize_runtime_constants();
22
+ return self;
23
+ }
24
+
25
+ static VALUE initialize(VALUE self, VALUE type_arg, VALUE str_arg) {
26
+ void *self_data = NULL;
27
+ void *input_string = NULL;
28
+
29
+ Check_Type(type_arg, T_FIXNUM);
30
+ int prot = NUM2INT(type_arg);
31
+
32
+ Check_Type(str_arg, T_STRING);
33
+
34
+ uint32_t len = RSTRING_LEN(str_arg);
35
+ if (len != 0) {
36
+ input_string = calloc(len, sizeof(char));
37
+ memcpy(input_string, StringValuePtr(str_arg), len);
38
+ }
39
+
40
+ Data_Get_Struct(self, void *, self_data);
41
+ serializer_init(self_data, prot, input_string, len);
42
+
43
+ return self;
44
+ }
45
+
46
+ VALUE sparsam_struct_initialize(int argc, VALUE* argv, VALUE self) {
47
+ if (argc > 1) {
48
+ rb_raise(rb_eArgError, "wrong number of arguments (given %d, expected 0..1)", argc);
49
+ }
50
+
51
+ VALUE defaults = rb_const_get(rb_obj_class(self), intern_for_DEFAULT_VALUES);
52
+
53
+ if (defaults != Qnil) {
54
+ rb_funcall(self, intern_for_assign_defaults, 1, defaults);
55
+ }
56
+
57
+ if (argc == 1) {
58
+ rb_funcall(self, intern_for_assign_from_arg, 1, argv[0]);
59
+ }
60
+
61
+ if (rb_block_given_p()) {
62
+ rb_yield(self);
63
+ }
64
+
65
+ return Qnil;
66
+ }
67
+
68
+ void Init_sparsam_native() {
69
+ Sparsam = rb_define_module("Sparsam");
70
+ rb_define_singleton_method(Sparsam, "init!", sparsam_init_bang, 0);
71
+ rb_define_singleton_method(Sparsam, "cache_fields", cache_fields, 1);
72
+ rb_define_singleton_method(Sparsam, "validate", serializer_validate, 3);
73
+ VALUE SparsamSerializer = rb_define_class_under(Sparsam, "Serializer", rb_cObject);
74
+ SparsamNativeError =
75
+ rb_define_class_under(Sparsam, "Exception", rb_eStandardError);
76
+ rb_define_alloc_func(SparsamSerializer, allocate);
77
+ rb_define_method(SparsamSerializer, "initialize", initialize, 2);
78
+ rb_define_method(SparsamSerializer, "serialize", serializer_writeStruct, 2);
79
+ rb_define_method(SparsamSerializer, "deserialize", serializer_readStruct, 1);
80
+ rb_define_const(Sparsam, "CompactProtocol", INT2FIX(compact));
81
+ rb_define_const(Sparsam, "BinaryProtocol", INT2FIX(binary));
82
+ rb_define_const(Sparsam, "UNION", INT2FIX(t_union));
83
+ rb_define_const(Sparsam, "STRUCT", INT2FIX(t_struct));
84
+ rb_define_const(Sparsam, "NORMAL", INT2FIX(normal));
85
+ rb_define_const(Sparsam, "STRICT", INT2FIX(strict));
86
+ rb_define_const(Sparsam, "RECURSIVE", INT2FIX(recursive));
87
+
88
+ intern_for_DEFAULT_VALUES = rb_intern("DEFAULT_VALUES");
89
+ intern_for_assign_defaults = rb_intern("assign_defaults");
90
+ intern_for_assign_from_arg = rb_intern("assign_from_arg");
91
+
92
+
93
+ VALUE SparsamStructInitialization = rb_define_module_under(Sparsam, "StructInitialization");
94
+ rb_define_method(SparsamStructInitialization, "initialize", sparsam_struct_initialize, -1);
95
+ initialize_constants();
96
+ }
@@ -0,0 +1,716 @@
1
+ #include "serializer.h"
2
+ #include <functional>
3
+ #include <map>
4
+ #include <ruby/encoding.h>
5
+ #include <stdio.h>
6
+ #include <thrift/protocol/TBinaryProtocol.h>
7
+ #include <thrift/protocol/TCompactProtocol.h>
8
+ #include <boost/make_shared.hpp>
9
+ #include <vector>
10
+
11
+ using namespace std;
12
+ using ::apache::thrift::protocol::TType;
13
+ using ::apache::thrift::transport::TMemoryBuffer;
14
+ using namespace ::apache::thrift;
15
+
16
+ VALUE sym_for_class;
17
+ VALUE sym_for_binary;
18
+ VALUE sym_for_type;
19
+ VALUE sym_for_element;
20
+ VALUE sym_for_key;
21
+ VALUE sym_for_value;
22
+ VALUE sym_for_optional;
23
+ VALUE sym_for_name;
24
+ VALUE intern_for_FIELDS;
25
+ VALUE intern_for_new;
26
+ VALUE intern_for_keys;
27
+ VALUE intern_for_values;
28
+ ID intern_for_setfield_ivar;
29
+ VALUE intern_for_to_a;
30
+ VALUE klass_for_union;
31
+ VALUE klass_for_integer;
32
+ VALUE klass_for_float;
33
+ VALUE klass_for_string;
34
+ VALUE klass_for_hash;
35
+ VALUE klass_for_set;
36
+ VALUE klass_for_array;
37
+
38
+ extern VALUE Sparsam;
39
+ extern VALUE SparsamNativeError;
40
+ VALUE SparsamTypeMismatchError;
41
+ VALUE SparsamMissingMandatory;
42
+ VALUE SparsamUnionException;
43
+ VALUE SparsamUnknownTypeException;
44
+
45
+ KlassFieldsCache klassCache; // consider the memory leaked.
46
+ std::unordered_set<VALUE> unions;
47
+
48
+ void *serializer_create() { return (void *)(new ThriftSerializer()); }
49
+
50
+ void serializer_free(void *data) {
51
+ ThriftSerializer *ts = (ThriftSerializer *)(data);
52
+ delete ts;
53
+ }
54
+
55
+ void initialize_constants() {
56
+ sym_for_class = ID2SYM(rb_intern("class"));
57
+ sym_for_binary = ID2SYM(rb_intern("binary"));
58
+ sym_for_type = ID2SYM(rb_intern("type"));
59
+ sym_for_element = ID2SYM(rb_intern("element"));
60
+ sym_for_key = ID2SYM(rb_intern("key"));
61
+ sym_for_value = ID2SYM(rb_intern("value"));
62
+ sym_for_optional = ID2SYM(rb_intern("optional"));
63
+ sym_for_name = ID2SYM(rb_intern("name"));
64
+ intern_for_FIELDS = rb_intern("FIELDS");
65
+ intern_for_new = rb_intern("new");
66
+ intern_for_keys = rb_intern("keys");
67
+ intern_for_values = rb_intern("values");
68
+ intern_for_to_a = rb_intern("to_a");
69
+ intern_for_setfield_ivar = rb_intern("@setfield");
70
+ klass_for_set = rb_const_get_at(rb_cObject, rb_intern("Set"));
71
+ klass_for_integer = rb_const_get_at(rb_cObject, rb_intern("Integer"));
72
+ klass_for_float = rb_const_get_at(rb_cObject, rb_intern("Float"));
73
+ klass_for_string = rb_const_get_at(rb_cObject, rb_intern("String"));
74
+ klass_for_hash = rb_const_get_at(rb_cObject, rb_intern("Hash"));
75
+ klass_for_array = rb_const_get_at(rb_cObject, rb_intern("Array"));
76
+ }
77
+
78
+ void initialize_runtime_constants() {
79
+ klass_for_union = rb_const_get_at(Sparsam, rb_intern("Union"));
80
+ SparsamMissingMandatory = rb_const_get_at(Sparsam, rb_intern("MissingMandatory"));
81
+ SparsamTypeMismatchError = rb_const_get_at(Sparsam, rb_intern("TypeMismatch"));
82
+ SparsamUnionException = rb_const_get_at(Sparsam, rb_intern("UnionException"));
83
+ SparsamUnknownTypeException =
84
+ rb_const_get_at(Sparsam, rb_intern("UnknownTypeException"));
85
+ }
86
+
87
+ void serializer_init(void *serializer, int protocol, void *str_arg1,
88
+ uint32_t len) {
89
+ using ::boost::shared_ptr;
90
+ using ::apache::thrift::protocol::TProtocol;
91
+ using ::apache::thrift::protocol::TBinaryProtocol;
92
+ using ::apache::thrift::protocol::TCompactProtocol;
93
+
94
+ ThriftSerializer *ts = (ThriftSerializer *)(serializer);
95
+ shared_ptr<TMemoryBuffer> strBuffer;
96
+ if (str_arg1 != NULL) {
97
+ strBuffer = boost::make_shared<TMemoryBuffer>(
98
+ (uint8_t *)str_arg1,
99
+ len,
100
+ TMemoryBuffer::TAKE_OWNERSHIP
101
+ );
102
+ } else {
103
+ strBuffer = boost::make_shared<TMemoryBuffer>();
104
+ }
105
+ Proto proto = static_cast<Proto>(protocol);
106
+ if (proto == compact) {
107
+ ts->tprot = shared_ptr<TProtocol>(new TCompactProtocol(strBuffer));
108
+ } else if (proto == binary) {
109
+ ts->tprot = shared_ptr<TProtocol>(new TBinaryProtocol(strBuffer));
110
+ } else {
111
+ rb_raise(SparsamNativeError, "Unknown protocol %d", proto);
112
+ }
113
+ ts->tmb = strBuffer;
114
+ }
115
+
116
+ #define get_ts() \
117
+ void *self_data = NULL; \
118
+ Data_Get_Struct(self, void, self_data); \
119
+ ThriftSerializer *ts = (ThriftSerializer *)(self_data);
120
+
121
+ #define watch_for_texcept() try {
122
+
123
+ #define catch_thrift_and_reraise() \
124
+ } \
125
+ catch (::apache::thrift::TException e) { \
126
+ rb_raise(SparsamNativeError, "%s", e.what()); \
127
+ return Qnil; \
128
+ }
129
+
130
+
131
+ static inline VALUE make_ruby_string(const string &val) {
132
+ return rb_enc_str_new(val.c_str(), val.size(), rb_utf8_encoding());
133
+ }
134
+
135
+ static inline VALUE make_ruby_binary(const string &val) {
136
+ return rb_str_new(val.c_str(), val.size());
137
+ }
138
+
139
+ static inline VALUE make_ruby_bool(bool val) { return val ? Qtrue : Qfalse; }
140
+
141
+ void ThriftSerializer::skip_n_type(uint32_t n, TType ttype) {
142
+ for (uint32_t i = 0; i < n; i++) {
143
+ this->tprot->skip(ttype);
144
+ }
145
+ }
146
+
147
+ void ThriftSerializer::skip_n_pair(uint32_t n, TType type_a, TType type_b) {
148
+ for (uint32_t i = 0; i < n; i++) {
149
+ this->tprot->skip(type_a);
150
+ this->tprot->skip(type_b);
151
+ }
152
+ }
153
+
154
+ // Blatantly copied protobuf's design
155
+ // https://git.io/vHuUn
156
+ // CONVERT is new here, because we're targeting ruby
157
+ #define HANDLE_TYPE(TYPE, CPPTYPE, READ_METHOD, CONVERT) \
158
+ case protocol::T_##TYPE: { \
159
+ CPPTYPE value; \
160
+ this->tprot->read##READ_METHOD(value); \
161
+ ret = CONVERT(value); \
162
+ break; \
163
+ }
164
+
165
+ VALUE ThriftSerializer::readAny(TType ttype, FieldInfo *field_info) {
166
+ VALUE ret = Qnil;
167
+ switch (ttype) {
168
+
169
+ // Handle all the non-container types by marco
170
+ HANDLE_TYPE(I16, int16_t, I16, INT2FIX)
171
+ HANDLE_TYPE(I32, int32_t, I32, INT2FIX)
172
+ HANDLE_TYPE(I64, int64_t, I64, LL2NUM)
173
+ HANDLE_TYPE(BOOL, bool, Bool, make_ruby_bool)
174
+ HANDLE_TYPE(DOUBLE, double, Double, DBL2NUM)
175
+ HANDLE_TYPE(BYTE, int8_t, Byte, INT2FIX)
176
+
177
+ case protocol::T_STRING: {
178
+ string value;
179
+ if (field_info->isBinaryString) { // if (field_info[:binary])
180
+ this->tprot->readBinary(value);
181
+ ret = make_ruby_binary(value);
182
+ } else {
183
+ this->tprot->readString(value);
184
+ ret = make_ruby_string(value);
185
+ }
186
+ break;
187
+ }
188
+
189
+ case protocol::T_LIST: {
190
+ TType element_type;
191
+ uint32_t size;
192
+
193
+ this->tprot->readListBegin(element_type, size);
194
+ if (field_info->elementType == NULL ||
195
+ element_type != field_info->elementType->ftype) {
196
+ this->skip_n_type(size, element_type);
197
+ break;
198
+ }
199
+ ret = rb_ary_new2(size);
200
+
201
+ for (uint32_t i = 0; i < size; i++) {
202
+ rb_ary_store(ret, i,
203
+ this->readAny(element_type, field_info->elementType));
204
+ }
205
+ this->tprot->readListEnd();
206
+
207
+ break;
208
+ }
209
+
210
+ case protocol::T_SET: {
211
+ TType element_type;
212
+ uint32_t size;
213
+
214
+ this->tprot->readSetBegin(element_type, size);
215
+ if (field_info->elementType == NULL ||
216
+ element_type != field_info->elementType->ftype) {
217
+ this->skip_n_type(size, element_type);
218
+ break;
219
+ }
220
+ VALUE ary = rb_ary_new2(size);
221
+
222
+ for (uint32_t i = 0; i < size; i++) {
223
+ rb_ary_store(ary, i,
224
+ this->readAny(element_type, field_info->elementType));
225
+ }
226
+ ret = rb_class_new_instance(1, &ary, klass_for_set);
227
+ this->tprot->readSetEnd();
228
+ break;
229
+ }
230
+
231
+ case protocol::T_STRUCT: {
232
+ string cname;
233
+ this->tprot->readStructBegin(cname);
234
+ if (unions.count(field_info->klass) == 1) {
235
+ ret = this->readUnion(field_info->klass);
236
+ } else {
237
+ ret = this->readStruct(field_info->klass);
238
+ }
239
+ this->tprot->readStructEnd();
240
+ break;
241
+ }
242
+
243
+ case protocol::T_MAP: {
244
+ TType key_type, value_type;
245
+ uint32_t size;
246
+ VALUE k, v;
247
+
248
+ this->tprot->readMapBegin(key_type, value_type, size);
249
+
250
+ if (field_info->keyType == NULL ||
251
+ field_info->elementType == NULL) { // no type check to be consistent
252
+ skip_n_pair(size, key_type, value_type);
253
+ break;
254
+ }
255
+
256
+ ret = rb_hash_new();
257
+ for (uint32_t i = 0; i < size; i++) {
258
+ k = this->readAny(key_type, field_info->keyType);
259
+ v = this->readAny(value_type, field_info->elementType);
260
+ rb_hash_aset(ret, k, v);
261
+ }
262
+ this->tprot->readMapEnd();
263
+ break;
264
+ }
265
+
266
+ default:
267
+ this->tprot->skip(ttype);
268
+ rb_raise(SparsamUnknownTypeException, "Received unknown type with id: %d", ttype);
269
+ break;
270
+ }
271
+
272
+ return ret;
273
+ }
274
+
275
+ #undef HANDLE_TYPE
276
+
277
+ VALUE ThriftSerializer::readStruct(VALUE klass) {
278
+
279
+ string cname;
280
+ FieldBegin fieldBegin;
281
+ TType typeId;
282
+ FieldInfo *fieldInfo;
283
+ VALUE ret = rb_class_new_instance(0, NULL, klass); // ret = &klass.new
284
+ auto fields = FindOrCreateFieldInfoMap(klass);
285
+
286
+ while (true) {
287
+ this->tprot->readFieldBegin(cname, fieldBegin.ftype, fieldBegin.fid);
288
+ if (fieldBegin.ftype == protocol::T_STOP) {
289
+ break;
290
+ }
291
+ auto iter = fields->find(fieldBegin.fid);
292
+
293
+ if (iter == fields->end()) {
294
+ this->tprot->skip(fieldBegin.ftype);
295
+ this->tprot->readFieldEnd();
296
+ continue;
297
+ }
298
+
299
+ fieldInfo = iter->second;
300
+
301
+ typeId = fieldInfo->ftype;
302
+
303
+ if (typeId != fieldBegin.ftype) {
304
+ rb_raise(SparsamTypeMismatchError, "Type Mismatch. Defenition: %d, Actual: %d",
305
+ fieldBegin.ftype, typeId);
306
+ }
307
+
308
+ VALUE rb_value = this->readAny(fieldBegin.ftype, iter->second);
309
+ if (!NIL_P(rb_value)) {
310
+ rb_ivar_set(ret, fieldInfo->ivarName, rb_value);
311
+ }
312
+
313
+ this->tprot->readFieldEnd();
314
+ }
315
+ return ret;
316
+ }
317
+
318
+ VALUE ThriftSerializer::readUnion(VALUE klass) {
319
+ string cname;
320
+ FieldBegin fieldBegin;
321
+
322
+ VALUE ret = rb_class_new_instance(0, NULL, klass); // ret = &klass.new
323
+ auto fields = FindOrCreateFieldInfoMap(klass);
324
+
325
+ VALUE key, rb_value;
326
+
327
+ this->tprot->readFieldBegin(cname, fieldBegin.ftype, fieldBegin.fid);
328
+ if (fieldBegin.ftype == protocol::T_STOP) {
329
+ return ret;
330
+ }
331
+
332
+ auto iter = fields->find(fieldBegin.fid);
333
+
334
+ if (iter == fields->end()) {
335
+ this->tprot->skip(fieldBegin.ftype);
336
+ this->tprot->readFieldEnd();
337
+ return ret;
338
+ }
339
+
340
+ rb_value = this->readAny(fieldBegin.ftype, iter->second);
341
+ if (!NIL_P(rb_value)) {
342
+ rb_ivar_set(ret, intern_for_setfield_ivar, iter->second->symName);
343
+ rb_ivar_set(ret, iter->second->ivarName, rb_value);
344
+ }
345
+
346
+ this->tprot->readFieldEnd();
347
+ this->tprot->readFieldBegin(cname, fieldBegin.ftype, fieldBegin.fid);
348
+
349
+ if (fieldBegin.ftype != protocol::T_STOP) {
350
+ rb_raise(SparsamUnionException, "More than one element in union.");
351
+ return Qnil;
352
+ }
353
+
354
+ return ret;
355
+ }
356
+
357
+ // for the unary `+` before lambda:
358
+ // https://stackoverflow.com/a/18889029/4944625
359
+ // explicit cast to work with signature: (int (*)(...))
360
+ #define HASH_FOREACH_BEGIN(hash, ...) \
361
+ void *_args[] = {__VA_ARGS__}; \
362
+ rb_hash_foreach(hash, (int (*)(ANYARGS))(+[](VALUE k, VALUE v, VALUE args) { \
363
+ void **argv = (void **) args;
364
+
365
+ #define HASH_FOREACH_RET() return (int)ST_CONTINUE;
366
+
367
+ #define HASH_FOREACH_ABORT() return (int)ST_STOP;
368
+
369
+ #define HASH_FOREACH_END() \
370
+ HASH_FOREACH_RET() \
371
+ }), (VALUE) _args);
372
+
373
+ #define HANDLE_TYPE(TYPE, WRITE_METHOD, CONVERT) \
374
+ case protocol::T_##TYPE: { \
375
+ this->tprot->write##WRITE_METHOD(CONVERT(actual)); \
376
+ break; \
377
+ }
378
+
379
+ void ThriftSerializer::writeAny(TType ttype, FieldInfo *field_info,
380
+ VALUE actual) {
381
+ switch (ttype) {
382
+ HANDLE_TYPE(I16, I16, NUM2SHORT)
383
+ HANDLE_TYPE(I32, I32, NUM2INT)
384
+ HANDLE_TYPE(I64, I64, NUM2LL)
385
+ HANDLE_TYPE(BOOL, Bool, RTEST)
386
+ HANDLE_TYPE(DOUBLE, Double, NUM2DBL)
387
+ HANDLE_TYPE(BYTE, Byte, NUM2SHORT)
388
+
389
+ case protocol::T_STRING: {
390
+ string data = string(StringValuePtr(actual), RSTRING_LEN(actual));
391
+ if (field_info->isBinaryString) {
392
+ this->tprot->writeBinary(data);
393
+ } else {
394
+ this->tprot->writeString(data);
395
+ }
396
+ break;
397
+ }
398
+
399
+ case protocol::T_LIST: {
400
+ long length = RARRAY_LEN(actual);
401
+ TType elem = field_info->elementType->ftype;
402
+ this->tprot->writeListBegin(elem, static_cast<size_t>(length));
403
+ for (long i = 0; i < length; i++) {
404
+ this->writeAny(elem, field_info->elementType, rb_ary_entry(actual, i));
405
+ }
406
+ this->tprot->writeListEnd();
407
+ break;
408
+ }
409
+
410
+ case protocol::T_SET: {
411
+ VALUE ary = rb_funcall(actual, intern_for_to_a, 0);
412
+ long length = RARRAY_LEN(ary);
413
+ TType elem = field_info->elementType->ftype;
414
+ this->tprot->writeListBegin(elem, static_cast<size_t>(length));
415
+ for (long i = 0; i < length; i++) {
416
+ this->writeAny(elem, field_info->elementType, rb_ary_entry(ary, i));
417
+ }
418
+ this->tprot->writeListEnd();
419
+ break;
420
+ }
421
+
422
+ case protocol::T_MAP: {
423
+ TType keyTType = field_info->keyType->ftype,
424
+ valueTType = field_info->elementType->ftype;
425
+ this->tprot->writeMapBegin(keyTType, valueTType,
426
+ static_cast<size_t>(RHASH_SIZE(actual)));
427
+ HASH_FOREACH_BEGIN(actual, this, field_info)
428
+ ThriftSerializer *that = (ThriftSerializer *)argv[0];
429
+ FieldInfo *field_info = (FieldInfo *)argv[1];
430
+ that->writeAny(field_info->keyType->ftype, field_info->keyType, k);
431
+ that->writeAny(field_info->elementType->ftype, field_info->elementType, v);
432
+ HASH_FOREACH_END()
433
+ this->tprot->writeMapEnd();
434
+ break;
435
+ }
436
+
437
+ case protocol::T_STRUCT: {
438
+ static const string cname = "";
439
+ this->tprot->writeStructBegin(cname.c_str());
440
+ this->writeStruct(field_info->klass, actual);
441
+ this->tprot->writeFieldStop();
442
+ this->tprot->writeStructEnd();
443
+ break;
444
+ }
445
+
446
+ default: { break; }
447
+ }
448
+ }
449
+
450
+ #undef HANDLE_TYPE
451
+
452
+ void ThriftSerializer::writeStruct(VALUE klass, VALUE data) {
453
+ static const string cname = "";
454
+ FieldBegin fieldBegin;
455
+ FieldInfo *fieldInfo;
456
+ auto fields = FindOrCreateFieldInfoMap(klass);
457
+ for (auto const & entry : *fields) {
458
+ fieldBegin.fid = entry.first;
459
+ fieldInfo = entry.second;
460
+ fieldBegin.ftype = fieldInfo->ftype;
461
+ VALUE actual = rb_ivar_get(data, fieldInfo->ivarName);
462
+ if (!NIL_P(actual)) {
463
+ this->tprot->writeFieldBegin(cname.c_str(), fieldBegin.ftype, fieldBegin.fid);
464
+ this->writeAny(fieldBegin.ftype, entry.second, actual);
465
+ this->tprot->writeFieldEnd();
466
+ }
467
+ }
468
+ }
469
+
470
+ VALUE serializer_writeStruct(VALUE self, VALUE klass, VALUE data) {
471
+ watch_for_texcept() get_ts();
472
+ static const string cname = "";
473
+ ts->tprot->writeStructBegin(cname.c_str());
474
+ ts->writeStruct(klass, data);
475
+ ts->tprot->writeFieldStop();
476
+ ts->tprot->writeStructEnd();
477
+ string retval = ts->tmb->getBufferAsString();
478
+ return rb_str_new(retval.c_str(), retval.size());
479
+ catch_thrift_and_reraise();
480
+ }
481
+
482
+ VALUE serializer_readStruct(VALUE self, VALUE klass) {
483
+ watch_for_texcept() get_ts();
484
+ string cname;
485
+ VALUE ret;
486
+ ts->tprot->readStructBegin(cname);
487
+ ret = ts->readStruct(klass);
488
+ ts->tprot->readStructEnd();
489
+ return ret;
490
+ catch_thrift_and_reraise();
491
+ }
492
+
493
+ static inline void raise_type_mismatch() {
494
+ rb_raise(SparsamTypeMismatchError, "Type mismatch in field data");
495
+ }
496
+
497
+ bool validateArray(FieldInfo *type, VALUE arr, bool recursive) {
498
+ long length = RARRAY_LEN(arr);
499
+ for (long i = 0; i < length; i++) {
500
+ if (!validateAny(type, rb_ary_entry(arr, i), recursive)) {
501
+ rb_raise(SparsamTypeMismatchError, "Type mismatch in container element");
502
+ return false;
503
+ }
504
+ }
505
+ return true;
506
+ }
507
+
508
+ #define TEST_RB_VAL_FOR_CLASS(VAL, KLASS) \
509
+ if (!RTEST(rb_obj_is_kind_of(VAL, KLASS))) { \
510
+ raise_type_mismatch(); \
511
+ ret = false; \
512
+ }
513
+
514
+ #define HANDLE_TYPE(TYPE, KLASS) \
515
+ case protocol::T_##TYPE: { \
516
+ TEST_RB_VAL_FOR_CLASS(val, KLASS) \
517
+ break; \
518
+ }
519
+
520
+ bool validateAny(FieldInfo *type, VALUE val, bool recursive) {
521
+ bool ret = true;
522
+ switch (type->ftype) {
523
+
524
+ HANDLE_TYPE(BYTE, klass_for_integer)
525
+ HANDLE_TYPE(I16, klass_for_integer)
526
+ HANDLE_TYPE(I32, klass_for_integer)
527
+ HANDLE_TYPE(I64, klass_for_integer)
528
+ HANDLE_TYPE(DOUBLE, klass_for_float)
529
+ HANDLE_TYPE(STRING, klass_for_string)
530
+
531
+ case protocol::T_STRUCT: {
532
+ TEST_RB_VAL_FOR_CLASS(val, type->klass)
533
+ if (ret && recursive) {
534
+ ret = validateStruct(type->klass, val, true, recursive);
535
+ }
536
+ break;
537
+ }
538
+
539
+ case protocol::T_SET: {
540
+ TEST_RB_VAL_FOR_CLASS(val, klass_for_set)
541
+ if (ret) {
542
+ VALUE ary = rb_funcall(val, intern_for_to_a, 0);
543
+ ret = validateArray(type->elementType, ary, recursive);
544
+ }
545
+ break;
546
+ }
547
+ case protocol::T_LIST: {
548
+ TEST_RB_VAL_FOR_CLASS(val, klass_for_array)
549
+ if (ret) {
550
+ ret = validateArray(type->elementType, val, recursive);
551
+ }
552
+ break;
553
+ }
554
+
555
+ case protocol::T_MAP: {
556
+ TEST_RB_VAL_FOR_CLASS(val, klass_for_hash)
557
+ if (ret) {
558
+ bool flag = true;
559
+ HASH_FOREACH_BEGIN(val, &flag, type->keyType, type->elementType,
560
+ &recursive)
561
+ bool *flag = (bool *)argv[0], *recursive = (bool *)argv[3];
562
+ FieldInfo *field_info_key = (FieldInfo *)argv[1];
563
+ FieldInfo *field_info_value = (FieldInfo *)argv[2];
564
+ if (!validateAny(field_info_key, k, *recursive) ||
565
+ !validateAny(field_info_value, v, *recursive)) {
566
+ *flag = false;
567
+ HASH_FOREACH_ABORT()
568
+ }
569
+ HASH_FOREACH_RET()
570
+ HASH_FOREACH_END()
571
+ }
572
+ break;
573
+ }
574
+
575
+ default: {
576
+ rb_raise(SparsamUnknownTypeException, "Unknown type received.");
577
+ ret = false;
578
+ break;
579
+ }
580
+ }
581
+ return ret;
582
+ }
583
+ #undef HANDLE_TYPE
584
+ #undef TEST_RB_VAL_FOR_CLASS
585
+
586
+ bool validateStruct(VALUE klass, VALUE data, bool validateContainerTypes,
587
+ bool recursive) {
588
+ if (!RTEST(rb_obj_is_kind_of(data, klass))) {
589
+ rb_raise(SparsamTypeMismatchError, "Wrong type of struct given for data");
590
+ return false;
591
+ }
592
+ auto fields = FindOrCreateFieldInfoMap(klass);
593
+ for (auto const &entry : *fields) {
594
+ VALUE val = rb_ivar_get(data, entry.second->ivarName);
595
+ if (NIL_P(val)) {
596
+ if (!entry.second->isOptional) {
597
+ rb_raise(SparsamMissingMandatory, "Missing: fieldID %d", entry.first);
598
+ return false;
599
+ }
600
+ continue;
601
+ }
602
+ if (validateContainerTypes && !validateAny(entry.second, val, recursive)) {
603
+ return false;
604
+ }
605
+ }
606
+ return true;
607
+ }
608
+
609
+ VALUE serializer_validate(VALUE self, VALUE klass, VALUE data,
610
+ VALUE strictness) {
611
+ switch (static_cast<ValidateStrictness>(FIX2INT(strictness))) {
612
+ case strict: {
613
+ return validateStruct(klass, data, true, false) ? Qtrue : Qfalse;
614
+ }
615
+ case recursive: {
616
+ return validateStruct(klass, data, true, true) ? Qtrue : Qfalse;
617
+ }
618
+ default: {
619
+ return validateStruct(klass, data, false, false) ? Qtrue : Qfalse;
620
+ }
621
+ }
622
+ }
623
+
624
+ #define R_FIX_TO_TTYPE(x) (static_cast<TType>(FIX2INT(x)))
625
+
626
+ FieldInfoMap *FindOrCreateFieldInfoMap(VALUE klass) {
627
+ auto iter = klassCache.find(klass);
628
+ if (iter == klassCache.end()) {
629
+ if (RTEST(rb_class_inherited_p(klass, klass_for_union))) {
630
+ unions.insert(klass);
631
+ }
632
+ auto ret = CreateFieldInfoMap(klass);
633
+ klassCache[klass] = ret;
634
+ return ret;
635
+ } else {
636
+ return iter->second;
637
+ }
638
+ }
639
+
640
+ ID field_name_to_ivar_id(VALUE str_name) {
641
+ if (str_name != Qnil) {
642
+ return rb_intern_str(rb_str_concat(rb_str_new2("@"), str_name));
643
+ } else {
644
+ return NULL;
645
+ }
646
+ }
647
+
648
+ VALUE field_name_to_sym(VALUE str_name) {
649
+ if (str_name != Qnil) {
650
+ return ID2SYM(rb_intern_str(str_name));
651
+ } else {
652
+ return NULL;
653
+ }
654
+ }
655
+
656
+ // each FieldInfoMap has multiple FieldInfos
657
+ FieldInfo *CreateFieldInfo(VALUE field_map_entry) {
658
+ FieldInfo *fieldInfo = new FieldInfo();
659
+ fieldInfo->ftype =
660
+ R_FIX_TO_TTYPE(rb_hash_aref(field_map_entry, sym_for_type));
661
+ fieldInfo->isOptional =
662
+ RTEST(rb_hash_aref(field_map_entry, sym_for_optional));
663
+ fieldInfo->ivarName = field_name_to_ivar_id(rb_hash_aref(field_map_entry, sym_for_name));
664
+ fieldInfo->symName = field_name_to_sym(rb_hash_aref(field_map_entry, sym_for_name));
665
+ switch (fieldInfo->ftype) {
666
+ case protocol::T_STRING: {
667
+ if (RTEST(rb_hash_aref(field_map_entry, sym_for_binary))) {
668
+ fieldInfo->isBinaryString = true;
669
+ }
670
+ break;
671
+ }
672
+ case protocol::T_STRUCT: {
673
+ fieldInfo->klass = rb_hash_aref(field_map_entry, sym_for_class);
674
+ break;
675
+ }
676
+ case protocol::T_LIST:
677
+ case protocol::T_SET: {
678
+ fieldInfo->elementType =
679
+ CreateFieldInfo(rb_hash_aref(field_map_entry, sym_for_element));
680
+ break;
681
+ }
682
+ case protocol::T_MAP: {
683
+ fieldInfo->keyType =
684
+ CreateFieldInfo(rb_hash_aref(field_map_entry, sym_for_key));
685
+ fieldInfo->elementType =
686
+ CreateFieldInfo(rb_hash_aref(field_map_entry, sym_for_value));
687
+ break;
688
+ }
689
+ default:
690
+ break;
691
+ }
692
+ return fieldInfo;
693
+ }
694
+
695
+ // each klass has a FieldInfoMap
696
+ FieldInfoMap *CreateFieldInfoMap(VALUE klass) {
697
+ FieldInfoMap *fieldMap = new FieldInfoMap();
698
+ VALUE field_map = rb_const_get_at(klass, intern_for_FIELDS);
699
+
700
+ HASH_FOREACH_BEGIN(field_map, fieldMap)
701
+ FieldInfoMap *fieldMap = (FieldInfoMap *)argv[0];
702
+ (*fieldMap)[FIX2INT(k)] = CreateFieldInfo(v);
703
+ HASH_FOREACH_END()
704
+ return fieldMap;
705
+ }
706
+
707
+ VALUE cache_fields(VALUE self, VALUE klass) {
708
+ FindOrCreateFieldInfoMap(klass);
709
+ return Qnil;
710
+ }
711
+
712
+ #undef HASH_FOREACH_BEGIN
713
+ #undef HASH_FOEACH_RET
714
+ #undef HASH_FOREACH_END
715
+
716
+ #undef R_FIX_TO_TTYPE