sparsam 0.2.5 → 0.2.6
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.
- checksums.yaml +4 -4
- data/ext/ruby_hooks.c +16 -12
- data/ext/serializer.cpp +283 -264
- data/ext/serializer.h +41 -20
- data/spec/sparsam_spec.rb +38 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5df1feed622f050f564cd17dd088ae575a544b1b26511f19b2bb8cec3d46d34a
|
4
|
+
data.tar.gz: 85622057c2fd20ef6af4e325a62a61858f6c0d73d8575b3a42cbf3871c0df86b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f872a492fd9870cac0a19bd58fb458f02a7d0bc619e0a892aa7a6415c2a2d91d161634f390167f33ee67ed870ba42bafea5678f616f40d413b6e2e23c7e181f7
|
7
|
+
data.tar.gz: bbb8fd020a8bced074581523ee6bab24669ad88f244797957deac3b774a57d3c00425e6975ae9ba40925c04b56e1e91f6ff49b5bedec7bfd0b690cc3bfc18a0d
|
data/ext/ruby_hooks.c
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
#include "stdio.h"
|
2
1
|
#include <ruby.h>
|
3
2
|
#include <ruby/intern.h>
|
4
3
|
#include "serializer.h"
|
4
|
+
#include "stdio.h"
|
5
5
|
|
6
6
|
VALUE Sparsam = Qnil;
|
7
7
|
VALUE static_zero_array;
|
@@ -11,10 +11,10 @@ ID intern_for_DEFAULT_VALUES;
|
|
11
11
|
ID intern_for_assign_defaults;
|
12
12
|
ID intern_for_assign_from_arg;
|
13
13
|
|
14
|
-
static void deallocate(void
|
14
|
+
static void deallocate(void* data) { serializer_free(data); }
|
15
15
|
|
16
16
|
static VALUE allocate(VALUE klass) {
|
17
|
-
void
|
17
|
+
void* data = serializer_create();
|
18
18
|
return Data_Wrap_Struct(klass, NULL, deallocate, data);
|
19
19
|
}
|
20
20
|
|
@@ -24,8 +24,8 @@ static VALUE sparsam_init_bang(VALUE self) {
|
|
24
24
|
}
|
25
25
|
|
26
26
|
static VALUE initialize(VALUE self, VALUE type_arg, VALUE str_arg) {
|
27
|
-
void
|
28
|
-
void
|
27
|
+
void* self_data = NULL;
|
28
|
+
void* input_string = NULL;
|
29
29
|
|
30
30
|
Check_Type(type_arg, T_FIXNUM);
|
31
31
|
int prot = NUM2INT(type_arg);
|
@@ -38,7 +38,7 @@ static VALUE initialize(VALUE self, VALUE type_arg, VALUE str_arg) {
|
|
38
38
|
memcpy(input_string, StringValuePtr(str_arg), len);
|
39
39
|
}
|
40
40
|
|
41
|
-
Data_Get_Struct(self, void
|
41
|
+
Data_Get_Struct(self, void*, self_data);
|
42
42
|
serializer_init(self_data, prot, input_string, len);
|
43
43
|
|
44
44
|
return self;
|
@@ -46,7 +46,8 @@ static VALUE initialize(VALUE self, VALUE type_arg, VALUE str_arg) {
|
|
46
46
|
|
47
47
|
VALUE sparsam_struct_initialize(int argc, VALUE* argv, VALUE self) {
|
48
48
|
if (argc > 1) {
|
49
|
-
rb_raise(rb_eArgError,
|
49
|
+
rb_raise(rb_eArgError,
|
50
|
+
"wrong number of arguments (given %d, expected 0..1)", argc);
|
50
51
|
}
|
51
52
|
|
52
53
|
VALUE defaults = rb_const_get(rb_obj_class(self), intern_for_DEFAULT_VALUES);
|
@@ -70,14 +71,16 @@ void Init_sparsam_native() {
|
|
70
71
|
Sparsam = rb_define_module("Sparsam");
|
71
72
|
rb_define_singleton_method(Sparsam, "init!", sparsam_init_bang, 0);
|
72
73
|
rb_define_singleton_method(Sparsam, "cache_fields", cache_fields, 1);
|
73
|
-
VALUE SparsamSerializer =
|
74
|
+
VALUE SparsamSerializer =
|
75
|
+
rb_define_class_under(Sparsam, "Serializer", rb_cObject);
|
74
76
|
SparsamNativeError =
|
75
77
|
rb_define_class_under(Sparsam, "Exception", rb_eStandardError);
|
76
78
|
rb_define_alloc_func(SparsamSerializer, allocate);
|
77
79
|
rb_define_method(SparsamSerializer, "initialize", initialize, 2);
|
78
80
|
rb_define_method(SparsamSerializer, "serialize", serializer_writeStruct, 2);
|
79
81
|
rb_define_method(SparsamSerializer, "deserialize", serializer_readStruct, 1);
|
80
|
-
rb_define_method(SparsamSerializer, "deserializeUnion", serializer_readUnion,
|
82
|
+
rb_define_method(SparsamSerializer, "deserializeUnion", serializer_readUnion,
|
83
|
+
1);
|
81
84
|
rb_define_const(Sparsam, "CompactProtocol", INT2FIX(compact));
|
82
85
|
rb_define_const(Sparsam, "BinaryProtocol", INT2FIX(binary));
|
83
86
|
rb_define_const(Sparsam, "UNION", INT2FIX(t_union));
|
@@ -90,8 +93,9 @@ void Init_sparsam_native() {
|
|
90
93
|
intern_for_assign_defaults = rb_intern("assign_defaults");
|
91
94
|
intern_for_assign_from_arg = rb_intern("assign_from_arg");
|
92
95
|
|
93
|
-
|
94
|
-
|
95
|
-
rb_define_method(SparsamStructInitialization, "initialize",
|
96
|
+
VALUE SparsamStructInitialization =
|
97
|
+
rb_define_module_under(Sparsam, "StructInitialization");
|
98
|
+
rb_define_method(SparsamStructInitialization, "initialize",
|
99
|
+
sparsam_struct_initialize, -1);
|
96
100
|
initialize_constants();
|
97
101
|
}
|
data/ext/serializer.cpp
CHANGED
@@ -2,15 +2,15 @@ extern "C" {
|
|
2
2
|
#include <ruby.h>
|
3
3
|
#include <ruby/intern.h>
|
4
4
|
}
|
5
|
-
#include "serializer.h"
|
6
|
-
#include <functional>
|
7
|
-
#include <map>
|
8
5
|
#include <ruby/encoding.h>
|
9
6
|
#include <stdio.h>
|
10
7
|
#include <thrift/protocol/TBinaryProtocol.h>
|
11
8
|
#include <thrift/protocol/TCompactProtocol.h>
|
12
9
|
#include <boost/make_shared.hpp>
|
10
|
+
#include <functional>
|
11
|
+
#include <map>
|
13
12
|
#include <vector>
|
13
|
+
#include "serializer.h"
|
14
14
|
|
15
15
|
using namespace std;
|
16
16
|
using ::apache::thrift::protocol::TType;
|
@@ -48,7 +48,7 @@ VALUE SparsamUnknownTypeException;
|
|
48
48
|
|
49
49
|
VALUE SetClass;
|
50
50
|
|
51
|
-
KlassFieldsCache klassCache;
|
51
|
+
KlassFieldsCache klassCache; // consider the memory leaked.
|
52
52
|
std::unordered_set<VALUE> unions;
|
53
53
|
|
54
54
|
void *serializer_create() { return (void *)(new ThriftSerializer()); }
|
@@ -83,8 +83,10 @@ void initialize_constants() {
|
|
83
83
|
|
84
84
|
void initialize_runtime_constants() {
|
85
85
|
klass_for_union = rb_const_get_at(Sparsam, rb_intern("Union"));
|
86
|
-
SparsamMissingMandatory =
|
87
|
-
|
86
|
+
SparsamMissingMandatory =
|
87
|
+
rb_const_get_at(Sparsam, rb_intern("MissingMandatory"));
|
88
|
+
SparsamTypeMismatchError =
|
89
|
+
rb_const_get_at(Sparsam, rb_intern("TypeMismatch"));
|
88
90
|
SparsamUnionException = rb_const_get_at(Sparsam, rb_intern("UnionException"));
|
89
91
|
SparsamUnknownTypeException =
|
90
92
|
rb_const_get_at(Sparsam, rb_intern("UnknownTypeException"));
|
@@ -93,19 +95,16 @@ void initialize_runtime_constants() {
|
|
93
95
|
|
94
96
|
void serializer_init(void *serializer, int protocol, void *str_arg1,
|
95
97
|
uint32_t len) {
|
96
|
-
using ::boost::shared_ptr;
|
97
|
-
using ::apache::thrift::protocol::TProtocol;
|
98
98
|
using ::apache::thrift::protocol::TBinaryProtocol;
|
99
99
|
using ::apache::thrift::protocol::TCompactProtocol;
|
100
|
+
using ::apache::thrift::protocol::TProtocol;
|
101
|
+
using ::boost::shared_ptr;
|
100
102
|
|
101
103
|
ThriftSerializer *ts = (ThriftSerializer *)(serializer);
|
102
104
|
shared_ptr<TMemoryBuffer> strBuffer;
|
103
105
|
if (str_arg1 != NULL) {
|
104
106
|
strBuffer = boost::make_shared<TMemoryBuffer>(
|
105
|
-
|
106
|
-
len,
|
107
|
-
TMemoryBuffer::TAKE_OWNERSHIP
|
108
|
-
);
|
107
|
+
(uint8_t *)str_arg1, len, TMemoryBuffer::TAKE_OWNERSHIP);
|
109
108
|
} else {
|
110
109
|
strBuffer = boost::make_shared<TMemoryBuffer>();
|
111
110
|
}
|
@@ -120,21 +119,19 @@ void serializer_init(void *serializer, int protocol, void *str_arg1,
|
|
120
119
|
ts->tmb = strBuffer;
|
121
120
|
}
|
122
121
|
|
123
|
-
#define get_ts()
|
124
|
-
void *self_data = NULL;
|
125
|
-
Data_Get_Struct(self, void, self_data);
|
122
|
+
#define get_ts() \
|
123
|
+
void *self_data = NULL; \
|
124
|
+
Data_Get_Struct(self, void, self_data); \
|
126
125
|
ThriftSerializer *ts = (ThriftSerializer *)(self_data);
|
127
126
|
|
128
127
|
#define watch_for_texcept() try {
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
return Qnil; \
|
128
|
+
#define catch_thrift_and_reraise() \
|
129
|
+
} \
|
130
|
+
catch (::apache::thrift::TException e) { \
|
131
|
+
rb_raise(SparsamNativeError, "%s", e.what()); \
|
132
|
+
return Qnil; \
|
135
133
|
}
|
136
134
|
|
137
|
-
|
138
135
|
static inline VALUE make_ruby_string(const string &val) {
|
139
136
|
return rb_enc_str_new(val.c_str(), val.size(), rb_utf8_encoding());
|
140
137
|
}
|
@@ -143,43 +140,58 @@ static inline VALUE make_ruby_binary(const string &val) {
|
|
143
140
|
return rb_str_new(val.c_str(), val.size());
|
144
141
|
}
|
145
142
|
|
146
|
-
static void raise_exc_with_struct_and_field_names(
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
VALUE field_sym) {
|
143
|
+
static void raise_exc_with_struct_and_field_names(VALUE exc_class,
|
144
|
+
VALUE msg_prefix,
|
145
|
+
VALUE outer_struct_class,
|
146
|
+
VALUE field_sym) {
|
151
147
|
VALUE struct_name = rb_class_name(outer_struct_class);
|
152
148
|
VALUE field_name = rb_sym_to_s(field_sym);
|
153
149
|
|
154
|
-
VALUE msg = rb_sprintf("%s (in %s#%s)",
|
155
|
-
|
156
|
-
RSTRING_PTR(struct_name),
|
157
|
-
RSTRING_PTR(field_name));
|
150
|
+
VALUE msg = rb_sprintf("%s (in %s#%s)", RSTRING_PTR(msg_prefix),
|
151
|
+
RSTRING_PTR(struct_name), RSTRING_PTR(field_name));
|
158
152
|
VALUE args[3] = {msg, struct_name, field_name};
|
159
153
|
VALUE e = rb_class_new_instance(3, args, exc_class);
|
160
154
|
rb_exc_raise(e);
|
161
155
|
}
|
162
156
|
|
163
|
-
static void raise_type_mismatch(VALUE outer_struct, VALUE field_sym
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
157
|
+
static void raise_type_mismatch(VALUE outer_struct, VALUE field_sym,
|
158
|
+
const char *expected, VALUE actual) {
|
159
|
+
VALUE actual_name = rb_class_name(CLASS_OF(actual));
|
160
|
+
VALUE msg = rb_sprintf(
|
161
|
+
"Mismatched type (expected to be compatible with: %s, found: %s)",
|
162
|
+
expected, RSTRING_PTR(actual_name));
|
163
|
+
|
164
|
+
raise_exc_with_struct_and_field_names(SparsamTypeMismatchError, msg,
|
165
|
+
CLASS_OF(outer_struct), field_sym);
|
166
|
+
}
|
167
|
+
|
168
|
+
static void raise_type_mismatch(VALUE outer_struct, VALUE field_sym, int ttype,
|
169
|
+
VALUE actual) {
|
170
|
+
raise_type_mismatch(outer_struct, field_sym, TTypeName((size_t)ttype).c_str(),
|
171
|
+
actual);
|
169
172
|
}
|
170
173
|
|
171
|
-
static
|
172
|
-
|
174
|
+
static void raise_type_mismatch(VALUE outer_struct, VALUE field_sym,
|
175
|
+
VALUE klass, VALUE actual) {
|
176
|
+
VALUE expected_name = rb_class_name(klass);
|
177
|
+
raise_type_mismatch(outer_struct, field_sym, RSTRING_PTR(expected_name),
|
178
|
+
actual);
|
179
|
+
}
|
180
|
+
|
181
|
+
static inline long raise_type_mismatch_as_value(VALUE outer_struct,
|
182
|
+
VALUE field_sym, int ttype,
|
183
|
+
VALUE actual) {
|
184
|
+
raise_type_mismatch(outer_struct, field_sym, ttype, actual);
|
173
185
|
return 0;
|
174
186
|
}
|
175
187
|
|
176
|
-
static inline void Sparsam_Check_Type(VALUE x, int t, VALUE outer_struct,
|
188
|
+
static inline void Sparsam_Check_Type(VALUE x, int t, VALUE outer_struct,
|
189
|
+
VALUE field_sym) {
|
177
190
|
if (!(RB_TYPE_P(x, t))) {
|
178
|
-
raise_type_mismatch(outer_struct, field_sym);
|
191
|
+
raise_type_mismatch(outer_struct, field_sym, t, x);
|
179
192
|
}
|
180
193
|
}
|
181
194
|
|
182
|
-
|
183
195
|
static inline VALUE make_ruby_bool(bool val) { return val ? Qtrue : Qfalse; }
|
184
196
|
|
185
197
|
void ThriftSerializer::skip_n_type(uint32_t n, TType ttype) {
|
@@ -198,18 +210,17 @@ void ThriftSerializer::skip_n_pair(uint32_t n, TType type_a, TType type_b) {
|
|
198
210
|
// Blatantly copied protobuf's design
|
199
211
|
// https://git.io/vHuUn
|
200
212
|
// CONVERT is new here, because we're targeting ruby
|
201
|
-
#define HANDLE_TYPE(TYPE, CPPTYPE, READ_METHOD, CONVERT)
|
202
|
-
case protocol::T_##TYPE: {
|
203
|
-
CPPTYPE value;
|
204
|
-
this->tprot->read##READ_METHOD(value);
|
205
|
-
ret = CONVERT(value);
|
206
|
-
break;
|
213
|
+
#define HANDLE_TYPE(TYPE, CPPTYPE, READ_METHOD, CONVERT) \
|
214
|
+
case protocol::T_##TYPE: { \
|
215
|
+
CPPTYPE value; \
|
216
|
+
this->tprot->read##READ_METHOD(value); \
|
217
|
+
ret = CONVERT(value); \
|
218
|
+
break; \
|
207
219
|
}
|
208
220
|
|
209
221
|
VALUE ThriftSerializer::readAny(TType ttype, FieldInfo *field_info) {
|
210
222
|
VALUE ret = Qnil;
|
211
223
|
switch (ttype) {
|
212
|
-
|
213
224
|
// Handle all the non-container types by marco
|
214
225
|
HANDLE_TYPE(I16, int16_t, I16, INT2FIX)
|
215
226
|
HANDLE_TYPE(I32, int32_t, I32, INT2FIX)
|
@@ -218,99 +229,100 @@ VALUE ThriftSerializer::readAny(TType ttype, FieldInfo *field_info) {
|
|
218
229
|
HANDLE_TYPE(DOUBLE, double, Double, DBL2NUM)
|
219
230
|
HANDLE_TYPE(BYTE, int8_t, Byte, INT2FIX)
|
220
231
|
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
232
|
+
case protocol::T_STRING: {
|
233
|
+
string value;
|
234
|
+
if (field_info->isBinaryString) { // if (field_info[:binary])
|
235
|
+
this->tprot->readBinary(value);
|
236
|
+
ret = make_ruby_binary(value);
|
237
|
+
} else {
|
238
|
+
this->tprot->readString(value);
|
239
|
+
ret = make_ruby_string(value);
|
240
|
+
}
|
241
|
+
break;
|
229
242
|
}
|
230
|
-
break;
|
231
|
-
}
|
232
243
|
|
233
|
-
|
234
|
-
|
235
|
-
|
244
|
+
case protocol::T_LIST: {
|
245
|
+
TType element_type;
|
246
|
+
uint32_t size;
|
236
247
|
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
248
|
+
this->tprot->readListBegin(element_type, size);
|
249
|
+
if (field_info->elementType == NULL ||
|
250
|
+
element_type != field_info->elementType->ftype) {
|
251
|
+
this->skip_n_type(size, element_type);
|
252
|
+
break;
|
253
|
+
}
|
254
|
+
ret = rb_ary_new2(size);
|
244
255
|
|
245
|
-
|
246
|
-
|
247
|
-
|
256
|
+
for (uint32_t i = 0; i < size; i++) {
|
257
|
+
rb_ary_store(ret, i,
|
258
|
+
this->readAny(element_type, field_info->elementType));
|
259
|
+
}
|
260
|
+
this->tprot->readListEnd();
|
261
|
+
|
262
|
+
break;
|
248
263
|
}
|
249
|
-
this->tprot->readListEnd();
|
250
264
|
|
251
|
-
|
252
|
-
|
265
|
+
case protocol::T_SET: {
|
266
|
+
TType element_type;
|
267
|
+
uint32_t size;
|
253
268
|
|
254
|
-
|
255
|
-
|
256
|
-
|
269
|
+
this->tprot->readSetBegin(element_type, size);
|
270
|
+
if (field_info->elementType == NULL ||
|
271
|
+
element_type != field_info->elementType->ftype) {
|
272
|
+
this->skip_n_type(size, element_type);
|
273
|
+
break;
|
274
|
+
}
|
275
|
+
VALUE ary = rb_ary_new2(size);
|
257
276
|
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
277
|
+
for (uint32_t i = 0; i < size; i++) {
|
278
|
+
rb_ary_store(ary, i,
|
279
|
+
this->readAny(element_type, field_info->elementType));
|
280
|
+
}
|
281
|
+
ret = rb_class_new_instance(1, &ary, klass_for_set);
|
282
|
+
this->tprot->readSetEnd();
|
262
283
|
break;
|
263
284
|
}
|
264
|
-
VALUE ary = rb_ary_new2(size);
|
265
285
|
|
266
|
-
|
267
|
-
|
268
|
-
|
286
|
+
case protocol::T_STRUCT: {
|
287
|
+
string cname;
|
288
|
+
this->tprot->readStructBegin(cname);
|
289
|
+
if (unions.count(field_info->klass) == 1) {
|
290
|
+
ret = this->readUnion(field_info->klass);
|
291
|
+
} else {
|
292
|
+
ret = this->readStruct(field_info->klass);
|
293
|
+
}
|
294
|
+
this->tprot->readStructEnd();
|
295
|
+
break;
|
269
296
|
}
|
270
|
-
ret = rb_class_new_instance(1, &ary, klass_for_set);
|
271
|
-
this->tprot->readSetEnd();
|
272
|
-
break;
|
273
|
-
}
|
274
297
|
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
ret = this->readUnion(field_info->klass);
|
280
|
-
} else {
|
281
|
-
ret = this->readStruct(field_info->klass);
|
282
|
-
}
|
283
|
-
this->tprot->readStructEnd();
|
284
|
-
break;
|
285
|
-
}
|
298
|
+
case protocol::T_MAP: {
|
299
|
+
TType key_type, value_type;
|
300
|
+
uint32_t size;
|
301
|
+
VALUE k, v;
|
286
302
|
|
287
|
-
|
288
|
-
TType key_type, value_type;
|
289
|
-
uint32_t size;
|
290
|
-
VALUE k, v;
|
303
|
+
this->tprot->readMapBegin(key_type, value_type, size);
|
291
304
|
|
292
|
-
|
305
|
+
if (field_info->keyType == NULL ||
|
306
|
+
field_info->elementType == NULL) { // no type check to be consistent
|
307
|
+
skip_n_pair(size, key_type, value_type);
|
308
|
+
break;
|
309
|
+
}
|
293
310
|
|
294
|
-
|
295
|
-
|
296
|
-
|
311
|
+
ret = rb_hash_new();
|
312
|
+
for (uint32_t i = 0; i < size; i++) {
|
313
|
+
k = this->readAny(key_type, field_info->keyType);
|
314
|
+
v = this->readAny(value_type, field_info->elementType);
|
315
|
+
rb_hash_aset(ret, k, v);
|
316
|
+
}
|
317
|
+
this->tprot->readMapEnd();
|
297
318
|
break;
|
298
319
|
}
|
299
320
|
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
}
|
306
|
-
this->tprot->readMapEnd();
|
307
|
-
break;
|
308
|
-
}
|
309
|
-
|
310
|
-
default:
|
311
|
-
this->tprot->skip(ttype);
|
312
|
-
rb_raise(SparsamUnknownTypeException, "Received unknown type with id: %d", ttype);
|
313
|
-
break;
|
321
|
+
default:
|
322
|
+
this->tprot->skip(ttype);
|
323
|
+
rb_raise(SparsamUnknownTypeException, "Received unknown type with id: %d",
|
324
|
+
ttype);
|
325
|
+
break;
|
314
326
|
}
|
315
327
|
|
316
328
|
return ret;
|
@@ -319,12 +331,11 @@ VALUE ThriftSerializer::readAny(TType ttype, FieldInfo *field_info) {
|
|
319
331
|
#undef HANDLE_TYPE
|
320
332
|
|
321
333
|
VALUE ThriftSerializer::readStruct(VALUE klass) {
|
322
|
-
|
323
334
|
string cname;
|
324
335
|
FieldBegin fieldBegin;
|
325
336
|
TType typeId;
|
326
337
|
FieldInfo *fieldInfo;
|
327
|
-
VALUE ret = rb_class_new_instance(0, NULL, klass);
|
338
|
+
VALUE ret = rb_class_new_instance(0, NULL, klass); // ret = &klass.new
|
328
339
|
auto fields = FindOrCreateFieldInfoMap(klass);
|
329
340
|
|
330
341
|
while (true) {
|
@@ -346,10 +357,11 @@ VALUE ThriftSerializer::readStruct(VALUE klass) {
|
|
346
357
|
|
347
358
|
if (typeId != fieldBegin.ftype) {
|
348
359
|
raise_exc_with_struct_and_field_names(
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
360
|
+
SparsamTypeMismatchError,
|
361
|
+
rb_sprintf("Mismatched type (definition: %s, found: %s)",
|
362
|
+
TTypeName(fieldBegin.ftype).c_str(),
|
363
|
+
TTypeName(typeId).c_str()),
|
364
|
+
klass, fieldInfo->symName);
|
353
365
|
}
|
354
366
|
|
355
367
|
VALUE rb_value = this->readAny(fieldBegin.ftype, iter->second);
|
@@ -366,7 +378,7 @@ VALUE ThriftSerializer::readUnion(VALUE klass) {
|
|
366
378
|
string cname;
|
367
379
|
FieldBegin fieldBegin;
|
368
380
|
|
369
|
-
VALUE ret = rb_class_new_instance(0, NULL, klass);
|
381
|
+
VALUE ret = rb_class_new_instance(0, NULL, klass); // ret = &klass.new
|
370
382
|
auto fields = FindOrCreateFieldInfoMap(klass);
|
371
383
|
|
372
384
|
VALUE key, rb_value;
|
@@ -404,23 +416,22 @@ VALUE ThriftSerializer::readUnion(VALUE klass) {
|
|
404
416
|
// for the unary `+` before lambda:
|
405
417
|
// https://stackoverflow.com/a/18889029/4944625
|
406
418
|
// explicit cast to work with signature: (int (*)(...))
|
407
|
-
#define HASH_FOREACH_BEGIN(hash, ...)
|
408
|
-
void *_args[] = {__VA_ARGS__};
|
419
|
+
#define HASH_FOREACH_BEGIN(hash, ...) \
|
420
|
+
void *_args[] = {__VA_ARGS__}; \
|
409
421
|
rb_hash_foreach(hash, (int (*)(ANYARGS))(+[](VALUE k, VALUE v, VALUE args) { \
|
410
422
|
void **argv = (void **) args;
|
411
|
-
|
412
423
|
#define HASH_FOREACH_RET() return (int)ST_CONTINUE;
|
413
424
|
|
414
425
|
#define HASH_FOREACH_ABORT() return (int)ST_STOP;
|
415
426
|
|
416
|
-
#define HASH_FOREACH_END()
|
417
|
-
HASH_FOREACH_RET()
|
427
|
+
#define HASH_FOREACH_END() \
|
428
|
+
HASH_FOREACH_RET() \
|
418
429
|
}), (VALUE) _args);
|
419
430
|
|
420
|
-
#define HANDLE_TYPE(TYPE, WRITE_METHOD, CONVERT)
|
421
|
-
case protocol::T_##TYPE: {
|
422
|
-
this->tprot->write##WRITE_METHOD(CONVERT);
|
423
|
-
break;
|
431
|
+
#define HANDLE_TYPE(TYPE, WRITE_METHOD, CONVERT) \
|
432
|
+
case protocol::T_##TYPE: { \
|
433
|
+
this->tprot->write##WRITE_METHOD(CONVERT); \
|
434
|
+
break; \
|
424
435
|
}
|
425
436
|
|
426
437
|
static inline long raise_bignum_range_error_as_value() {
|
@@ -428,19 +439,21 @@ static inline long raise_bignum_range_error_as_value() {
|
|
428
439
|
return 0;
|
429
440
|
}
|
430
441
|
|
431
|
-
#define CONVERT_FIXNUM(CONVERT)
|
432
|
-
((FIXNUM_P(actual))
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
442
|
+
#define CONVERT_FIXNUM(CONVERT, INTENDED) \
|
443
|
+
((FIXNUM_P(actual)) \
|
444
|
+
? CONVERT(actual) \
|
445
|
+
: ((RB_TYPE_P(actual, T_BIGNUM)) \
|
446
|
+
? raise_bignum_range_error_as_value() \
|
447
|
+
: raise_type_mismatch_as_value(outer_struct, field_sym, \
|
448
|
+
INTENDED, actual)))
|
449
|
+
|
450
|
+
#define CONVERT_I64 \
|
451
|
+
((FIXNUM_P(actual)) \
|
452
|
+
? (LONG_LONG)FIX2LONG(actual) \
|
453
|
+
: ((RB_TYPE_P(actual, T_BIGNUM)) \
|
454
|
+
? rb_big2ll(actual) \
|
455
|
+
: raise_type_mismatch_as_value(outer_struct, field_sym, \
|
456
|
+
protocol::T_I64, actual)))
|
444
457
|
|
445
458
|
#ifdef RB_FLOAT_TYPE_P
|
446
459
|
#define FLOAT_TYPE_P(x) RB_FLOAT_TYPE_P(x)
|
@@ -448,19 +461,21 @@ static inline long raise_bignum_range_error_as_value() {
|
|
448
461
|
#define FLOAT_TYPE_P(x) RB_TYPE_P(x, T_FLOAT)
|
449
462
|
#endif
|
450
463
|
|
451
|
-
#define CONVERT_FLOAT(CONVERT)
|
452
|
-
((FLOAT_TYPE_P(actual))
|
453
|
-
|
454
|
-
|
464
|
+
#define CONVERT_FLOAT(CONVERT) \
|
465
|
+
((FLOAT_TYPE_P(actual)) \
|
466
|
+
? CONVERT(actual) \
|
467
|
+
: raise_type_mismatch_as_value(outer_struct, field_sym, \
|
468
|
+
protocol::T_DOUBLE, actual))
|
455
469
|
|
456
|
-
static inline bool convertBool(VALUE actual, VALUE outer_struct,
|
470
|
+
static inline bool convertBool(VALUE actual, VALUE outer_struct,
|
471
|
+
VALUE field_sym) {
|
457
472
|
switch (actual) {
|
458
473
|
case Qtrue:
|
459
474
|
return true;
|
460
475
|
case Qfalse:
|
461
476
|
return false;
|
462
477
|
default:
|
463
|
-
raise_type_mismatch(outer_struct, field_sym);
|
478
|
+
raise_type_mismatch(outer_struct, field_sym, protocol::T_BOOL, actual);
|
464
479
|
}
|
465
480
|
|
466
481
|
/* unreachable */
|
@@ -477,7 +492,7 @@ static inline char byte_convert(VALUE x) {
|
|
477
492
|
short s = SHORT_CONVERT(x);
|
478
493
|
|
479
494
|
if (s <= 127 && s >= -128) {
|
480
|
-
return (char)
|
495
|
+
return (char)s;
|
481
496
|
} else {
|
482
497
|
rb_raise(rb_eRangeError, "integer %d out of range for char", s);
|
483
498
|
}
|
@@ -487,89 +502,94 @@ static inline char byte_convert(VALUE x) {
|
|
487
502
|
}
|
488
503
|
|
489
504
|
void ThriftSerializer::writeAny(TType ttype, FieldInfo *field_info,
|
490
|
-
VALUE actual, VALUE outer_struct,
|
505
|
+
VALUE actual, VALUE outer_struct,
|
506
|
+
VALUE field_sym) {
|
491
507
|
switch (ttype) {
|
492
|
-
HANDLE_TYPE(I16, I16, CONVERT_FIXNUM(SHORT_CONVERT))
|
493
|
-
HANDLE_TYPE(I32, I32, CONVERT_FIXNUM(FIX2INT))
|
508
|
+
HANDLE_TYPE(I16, I16, CONVERT_FIXNUM(SHORT_CONVERT, protocol::T_I16))
|
509
|
+
HANDLE_TYPE(I32, I32, CONVERT_FIXNUM(FIX2INT, protocol::T_I32))
|
494
510
|
HANDLE_TYPE(I64, I64, CONVERT_I64)
|
495
511
|
HANDLE_TYPE(BOOL, Bool, convertBool(actual, outer_struct, field_sym))
|
496
512
|
HANDLE_TYPE(DOUBLE, Double, CONVERT_FLOAT(NUM2DBL))
|
497
|
-
HANDLE_TYPE(BYTE, Byte, CONVERT_FIXNUM(byte_convert))
|
513
|
+
HANDLE_TYPE(BYTE, Byte, CONVERT_FIXNUM(byte_convert, protocol::T_BYTE))
|
498
514
|
|
499
|
-
|
500
|
-
|
515
|
+
case protocol::T_STRING: {
|
516
|
+
Sparsam_Check_Type(actual, T_STRING, outer_struct, field_sym);
|
501
517
|
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
518
|
+
string data = string(StringValuePtr(actual), RSTRING_LEN(actual));
|
519
|
+
if (field_info->isBinaryString) {
|
520
|
+
this->tprot->writeBinary(data);
|
521
|
+
} else {
|
522
|
+
this->tprot->writeString(data);
|
523
|
+
}
|
524
|
+
break;
|
507
525
|
}
|
508
|
-
break;
|
509
|
-
}
|
510
526
|
|
511
|
-
|
512
|
-
|
527
|
+
case protocol::T_LIST: {
|
528
|
+
Sparsam_Check_Type(actual, T_ARRAY, outer_struct, field_sym);
|
513
529
|
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
530
|
+
long length = RARRAY_LEN(actual);
|
531
|
+
TType elem = field_info->elementType->ftype;
|
532
|
+
this->tprot->writeListBegin(elem, static_cast<size_t>(length));
|
533
|
+
for (long i = 0; i < length; i++) {
|
534
|
+
this->writeAny(elem, field_info->elementType, rb_ary_entry(actual, i),
|
535
|
+
outer_struct, field_sym);
|
536
|
+
}
|
537
|
+
this->tprot->writeListEnd();
|
538
|
+
break;
|
519
539
|
}
|
520
|
-
this->tprot->writeListEnd();
|
521
|
-
break;
|
522
|
-
}
|
523
540
|
|
524
|
-
|
525
|
-
|
526
|
-
|
541
|
+
case protocol::T_SET: {
|
542
|
+
if (rb_class_real(CLASS_OF(actual)) != SetClass) {
|
543
|
+
raise_type_mismatch(outer_struct, field_sym, protocol::T_SET, actual);
|
544
|
+
}
|
545
|
+
|
546
|
+
VALUE ary = rb_funcall(actual, intern_for_to_a, 0);
|
547
|
+
long length = RARRAY_LEN(ary);
|
548
|
+
TType elem = field_info->elementType->ftype;
|
549
|
+
this->tprot->writeListBegin(elem, static_cast<size_t>(length));
|
550
|
+
for (long i = 0; i < length; i++) {
|
551
|
+
this->writeAny(elem, field_info->elementType, rb_ary_entry(ary, i),
|
552
|
+
outer_struct, field_sym);
|
553
|
+
}
|
554
|
+
this->tprot->writeListEnd();
|
555
|
+
break;
|
527
556
|
}
|
528
557
|
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
this->
|
558
|
+
case protocol::T_MAP: {
|
559
|
+
Sparsam_Check_Type(actual, T_HASH, outer_struct, field_sym);
|
560
|
+
|
561
|
+
TType keyTType = field_info->keyType->ftype,
|
562
|
+
valueTType = field_info->elementType->ftype;
|
563
|
+
this->tprot->writeMapBegin(keyTType, valueTType,
|
564
|
+
static_cast<size_t>(RHASH_SIZE(actual)));
|
565
|
+
HASH_FOREACH_BEGIN(actual, this, field_info, &outer_struct, &field_sym)
|
566
|
+
ThriftSerializer *that = (ThriftSerializer *)argv[0];
|
567
|
+
FieldInfo *field_info = (FieldInfo *)argv[1];
|
568
|
+
VALUE *outer_struct = (VALUE *)argv[2];
|
569
|
+
VALUE *field_sym = (VALUE *)argv[3];
|
570
|
+
that->writeAny(field_info->keyType->ftype, field_info->keyType, k,
|
571
|
+
*outer_struct, *field_sym);
|
572
|
+
that->writeAny(field_info->elementType->ftype, field_info->elementType, v,
|
573
|
+
*outer_struct, *field_sym);
|
574
|
+
HASH_FOREACH_END()
|
575
|
+
this->tprot->writeMapEnd();
|
576
|
+
break;
|
535
577
|
}
|
536
|
-
this->tprot->writeListEnd();
|
537
|
-
break;
|
538
|
-
}
|
539
578
|
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
valueTType = field_info->elementType->ftype;
|
545
|
-
this->tprot->writeMapBegin(keyTType, valueTType,
|
546
|
-
static_cast<size_t>(RHASH_SIZE(actual)));
|
547
|
-
HASH_FOREACH_BEGIN(actual, this, field_info, &outer_struct, &field_sym)
|
548
|
-
ThriftSerializer *that = (ThriftSerializer *)argv[0];
|
549
|
-
FieldInfo *field_info = (FieldInfo *)argv[1];
|
550
|
-
VALUE *outer_struct = (VALUE *)argv[2];
|
551
|
-
VALUE *field_sym = (VALUE *)argv[3];
|
552
|
-
that->writeAny(field_info->keyType->ftype, field_info->keyType, k, *outer_struct, *field_sym);
|
553
|
-
that->writeAny(field_info->elementType->ftype, field_info->elementType, v, *outer_struct, *field_sym);
|
554
|
-
HASH_FOREACH_END()
|
555
|
-
this->tprot->writeMapEnd();
|
556
|
-
break;
|
557
|
-
}
|
579
|
+
case protocol::T_STRUCT: {
|
580
|
+
if (rb_class_real(CLASS_OF(actual)) != field_info->klass) {
|
581
|
+
raise_type_mismatch(outer_struct, field_sym, field_info->klass, actual);
|
582
|
+
}
|
558
583
|
|
559
|
-
|
560
|
-
|
561
|
-
|
584
|
+
static const string cname = "";
|
585
|
+
this->tprot->writeStructBegin(cname.c_str());
|
586
|
+
this->writeStruct(field_info->klass, actual);
|
587
|
+
this->tprot->writeFieldStop();
|
588
|
+
this->tprot->writeStructEnd();
|
589
|
+
break;
|
562
590
|
}
|
563
591
|
|
564
|
-
|
565
|
-
this->tprot->writeStructBegin(cname.c_str());
|
566
|
-
this->writeStruct(field_info->klass, actual);
|
567
|
-
this->tprot->writeFieldStop();
|
568
|
-
this->tprot->writeStructEnd();
|
569
|
-
break;
|
570
|
-
}
|
571
|
-
|
572
|
-
default: { break; }
|
592
|
+
default: { break; }
|
573
593
|
}
|
574
594
|
}
|
575
595
|
|
@@ -582,10 +602,8 @@ static bool checkRequiredFields(VALUE klass, VALUE data) {
|
|
582
602
|
VALUE val = rb_ivar_get(data, entry.second->ivarName);
|
583
603
|
if (NIL_P(val)) {
|
584
604
|
raise_exc_with_struct_and_field_names(
|
585
|
-
|
586
|
-
|
587
|
-
klass,
|
588
|
-
entry.second->symName);
|
605
|
+
SparsamMissingMandatory, rb_str_new2("Required field missing"),
|
606
|
+
klass, entry.second->symName);
|
589
607
|
return false;
|
590
608
|
}
|
591
609
|
}
|
@@ -604,14 +622,16 @@ void ThriftSerializer::writeStruct(VALUE klass, VALUE data) {
|
|
604
622
|
return;
|
605
623
|
}
|
606
624
|
|
607
|
-
for (auto const &
|
625
|
+
for (auto const &entry : *fields) {
|
608
626
|
fieldBegin.fid = entry.first;
|
609
627
|
fieldInfo = entry.second;
|
610
628
|
fieldBegin.ftype = fieldInfo->ftype;
|
611
629
|
VALUE actual = rb_ivar_get(data, fieldInfo->ivarName);
|
612
630
|
if (!NIL_P(actual)) {
|
613
|
-
this->tprot->writeFieldBegin(cname.c_str(), fieldBegin.ftype,
|
614
|
-
|
631
|
+
this->tprot->writeFieldBegin(cname.c_str(), fieldBegin.ftype,
|
632
|
+
fieldBegin.fid);
|
633
|
+
this->writeAny(fieldBegin.ftype, entry.second, actual, data,
|
634
|
+
fieldInfo->symName);
|
615
635
|
this->tprot->writeFieldEnd();
|
616
636
|
}
|
617
637
|
}
|
@@ -625,13 +645,10 @@ VALUE serializer_writeStruct(VALUE self, VALUE klass, VALUE data) {
|
|
625
645
|
VALUE actual_name = rb_class_name(CLASS_OF(data));
|
626
646
|
|
627
647
|
raise_exc_with_struct_and_field_names(
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
RSTRING_PTR(actual_name)),
|
633
|
-
CLASS_OF(data),
|
634
|
-
ID2SYM(rb_intern("(root)")));
|
648
|
+
SparsamTypeMismatchError,
|
649
|
+
rb_sprintf("Mismatched type passed to serialize (expected: %s got: %s)",
|
650
|
+
RSTRING_PTR(expected_name), RSTRING_PTR(actual_name)),
|
651
|
+
CLASS_OF(data), ID2SYM(rb_intern("(root)")));
|
635
652
|
|
636
653
|
RB_GC_GUARD(expected_name);
|
637
654
|
RB_GC_GUARD(actual_name);
|
@@ -708,34 +725,36 @@ FieldInfo *CreateFieldInfo(VALUE field_map_entry) {
|
|
708
725
|
R_FIX_TO_TTYPE(rb_hash_aref(field_map_entry, sym_for_type));
|
709
726
|
fieldInfo->isOptional =
|
710
727
|
RTEST(rb_hash_aref(field_map_entry, sym_for_optional));
|
711
|
-
fieldInfo->ivarName =
|
712
|
-
|
728
|
+
fieldInfo->ivarName =
|
729
|
+
field_name_to_ivar_id(rb_hash_aref(field_map_entry, sym_for_name));
|
730
|
+
fieldInfo->symName =
|
731
|
+
field_name_to_sym(rb_hash_aref(field_map_entry, sym_for_name));
|
713
732
|
switch (fieldInfo->ftype) {
|
714
|
-
|
715
|
-
|
716
|
-
|
733
|
+
case protocol::T_STRING: {
|
734
|
+
if (RTEST(rb_hash_aref(field_map_entry, sym_for_binary))) {
|
735
|
+
fieldInfo->isBinaryString = true;
|
736
|
+
}
|
737
|
+
break;
|
717
738
|
}
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
default:
|
738
|
-
break;
|
739
|
+
case protocol::T_STRUCT: {
|
740
|
+
fieldInfo->klass = rb_hash_aref(field_map_entry, sym_for_class);
|
741
|
+
break;
|
742
|
+
}
|
743
|
+
case protocol::T_LIST:
|
744
|
+
case protocol::T_SET: {
|
745
|
+
fieldInfo->elementType =
|
746
|
+
CreateFieldInfo(rb_hash_aref(field_map_entry, sym_for_element));
|
747
|
+
break;
|
748
|
+
}
|
749
|
+
case protocol::T_MAP: {
|
750
|
+
fieldInfo->keyType =
|
751
|
+
CreateFieldInfo(rb_hash_aref(field_map_entry, sym_for_key));
|
752
|
+
fieldInfo->elementType =
|
753
|
+
CreateFieldInfo(rb_hash_aref(field_map_entry, sym_for_value));
|
754
|
+
break;
|
755
|
+
}
|
756
|
+
default:
|
757
|
+
break;
|
739
758
|
}
|
740
759
|
return fieldInfo;
|
741
760
|
}
|
data/ext/serializer.h
CHANGED
@@ -18,9 +18,9 @@ enum TOType {
|
|
18
18
|
|
19
19
|
enum ValidateStrictness { normal = 0, strict = 1, recursive = 2 };
|
20
20
|
|
21
|
-
void serializer_free(void
|
22
|
-
void
|
23
|
-
void serializer_init(void
|
21
|
+
void serializer_free(void* data);
|
22
|
+
void* serializer_create();
|
23
|
+
void serializer_init(void* serializer, int protocol, void* str_arg1,
|
24
24
|
uint32_t len);
|
25
25
|
|
26
26
|
VALUE serializer_readStruct(VALUE self, VALUE klass);
|
@@ -36,18 +36,36 @@ void initialize_constants();
|
|
36
36
|
void initialize_runtime_constants();
|
37
37
|
|
38
38
|
#ifdef __cplusplus
|
39
|
-
}
|
39
|
+
} // end extern "C"
|
40
40
|
|
41
|
+
#include <thrift/protocol/TProtocol.h>
|
42
|
+
#include <thrift/transport/TBufferTransports.h>
|
41
43
|
#include <boost/shared_ptr.hpp>
|
42
44
|
#include <map>
|
43
45
|
#include <string>
|
44
|
-
#include <thrift/protocol/TProtocol.h>
|
45
|
-
#include <thrift/transport/TBufferTransports.h>
|
46
46
|
#include <unordered_set>
|
47
47
|
#include "third-party/sparsepp/sparsepp/spp.h"
|
48
48
|
|
49
49
|
using ::apache::thrift::protocol::TType;
|
50
50
|
|
51
|
+
// transposed from:
|
52
|
+
// https://github.com/apache/thrift/blob/0.10.0/lib/cpp/src/thrift/protocol/TProtocol.h#L176
|
53
|
+
// with: https://gist.github.com/andyfangdz/d4d52daa9f8a75223e76e92657036bb0
|
54
|
+
// fun fact: it's not sorted there
|
55
|
+
const std::string TTypeNames[] = {
|
56
|
+
"T_STOP", "T_VOID", "T_BOOL", "T_BYTE or T_I08",
|
57
|
+
"T_DOUBLE", "T_UNKNOWN", "T_I16", "T_UNKNOWN",
|
58
|
+
"T_I32", "T_U64", "T_I64", "T_STRING or T_UTF7",
|
59
|
+
"T_STRUCT", "T_MAP", "T_SET", "T_LIST",
|
60
|
+
"T_UTF8", "T_UTF16"};
|
61
|
+
|
62
|
+
const size_t TTypeMaxID = sizeof(TTypeNames) / sizeof(TTypeNames[0]);
|
63
|
+
|
64
|
+
const std::string TTypeUnknown = "T_UNKNOWN";
|
65
|
+
|
66
|
+
#define TTypeName(typeId) \
|
67
|
+
(typeId < TTypeMaxID ? TTypeNames[typeId] : TTypeUnknown)
|
68
|
+
|
51
69
|
typedef uint16_t FieldIdIndex;
|
52
70
|
typedef uint16_t KlassIndex;
|
53
71
|
|
@@ -60,38 +78,41 @@ typedef struct FieldBegin {
|
|
60
78
|
|
61
79
|
typedef struct FieldInfo {
|
62
80
|
TType ftype;
|
63
|
-
VALUE klass;
|
64
|
-
ID ivarName;
|
65
|
-
VALUE symName;
|
81
|
+
VALUE klass; // set if TTYPE is struct or union
|
82
|
+
ID ivarName; // set if field is on struct
|
83
|
+
VALUE symName; // set if field is on struct/union
|
66
84
|
bool isOptional;
|
67
85
|
bool isBinaryString;
|
68
|
-
FieldInfo
|
69
|
-
FieldInfo
|
86
|
+
FieldInfo* elementType; // element of list or set, or map
|
87
|
+
FieldInfo* keyType; // type of key in maps
|
70
88
|
} FieldInfo;
|
71
89
|
|
72
|
-
typedef std::map<FieldID, FieldInfo
|
73
|
-
typedef spp::sparse_hash_map<VALUE, FieldInfoMap
|
90
|
+
typedef std::map<FieldID, FieldInfo*> FieldInfoMap;
|
91
|
+
typedef spp::sparse_hash_map<VALUE, FieldInfoMap*> KlassFieldsCache;
|
74
92
|
|
75
93
|
class ThriftSerializer {
|
76
|
-
public:
|
94
|
+
public:
|
77
95
|
ThriftSerializer(){};
|
96
|
+
// clang-format off
|
78
97
|
boost::shared_ptr< ::apache::thrift::protocol::TProtocol > tprot;
|
79
98
|
boost::shared_ptr< ::apache::thrift::transport::TMemoryBuffer > tmb;
|
99
|
+
// clang-format on
|
80
100
|
|
81
101
|
VALUE readStruct(VALUE klass);
|
82
102
|
VALUE readUnion(VALUE klass);
|
83
103
|
void writeStruct(VALUE klass, VALUE data);
|
84
104
|
|
85
|
-
private:
|
86
|
-
VALUE readAny(TType ttype, FieldInfo
|
87
|
-
void writeAny(TType ttype, FieldInfo
|
105
|
+
private:
|
106
|
+
VALUE readAny(TType ttype, FieldInfo* field_info);
|
107
|
+
void writeAny(TType ttype, FieldInfo* field_info, VALUE data,
|
108
|
+
VALUE outer_struct, VALUE field_sym);
|
88
109
|
void skip_n_type(uint32_t n, TType ttype);
|
89
110
|
void skip_n_pair(uint32_t n, TType type_a, TType type_b);
|
90
111
|
};
|
91
112
|
|
92
|
-
FieldInfoMap
|
93
|
-
FieldInfo
|
94
|
-
FieldInfoMap
|
113
|
+
FieldInfoMap* FindOrCreateFieldInfoMap(VALUE klass);
|
114
|
+
FieldInfo* CreateFieldInfo(VALUE field_map_entry);
|
115
|
+
FieldInfoMap* CreateFieldInfoMap(VALUE klass);
|
95
116
|
|
96
117
|
#endif
|
97
118
|
#endif
|
data/spec/sparsam_spec.rb
CHANGED
@@ -227,6 +227,44 @@ describe 'Sparsam' do
|
|
227
227
|
e.field_name.should == "id_i32"
|
228
228
|
end
|
229
229
|
|
230
|
+
it "includes additional information on serializing mismatched types" do
|
231
|
+
data = MiniRequired.new
|
232
|
+
data.id_i32 = "not an i32"
|
233
|
+
|
234
|
+
e = nil
|
235
|
+
begin
|
236
|
+
data.serialize
|
237
|
+
rescue Sparsam::TypeMismatch => exception
|
238
|
+
e = exception
|
239
|
+
end
|
240
|
+
e.message.should include("T_I32", "String")
|
241
|
+
|
242
|
+
data = EveryType.new
|
243
|
+
data.a_struct = EveryType.new
|
244
|
+
|
245
|
+
e = nil
|
246
|
+
begin
|
247
|
+
data.serialize
|
248
|
+
rescue Sparsam::TypeMismatch => exception
|
249
|
+
e = exception
|
250
|
+
end
|
251
|
+
e.message.should include("US", "EveryType")
|
252
|
+
end
|
253
|
+
|
254
|
+
it "includes additional information on deserializing mismatched types" do
|
255
|
+
data = NotSS.new
|
256
|
+
data.id_s = "not an i32"
|
257
|
+
|
258
|
+
serialized = data.serialize
|
259
|
+
|
260
|
+
begin
|
261
|
+
Sparsam::Deserializer.deserialize(SS, serialized)
|
262
|
+
rescue Sparsam::TypeMismatch => exception
|
263
|
+
e = exception
|
264
|
+
end
|
265
|
+
e.message.should include("T_I32", "T_STRING")
|
266
|
+
end
|
267
|
+
|
230
268
|
it "will throw errors when given junk data" do
|
231
269
|
expect {
|
232
270
|
Sparsam::Deserializer.deserialize(SS, "wolololololol")
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sparsam
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Airbnb Thrift Developers
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-07-
|
11
|
+
date: 2018-07-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|