json_scanner 0.3.1 → 1.0.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/json_scanner/extconf.rb +7 -1
- data/ext/json_scanner/json_scanner.c +247 -83
- data/lib/json_scanner/version.rb +1 -1
- data/lib/json_scanner.rb +86 -1
- metadata +17 -8
- data/README.md +0 -166
- data/spec/extensiontesttask.rb +0 -128
- data/spec/json_scanner_spec.c +0 -0
- data/spec/json_scanner_spec.rb +0 -360
- data/spec/spec_helper.rb +0 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 59b0c3b3bbb9680bf3cb26983b6d3185a3af14ce45e538e953857a2a453d9391
|
4
|
+
data.tar.gz: d86a61eead87fb858fbc8bf997d6a71d578229426ba4cf36a04ecbfdbd6cf3b1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 638696fd097025ce8aa23eef8095b231d7e9c51905800a6f476dcf2e13d314620302b2cc6988e32f012d08cb7986e150c5bb7369b6ba0e407f933bba0a42489a
|
7
|
+
data.tar.gz: 4a54c918f1dc4136e3b5cc93cbc94a98bc225fdb1611f8ab666f926a3834e0b45d6b12c584b762d6a3fc207af4f459df97c0614af910e3b58215b57c3ba4df86
|
data/ext/json_scanner/extconf.rb
CHANGED
@@ -7,7 +7,13 @@ require "mkmf"
|
|
7
7
|
# selectively, or entirely remove this flag.
|
8
8
|
append_cflags("-fvisibility=hidden")
|
9
9
|
|
10
|
-
|
10
|
+
idefault, ldefault = if with_config("libyajl2-gem")
|
11
|
+
require "libyajl2"
|
12
|
+
[Libyajl2.include_path, Libyajl2.opt_path]
|
13
|
+
else
|
14
|
+
["", ""]
|
15
|
+
end
|
16
|
+
dir_config("yajl", idefault, ldefault)
|
11
17
|
|
12
18
|
unless have_library("yajl") && have_header("yajl/yajl_parse.h") && have_header("yajl/yajl_gen.h")
|
13
19
|
abort "yajl library not found"
|
@@ -1,11 +1,12 @@
|
|
1
1
|
#include "json_scanner.h"
|
2
2
|
|
3
3
|
VALUE rb_mJsonScanner;
|
4
|
-
VALUE
|
4
|
+
VALUE rb_cJsonScannerSelector;
|
5
|
+
VALUE rb_cJsonScannerOptions;
|
5
6
|
VALUE rb_eJsonScannerParseError;
|
6
7
|
#define BYTES_CONSUMED "bytes_consumed"
|
7
8
|
ID rb_iv_bytes_consumed;
|
8
|
-
#define SCAN_KWARGS_SIZE
|
9
|
+
#define SCAN_KWARGS_SIZE 9
|
9
10
|
ID scan_kwargs_table[SCAN_KWARGS_SIZE];
|
10
11
|
|
11
12
|
VALUE null_sym;
|
@@ -85,6 +86,7 @@ typedef struct
|
|
85
86
|
// Easier to use a Ruby array for result than convert later
|
86
87
|
// must be supplied by the caller and RB_GC_GUARD-ed if it isn't on the stack
|
87
88
|
VALUE points_list;
|
89
|
+
VALUE roots_info_list;
|
88
90
|
// by depth
|
89
91
|
size_t *starts;
|
90
92
|
// VALUE rb_err;
|
@@ -92,12 +94,68 @@ typedef struct
|
|
92
94
|
size_t yajl_bytes_consumed;
|
93
95
|
} scan_ctx;
|
94
96
|
|
95
|
-
|
97
|
+
typedef struct
|
98
|
+
{
|
99
|
+
int with_path;
|
100
|
+
int verbose_error;
|
101
|
+
int allow_comments;
|
102
|
+
int dont_validate_strings;
|
103
|
+
int allow_trailing_garbage;
|
104
|
+
int allow_multiple_values;
|
105
|
+
int allow_partial_values;
|
106
|
+
int symbolize_path_keys;
|
107
|
+
int with_roots_info;
|
108
|
+
} scan_options;
|
109
|
+
#define SCAN_OPTION_VALUE_MASK 1
|
110
|
+
#define SCAN_OPTION_SET_MASK (1 << 1)
|
111
|
+
#define SCAN_OPTION(options, field) ((options)->field & SCAN_OPTION_VALUE_MASK)
|
112
|
+
#define SCAN_OPTION_IS_SET(options, field) ((options)->field & SCAN_OPTION_SET_MASK)
|
113
|
+
#define SCAN_OPTION_SET(options, field, value) ((options)->field = ((value) & SCAN_OPTION_VALUE_MASK) | SCAN_OPTION_SET_MASK)
|
114
|
+
#define SCAN_OPTION_FALSE(options, field) \
|
115
|
+
(!SCAN_OPTION(options, field) && ((options)->field & SCAN_OPTION_SET_MASK))
|
116
|
+
|
117
|
+
static void scan_options_init(scan_options *options, VALUE kwargs)
|
118
|
+
{
|
119
|
+
options->with_path = 0;
|
120
|
+
options->verbose_error = 0;
|
121
|
+
options->allow_comments = 0;
|
122
|
+
options->dont_validate_strings = 0;
|
123
|
+
options->allow_trailing_garbage = 0;
|
124
|
+
options->allow_multiple_values = 0;
|
125
|
+
options->allow_partial_values = 0;
|
126
|
+
options->symbolize_path_keys = 0;
|
127
|
+
options->with_roots_info = 0;
|
128
|
+
if (kwargs != Qnil)
|
129
|
+
{
|
130
|
+
VALUE kwargs_values[SCAN_KWARGS_SIZE];
|
131
|
+
rb_get_kwargs(kwargs, scan_kwargs_table, 0, SCAN_KWARGS_SIZE, kwargs_values);
|
132
|
+
if (kwargs_values[0] != Qundef)
|
133
|
+
SCAN_OPTION_SET(options, with_path, RTEST(kwargs_values[0]));
|
134
|
+
if (kwargs_values[1] != Qundef)
|
135
|
+
SCAN_OPTION_SET(options, verbose_error, RTEST(kwargs_values[1]));
|
136
|
+
if (kwargs_values[2] != Qundef)
|
137
|
+
SCAN_OPTION_SET(options, allow_comments, RTEST(kwargs_values[2]));
|
138
|
+
if (kwargs_values[3] != Qundef)
|
139
|
+
SCAN_OPTION_SET(options, dont_validate_strings, RTEST(kwargs_values[3]));
|
140
|
+
if (kwargs_values[4] != Qundef)
|
141
|
+
SCAN_OPTION_SET(options, allow_trailing_garbage, RTEST(kwargs_values[4]));
|
142
|
+
if (kwargs_values[5] != Qundef)
|
143
|
+
SCAN_OPTION_SET(options, allow_multiple_values, RTEST(kwargs_values[5]));
|
144
|
+
if (kwargs_values[6] != Qundef)
|
145
|
+
SCAN_OPTION_SET(options, allow_partial_values, RTEST(kwargs_values[6]));
|
146
|
+
if (kwargs_values[7] != Qundef)
|
147
|
+
SCAN_OPTION_SET(options, symbolize_path_keys, RTEST(kwargs_values[8]));
|
148
|
+
if (kwargs_values[8] != Qundef)
|
149
|
+
SCAN_OPTION_SET(options, with_roots_info, RTEST(kwargs_values[8]));
|
150
|
+
}
|
151
|
+
}
|
152
|
+
|
153
|
+
static inline size_t scan_ctx_get_bytes_consumed(scan_ctx *ctx)
|
96
154
|
{
|
97
155
|
return ctx->yajl_bytes_consumed + yajl_get_bytes_consumed(ctx->handle);
|
98
156
|
}
|
99
157
|
|
100
|
-
inline void scan_ctx_save_bytes_consumed(scan_ctx *ctx)
|
158
|
+
static inline void scan_ctx_save_bytes_consumed(scan_ctx *ctx)
|
101
159
|
{
|
102
160
|
ctx->yajl_bytes_consumed += yajl_get_bytes_consumed(ctx->handle);
|
103
161
|
}
|
@@ -175,7 +233,7 @@ void scan_ctx_debug(scan_ctx *ctx)
|
|
175
233
|
|
176
234
|
// FIXME: This will cause memory leak if ruby_xmalloc raises
|
177
235
|
// path_ary must be RB_GC_GUARD-ed by the caller
|
178
|
-
VALUE scan_ctx_init(scan_ctx *ctx, VALUE path_ary, VALUE string_keys)
|
236
|
+
static VALUE scan_ctx_init(scan_ctx *ctx, VALUE path_ary, VALUE string_keys)
|
179
237
|
{
|
180
238
|
int path_ary_len;
|
181
239
|
paths_t *paths;
|
@@ -311,8 +369,8 @@ VALUE scan_ctx_init(scan_ctx *ctx, VALUE path_ary, VALUE string_keys)
|
|
311
369
|
return Qundef; // no error
|
312
370
|
}
|
313
371
|
|
314
|
-
// resets temporary values in the
|
315
|
-
void scan_ctx_reset(scan_ctx *ctx, VALUE points_list, int with_path, int symbolize_path_keys)
|
372
|
+
// resets temporary values in the selector
|
373
|
+
static void scan_ctx_reset(scan_ctx *ctx, VALUE points_list, VALUE roots_info_list, int with_path, int symbolize_path_keys)
|
316
374
|
{
|
317
375
|
// TODO: reset matched_depth if implemented
|
318
376
|
ctx->current_path_len = 0;
|
@@ -320,11 +378,12 @@ void scan_ctx_reset(scan_ctx *ctx, VALUE points_list, int with_path, int symboli
|
|
320
378
|
ctx->handle = NULL;
|
321
379
|
ctx->yajl_bytes_consumed = 0;
|
322
380
|
ctx->points_list = points_list;
|
381
|
+
ctx->roots_info_list = roots_info_list;
|
323
382
|
ctx->with_path = with_path;
|
324
383
|
ctx->symbolize_path_keys = symbolize_path_keys;
|
325
384
|
}
|
326
385
|
|
327
|
-
void scan_ctx_free(scan_ctx *ctx)
|
386
|
+
static void scan_ctx_free(scan_ctx *ctx)
|
328
387
|
{
|
329
388
|
// fprintf(stderr, "scan_ctx_free\n");
|
330
389
|
if (!ctx)
|
@@ -341,7 +400,7 @@ void scan_ctx_free(scan_ctx *ctx)
|
|
341
400
|
}
|
342
401
|
|
343
402
|
// noexcept
|
344
|
-
inline void increment_arr_index(scan_ctx *sctx)
|
403
|
+
static inline void increment_arr_index(scan_ctx *sctx)
|
345
404
|
{
|
346
405
|
// remember - any value can be root
|
347
406
|
// TODO: Maybe make current_path_len 1 shorter and get rid of -1; need to change all compares
|
@@ -362,7 +421,7 @@ typedef enum
|
|
362
421
|
} value_type;
|
363
422
|
|
364
423
|
// noexcept
|
365
|
-
VALUE create_point(scan_ctx *sctx, value_type type, size_t length)
|
424
|
+
static VALUE create_point(scan_ctx *sctx, value_type type, size_t length)
|
366
425
|
{
|
367
426
|
VALUE values[3], point;
|
368
427
|
size_t curr_pos = scan_ctx_get_bytes_consumed(sctx);
|
@@ -403,7 +462,7 @@ VALUE create_point(scan_ctx *sctx, value_type type, size_t length)
|
|
403
462
|
}
|
404
463
|
|
405
464
|
// noexcept
|
406
|
-
VALUE create_path(scan_ctx *sctx)
|
465
|
+
static VALUE create_path(scan_ctx *sctx)
|
407
466
|
{
|
408
467
|
VALUE path = rb_ary_new_capa(sctx->current_path_len);
|
409
468
|
for (int i = 0; i < sctx->current_path_len; i++)
|
@@ -429,7 +488,16 @@ VALUE create_path(scan_ctx *sctx)
|
|
429
488
|
}
|
430
489
|
|
431
490
|
// noexcept
|
432
|
-
void
|
491
|
+
static inline void save_root_info(scan_ctx *sctx, VALUE type, size_t len)
|
492
|
+
{
|
493
|
+
if (sctx->roots_info_list != Qundef && sctx->current_path_len == 0)
|
494
|
+
{
|
495
|
+
rb_ary_push(sctx->roots_info_list, rb_ary_new_from_args(2, type, ULL2NUM(scan_ctx_get_bytes_consumed(sctx) - len)));
|
496
|
+
}
|
497
|
+
}
|
498
|
+
|
499
|
+
// noexcept
|
500
|
+
static void save_point(scan_ctx *sctx, value_type type, size_t length)
|
433
501
|
{
|
434
502
|
// TODO: Abort parsing if all paths are matched and no more mathces are possible: only trivial key/index matchers at the current level
|
435
503
|
// TODO: Don't re-compare already matched prefixes; hard to invalidate, though
|
@@ -490,9 +558,10 @@ void save_point(scan_ctx *sctx, value_type type, size_t length)
|
|
490
558
|
}
|
491
559
|
|
492
560
|
// noexcept
|
493
|
-
int scan_on_null(void *ctx)
|
561
|
+
static int scan_on_null(void *ctx)
|
494
562
|
{
|
495
563
|
scan_ctx *sctx = (scan_ctx *)ctx;
|
564
|
+
save_root_info(sctx, null_sym, 4);
|
496
565
|
if (sctx->current_path_len > sctx->max_path_len)
|
497
566
|
return true;
|
498
567
|
increment_arr_index(sctx);
|
@@ -501,9 +570,10 @@ int scan_on_null(void *ctx)
|
|
501
570
|
}
|
502
571
|
|
503
572
|
// noexcept
|
504
|
-
int scan_on_boolean(void *ctx, int bool_val)
|
573
|
+
static int scan_on_boolean(void *ctx, int bool_val)
|
505
574
|
{
|
506
575
|
scan_ctx *sctx = (scan_ctx *)ctx;
|
576
|
+
save_root_info(sctx, boolean_sym, bool_val ? 4 : 5);
|
507
577
|
if (sctx->current_path_len > sctx->max_path_len)
|
508
578
|
return true;
|
509
579
|
increment_arr_index(sctx);
|
@@ -512,9 +582,10 @@ int scan_on_boolean(void *ctx, int bool_val)
|
|
512
582
|
}
|
513
583
|
|
514
584
|
// noexcept
|
515
|
-
int scan_on_number(void *ctx, const char *val, size_t len)
|
585
|
+
static int scan_on_number(void *ctx, const char *val, size_t len)
|
516
586
|
{
|
517
587
|
scan_ctx *sctx = (scan_ctx *)ctx;
|
588
|
+
save_root_info(sctx, number_sym, len);
|
518
589
|
if (sctx->current_path_len > sctx->max_path_len)
|
519
590
|
return true;
|
520
591
|
increment_arr_index(sctx);
|
@@ -523,9 +594,10 @@ int scan_on_number(void *ctx, const char *val, size_t len)
|
|
523
594
|
}
|
524
595
|
|
525
596
|
// noexcept
|
526
|
-
int scan_on_string(void *ctx, const unsigned char *val, size_t len)
|
597
|
+
static int scan_on_string(void *ctx, const unsigned char *val, size_t len)
|
527
598
|
{
|
528
599
|
scan_ctx *sctx = (scan_ctx *)ctx;
|
600
|
+
save_root_info(sctx, string_sym, len + 2);
|
529
601
|
if (sctx->current_path_len > sctx->max_path_len)
|
530
602
|
return true;
|
531
603
|
increment_arr_index(sctx);
|
@@ -534,9 +606,11 @@ int scan_on_string(void *ctx, const unsigned char *val, size_t len)
|
|
534
606
|
}
|
535
607
|
|
536
608
|
// noexcept
|
537
|
-
int scan_on_start_object(void *ctx)
|
609
|
+
static int scan_on_start_object(void *ctx)
|
538
610
|
{
|
539
611
|
scan_ctx *sctx = (scan_ctx *)ctx;
|
612
|
+
// Save in the beginning in case of a partial value
|
613
|
+
save_root_info(sctx, object_sym, 1);
|
540
614
|
if (sctx->current_path_len > sctx->max_path_len)
|
541
615
|
{
|
542
616
|
sctx->current_path_len++;
|
@@ -551,7 +625,7 @@ int scan_on_start_object(void *ctx)
|
|
551
625
|
}
|
552
626
|
|
553
627
|
// noexcept
|
554
|
-
int scan_on_key(void *ctx, const unsigned char *key, size_t len)
|
628
|
+
static int scan_on_key(void *ctx, const unsigned char *key, size_t len)
|
555
629
|
{
|
556
630
|
scan_ctx *sctx = (scan_ctx *)ctx;
|
557
631
|
if (sctx->current_path_len > sctx->max_path_len)
|
@@ -564,7 +638,7 @@ int scan_on_key(void *ctx, const unsigned char *key, size_t len)
|
|
564
638
|
}
|
565
639
|
|
566
640
|
// noexcept
|
567
|
-
int scan_on_end_object(void *ctx)
|
641
|
+
static int scan_on_end_object(void *ctx)
|
568
642
|
{
|
569
643
|
scan_ctx *sctx = (scan_ctx *)ctx;
|
570
644
|
sctx->current_path_len--;
|
@@ -574,9 +648,11 @@ int scan_on_end_object(void *ctx)
|
|
574
648
|
}
|
575
649
|
|
576
650
|
// noexcept
|
577
|
-
int scan_on_start_array(void *ctx)
|
651
|
+
static int scan_on_start_array(void *ctx)
|
578
652
|
{
|
579
653
|
scan_ctx *sctx = (scan_ctx *)ctx;
|
654
|
+
// Save in the beginning in case of a partial value
|
655
|
+
save_root_info(sctx, array_sym, 1);
|
580
656
|
if (sctx->current_path_len > sctx->max_path_len)
|
581
657
|
{
|
582
658
|
sctx->current_path_len++;
|
@@ -594,7 +670,7 @@ int scan_on_start_array(void *ctx)
|
|
594
670
|
}
|
595
671
|
|
596
672
|
// noexcept
|
597
|
-
int scan_on_end_array(void *ctx)
|
673
|
+
static int scan_on_end_array(void *ctx)
|
598
674
|
{
|
599
675
|
scan_ctx *sctx = (scan_ctx *)ctx;
|
600
676
|
sctx->current_path_len--;
|
@@ -603,13 +679,13 @@ int scan_on_end_array(void *ctx)
|
|
603
679
|
return true;
|
604
680
|
}
|
605
681
|
|
606
|
-
void
|
682
|
+
static void selector_free(void *data)
|
607
683
|
{
|
608
684
|
scan_ctx_free((scan_ctx *)data);
|
609
685
|
ruby_xfree(data);
|
610
686
|
}
|
611
687
|
|
612
|
-
size_t
|
688
|
+
static size_t selector_size(const void *data)
|
613
689
|
{
|
614
690
|
// see ObjectSpace.memsize_of
|
615
691
|
scan_ctx *ctx = (scan_ctx *)data;
|
@@ -631,16 +707,16 @@ size_t config_size(const void *data)
|
|
631
707
|
return res;
|
632
708
|
}
|
633
709
|
|
634
|
-
static const rb_data_type_t
|
635
|
-
.wrap_struct_name = "
|
710
|
+
static const rb_data_type_t selector_type = {
|
711
|
+
.wrap_struct_name = "json_scanner_selector",
|
636
712
|
.function = {
|
637
|
-
.dfree =
|
638
|
-
.dsize =
|
713
|
+
.dfree = selector_free,
|
714
|
+
.dsize = selector_size,
|
639
715
|
},
|
640
716
|
.flags = RUBY_TYPED_FREE_IMMEDIATELY,
|
641
717
|
};
|
642
718
|
|
643
|
-
VALUE
|
719
|
+
static VALUE selector_alloc(VALUE self)
|
644
720
|
{
|
645
721
|
scan_ctx *ctx = ruby_xmalloc(sizeof(scan_ctx));
|
646
722
|
ctx->paths = NULL;
|
@@ -648,15 +724,15 @@ VALUE config_alloc(VALUE self)
|
|
648
724
|
ctx->current_path = NULL;
|
649
725
|
ctx->max_path_len = 0;
|
650
726
|
ctx->starts = NULL;
|
651
|
-
scan_ctx_reset(ctx, Qundef, false, false);
|
652
|
-
return TypedData_Wrap_Struct(self, &
|
727
|
+
scan_ctx_reset(ctx, Qundef, Qundef, false, false);
|
728
|
+
return TypedData_Wrap_Struct(self, &selector_type, ctx);
|
653
729
|
}
|
654
730
|
|
655
|
-
VALUE
|
731
|
+
static VALUE selector_m_initialize(VALUE self, VALUE path_ary)
|
656
732
|
{
|
657
733
|
scan_ctx *ctx;
|
658
734
|
VALUE scan_ctx_init_err, string_keys;
|
659
|
-
TypedData_Get_Struct(self, scan_ctx, &
|
735
|
+
TypedData_Get_Struct(self, scan_ctx, &selector_type, ctx);
|
660
736
|
string_keys = rb_ary_new();
|
661
737
|
scan_ctx_init_err = scan_ctx_init(ctx, path_ary, string_keys);
|
662
738
|
if (scan_ctx_init_err != Qundef)
|
@@ -667,15 +743,15 @@ VALUE config_m_initialize(VALUE self, VALUE path_ary)
|
|
667
743
|
return self;
|
668
744
|
}
|
669
745
|
|
670
|
-
VALUE
|
746
|
+
static VALUE selector_m_inspect(VALUE self)
|
671
747
|
{
|
672
748
|
scan_ctx *ctx;
|
673
749
|
VALUE res;
|
674
|
-
TypedData_Get_Struct(self, scan_ctx, &
|
750
|
+
TypedData_Get_Struct(self, scan_ctx, &selector_type, ctx);
|
675
751
|
res = rb_sprintf("#<%" PRIsVALUE " [", rb_class_name(CLASS_OF(self)));
|
676
752
|
for (int i = 0; ctx->paths && i < ctx->paths_len; i++)
|
677
753
|
{
|
678
|
-
|
754
|
+
rb_str_buf_cat_ascii(res, "[");
|
679
755
|
for (int j = 0; j < ctx->paths[i].len; j++)
|
680
756
|
{
|
681
757
|
switch (ctx->paths[i].elems[j].type)
|
@@ -687,20 +763,92 @@ VALUE config_m_inspect(VALUE self)
|
|
687
763
|
rb_str_catf(res, "%ld", ctx->paths[i].elems[j].value.index);
|
688
764
|
break;
|
689
765
|
case MATCHER_INDEX_RANGE:
|
690
|
-
rb_str_catf(res, "(%ld..%ld)", ctx->paths[i].elems[j].value.range.start, ctx->paths[i].elems[j].value.range.end);
|
766
|
+
rb_str_catf(res, "(%ld..%ld)", ctx->paths[i].elems[j].value.range.start, ctx->paths[i].elems[j].value.range.end == LONG_MAX ? -1L : ctx->paths[i].elems[j].value.range.end);
|
691
767
|
break;
|
692
768
|
case MATCHER_ANY_KEY:
|
693
|
-
|
769
|
+
rb_str_buf_cat_ascii(res, "('*'..'*')");
|
694
770
|
break;
|
695
771
|
}
|
696
772
|
if (j < ctx->paths[i].len - 1)
|
697
|
-
|
773
|
+
rb_str_buf_cat_ascii(res, ", ");
|
698
774
|
}
|
699
|
-
|
775
|
+
rb_str_buf_cat_ascii(res, "]");
|
700
776
|
if (i < ctx->paths_len - 1)
|
701
|
-
|
777
|
+
rb_str_buf_cat_ascii(res, ", ");
|
702
778
|
}
|
703
|
-
|
779
|
+
rb_str_buf_cat_ascii(res, "]>");
|
780
|
+
return res;
|
781
|
+
}
|
782
|
+
|
783
|
+
static VALUE selector_m_length(VALUE self)
|
784
|
+
{
|
785
|
+
scan_ctx *ctx;
|
786
|
+
TypedData_Get_Struct(self, scan_ctx, &selector_type, ctx);
|
787
|
+
return INT2FIX(ctx->paths_len);
|
788
|
+
}
|
789
|
+
|
790
|
+
static size_t options_size(const void *data)
|
791
|
+
{
|
792
|
+
return sizeof(scan_options);
|
793
|
+
}
|
794
|
+
|
795
|
+
static const rb_data_type_t options_type = {
|
796
|
+
.wrap_struct_name = "json_scanner_options",
|
797
|
+
.function = {
|
798
|
+
.dfree = RUBY_DEFAULT_FREE,
|
799
|
+
.dsize = options_size,
|
800
|
+
},
|
801
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY,
|
802
|
+
};
|
803
|
+
|
804
|
+
static VALUE options_alloc(VALUE self)
|
805
|
+
{
|
806
|
+
// NOT INITIALIZED
|
807
|
+
scan_options *options;
|
808
|
+
return TypedData_Make_Struct(self, scan_options, &options_type, options);
|
809
|
+
}
|
810
|
+
|
811
|
+
static VALUE options_m_initialize(int argc, VALUE *argv, VALUE self)
|
812
|
+
{
|
813
|
+
VALUE kwargs;
|
814
|
+
scan_options *options;
|
815
|
+
TypedData_Get_Struct(self, scan_options, &options_type, options);
|
816
|
+
#if RUBY_API_VERSION_MAJOR > 2 || (RUBY_API_VERSION_MAJOR == 2 && RUBY_API_VERSION_MINOR >= 7)
|
817
|
+
rb_scan_args_kw(RB_SCAN_ARGS_LAST_HASH_KEYWORDS, argc, argv, "0:", &kwargs);
|
818
|
+
#else
|
819
|
+
rb_scan_args(argc, argv, "0:", &kwargs);
|
820
|
+
#endif
|
821
|
+
scan_options_init(options, kwargs);
|
822
|
+
return self;
|
823
|
+
}
|
824
|
+
|
825
|
+
static VALUE options_m_inspect(VALUE self)
|
826
|
+
{
|
827
|
+
VALUE res;
|
828
|
+
scan_options *options;
|
829
|
+
TypedData_Get_Struct(self, scan_options, &options_type, options);
|
830
|
+
res = rb_sprintf("#<%" PRIsVALUE " {", rb_class_name(CLASS_OF(self)));
|
831
|
+
if (SCAN_OPTION_IS_SET(options, with_path))
|
832
|
+
rb_str_catf(res, "with_path: %s, ", SCAN_OPTION(options, with_path) ? "true" : "false");
|
833
|
+
if (SCAN_OPTION_IS_SET(options, verbose_error))
|
834
|
+
rb_str_catf(res, "verbose_error: %s, ", SCAN_OPTION(options, verbose_error) ? "true" : "false");
|
835
|
+
if (SCAN_OPTION_IS_SET(options, allow_comments))
|
836
|
+
rb_str_catf(res, "allow_comments: %s, ", SCAN_OPTION(options, allow_comments) ? "true" : "false");
|
837
|
+
if (SCAN_OPTION_IS_SET(options, dont_validate_strings))
|
838
|
+
rb_str_catf(res, "dont_validate_strings: %s, ", SCAN_OPTION(options, dont_validate_strings) ? "true" : "false");
|
839
|
+
if (SCAN_OPTION_IS_SET(options, allow_trailing_garbage))
|
840
|
+
rb_str_catf(res, "allow_trailing_garbage: %s, ", SCAN_OPTION(options, allow_trailing_garbage) ? "true" : "false");
|
841
|
+
if (SCAN_OPTION_IS_SET(options, allow_multiple_values))
|
842
|
+
rb_str_catf(res, "allow_multiple_values: %s, ", SCAN_OPTION(options, allow_multiple_values) ? "true" : "false");
|
843
|
+
if (SCAN_OPTION_IS_SET(options, allow_partial_values))
|
844
|
+
rb_str_catf(res, "allow_partial_values: %s, ", SCAN_OPTION(options, allow_partial_values) ? "true" : "false");
|
845
|
+
if (SCAN_OPTION_IS_SET(options, symbolize_path_keys))
|
846
|
+
rb_str_catf(res, "symbolize_path_keys: %s, ", SCAN_OPTION(options, symbolize_path_keys) ? "true" : "false");
|
847
|
+
if (SCAN_OPTION_IS_SET(options, with_roots_info))
|
848
|
+
rb_str_catf(res, "with_roots_info: %s, ", SCAN_OPTION(options, with_roots_info) ? "true" : "false");
|
849
|
+
if (RSTRING_END(res)[-1] == ' ')
|
850
|
+
rb_str_resize(res, RSTRING_LEN(res) - 2);
|
851
|
+
rb_str_buf_cat_ascii(res, "}>");
|
704
852
|
return res;
|
705
853
|
}
|
706
854
|
|
@@ -719,52 +867,60 @@ static yajl_callbacks scan_callbacks = {
|
|
719
867
|
|
720
868
|
// def scan(json_str, path_arr, opts)
|
721
869
|
// opts
|
722
|
-
// with_path: false, verbose_error: false,
|
870
|
+
// with_path: false, verbose_error: false, symbolize_path_keys: false, with_roots_info: false
|
723
871
|
// the following opts converted to bool and passed to yajl_config if provided, ignored if not provided
|
724
872
|
// allow_comments, dont_validate_strings, allow_trailing_garbage, allow_multiple_values, allow_partial_values
|
725
|
-
VALUE scan(int argc, VALUE *argv, VALUE self)
|
873
|
+
static VALUE scan(int argc, VALUE *argv, VALUE self)
|
726
874
|
{
|
727
|
-
VALUE json_str, path_ary,
|
728
|
-
|
875
|
+
VALUE json_str, path_ary, rb_options;
|
876
|
+
scan_options options;
|
729
877
|
|
730
|
-
int with_path = false, verbose_error = false, symbolize_path_keys = false;
|
731
878
|
char *json_text;
|
732
879
|
size_t json_text_len;
|
733
880
|
yajl_handle handle;
|
734
881
|
yajl_status stat;
|
735
882
|
scan_ctx *ctx;
|
736
883
|
int free_ctx = true;
|
737
|
-
VALUE err_msg = Qnil, bytes_consumed, result;
|
884
|
+
VALUE err_msg = Qnil, bytes_consumed = Qnil, result, roots_info_result = Qundef;
|
738
885
|
// Turned out callbacks can't raise exceptions
|
739
886
|
// VALUE callback_err;
|
740
|
-
|
741
|
-
|
742
|
-
#else
|
743
|
-
rb_scan_args(argc, argv, "21:", &json_str, &path_ary, &with_path_flag, &kwargs);
|
744
|
-
#endif
|
887
|
+
rb_scan_args(argc, argv, "21", &json_str, &path_ary, &rb_options);
|
888
|
+
rb_check_type(json_str, T_STRING);
|
745
889
|
// rb_io_write(rb_stderr, rb_sprintf("with_path_flag: %" PRIsVALUE " \n", with_path_flag));
|
746
|
-
|
747
|
-
if (kwargs != Qnil)
|
890
|
+
switch (TYPE(rb_options))
|
748
891
|
{
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
if (
|
755
|
-
|
892
|
+
case T_HASH:
|
893
|
+
case T_NIL:
|
894
|
+
scan_options_init(&options, rb_options);
|
895
|
+
break;
|
896
|
+
case T_DATA:
|
897
|
+
if (rb_obj_is_kind_of(rb_options, rb_cJsonScannerOptions))
|
898
|
+
{
|
899
|
+
scan_options *ptr;
|
900
|
+
TypedData_Get_Struct(rb_options, scan_options, &options_type, ptr);
|
901
|
+
options = *ptr;
|
902
|
+
}
|
903
|
+
else
|
904
|
+
{
|
905
|
+
rb_raise(rb_eTypeError, "Expected a Hash or %" PRIsVALUE ", got %" PRIsVALUE, rb_cJsonScannerOptions, rb_obj_class(rb_options));
|
906
|
+
}
|
907
|
+
break;
|
908
|
+
default:
|
909
|
+
rb_raise(rb_eTypeError, "Expected a Hash or %" PRIsVALUE ", got %" PRIsVALUE, rb_cJsonScannerOptions, rb_obj_class(rb_options));
|
910
|
+
break;
|
756
911
|
}
|
757
|
-
|
912
|
+
if (SCAN_OPTION(&options, with_roots_info))
|
913
|
+
roots_info_result = rb_ary_new();
|
758
914
|
json_text = RSTRING_PTR(json_str);
|
759
915
|
#if LONG_MAX > SIZE_MAX
|
760
916
|
json_text_len = RSTRING_LENINT(json_str);
|
761
917
|
#else
|
762
918
|
json_text_len = RSTRING_LEN(json_str);
|
763
919
|
#endif
|
764
|
-
if (rb_obj_is_kind_of(path_ary,
|
920
|
+
if (rb_obj_is_kind_of(path_ary, rb_cJsonScannerSelector))
|
765
921
|
{
|
766
922
|
free_ctx = false;
|
767
|
-
TypedData_Get_Struct(path_ary, scan_ctx, &
|
923
|
+
TypedData_Get_Struct(path_ary, scan_ctx, &selector_type, ctx);
|
768
924
|
}
|
769
925
|
else
|
770
926
|
{
|
@@ -783,23 +939,20 @@ VALUE scan(int argc, VALUE *argv, VALUE self)
|
|
783
939
|
{
|
784
940
|
rb_ary_push(result, rb_ary_new());
|
785
941
|
}
|
786
|
-
scan_ctx_reset(ctx, result, with_path, symbolize_path_keys);
|
942
|
+
scan_ctx_reset(ctx, result, roots_info_result, SCAN_OPTION(&options, with_path), SCAN_OPTION(&options, symbolize_path_keys));
|
787
943
|
// scan_ctx_debug(ctx);
|
788
944
|
|
789
945
|
handle = yajl_alloc(&scan_callbacks, NULL, (void *)ctx);
|
790
|
-
if (
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
if (kwargs_values[6] != Qundef)
|
801
|
-
yajl_config(handle, yajl_allow_partial_values, RTEST(kwargs_values[6]));
|
802
|
-
}
|
946
|
+
if (SCAN_OPTION_IS_SET(&options, allow_comments))
|
947
|
+
yajl_config(handle, yajl_allow_comments, SCAN_OPTION(&options, allow_comments));
|
948
|
+
if (SCAN_OPTION_IS_SET(&options, dont_validate_strings))
|
949
|
+
yajl_config(handle, yajl_dont_validate_strings, SCAN_OPTION(&options, dont_validate_strings));
|
950
|
+
if (SCAN_OPTION_IS_SET(&options, allow_trailing_garbage))
|
951
|
+
yajl_config(handle, yajl_allow_trailing_garbage, SCAN_OPTION(&options, allow_trailing_garbage));
|
952
|
+
if (SCAN_OPTION_IS_SET(&options, allow_multiple_values))
|
953
|
+
yajl_config(handle, yajl_allow_multiple_values, SCAN_OPTION(&options, allow_multiple_values));
|
954
|
+
if (SCAN_OPTION_IS_SET(&options, allow_partial_values))
|
955
|
+
yajl_config(handle, yajl_allow_partial_values, SCAN_OPTION(&options, allow_partial_values));
|
803
956
|
ctx->handle = handle;
|
804
957
|
stat = yajl_parse(handle, (unsigned char *)json_text, json_text_len);
|
805
958
|
if (stat == yajl_status_ok)
|
@@ -810,7 +963,7 @@ VALUE scan(int argc, VALUE *argv, VALUE self)
|
|
810
963
|
|
811
964
|
if (stat != yajl_status_ok)
|
812
965
|
{
|
813
|
-
char *str = (char *)yajl_get_error(handle, verbose_error, (unsigned char *)json_text, json_text_len);
|
966
|
+
char *str = (char *)yajl_get_error(handle, SCAN_OPTION(&options, verbose_error), (unsigned char *)json_text, json_text_len);
|
814
967
|
err_msg = rb_utf8_str_new_cstr(str);
|
815
968
|
bytes_consumed = ULL2NUM(scan_ctx_get_bytes_consumed(ctx));
|
816
969
|
yajl_free_error(handle, (unsigned char *)str);
|
@@ -849,6 +1002,10 @@ VALUE scan(int argc, VALUE *argv, VALUE self)
|
|
849
1002
|
}
|
850
1003
|
// if (callback_err != Qnil)
|
851
1004
|
// rb_exc_raise(callback_err);
|
1005
|
+
if (roots_info_result != Qundef)
|
1006
|
+
{
|
1007
|
+
result = rb_ary_new_from_args(2, result, roots_info_result);
|
1008
|
+
}
|
852
1009
|
return result;
|
853
1010
|
}
|
854
1011
|
|
@@ -856,10 +1013,16 @@ RUBY_FUNC_EXPORTED void
|
|
856
1013
|
Init_json_scanner(void)
|
857
1014
|
{
|
858
1015
|
rb_mJsonScanner = rb_define_module("JsonScanner");
|
859
|
-
|
860
|
-
rb_define_alloc_func(
|
861
|
-
rb_define_method(
|
862
|
-
rb_define_method(
|
1016
|
+
rb_cJsonScannerSelector = rb_define_class_under(rb_mJsonScanner, "Selector", rb_cObject);
|
1017
|
+
rb_define_alloc_func(rb_cJsonScannerSelector, selector_alloc);
|
1018
|
+
rb_define_method(rb_cJsonScannerSelector, "initialize", selector_m_initialize, 1);
|
1019
|
+
rb_define_method(rb_cJsonScannerSelector, "inspect", selector_m_inspect, 0);
|
1020
|
+
rb_define_method(rb_cJsonScannerSelector, "length", selector_m_length, 0);
|
1021
|
+
rb_define_alias(rb_cJsonScannerSelector, "size", "length");
|
1022
|
+
rb_cJsonScannerOptions = rb_define_class_under(rb_mJsonScanner, "Options", rb_cObject);
|
1023
|
+
rb_define_alloc_func(rb_cJsonScannerOptions, options_alloc);
|
1024
|
+
rb_define_method(rb_cJsonScannerOptions, "initialize", options_m_initialize, -1);
|
1025
|
+
rb_define_method(rb_cJsonScannerOptions, "inspect", options_m_inspect, 0);
|
863
1026
|
rb_define_const(rb_mJsonScanner, "ANY_INDEX", rb_range_new(INT2FIX(0), INT2FIX(-1), false));
|
864
1027
|
any_key_sym = rb_id2sym(rb_intern("*"));
|
865
1028
|
rb_define_const(rb_mJsonScanner, "ANY_KEY", rb_range_new(any_key_sym, any_key_sym, false));
|
@@ -881,4 +1044,5 @@ Init_json_scanner(void)
|
|
881
1044
|
scan_kwargs_table[5] = rb_intern("allow_multiple_values");
|
882
1045
|
scan_kwargs_table[6] = rb_intern("allow_partial_values");
|
883
1046
|
scan_kwargs_table[7] = rb_intern("symbolize_path_keys");
|
1047
|
+
scan_kwargs_table[8] = rb_intern("with_roots_info");
|
884
1048
|
}
|
data/lib/json_scanner/version.rb
CHANGED