sparsam 0.1.5 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/ext/ruby_hooks.c +0 -1
- data/ext/serializer.cpp +160 -175
- data/ext/serializer.h +1 -4
- data/lib/sparsam.rb +8 -0
- data/lib/sparsam/base_class.rb +0 -13
- data/lib/sparsam/types.rb +0 -66
- data/spec/sparsam_spec.rb +40 -3
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 87e15b3482d526230c19c17ae70ab49f6648d9a3
|
4
|
+
data.tar.gz: c807cb67e084c9a7ab0ab17e18fb8ee6ea3abfe9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4d4efd67ffeca9291b57f9f1727b7234ed64a94325c430b2edce04c2204b1fc4d78e2d4aab06700fb91bd0a01f2e9c81ecbe4a98656f2bd343ca1962365f1458
|
7
|
+
data.tar.gz: b6c6d48b793f6b9dea35565a634f9d26f85238eedc93343612d7913dec113b761d3e7490f3be0e099f49faeecaae5f2ff11ed02f75fc79c233665e834d2fdc13
|
data/ext/ruby_hooks.c
CHANGED
@@ -69,7 +69,6 @@ void Init_sparsam_native() {
|
|
69
69
|
Sparsam = rb_define_module("Sparsam");
|
70
70
|
rb_define_singleton_method(Sparsam, "init!", sparsam_init_bang, 0);
|
71
71
|
rb_define_singleton_method(Sparsam, "cache_fields", cache_fields, 1);
|
72
|
-
rb_define_singleton_method(Sparsam, "validate", serializer_validate, 3);
|
73
72
|
VALUE SparsamSerializer = rb_define_class_under(Sparsam, "Serializer", rb_cObject);
|
74
73
|
SparsamNativeError =
|
75
74
|
rb_define_class_under(Sparsam, "Exception", rb_eStandardError);
|
data/ext/serializer.cpp
CHANGED
@@ -42,6 +42,8 @@ VALUE SparsamMissingMandatory;
|
|
42
42
|
VALUE SparsamUnionException;
|
43
43
|
VALUE SparsamUnknownTypeException;
|
44
44
|
|
45
|
+
VALUE SetClass;
|
46
|
+
|
45
47
|
KlassFieldsCache klassCache; // consider the memory leaked.
|
46
48
|
std::unordered_set<VALUE> unions;
|
47
49
|
|
@@ -82,6 +84,7 @@ void initialize_runtime_constants() {
|
|
82
84
|
SparsamUnionException = rb_const_get_at(Sparsam, rb_intern("UnionException"));
|
83
85
|
SparsamUnknownTypeException =
|
84
86
|
rb_const_get_at(Sparsam, rb_intern("UnknownTypeException"));
|
87
|
+
SetClass = rb_const_get_at(rb_cObject, rb_intern("Set"));
|
85
88
|
}
|
86
89
|
|
87
90
|
void serializer_init(void *serializer, int protocol, void *str_arg1,
|
@@ -138,16 +141,41 @@ static inline VALUE make_ruby_binary(const string &val) {
|
|
138
141
|
|
139
142
|
static void raise_exc_with_struct_and_field_names(
|
140
143
|
VALUE exc_class,
|
141
|
-
VALUE
|
144
|
+
VALUE msg_prefix,
|
142
145
|
VALUE outer_struct_class,
|
143
146
|
VALUE field_sym) {
|
144
147
|
VALUE struct_name = rb_class_name(outer_struct_class);
|
145
148
|
VALUE field_name = rb_sym_to_s(field_sym);
|
149
|
+
|
150
|
+
VALUE msg = rb_sprintf("%s (in %s#%s)",
|
151
|
+
RSTRING_PTR(msg_prefix),
|
152
|
+
RSTRING_PTR(struct_name),
|
153
|
+
RSTRING_PTR(field_name));
|
146
154
|
VALUE args[3] = {msg, struct_name, field_name};
|
147
155
|
VALUE e = rb_class_new_instance(3, args, exc_class);
|
148
156
|
rb_exc_raise(e);
|
149
157
|
}
|
150
158
|
|
159
|
+
static void raise_type_mismatch(VALUE outer_struct, VALUE field_sym) {
|
160
|
+
raise_exc_with_struct_and_field_names(
|
161
|
+
SparsamTypeMismatchError,
|
162
|
+
rb_str_new2("Mismatched type"),
|
163
|
+
CLASS_OF(outer_struct),
|
164
|
+
field_sym);
|
165
|
+
}
|
166
|
+
|
167
|
+
static inline long raise_type_mismatch_as_value(VALUE outer_struct, VALUE field_sym) {
|
168
|
+
raise_type_mismatch(outer_struct, field_sym);
|
169
|
+
return 0;
|
170
|
+
}
|
171
|
+
|
172
|
+
static inline void Sparsam_Check_Type(VALUE x, int t, VALUE outer_struct, VALUE field_sym) {
|
173
|
+
if (!(RB_TYPE_P(x, t))) {
|
174
|
+
raise_type_mismatch(outer_struct, field_sym);
|
175
|
+
}
|
176
|
+
}
|
177
|
+
|
178
|
+
|
151
179
|
static inline VALUE make_ruby_bool(bool val) { return val ? Qtrue : Qfalse; }
|
152
180
|
|
153
181
|
void ThriftSerializer::skip_n_type(uint32_t n, TType ttype) {
|
@@ -315,7 +343,7 @@ VALUE ThriftSerializer::readStruct(VALUE klass) {
|
|
315
343
|
if (typeId != fieldBegin.ftype) {
|
316
344
|
raise_exc_with_struct_and_field_names(
|
317
345
|
SparsamTypeMismatchError,
|
318
|
-
rb_sprintf("
|
346
|
+
rb_sprintf("Mismatched type (definition: %d, found: %d)", fieldBegin.ftype, typeId),
|
319
347
|
klass,
|
320
348
|
fieldInfo->symName);
|
321
349
|
}
|
@@ -387,22 +415,85 @@ VALUE ThriftSerializer::readUnion(VALUE klass) {
|
|
387
415
|
|
388
416
|
#define HANDLE_TYPE(TYPE, WRITE_METHOD, CONVERT) \
|
389
417
|
case protocol::T_##TYPE: { \
|
390
|
-
this->tprot->write##WRITE_METHOD(CONVERT
|
418
|
+
this->tprot->write##WRITE_METHOD(CONVERT); \
|
391
419
|
break; \
|
392
420
|
}
|
393
421
|
|
422
|
+
static inline long raise_bignum_range_error_as_value() {
|
423
|
+
rb_raise(rb_eRangeError, "bignum too big to convert");
|
424
|
+
return 0;
|
425
|
+
}
|
426
|
+
|
427
|
+
#define CONVERT_FIXNUM(CONVERT) \
|
428
|
+
((FIXNUM_P(actual)) ? \
|
429
|
+
CONVERT(actual) : \
|
430
|
+
((RB_TYPE_P(actual, T_BIGNUM)) ? \
|
431
|
+
raise_bignum_range_error_as_value() : \
|
432
|
+
raise_type_mismatch_as_value(outer_struct, field_sym)))
|
433
|
+
|
434
|
+
#define CONVERT_I64 \
|
435
|
+
((FIXNUM_P(actual)) ? \
|
436
|
+
(LONG_LONG)FIX2LONG(actual) : \
|
437
|
+
((RB_TYPE_P(actual, T_BIGNUM)) ? \
|
438
|
+
rb_big2ll(actual) : \
|
439
|
+
raise_type_mismatch_as_value(outer_struct, field_sym)))
|
440
|
+
|
441
|
+
#ifdef RB_FLOAT_TYPE_P
|
442
|
+
#define FLOAT_TYPE_P(x) RB_FLOAT_TYPE_P(x)
|
443
|
+
#else
|
444
|
+
#define FLOAT_TYPE_P(x) RB_TYPE_P(x, T_FLOAT)
|
445
|
+
#endif
|
446
|
+
|
447
|
+
#define CONVERT_FLOAT(CONVERT) \
|
448
|
+
((FLOAT_TYPE_P(actual)) ? \
|
449
|
+
CONVERT(actual) : \
|
450
|
+
raise_type_mismatch_as_value(outer_struct, field_sym)) \
|
451
|
+
|
452
|
+
static inline bool convertBool(VALUE actual, VALUE outer_struct, VALUE field_sym) {
|
453
|
+
switch (actual) {
|
454
|
+
case Qtrue:
|
455
|
+
return true;
|
456
|
+
case Qfalse:
|
457
|
+
return false;
|
458
|
+
default:
|
459
|
+
raise_type_mismatch(outer_struct, field_sym);
|
460
|
+
}
|
461
|
+
|
462
|
+
/* unreachable */
|
463
|
+
return false;
|
464
|
+
}
|
465
|
+
|
466
|
+
#ifdef FIX2SHORT
|
467
|
+
#define SHORT_CONVERT(x) FIX2SHORT(x)
|
468
|
+
#else
|
469
|
+
#define SHORT_CONVERT(x) ((short)FIX2INT(x))
|
470
|
+
#endif
|
471
|
+
|
472
|
+
static inline char byte_convert(VALUE x) {
|
473
|
+
short s = SHORT_CONVERT(x);
|
474
|
+
|
475
|
+
if (s <= 127 && s >= -128) {
|
476
|
+
return (char) s;
|
477
|
+
} else {
|
478
|
+
rb_raise(rb_eRangeError, "integer %d out of range for char", s);
|
479
|
+
}
|
480
|
+
|
481
|
+
/* unreachable */
|
482
|
+
return 0;
|
483
|
+
}
|
484
|
+
|
394
485
|
void ThriftSerializer::writeAny(TType ttype, FieldInfo *field_info,
|
395
|
-
VALUE actual) {
|
486
|
+
VALUE actual, VALUE outer_struct, VALUE field_sym) {
|
396
487
|
switch (ttype) {
|
397
|
-
HANDLE_TYPE(I16, I16,
|
398
|
-
HANDLE_TYPE(I32, I32,
|
399
|
-
HANDLE_TYPE(I64, I64,
|
400
|
-
HANDLE_TYPE(BOOL, Bool,
|
401
|
-
HANDLE_TYPE(DOUBLE, Double, NUM2DBL)
|
402
|
-
HANDLE_TYPE(BYTE, Byte,
|
488
|
+
HANDLE_TYPE(I16, I16, CONVERT_FIXNUM(SHORT_CONVERT))
|
489
|
+
HANDLE_TYPE(I32, I32, CONVERT_FIXNUM(FIX2INT))
|
490
|
+
HANDLE_TYPE(I64, I64, CONVERT_I64)
|
491
|
+
HANDLE_TYPE(BOOL, Bool, convertBool(actual, outer_struct, field_sym))
|
492
|
+
HANDLE_TYPE(DOUBLE, Double, CONVERT_FLOAT(NUM2DBL))
|
493
|
+
HANDLE_TYPE(BYTE, Byte, CONVERT_FIXNUM(byte_convert))
|
403
494
|
|
404
495
|
case protocol::T_STRING: {
|
405
|
-
|
496
|
+
Sparsam_Check_Type(actual, T_STRING, outer_struct, field_sym);
|
406
497
|
|
407
498
|
string data = string(StringValuePtr(actual), RSTRING_LEN(actual));
|
408
499
|
if (field_info->isBinaryString) {
|
@@ -414,48 +505,58 @@ void ThriftSerializer::writeAny(TType ttype, FieldInfo *field_info,
|
|
414
505
|
}
|
415
506
|
|
416
507
|
case protocol::T_LIST: {
|
417
|
-
|
508
|
+
Sparsam_Check_Type(actual, T_ARRAY, outer_struct, field_sym);
|
418
509
|
|
419
510
|
long length = RARRAY_LEN(actual);
|
420
511
|
TType elem = field_info->elementType->ftype;
|
421
512
|
this->tprot->writeListBegin(elem, static_cast<size_t>(length));
|
422
513
|
for (long i = 0; i < length; i++) {
|
423
|
-
this->writeAny(elem, field_info->elementType, rb_ary_entry(actual, i));
|
514
|
+
this->writeAny(elem, field_info->elementType, rb_ary_entry(actual, i), outer_struct, field_sym);
|
424
515
|
}
|
425
516
|
this->tprot->writeListEnd();
|
426
517
|
break;
|
427
518
|
}
|
428
519
|
|
429
520
|
case protocol::T_SET: {
|
521
|
+
if (CLASS_OF(actual) != SetClass) {
|
522
|
+
raise_type_mismatch(outer_struct, field_sym);
|
523
|
+
}
|
524
|
+
|
430
525
|
VALUE ary = rb_funcall(actual, intern_for_to_a, 0);
|
431
526
|
long length = RARRAY_LEN(ary);
|
432
527
|
TType elem = field_info->elementType->ftype;
|
433
528
|
this->tprot->writeListBegin(elem, static_cast<size_t>(length));
|
434
529
|
for (long i = 0; i < length; i++) {
|
435
|
-
this->writeAny(elem, field_info->elementType, rb_ary_entry(ary, i));
|
530
|
+
this->writeAny(elem, field_info->elementType, rb_ary_entry(ary, i), outer_struct, field_sym);
|
436
531
|
}
|
437
532
|
this->tprot->writeListEnd();
|
438
533
|
break;
|
439
534
|
}
|
440
535
|
|
441
536
|
case protocol::T_MAP: {
|
442
|
-
|
537
|
+
Sparsam_Check_Type(actual, T_HASH, outer_struct, field_sym);
|
443
538
|
|
444
539
|
TType keyTType = field_info->keyType->ftype,
|
445
540
|
valueTType = field_info->elementType->ftype;
|
446
541
|
this->tprot->writeMapBegin(keyTType, valueTType,
|
447
542
|
static_cast<size_t>(RHASH_SIZE(actual)));
|
448
|
-
HASH_FOREACH_BEGIN(actual, this, field_info)
|
543
|
+
HASH_FOREACH_BEGIN(actual, this, field_info, &outer_struct, &field_sym)
|
449
544
|
ThriftSerializer *that = (ThriftSerializer *)argv[0];
|
450
545
|
FieldInfo *field_info = (FieldInfo *)argv[1];
|
451
|
-
|
452
|
-
|
546
|
+
VALUE *outer_struct = (VALUE *)argv[2];
|
547
|
+
VALUE *field_sym = (VALUE *)argv[3];
|
548
|
+
that->writeAny(field_info->keyType->ftype, field_info->keyType, k, *outer_struct, *field_sym);
|
549
|
+
that->writeAny(field_info->elementType->ftype, field_info->elementType, v, *outer_struct, *field_sym);
|
453
550
|
HASH_FOREACH_END()
|
454
551
|
this->tprot->writeMapEnd();
|
455
552
|
break;
|
456
553
|
}
|
457
554
|
|
458
555
|
case protocol::T_STRUCT: {
|
556
|
+
if (CLASS_OF(actual) != field_info->klass) {
|
557
|
+
raise_type_mismatch(outer_struct, field_sym);
|
558
|
+
}
|
559
|
+
|
459
560
|
static const string cname = "";
|
460
561
|
this->tprot->writeStructBegin(cname.c_str());
|
461
562
|
this->writeStruct(field_info->klass, actual);
|
@@ -470,13 +571,32 @@ void ThriftSerializer::writeAny(TType ttype, FieldInfo *field_info,
|
|
470
571
|
|
471
572
|
#undef HANDLE_TYPE
|
472
573
|
|
574
|
+
static bool checkRequiredFields(VALUE klass, VALUE data) {
|
575
|
+
auto fields = FindOrCreateFieldInfoMap(klass);
|
576
|
+
for (auto const &entry : *fields) {
|
577
|
+
if (!entry.second->isOptional) {
|
578
|
+
VALUE val = rb_ivar_get(data, entry.second->ivarName);
|
579
|
+
if (NIL_P(val)) {
|
580
|
+
raise_exc_with_struct_and_field_names(
|
581
|
+
SparsamMissingMandatory,
|
582
|
+
rb_str_new2("Required field missing"),
|
583
|
+
klass,
|
584
|
+
entry.second->symName);
|
585
|
+
return false;
|
586
|
+
}
|
587
|
+
}
|
588
|
+
}
|
589
|
+
|
590
|
+
return true;
|
591
|
+
}
|
592
|
+
|
473
593
|
void ThriftSerializer::writeStruct(VALUE klass, VALUE data) {
|
474
594
|
static const string cname = "";
|
475
595
|
FieldBegin fieldBegin;
|
476
596
|
FieldInfo *fieldInfo;
|
477
597
|
auto fields = FindOrCreateFieldInfoMap(klass);
|
478
598
|
|
479
|
-
if (!
|
599
|
+
if (!checkRequiredFields(klass, data)) {
|
480
600
|
return;
|
481
601
|
}
|
482
602
|
|
@@ -487,7 +607,7 @@ void ThriftSerializer::writeStruct(VALUE klass, VALUE data) {
|
|
487
607
|
VALUE actual = rb_ivar_get(data, fieldInfo->ivarName);
|
488
608
|
if (!NIL_P(actual)) {
|
489
609
|
this->tprot->writeFieldBegin(cname.c_str(), fieldBegin.ftype, fieldBegin.fid);
|
490
|
-
this->writeAny(fieldBegin.ftype, entry.second, actual);
|
610
|
+
this->writeAny(fieldBegin.ftype, entry.second, actual, data, fieldInfo->symName);
|
491
611
|
this->tprot->writeFieldEnd();
|
492
612
|
}
|
493
613
|
}
|
@@ -495,6 +615,24 @@ void ThriftSerializer::writeStruct(VALUE klass, VALUE data) {
|
|
495
615
|
|
496
616
|
VALUE serializer_writeStruct(VALUE self, VALUE klass, VALUE data) {
|
497
617
|
watch_for_texcept() get_ts();
|
618
|
+
|
619
|
+
if (CLASS_OF(data) != klass) {
|
620
|
+
VALUE expected_name = rb_class_name(klass);
|
621
|
+
VALUE actual_name = rb_class_name(CLASS_OF(data));
|
622
|
+
|
623
|
+
raise_exc_with_struct_and_field_names(
|
624
|
+
SparsamTypeMismatchError,
|
625
|
+
rb_sprintf(
|
626
|
+
"Mismatched type passed to serialize (expected: %s got: %s)",
|
627
|
+
RSTRING_PTR(expected_name),
|
628
|
+
RSTRING_PTR(actual_name)),
|
629
|
+
data,
|
630
|
+
ID2SYM(rb_intern("(root)")));
|
631
|
+
|
632
|
+
RB_GC_GUARD(expected_name);
|
633
|
+
RB_GC_GUARD(actual_name);
|
634
|
+
}
|
635
|
+
|
498
636
|
static const string cname = "";
|
499
637
|
ts->tprot->writeStructBegin(cname.c_str());
|
500
638
|
ts->writeStruct(klass, data);
|
@@ -516,159 +654,6 @@ VALUE serializer_readStruct(VALUE self, VALUE klass) {
|
|
516
654
|
catch_thrift_and_reraise();
|
517
655
|
}
|
518
656
|
|
519
|
-
static void raise_type_mismatch(VALUE outer_struct, VALUE field_sym) {
|
520
|
-
raise_exc_with_struct_and_field_names(
|
521
|
-
SparsamTypeMismatchError,
|
522
|
-
rb_str_new2("Type mismatch in field data"),
|
523
|
-
CLASS_OF(outer_struct),
|
524
|
-
field_sym);
|
525
|
-
}
|
526
|
-
|
527
|
-
bool validateArray(
|
528
|
-
FieldInfo *type,
|
529
|
-
VALUE arr,
|
530
|
-
bool recursive,
|
531
|
-
VALUE outer_struct,
|
532
|
-
VALUE field_sym) {
|
533
|
-
long length = RARRAY_LEN(arr);
|
534
|
-
for (long i = 0; i < length; i++) {
|
535
|
-
if (!validateAny(type, rb_ary_entry(arr, i), recursive, outer_struct, field_sym)) {
|
536
|
-
return false;
|
537
|
-
}
|
538
|
-
}
|
539
|
-
return true;
|
540
|
-
}
|
541
|
-
|
542
|
-
#define TEST_RB_VAL_FOR_CLASS(VAL, KLASS) \
|
543
|
-
if (!RTEST(rb_obj_is_kind_of(VAL, KLASS))) { \
|
544
|
-
raise_type_mismatch(outer_struct, field_sym); \
|
545
|
-
ret = false; \
|
546
|
-
}
|
547
|
-
|
548
|
-
#define HANDLE_TYPE(TYPE, KLASS) \
|
549
|
-
case protocol::T_##TYPE: { \
|
550
|
-
TEST_RB_VAL_FOR_CLASS(val, KLASS) \
|
551
|
-
break; \
|
552
|
-
}
|
553
|
-
|
554
|
-
bool validateAny(FieldInfo *type, VALUE val, bool recursive, VALUE outer_struct, VALUE field_sym) {
|
555
|
-
bool ret = true;
|
556
|
-
switch (type->ftype) {
|
557
|
-
|
558
|
-
HANDLE_TYPE(BYTE, klass_for_integer)
|
559
|
-
HANDLE_TYPE(I16, klass_for_integer)
|
560
|
-
HANDLE_TYPE(I32, klass_for_integer)
|
561
|
-
HANDLE_TYPE(I64, klass_for_integer)
|
562
|
-
HANDLE_TYPE(DOUBLE, klass_for_float)
|
563
|
-
HANDLE_TYPE(STRING, klass_for_string)
|
564
|
-
|
565
|
-
case protocol::T_BOOL: {
|
566
|
-
if ( !(val == Qtrue || val == Qfalse) ) {
|
567
|
-
raise_type_mismatch(outer_struct, field_sym);
|
568
|
-
ret = false;
|
569
|
-
}
|
570
|
-
break;
|
571
|
-
}
|
572
|
-
|
573
|
-
case protocol::T_STRUCT: {
|
574
|
-
TEST_RB_VAL_FOR_CLASS(val, type->klass)
|
575
|
-
if (ret && recursive) {
|
576
|
-
ret = validateStruct(type->klass, val, true, recursive);
|
577
|
-
}
|
578
|
-
break;
|
579
|
-
}
|
580
|
-
|
581
|
-
case protocol::T_SET: {
|
582
|
-
TEST_RB_VAL_FOR_CLASS(val, klass_for_set)
|
583
|
-
if (ret) {
|
584
|
-
VALUE ary = rb_funcall(val, intern_for_to_a, 0);
|
585
|
-
ret = validateArray(type->elementType, ary, recursive, outer_struct, field_sym);
|
586
|
-
}
|
587
|
-
break;
|
588
|
-
}
|
589
|
-
case protocol::T_LIST: {
|
590
|
-
TEST_RB_VAL_FOR_CLASS(val, klass_for_array)
|
591
|
-
if (ret) {
|
592
|
-
ret = validateArray(type->elementType, val, recursive, outer_struct, field_sym);
|
593
|
-
}
|
594
|
-
break;
|
595
|
-
}
|
596
|
-
|
597
|
-
case protocol::T_MAP: {
|
598
|
-
TEST_RB_VAL_FOR_CLASS(val, klass_for_hash)
|
599
|
-
if (ret) {
|
600
|
-
bool flag = true;
|
601
|
-
HASH_FOREACH_BEGIN(val, &flag, type->keyType, type->elementType,
|
602
|
-
&recursive, &outer_struct, &field_sym)
|
603
|
-
bool *flag = (bool *)argv[0], *recursive = (bool *)argv[3];
|
604
|
-
VALUE *outer_struct = (VALUE *)argv[4], *field_sym = (VALUE *)argv[5];
|
605
|
-
FieldInfo *field_info_key = (FieldInfo *)argv[1];
|
606
|
-
FieldInfo *field_info_value = (FieldInfo *)argv[2];
|
607
|
-
if (!validateAny(field_info_key, k, *recursive, *outer_struct, *field_sym) ||
|
608
|
-
!validateAny(field_info_value, v, *recursive, *outer_struct, *field_sym)) {
|
609
|
-
*flag = false;
|
610
|
-
HASH_FOREACH_ABORT()
|
611
|
-
}
|
612
|
-
HASH_FOREACH_RET()
|
613
|
-
HASH_FOREACH_END()
|
614
|
-
}
|
615
|
-
break;
|
616
|
-
}
|
617
|
-
|
618
|
-
default: {
|
619
|
-
rb_raise(SparsamUnknownTypeException, "Unknown type received.");
|
620
|
-
ret = false;
|
621
|
-
break;
|
622
|
-
}
|
623
|
-
}
|
624
|
-
return ret;
|
625
|
-
}
|
626
|
-
#undef HANDLE_TYPE
|
627
|
-
#undef TEST_RB_VAL_FOR_CLASS
|
628
|
-
|
629
|
-
bool validateStruct(VALUE klass, VALUE data, bool validateContainerTypes,
|
630
|
-
bool recursive) {
|
631
|
-
if (!RTEST(rb_obj_is_kind_of(data, klass))) {
|
632
|
-
rb_raise(SparsamTypeMismatchError, "Wrong type of struct given for data");
|
633
|
-
return false;
|
634
|
-
}
|
635
|
-
auto fields = FindOrCreateFieldInfoMap(klass);
|
636
|
-
for (auto const &entry : *fields) {
|
637
|
-
VALUE val = rb_ivar_get(data, entry.second->ivarName);
|
638
|
-
if (NIL_P(val)) {
|
639
|
-
if (!entry.second->isOptional) {
|
640
|
-
raise_exc_with_struct_and_field_names(
|
641
|
-
SparsamMissingMandatory,
|
642
|
-
rb_sprintf("Missing: fieldID %d", entry.first),
|
643
|
-
klass,
|
644
|
-
entry.second->symName);
|
645
|
-
return false;
|
646
|
-
}
|
647
|
-
continue;
|
648
|
-
}
|
649
|
-
if (validateContainerTypes &&
|
650
|
-
!validateAny(entry.second, val, recursive, data, entry.second->symName)) {
|
651
|
-
return false;
|
652
|
-
}
|
653
|
-
}
|
654
|
-
return true;
|
655
|
-
}
|
656
|
-
|
657
|
-
VALUE serializer_validate(VALUE self, VALUE klass, VALUE data,
|
658
|
-
VALUE strictness) {
|
659
|
-
switch (static_cast<ValidateStrictness>(FIX2INT(strictness))) {
|
660
|
-
case strict: {
|
661
|
-
return validateStruct(klass, data, true, false) ? Qtrue : Qfalse;
|
662
|
-
}
|
663
|
-
case recursive: {
|
664
|
-
return validateStruct(klass, data, true, true) ? Qtrue : Qfalse;
|
665
|
-
}
|
666
|
-
default: {
|
667
|
-
return validateStruct(klass, data, false, false) ? Qtrue : Qfalse;
|
668
|
-
}
|
669
|
-
}
|
670
|
-
}
|
671
|
-
|
672
657
|
#define R_FIX_TO_TTYPE(x) (static_cast<TType>(FIX2INT(x)))
|
673
658
|
|
674
659
|
FieldInfoMap *FindOrCreateFieldInfoMap(VALUE klass) {
|
@@ -689,7 +674,7 @@ ID field_name_to_ivar_id(VALUE str_name) {
|
|
689
674
|
if (str_name != Qnil) {
|
690
675
|
return rb_intern_str(rb_str_concat(rb_str_new2("@"), str_name));
|
691
676
|
} else {
|
692
|
-
return
|
677
|
+
return 0;
|
693
678
|
}
|
694
679
|
}
|
695
680
|
|
@@ -697,7 +682,7 @@ VALUE field_name_to_sym(VALUE str_name) {
|
|
697
682
|
if (str_name != Qnil) {
|
698
683
|
return ID2SYM(rb_intern_str(str_name));
|
699
684
|
} else {
|
700
|
-
return
|
685
|
+
return 0;
|
701
686
|
}
|
702
687
|
}
|
703
688
|
|
data/ext/serializer.h
CHANGED
@@ -85,14 +85,11 @@ public:
|
|
85
85
|
private:
|
86
86
|
VALUE readUnion(VALUE klass);
|
87
87
|
VALUE readAny(TType ttype, FieldInfo *field_info);
|
88
|
-
void writeAny(TType ttype, FieldInfo *field_info, VALUE data);
|
88
|
+
void writeAny(TType ttype, FieldInfo *field_info, VALUE data, VALUE outer_struct, VALUE field_sym);
|
89
89
|
void skip_n_type(uint32_t n, TType ttype);
|
90
90
|
void skip_n_pair(uint32_t n, TType type_a, TType type_b);
|
91
91
|
};
|
92
92
|
|
93
|
-
bool validateStruct(VALUE klass, VALUE data, bool validateContainerTypes,
|
94
|
-
bool recursive);
|
95
|
-
bool validateAny(FieldInfo *type, VALUE val, bool recursive, VALUE outer_struct, VALUE field_sym);
|
96
93
|
FieldInfoMap *FindOrCreateFieldInfoMap(VALUE klass);
|
97
94
|
FieldInfo *CreateFieldInfo(VALUE field_map_entry);
|
98
95
|
FieldInfoMap *CreateFieldInfoMap(VALUE klass);
|
data/lib/sparsam.rb
CHANGED
data/lib/sparsam/base_class.rb
CHANGED
@@ -23,22 +23,9 @@ module Sparsam
|
|
23
23
|
def self.generate_accessors(klass)
|
24
24
|
klass::FIELDS.each do |field_key, field_info|
|
25
25
|
field_accessor(klass, field_key, field_info)
|
26
|
-
qmark_isset_method(klass, field_info)
|
27
26
|
end
|
28
27
|
end
|
29
28
|
|
30
|
-
# TODO(Ben Hughes): Do we ever use those, these are an unexpected
|
31
|
-
# definition of predicate accessors
|
32
|
-
def self.qmark_isset_method(klass, field_info)
|
33
|
-
field_name = field_info[:name]
|
34
|
-
|
35
|
-
klass.class_eval(<<-EOF, __FILE__, __LINE__)
|
36
|
-
def #{field_name}?
|
37
|
-
!#{field_name}.nil?
|
38
|
-
end
|
39
|
-
EOF
|
40
|
-
end
|
41
|
-
|
42
29
|
def self.generate_default_values(klass)
|
43
30
|
fields_with_default_values = {}
|
44
31
|
klass::FIELDS.each do |fid, field_def|
|
data/lib/sparsam/types.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
# -*- coding: UTF-8 -*-
|
2
2
|
require 'set'
|
3
|
-
require 'sparsam/exceptions'
|
4
3
|
|
5
4
|
module Sparsam
|
6
5
|
module Types
|
@@ -25,64 +24,6 @@ module Sparsam
|
|
25
24
|
]).freeze
|
26
25
|
end
|
27
26
|
|
28
|
-
# Deprecated type checking
|
29
|
-
|
30
|
-
def self.check_type(value, field, name, skip_nil = true)
|
31
|
-
return if value.nil? && skip_nil
|
32
|
-
|
33
|
-
valid =
|
34
|
-
case field[:type]
|
35
|
-
when Types::VOID
|
36
|
-
nil === value
|
37
|
-
when Types::BOOL
|
38
|
-
true === value || false === value
|
39
|
-
when Types::BYTE, Types::I16, Types::I32, Types::I64
|
40
|
-
Integer === value
|
41
|
-
when Types::DOUBLE
|
42
|
-
Float === value
|
43
|
-
when Types::STRING
|
44
|
-
String === value
|
45
|
-
when Types::STRUCT
|
46
|
-
Struct === value || Union === value
|
47
|
-
when Types::MAP
|
48
|
-
Hash === value
|
49
|
-
when Types::SET
|
50
|
-
Set === value
|
51
|
-
when Types::LIST
|
52
|
-
Array === value
|
53
|
-
else
|
54
|
-
false
|
55
|
-
end
|
56
|
-
|
57
|
-
unless valid
|
58
|
-
raise TypeMismatch, "Expected #{type_name(field[:type])}, " \
|
59
|
-
"received #{value.class} for field #{name}"
|
60
|
-
end
|
61
|
-
|
62
|
-
# check elements now
|
63
|
-
case field[:type]
|
64
|
-
when Types::MAP
|
65
|
-
# This is still allocations per MAP, but better than per map entry
|
66
|
-
key_str = "#{name}.key"
|
67
|
-
value_str = "#{name}.value"
|
68
|
-
|
69
|
-
value.each_pair do |k, v|
|
70
|
-
check_type(k, field[:key], key_str, false)
|
71
|
-
check_type(v, field[:value], value_str, false)
|
72
|
-
end
|
73
|
-
when Types::SET, Types::LIST
|
74
|
-
element_str = "#{name}.element"
|
75
|
-
|
76
|
-
value.each do |el|
|
77
|
-
check_type(el, field[:element], element_str, false)
|
78
|
-
end
|
79
|
-
when Types::STRUCT
|
80
|
-
unless field[:class] == value.class
|
81
|
-
raise TypeMismatch, "Expected #{field[:class]}, received #{value.class} for field #{name}"
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
27
|
TYPE_NAME_SYM_MAPPING = Types.constants.each_with_object({}) do |const, h|
|
87
28
|
h[Types.const_get(const)] = const.to_sym
|
88
29
|
end
|
@@ -98,11 +39,4 @@ module Sparsam
|
|
98
39
|
def self.type_name(type)
|
99
40
|
TYPE_NAME_MAPPING[type]
|
100
41
|
end
|
101
|
-
|
102
|
-
module MessageTypes
|
103
|
-
CALL = 1
|
104
|
-
REPLY = 2
|
105
|
-
EXCEPTION = 3
|
106
|
-
ONEWAY = 4
|
107
|
-
end
|
108
42
|
end
|
data/spec/sparsam_spec.rb
CHANGED
@@ -361,7 +361,6 @@ describe 'Sparsam' do
|
|
361
361
|
struct: US.new(id_i32: 10),
|
362
362
|
union: UN.new(id_s: "woo"),
|
363
363
|
complex: Complex(1),
|
364
|
-
bigint: 2**128,
|
365
364
|
rational: Rational(2, 3),
|
366
365
|
}
|
367
366
|
|
@@ -398,9 +397,47 @@ describe 'Sparsam' do
|
|
398
397
|
)
|
399
398
|
end
|
400
399
|
|
401
|
-
|
400
|
+
expect {
|
402
401
|
s.serialize
|
403
|
-
|
402
|
+
}.to(
|
403
|
+
raise_error(Sparsam::TypeMismatch),
|
404
|
+
"assigning #{field} : #{type} a value of " \
|
405
|
+
"#{val.inspect} : #{val_type} did not raise TypeMismatch"
|
406
|
+
)
|
407
|
+
end
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
unless RUBY_VERSION =~ /^1\.9/
|
412
|
+
it "handles integer ranges" do
|
413
|
+
fields = {
|
414
|
+
a_byte: 8,
|
415
|
+
an_i16: 16,
|
416
|
+
an_i32: 32,
|
417
|
+
an_i64: 64,
|
418
|
+
}
|
419
|
+
|
420
|
+
fields.each do |field, size|
|
421
|
+
s = EveryType.new
|
422
|
+
|
423
|
+
max_val = 2**(size - 1) - 1
|
424
|
+
|
425
|
+
[max_val, ~max_val].each do |val|
|
426
|
+
s.send(:"#{field}=", val)
|
427
|
+
|
428
|
+
expect {
|
429
|
+
s.serialize
|
430
|
+
}.not_to raise_error, "#{field} of #{size} bits unable to hold #{val}"
|
431
|
+
end
|
432
|
+
|
433
|
+
[max_val + 1, ~(max_val + 1)].each do |val|
|
434
|
+
s.send(:"#{field}=", val)
|
435
|
+
expect {
|
436
|
+
s.serialize
|
437
|
+
}.to(
|
438
|
+
raise_error(RangeError),
|
439
|
+
"#{field} of #{size} bits apparently able to hold value #{val} in defiance of nature"
|
440
|
+
)
|
404
441
|
end
|
405
442
|
end
|
406
443
|
end
|
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.
|
4
|
+
version: 0.2.0
|
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: 2017-08-
|
11
|
+
date: 2017-08-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|