sparsam 0.1.5 → 0.2.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.
- 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
|