yajl-ruby 1.3.1 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of yajl-ruby might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2211f169082b89b1385a841f79d445cd559b405c
4
- data.tar.gz: 9074ca9bab2acf80a0a7069f2a608ee958b2eea8
3
+ metadata.gz: 5006e05689ab5c95a3e4dd98b03c66a7bb46394c
4
+ data.tar.gz: ecfd90a4a8993aa166c0f2b0b983dc05bfa96e51
5
5
  SHA512:
6
- metadata.gz: 20f296b807da6097dec709b153364def6cae62187223e64027871103261800e8edda82d5d2bfe96a9dbfb3e29a6b84f56e39c09b45d3bafd423335e86aa52186
7
- data.tar.gz: 89bee3f6029488f771d15a4ebc9e910a8c1ca7d276ef5ad001bb118e7754397bfb4a4065cf5a8b8cc5edffe222bdb669742b6cbe9c6572d3656b3d683393c2b8
6
+ metadata.gz: 9e21e2069c984380ce506562aa837b4e02207e3368085b7cbf651dd2075de0c4d0ec43e1e1006abcaa947c797b2ec6a59bba7c73bea17ae9b47e1f01b3552dbb
7
+ data.tar.gz: 01df11ba7ab076382eb270e692bb316f2d5ab06034275661f65ff58888a89d1ffed25d2a4081bd9cad9c8a0275eaa8c23411528d2ca9a3423a83e9bba59b2d57
@@ -1,7 +1,7 @@
1
1
  require 'mkmf'
2
2
  require 'rbconfig'
3
3
 
4
- $CFLAGS << ' -Wall -funroll-loops'
4
+ $CFLAGS << ' -Wall -funroll-loops -Wno-declaration-after-statement'
5
5
  $CFLAGS << ' -Werror-implicit-function-declaration -Wextra -O0 -ggdb3' if ENV['DEBUG']
6
6
 
7
7
  create_makefile('yajl/yajl')
@@ -22,6 +22,12 @@
22
22
  */
23
23
 
24
24
  #include "yajl_ext.h"
25
+ #include "yajl_lex.h"
26
+ #include "yajl_alloc.h"
27
+ #include "yajl_buf.h"
28
+ #include "yajl_encode.h"
29
+ #include "api/yajl_common.h"
30
+ #include "assert.h"
25
31
 
26
32
  #define YAJL_RB_TO_JSON \
27
33
  VALUE rb_encoder, cls; \
@@ -32,6 +38,25 @@
32
38
  } \
33
39
  return rb_yajl_encoder_encode(1, &self, rb_encoder); \
34
40
 
41
+ static void *rb_internal_malloc(void *ctx, unsigned int sz) {
42
+ return xmalloc(sz);
43
+ }
44
+
45
+ static void *rb_internal_realloc(void *ctx, void *previous, unsigned int sz) {
46
+ return xrealloc(previous, sz);
47
+ }
48
+
49
+ static void rb_internal_free(void *ctx, void *ptr) {
50
+ xfree(ptr);
51
+ }
52
+
53
+ static yajl_alloc_funcs rb_alloc_funcs = {
54
+ rb_internal_malloc,
55
+ rb_internal_realloc,
56
+ rb_internal_free,
57
+ NULL
58
+ };
59
+
35
60
  /* Helpers for building objects */
36
61
  static void yajl_check_and_fire_callback(void * ctx) {
37
62
  yajl_parser_wrapper * wrapper;
@@ -39,12 +64,12 @@ static void yajl_check_and_fire_callback(void * ctx) {
39
64
 
40
65
  /* No need to do any of this if the callback isn't even setup */
41
66
  if (wrapper->parse_complete_callback != Qnil) {
42
- int len = RARRAY_LEN(wrapper->builderStack);
67
+ long len = RARRAY_LEN(wrapper->builderStack);
43
68
  if (len == 1 && wrapper->nestedArrayLevel == 0 && wrapper->nestedHashLevel == 0) {
44
69
  rb_funcall(wrapper->parse_complete_callback, intern_call, 1, rb_ary_pop(wrapper->builderStack));
45
70
  }
46
71
  } else {
47
- int len = RARRAY_LEN(wrapper->builderStack);
72
+ long len = RARRAY_LEN(wrapper->builderStack);
48
73
  if (len == 1 && wrapper->nestedArrayLevel == 0 && wrapper->nestedHashLevel == 0) {
49
74
  wrapper->objectsFound++;
50
75
  if (wrapper->objectsFound > 1) {
@@ -76,7 +101,7 @@ static char *yajl_raise_encode_error_for_status(yajl_gen_status status, VALUE ob
76
101
  static void yajl_set_static_value(void * ctx, VALUE val) {
77
102
  yajl_parser_wrapper * wrapper;
78
103
  VALUE lastEntry, hash;
79
- int len;
104
+ long len;
80
105
 
81
106
  GetParser((VALUE)ctx, wrapper);
82
107
 
@@ -198,7 +223,7 @@ void yajl_encode_part(void * wrapper, VALUE obj, VALUE io) {
198
223
  case T_BIGNUM:
199
224
  str = rb_funcall(obj, intern_to_s, 0);
200
225
  cptr = RSTRING_PTR(str);
201
- len = RSTRING_LEN(str);
226
+ len = (unsigned int)RSTRING_LEN(str);
202
227
  if (memcmp(cptr, "NaN", 3) == 0 || memcmp(cptr, "Infinity", 8) == 0 || memcmp(cptr, "-Infinity", 9) == 0) {
203
228
  rb_raise(cEncodeError, "'%s' is an invalid number", cptr);
204
229
  }
@@ -206,7 +231,7 @@ void yajl_encode_part(void * wrapper, VALUE obj, VALUE io) {
206
231
  break;
207
232
  case T_STRING:
208
233
  cptr = RSTRING_PTR(obj);
209
- len = RSTRING_LEN(obj);
234
+ len = (unsigned int)RSTRING_LEN(obj);
210
235
  CHECK_STATUS(yajl_gen_string(w->encoder, (const unsigned char *)cptr, len));
211
236
  break;
212
237
  default:
@@ -214,13 +239,13 @@ void yajl_encode_part(void * wrapper, VALUE obj, VALUE io) {
214
239
  str = rb_funcall(obj, intern_to_json, 0);
215
240
  Check_Type(str, T_STRING);
216
241
  cptr = RSTRING_PTR(str);
217
- len = RSTRING_LEN(str);
242
+ len = (unsigned int)RSTRING_LEN(str);
218
243
  CHECK_STATUS(yajl_gen_number(w->encoder, cptr, len));
219
244
  } else {
220
245
  str = rb_funcall(obj, intern_to_s, 0);
221
246
  Check_Type(str, T_STRING);
222
247
  cptr = RSTRING_PTR(str);
223
- len = RSTRING_LEN(str);
248
+ len = (unsigned int)RSTRING_LEN(str);
224
249
  CHECK_STATUS(yajl_gen_string(w->encoder, (const unsigned char *)cptr, len));
225
250
  }
226
251
  break;
@@ -420,7 +445,7 @@ static VALUE rb_yajl_parser_new(int argc, VALUE * argv, VALUE klass) {
420
445
  cfg = (yajl_parser_config){allowComments, checkUTF8};
421
446
 
422
447
  obj = Data_Make_Struct(klass, yajl_parser_wrapper, yajl_parser_wrapper_mark, yajl_parser_wrapper_free, wrapper);
423
- wrapper->parser = yajl_alloc(&callbacks, &cfg, NULL, (void *)obj);
448
+ wrapper->parser = yajl_alloc(&callbacks, &cfg, &rb_alloc_funcs, (void *)obj);
424
449
  wrapper->nestedArrayLevel = 0;
425
450
  wrapper->nestedHashLevel = 0;
426
451
  wrapper->objectsFound = 0;
@@ -489,13 +514,13 @@ static VALUE rb_yajl_parser_parse(int argc, VALUE * argv, VALUE self) {
489
514
 
490
515
  if (TYPE(input) == T_STRING) {
491
516
  cptr = RSTRING_PTR(input);
492
- len = RSTRING_LEN(input);
517
+ len = (unsigned int)RSTRING_LEN(input);
493
518
  yajl_parse_chunk((const unsigned char*)cptr, len, wrapper->parser);
494
519
  } else if (rb_respond_to(input, intern_io_read)) {
495
520
  VALUE parsed = rb_str_new(0, FIX2LONG(rbufsize));
496
521
  while (rb_funcall(input, intern_io_read, 2, rbufsize, parsed) != Qnil) {
497
522
  cptr = RSTRING_PTR(parsed);
498
- len = RSTRING_LEN(parsed);
523
+ len = (unsigned int)RSTRING_LEN(parsed);
499
524
  yajl_parse_chunk((const unsigned char*)cptr, len, wrapper->parser);
500
525
  }
501
526
  } else {
@@ -535,7 +560,7 @@ static VALUE rb_yajl_parser_parse_chunk(VALUE self, VALUE chunk) {
535
560
 
536
561
  if (wrapper->parse_complete_callback != Qnil) {
537
562
  const char * cptr = RSTRING_PTR(chunk);
538
- len = RSTRING_LEN(chunk);
563
+ len = (unsigned int)RSTRING_LEN(chunk);
539
564
  yajl_parse_chunk((const unsigned char*)cptr, len, wrapper->parser);
540
565
  } else {
541
566
  rb_raise(cParseError, "The on_parse_complete callback isn't setup, parsing useless.");
@@ -560,6 +585,402 @@ static VALUE rb_yajl_parser_set_complete_cb(VALUE self, VALUE callback) {
560
585
  return Qnil;
561
586
  }
562
587
 
588
+ /*
589
+ * An event stream pulls data off the IO source into the buffer,
590
+ * then runs the lexer over that stream.
591
+ */
592
+ struct yajl_event_stream_s {
593
+ yajl_alloc_funcs *funcs;
594
+
595
+ VALUE stream; // source
596
+
597
+ VALUE buffer;
598
+ unsigned int offset;
599
+
600
+ yajl_lexer lexer; // event source
601
+ };
602
+
603
+ typedef struct yajl_event_stream_s *yajl_event_stream_t;
604
+
605
+ struct yajl_event_s {
606
+ yajl_tok token;
607
+ const char *buf;
608
+ unsigned int len;
609
+ };
610
+ typedef struct yajl_event_s yajl_event_t;
611
+
612
+ static yajl_event_t yajl_event_stream_next(yajl_event_stream_t parser, int pop) {
613
+ assert(parser->stream);
614
+ assert(parser->buffer);
615
+
616
+ while (1) {
617
+ if (parser->offset >= RSTRING_LEN(parser->buffer)) {
618
+ //printf("reading offset %d size %ld\n", parser->offset, RSTRING_LEN(parser->buffer));
619
+
620
+ // Refill the buffer
621
+ rb_funcall(parser->stream, intern_io_read, 2, INT2FIX(RSTRING_LEN(parser->buffer)), parser->buffer);
622
+ if (RSTRING_LEN(parser->buffer) == 0) {
623
+ yajl_event_t event = {
624
+ .token = yajl_tok_eof,
625
+ };
626
+ return event;
627
+ }
628
+
629
+ parser->offset = 0;
630
+ }
631
+
632
+ // Try to pull an event off the lexer
633
+ yajl_event_t event;
634
+
635
+ yajl_tok token;
636
+ if (pop == 0) {
637
+ //printf("peeking %p %ld %d\n", RSTRING_PTR(parser->buffer), RSTRING_LEN(parser->buffer), parser->offset);
638
+ token = yajl_lex_peek(parser->lexer, (const unsigned char *)RSTRING_PTR(parser->buffer), (unsigned int)RSTRING_LEN(parser->buffer), parser->offset);
639
+ //printf("peeked event %d\n", token);
640
+
641
+ if (token == yajl_tok_eof) {
642
+ parser->offset = (unsigned int)RSTRING_LEN(parser->buffer);
643
+ continue;
644
+ }
645
+
646
+ event.token = token;
647
+
648
+ return event;
649
+ }
650
+
651
+ //printf("popping\n");
652
+ token = yajl_lex_lex(parser->lexer, (const unsigned char *)RSTRING_PTR(parser->buffer), (unsigned int)RSTRING_LEN(parser->buffer), &parser->offset, (const unsigned char **)&event.buf, &event.len);
653
+ //printf("popped event %d\n", token);
654
+
655
+ if (token == yajl_tok_eof) {
656
+ continue;
657
+ }
658
+
659
+ event.token = token;
660
+
661
+ return event;
662
+ }
663
+
664
+ return (yajl_event_t){};
665
+ }
666
+
667
+ static VALUE rb_yajl_projector_filter_array_subtree(yajl_event_stream_t parser, VALUE schema, yajl_event_t event);
668
+ static VALUE rb_yajl_projector_filter_object_subtree(yajl_event_stream_t parser, VALUE schema, yajl_event_t event);
669
+ static void rb_yajl_projector_ignore_value(yajl_event_stream_t parser);
670
+ static void rb_yajl_projector_ignore_container(yajl_event_stream_t parser);
671
+ static VALUE rb_yajl_projector_build_simple_value(yajl_event_stream_t parser, yajl_event_t event);
672
+ static VALUE rb_yajl_projector_build_string(yajl_event_stream_t parser, yajl_event_t event);
673
+
674
+ static VALUE rb_yajl_projector_filter(yajl_event_stream_t parser, VALUE schema, yajl_event_t event) {
675
+ assert(parser->stream);
676
+
677
+ switch(event.token) {
678
+ case yajl_tok_left_brace:
679
+ return rb_yajl_projector_filter_array_subtree(parser, schema, event);
680
+ break;
681
+ case yajl_tok_left_bracket:
682
+ return rb_yajl_projector_filter_object_subtree(parser, schema, event);
683
+ break;
684
+ default:
685
+ return rb_yajl_projector_build_simple_value(parser, event);
686
+ }
687
+ }
688
+
689
+ static VALUE rb_yajl_projector_filter_array_subtree(yajl_event_stream_t parser, VALUE schema, yajl_event_t event) {
690
+ assert(event.token == yajl_tok_left_brace);
691
+
692
+ VALUE ary = rb_ary_new();
693
+
694
+ while (1) {
695
+ event = yajl_event_stream_next(parser, 1);
696
+
697
+ if (event.token == yajl_tok_right_brace) {
698
+ break;
699
+ }
700
+
701
+ VALUE val = rb_yajl_projector_filter(parser, schema, event);
702
+ rb_ary_push(ary, val);
703
+
704
+ event = yajl_event_stream_next(parser, 0);
705
+ if (event.token == yajl_tok_comma) {
706
+ event = yajl_event_stream_next(parser, 1);
707
+ assert(event.token == yajl_tok_comma);
708
+
709
+ event = yajl_event_stream_next(parser, 0);
710
+ if (!(event.token == yajl_tok_string || event.token == yajl_tok_integer || event.token == yajl_tok_double || event.token == yajl_tok_null || event.token == yajl_tok_bool || event.token == yajl_tok_left_bracket || event.token == yajl_tok_left_brace)) {
711
+ rb_raise(cParseError, "read a comma, expected a value to follow, actually read %s", yajl_tok_name(event.token));
712
+ }
713
+ } else if (event.token != yajl_tok_right_brace) {
714
+ rb_raise(cParseError, "didn't read a comma, expected closing array, actually read %s", yajl_tok_name(event.token));
715
+ }
716
+ }
717
+
718
+ return ary;
719
+ }
720
+
721
+ static VALUE rb_yajl_projector_filter_object_subtree(yajl_event_stream_t parser, VALUE schema, yajl_event_t event) {
722
+ assert(event.token == yajl_tok_left_bracket);
723
+
724
+ VALUE hsh = rb_hash_new();
725
+
726
+ while (1) {
727
+ event = yajl_event_stream_next(parser, 1);
728
+
729
+ if (event.token == yajl_tok_right_bracket) {
730
+ break;
731
+ }
732
+
733
+ if (!(event.token == yajl_tok_string || event.token == yajl_tok_string_with_escapes)) {
734
+ rb_raise(cParseError, "Expected string, unexpected stream event %s", yajl_tok_name(event.token));
735
+ }
736
+
737
+ VALUE key = rb_yajl_projector_build_string(parser, event);
738
+
739
+ event = yajl_event_stream_next(parser, 1);
740
+ if (!(event.token == yajl_tok_colon)) {
741
+ rb_raise(cParseError, "Expected colon, unexpected stream event %s", yajl_tok_name(event.token));
742
+ }
743
+
744
+ // nil schema means reify the subtree from here on
745
+ // otherwise if the schema has a key for this we want it
746
+ int interesting = (schema == Qnil || rb_funcall(schema, rb_intern("key?"), 1, key) == Qtrue);
747
+ if (!interesting) {
748
+ rb_yajl_projector_ignore_value(parser);
749
+ goto peek_comma;
750
+ }
751
+
752
+ yajl_event_t value_event = yajl_event_stream_next(parser, 1);
753
+
754
+ VALUE key_schema;
755
+ if (schema == Qnil) {
756
+ key_schema = Qnil;
757
+ } else {
758
+ key_schema = rb_hash_aref(schema, key);
759
+ }
760
+
761
+ VALUE val = rb_yajl_projector_filter(parser, key_schema, value_event);
762
+
763
+ rb_str_freeze(key);
764
+ rb_hash_aset(hsh, key, val);
765
+
766
+ peek_comma:
767
+
768
+ event = yajl_event_stream_next(parser, 0);
769
+ if (event.token == yajl_tok_comma) {
770
+ event = yajl_event_stream_next(parser, 1);
771
+ assert(event.token == yajl_tok_comma);
772
+
773
+ event = yajl_event_stream_next(parser, 0);
774
+ if (!(event.token == yajl_tok_string || event.token == yajl_tok_string_with_escapes)) {
775
+ rb_raise(cParseError, "read a comma, expected a key to follow, actually read %s", yajl_tok_name(event.token));
776
+ }
777
+ } else if (event.token != yajl_tok_right_bracket) {
778
+ rb_raise(cParseError, "read a value without tailing comma, expected closing bracket, actually read %s", yajl_tok_name(event.token));
779
+ }
780
+ }
781
+
782
+ return hsh;
783
+ }
784
+
785
+ /*
786
+ # After reading a key if we know we are not interested in the next value,
787
+ # read and discard all its stream events.
788
+ #
789
+ # Values can be simple (string, numeric, boolean, null) or compound (object
790
+ # or array).
791
+ #
792
+ # Returns nothing.
793
+ */
794
+ static void rb_yajl_projector_ignore_value(yajl_event_stream_t parser) {
795
+ yajl_event_t value_event = yajl_event_stream_next(parser, 1);
796
+
797
+ switch (value_event.token) {
798
+ case yajl_tok_null:
799
+ case yajl_tok_bool:
800
+ case yajl_tok_integer:
801
+ case yajl_tok_double:
802
+ case yajl_tok_string:
803
+ case yajl_tok_string_with_escapes:
804
+ return;
805
+ default:
806
+ break;
807
+ }
808
+
809
+ if (value_event.token == yajl_tok_left_brace || value_event.token == yajl_tok_left_bracket) {
810
+ rb_yajl_projector_ignore_container(parser);
811
+ return;
812
+ }
813
+
814
+ rb_raise(cParseError, "unknown value type to ignore %s", yajl_tok_name(value_event.token));
815
+ }
816
+
817
+ /*
818
+ # Given the start of an array or object, read until the closing event.
819
+ # Object structures can nest and this is considered.
820
+ #
821
+ # Returns nothing.
822
+ */
823
+ static void rb_yajl_projector_ignore_container(yajl_event_stream_t parser) {
824
+ int depth = 1;
825
+
826
+ while (depth > 0) {
827
+ yajl_event_t event = yajl_event_stream_next(parser, 1);
828
+
829
+ if (event.token == yajl_tok_eof) {
830
+ return;
831
+ }
832
+
833
+ if (event.token == yajl_tok_left_bracket || event.token == yajl_tok_left_brace) {
834
+ depth += 1;
835
+ } else if (event.token == yajl_tok_right_bracket || event.token == yajl_tok_right_brace) {
836
+ depth -= 1;
837
+ }
838
+ }
839
+ }
840
+
841
+ static VALUE rb_yajl_projector_build_simple_value(yajl_event_stream_t parser, yajl_event_t event) {
842
+ assert(parser->stream);
843
+
844
+ switch (event.token) {
845
+ case yajl_tok_null:;
846
+ return Qnil;
847
+ case yajl_tok_bool:;
848
+ if (memcmp(event.buf, "true", 4) == 0) {
849
+ return Qtrue;
850
+ } else if (memcmp(event.buf, "false", 4) == 0) {
851
+ return Qfalse;
852
+ } else {
853
+ rb_raise(cStandardError, "unknown boolean token %s", event.buf);
854
+ }
855
+ case yajl_tok_integer:;
856
+ case yajl_tok_double:;
857
+ char *buf = (char *)malloc(event.len + 1);
858
+ buf[event.len] = 0;
859
+ memcpy(buf, event.buf, event.len);
860
+
861
+ VALUE val;
862
+ if (memchr(buf, '.', event.len) ||
863
+ memchr(buf, 'e', event.len) ||
864
+ memchr(buf, 'E', event.len)) {
865
+ val = rb_float_new(strtod(buf, NULL));
866
+ } else {
867
+ val = rb_cstr2inum(buf, 10);
868
+ }
869
+ free(buf);
870
+
871
+ return val;
872
+
873
+ case yajl_tok_string:;
874
+ case yajl_tok_string_with_escapes:;
875
+ return rb_yajl_projector_build_string(parser, event);
876
+
877
+ case yajl_tok_eof:;
878
+ rb_raise(cParseError, "unexpected eof while constructing value");
879
+
880
+ case yajl_tok_comma:
881
+ rb_raise(cParseError, "unexpected comma while constructing value");
882
+
883
+ case yajl_tok_colon:
884
+ rb_raise(cParseError, "unexpected colon while constructing value");
885
+
886
+ default:;
887
+ assert(0);
888
+ }
889
+ }
890
+
891
+ static VALUE rb_yajl_projector_build_string(yajl_event_stream_t parser, yajl_event_t event) {
892
+ switch (event.token) {
893
+ case yajl_tok_string:; {
894
+ VALUE str = rb_str_new(event.buf, event.len);
895
+ rb_enc_associate(str, utf8Encoding);
896
+
897
+ rb_encoding *default_internal_enc = rb_default_internal_encoding();
898
+ if (default_internal_enc) {
899
+ str = rb_str_export_to_enc(str, default_internal_enc);
900
+ }
901
+
902
+ return str;
903
+ }
904
+
905
+ case yajl_tok_string_with_escapes:; {
906
+ //printf("decoding string with escapes\n");
907
+
908
+ yajl_buf strBuf = yajl_buf_alloc(parser->funcs);
909
+ yajl_string_decode(strBuf, (const unsigned char *)event.buf, event.len);
910
+
911
+ VALUE str = rb_str_new((const char *)yajl_buf_data(strBuf), yajl_buf_len(strBuf));
912
+ rb_enc_associate(str, utf8Encoding);
913
+
914
+ yajl_buf_free(strBuf);
915
+
916
+ rb_encoding *default_internal_enc = rb_default_internal_encoding();
917
+ if (default_internal_enc) {
918
+ str = rb_str_export_to_enc(str, default_internal_enc);
919
+ }
920
+
921
+ return str;
922
+ }
923
+
924
+ default:; {
925
+ assert(0);
926
+ }
927
+ }
928
+ }
929
+
930
+ static VALUE rb_protected_yajl_projector_filter(VALUE pointer) {
931
+ VALUE *args = (VALUE *)pointer;
932
+ return rb_yajl_projector_filter((struct yajl_event_stream_s *)args[0],
933
+ args[1],
934
+ *(yajl_event_t *)args[2]);
935
+ }
936
+
937
+ /*
938
+ * Document-method: project
939
+ */
940
+ static VALUE rb_yajl_projector_project(VALUE self, VALUE schema) {
941
+ VALUE stream = rb_iv_get(self, "@stream");
942
+
943
+ long buffer_size = FIX2LONG(rb_iv_get(self, "@buffer_size"));
944
+ VALUE buffer = rb_str_new(0, buffer_size);
945
+
946
+ struct yajl_event_stream_s parser = {
947
+ .funcs = &rb_alloc_funcs,
948
+
949
+ .stream = stream,
950
+
951
+ .buffer = buffer,
952
+ .offset = (unsigned int)buffer_size,
953
+
954
+ .lexer = yajl_lex_alloc(&rb_alloc_funcs, 0, 1),
955
+ };
956
+
957
+ yajl_event_t event = yajl_event_stream_next(&parser, 1);
958
+
959
+ RB_GC_GUARD(stream);
960
+ RB_GC_GUARD(buffer);
961
+
962
+ VALUE result;
963
+ int state = 0;
964
+
965
+ if (event.token == yajl_tok_left_brace || event.token == yajl_tok_left_bracket) {
966
+ VALUE args[3];
967
+ args[0] = (VALUE)&parser;
968
+ args[1] = schema;
969
+ args[2] = (VALUE)&event;
970
+ result = rb_protect(rb_protected_yajl_projector_filter,
971
+ (VALUE)args,
972
+ &state);
973
+ } else {
974
+ yajl_lex_free(parser.lexer);
975
+ rb_raise(cParseError, "expected left bracket or brace, actually read %s", yajl_tok_name(event.token));
976
+ }
977
+
978
+ yajl_lex_free(parser.lexer);
979
+ if (state) rb_jump_tag(state);
980
+
981
+ return result;
982
+ }
983
+
563
984
  /*
564
985
  * Document-class: Yajl::Encoder
565
986
  *
@@ -620,7 +1041,7 @@ static VALUE rb_yajl_encoder_new(int argc, VALUE * argv, VALUE klass) {
620
1041
 
621
1042
  obj = Data_Make_Struct(klass, yajl_encoder_wrapper, yajl_encoder_wrapper_mark, yajl_encoder_wrapper_free, wrapper);
622
1043
  wrapper->indentString = actualIndent;
623
- wrapper->encoder = yajl_gen_alloc(&cfg, NULL);
1044
+ wrapper->encoder = yajl_gen_alloc(&cfg, &rb_alloc_funcs);
624
1045
  wrapper->on_progress_callback = Qnil;
625
1046
  if (opts != Qnil && rb_funcall(opts, intern_has_key, 1, sym_terminator) == Qtrue) {
626
1047
  wrapper->terminator = rb_hash_aref(opts, sym_terminator);
@@ -900,6 +1321,7 @@ void Init_yajl() {
900
1321
 
901
1322
  cParseError = rb_define_class_under(mYajl, "ParseError", rb_eStandardError);
902
1323
  cEncodeError = rb_define_class_under(mYajl, "EncodeError", rb_eStandardError);
1324
+ cStandardError = rb_const_get(rb_cObject, rb_intern("StandardError"));
903
1325
 
904
1326
  cParser = rb_define_class_under(mYajl, "Parser", rb_cObject);
905
1327
  rb_define_singleton_method(cParser, "new", rb_yajl_parser_new, -1);
@@ -909,6 +1331,9 @@ void Init_yajl() {
909
1331
  rb_define_method(cParser, "<<", rb_yajl_parser_parse_chunk, 1);
910
1332
  rb_define_method(cParser, "on_parse_complete=", rb_yajl_parser_set_complete_cb, 1);
911
1333
 
1334
+ cProjector = rb_define_class_under(mYajl, "Projector", rb_cObject);
1335
+ rb_define_method(cProjector, "project", rb_yajl_projector_project, 1);
1336
+
912
1337
  cEncoder = rb_define_class_under(mYajl, "Encoder", rb_cObject);
913
1338
  rb_define_singleton_method(cEncoder, "new", rb_yajl_encoder_new, -1);
914
1339
  rb_define_method(cEncoder, "initialize", rb_yajl_encoder_init, -1);
@@ -53,7 +53,7 @@ static rb_encoding *utf8Encoding;
53
53
  #define RARRAY_LEN(s) (RARRAY(s)->len)
54
54
  #endif
55
55
 
56
- static VALUE cParseError, cEncodeError, mYajl, cParser, cEncoder;
56
+ static VALUE cStandardError, cParseError, cEncodeError, mYajl, cParser, cProjector, cEncoder;
57
57
  static ID intern_io_read, intern_call, intern_keys, intern_to_s,
58
58
  intern_to_json, intern_has_key, intern_to_sym, intern_as_json;
59
59
  static ID sym_allow_comments, sym_check_utf8, sym_pretty, sym_indent, sym_terminator, sym_symbolize_keys, sym_symbolize_names, sym_html_safe;
@@ -38,29 +38,25 @@
38
38
  #include <assert.h>
39
39
  #include <string.h>
40
40
 
41
- #ifdef YAJL_LEXER_DEBUG
42
- static const char *
43
- tokToStr(yajl_tok tok)
44
- {
41
+ const char *yajl_tok_name(yajl_tok tok) {
45
42
  switch (tok) {
46
43
  case yajl_tok_bool: return "bool";
47
44
  case yajl_tok_colon: return "colon";
48
45
  case yajl_tok_comma: return "comma";
49
46
  case yajl_tok_eof: return "eof";
50
47
  case yajl_tok_error: return "error";
51
- case yajl_tok_left_brace: return "brace";
52
- case yajl_tok_left_bracket: return "bracket";
48
+ case yajl_tok_left_brace: return "open_array";
49
+ case yajl_tok_left_bracket: return "open_object";
53
50
  case yajl_tok_null: return "null";
54
51
  case yajl_tok_integer: return "integer";
55
52
  case yajl_tok_double: return "double";
56
- case yajl_tok_right_brace: return "brace";
57
- case yajl_tok_right_bracket: return "bracket";
53
+ case yajl_tok_right_brace: return "close_array";
54
+ case yajl_tok_right_bracket: return "close_object";
58
55
  case yajl_tok_string: return "string";
59
56
  case yajl_tok_string_with_escapes: return "string_with_escapes";
60
57
  }
61
58
  return "unknown";
62
59
  }
63
- #endif
64
60
 
65
61
  /* Impact of the stream parsing feature on the lexer:
66
62
  *
@@ -740,6 +736,10 @@ yajl_tok yajl_lex_peek(yajl_lexer lexer, const unsigned char * jsonText,
740
736
  tok = yajl_lex_lex(lexer, jsonText, jsonTextLen, &offset,
741
737
  &outBuf, &outLen);
742
738
 
739
+ if (tok == yajl_tok_eof) {
740
+ return tok;
741
+ }
742
+
743
743
  lexer->bufOff = bufOff;
744
744
  lexer->bufInUse = bufInUse;
745
745
  yajl_buf_truncate(lexer->buf, bufLen);
@@ -36,33 +36,34 @@
36
36
  #include "api/yajl_common.h"
37
37
 
38
38
  typedef enum {
39
- yajl_tok_bool,
40
- yajl_tok_colon,
41
- yajl_tok_comma,
42
- yajl_tok_eof,
43
- yajl_tok_error,
44
- yajl_tok_left_brace,
45
- yajl_tok_left_bracket,
46
- yajl_tok_null,
47
- yajl_tok_right_brace,
48
- yajl_tok_right_bracket,
39
+ yajl_tok_bool, // 0
40
+ yajl_tok_colon, // 1
41
+ yajl_tok_comma, // 2
42
+ yajl_tok_eof, // 3
43
+ yajl_tok_error, // 4
44
+ yajl_tok_left_brace, // 5
45
+ yajl_tok_left_bracket, // 6
46
+ yajl_tok_null, // 7
47
+ yajl_tok_right_brace, // 8
48
+ yajl_tok_right_bracket, // 9
49
49
 
50
50
  /* we differentiate between integers and doubles to allow the
51
51
  * parser to interpret the number without re-scanning */
52
- yajl_tok_integer,
53
- yajl_tok_double,
52
+ yajl_tok_integer, // 10
53
+ yajl_tok_double, // 11
54
54
 
55
55
  /* we differentiate between strings which require further processing,
56
56
  * and strings that do not */
57
- yajl_tok_string,
58
- yajl_tok_string_with_escapes,
57
+ yajl_tok_string, // 12
58
+ yajl_tok_string_with_escapes, // 13
59
59
 
60
60
  /* comment tokens are not currently returned to the parser, ever */
61
- yajl_tok_comment
61
+ yajl_tok_comment // 14
62
62
  } yajl_tok;
63
63
 
64
64
  typedef struct yajl_lexer_t * yajl_lexer;
65
65
 
66
+ const char *yajl_tok_name(yajl_tok tok);
66
67
 
67
68
  YAJL_API
68
69
  yajl_lexer yajl_lex_alloc(yajl_alloc_funcs * alloc,
@@ -23,6 +23,13 @@ module Yajl
23
23
  Encoder.encode(obj, args, &block)
24
24
  end
25
25
 
26
+ class Projector
27
+ def initialize(stream, read_bufsize=4096)
28
+ @stream = stream
29
+ @buffer_size = read_bufsize
30
+ end
31
+ end
32
+
26
33
  class Parser
27
34
  # A helper method for parse-and-forget use-cases
28
35
  #
@@ -1,3 +1,3 @@
1
1
  module Yajl
2
- VERSION = '1.3.1'
2
+ VERSION = '1.4.0'
3
3
  end
@@ -0,0 +1,41 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper.rb')
2
+
3
+ require 'benchmark'
4
+ require 'benchmark/memory'
5
+
6
+ describe "file projection" do
7
+ it "projects file streams" do
8
+ schema = {
9
+ "forced" => nil,
10
+ "created" => nil,
11
+ "pusher" => {
12
+ "name" => nil,
13
+ },
14
+ "repository" => {
15
+ "name" => nil,
16
+ "full_name" => nil,
17
+ },
18
+ "ref" => nil,
19
+ "compare" => nil,
20
+ "commits" => {
21
+ "distinct" => nil,
22
+ "message" => nil,
23
+ "url" => nil,
24
+ "id" => nil,
25
+ "author" => {
26
+ "username" => nil,
27
+ }
28
+ }
29
+ }
30
+
31
+ file_path = ENV['JSON_FILE']
32
+ if file_path.nil? || file_path.empty?
33
+ return
34
+ end
35
+
36
+ Benchmark.memory { |x|
37
+ x.report("project (yajl)") { Yajl::Projector.new(File.open(file_path, 'r')).project(schema) }
38
+ x.compare!
39
+ }
40
+ end
41
+ end
@@ -0,0 +1,498 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper.rb')
2
+
3
+ require 'stringio'
4
+ require 'json'
5
+
6
+ describe "projection" do
7
+ it "should work" do
8
+ stream = StringIO.new('{"name": "keith", "age": 27}')
9
+ projector = Yajl::Projector.new(stream)
10
+ projection = projector.project({"name" => nil})
11
+ expect(projection['name']).to eql("keith")
12
+ end
13
+
14
+ it "should filter" do
15
+ stream = StringIO.new('{"name": "keith", "age": 27}')
16
+ projector = Yajl::Projector.new(stream)
17
+ projection = projector.project({"name" => nil})
18
+ expect(projection['age']).to eql(nil)
19
+ end
20
+
21
+ it "should raise an exception and not leak memory" do
22
+ stream = StringIO.new('foo')
23
+ projector = Yajl::Projector.new(stream)
24
+ expect {
25
+ projector.project({"name" => nil})
26
+ }.to raise_error(Yajl::ParseError)
27
+ end
28
+
29
+ it "should raise an exception and not segv" do
30
+ stream = StringIO.new('[,,,,]')
31
+ projector = Yajl::Projector.new(stream)
32
+ expect {
33
+ projector.project({"name" => nil})
34
+ }.to raise_error(Yajl::ParseError)
35
+ end
36
+
37
+ it "should raise an exception and not segv on colons" do
38
+ stream = StringIO.new('[::::]')
39
+ projector = Yajl::Projector.new(stream)
40
+ expect {
41
+ projector.project({"name" => nil})
42
+ }.to raise_error(Yajl::ParseError)
43
+ end
44
+
45
+ it "should behave the same way as the regular parser on bad tokens like comma" do
46
+ bad_json = '{"name": "keith", "age":, 27}'
47
+ stream = StringIO.new(bad_json)
48
+ projector = Yajl::Projector.new(stream)
49
+ expect {
50
+ projector.project({"name" => nil})
51
+ }.to raise_error(capture_exception_for(bad_json).class)
52
+ end
53
+
54
+ it "should behave the same way as the regular parser on bad tokens like colon" do
55
+ bad_json = '{"name": "keith", "age":: 27}'
56
+ stream = StringIO.new(bad_json)
57
+ projector = Yajl::Projector.new(stream)
58
+ expect {
59
+ projector.project({"name" => nil})
60
+ }.to raise_error(capture_exception_for(bad_json).class)
61
+ end
62
+
63
+ it "should behave the same way as the regular parser on not enough json" do
64
+ bad_json = '{"name": "keith", "age":'
65
+ stream = StringIO.new(bad_json)
66
+ projector = Yajl::Projector.new(stream)
67
+ expect {
68
+ projector.project({"name" => nil})
69
+ }.to raise_error(capture_exception_for(bad_json).class)
70
+ end
71
+
72
+ def capture_exception_for(bad_json)
73
+ Yajl::Parser.new.parse(bad_json)
74
+ rescue Exception => e
75
+ e
76
+ end
77
+
78
+ def project(schema, over: "", json: nil, stream: nil)
79
+ if stream.nil?
80
+ if json.nil?
81
+ json = over.to_json
82
+ end
83
+
84
+ stream = StringIO.new(json)
85
+ end
86
+
87
+ Yajl::Projector.new(stream).project(schema)
88
+ end
89
+
90
+ it "filters arrays" do
91
+ json = {
92
+ "users" => [
93
+ {
94
+ "name" => "keith",
95
+ "company" => "internet plumbing inc",
96
+ "department" => "janitorial",
97
+ },
98
+ {
99
+ "name" => "justin",
100
+ "company" => "big blue",
101
+ "department" => "programming?",
102
+ },
103
+ {
104
+ "name" => "alan",
105
+ "company" => "different colour of blue",
106
+ "department" => "drop bear containment",
107
+ }
108
+ ]
109
+ }.to_json
110
+
111
+ puts json
112
+
113
+ schema = {
114
+ # /users is an array of objects, each having many keys we only want name
115
+ "users" => {
116
+ "name" => nil,
117
+ }
118
+ }
119
+
120
+ expect(project(schema, json: json)).to eql({
121
+ "users" => [
122
+ { "name" => "keith" },
123
+ { "name" => "justin" },
124
+ { "name" => "alan" }
125
+ ]
126
+ })
127
+ end
128
+
129
+ it "filters top level arrays" do
130
+ json = [
131
+ {
132
+ "name" => "keith",
133
+ "personal detail" => "thing",
134
+ },
135
+ {
136
+ "name" => "cory",
137
+ "phone number" => "unknown",
138
+ }
139
+ ]
140
+
141
+ schema = {
142
+ "name" => nil,
143
+ }
144
+
145
+ expect(project(schema, over: json)).to eql([
146
+ { "name" => "keith" },
147
+ { "name" => "cory" },
148
+ ])
149
+ end
150
+
151
+ it "filters nested schemas" do
152
+ json = {
153
+ "foo" => 42,
154
+
155
+ "bar" => {
156
+ "name" => "keith",
157
+ "occupation" => "professional computering",
158
+ "age" => 26,
159
+ "hobbies" => [
160
+ "not computering",
161
+ ]
162
+ },
163
+
164
+ "qux" => {
165
+ "quux" => [
166
+ {
167
+ "name" => "Reactive X",
168
+ "members" => "many",
169
+ },
170
+ {
171
+ "name" => "lstoll",
172
+ "members" => "such",
173
+ },
174
+ {
175
+ "name" => "github",
176
+ "members" => "very",
177
+ },
178
+ {
179
+ "name" => "theleague",
180
+ "members" => "numerous",
181
+ }
182
+ ],
183
+
184
+ "corge" => {
185
+ "name" => "Brighton",
186
+ "address" =>"Buckingham Road",
187
+ },
188
+ },
189
+
190
+ "grault" => nil,
191
+
192
+ "waldo" => true,
193
+ }
194
+
195
+ schema = {
196
+ # include the /foo subtree (is a single number)
197
+ "foo" => nil,
198
+
199
+ # ignore the bar subtree (is an object)
200
+ # "bar" => ???
201
+
202
+ # include some of the /qux subtree (is an object)
203
+ "qux" => {
204
+ # include the whole /qux/quux subtree (is an array of objects)
205
+ "quux" => nil,
206
+
207
+ # include some of the /qux/corge subtree (is another object)
208
+ "corge" => {
209
+ # include name (is a string)
210
+ "name" => nil,
211
+ # include age (is missing from source doc)
212
+ "age" => nil,
213
+ # ignore address
214
+ # "address" => ???
215
+ },
216
+ },
217
+
218
+ # include the /grault subtree (is a null literal)
219
+ "grault" => nil,
220
+
221
+ # include the /waldo subtree (is a boolean literal)
222
+ "waldo" => nil,
223
+ }
224
+
225
+ expect(project(schema, over: json)).to eql({
226
+ "foo" => 42,
227
+
228
+ "qux" => {
229
+ "quux" => [
230
+ {
231
+ "name" => "Reactive X",
232
+ "members" => "many",
233
+ },
234
+ {
235
+ "name" => "lstoll",
236
+ "members" => "such",
237
+ },
238
+ {
239
+ "name" => "github",
240
+ "members" => "very",
241
+ },
242
+ {
243
+ "name" => "theleague",
244
+ "members" => "numerous",
245
+ }
246
+ ],
247
+
248
+ "corge" => {
249
+ "name" => "Brighton",
250
+ },
251
+ },
252
+
253
+ "grault" => nil,
254
+
255
+ "waldo" => true,
256
+ })
257
+ end
258
+
259
+ it "supports incompatible schemas" do
260
+ json = {
261
+ # surprise! the json doesn't include an object under the foo key
262
+ "foo" => 42,
263
+ }
264
+
265
+ schema = {
266
+ # include some of the /foo subtree
267
+ "foo" => {
268
+ # include the whole /foo/baz subtree
269
+ "baz" => nil,
270
+ }
271
+ }
272
+
273
+ # expect the 42 to be pulled out
274
+ expect(project(schema, over: json)).to eql({
275
+ "foo" => 42
276
+ })
277
+ end
278
+
279
+ it "supports nil schema" do
280
+ json = {
281
+ "foo" => "bar",
282
+ }
283
+
284
+ expect(project(nil, over: json)).to eql({
285
+ "foo" => "bar"
286
+ })
287
+ end
288
+
289
+ it "supports empty schema" do
290
+ json = {
291
+ "foo" => "bar",
292
+ }
293
+ expect(project({}, over: json)).to eql({})
294
+ end
295
+
296
+ it "supports object projection" do
297
+ json = {
298
+ "foo" => "bar",
299
+ "qux" => "quux",
300
+ }
301
+
302
+ schema = {
303
+ "foo" => nil,
304
+ }
305
+
306
+ expect(project(schema, over: json)).to eql({
307
+ "foo" => "bar"
308
+ })
309
+ end
310
+
311
+ it "projects the readme example" do
312
+ json = <<-EOJ
313
+ [
314
+ {
315
+ "user": {
316
+ "name": "keith",
317
+ "age": 26,
318
+ "jobs": [
319
+ {
320
+ "title": "director of overworking",
321
+ "company": "south coast software",
322
+ "department": "most"
323
+ },
324
+ {
325
+ "title": "some kind of computering",
326
+ "company": "github the website dot com",
327
+ "department": true
328
+ }
329
+ ]
330
+ },
331
+ "another key": {
332
+
333
+ },
334
+ "woah this document is huge": {
335
+
336
+ },
337
+ "many megabytes": {
338
+
339
+ },
340
+ "etc": {
341
+
342
+ }
343
+ }
344
+ ]
345
+ EOJ
346
+
347
+ schema = {
348
+ "user" => {
349
+ "name" => nil,
350
+ "jobs" => {
351
+ "title" => nil,
352
+ },
353
+ },
354
+ }
355
+
356
+ expect(project(schema, json: json)).to eql([{
357
+ "user" => {
358
+ "name" => "keith",
359
+ "jobs" => [
360
+ { "title" => "director of overworking" },
361
+ { "title" => "some kind of computering" },
362
+ ]
363
+ }
364
+ }])
365
+ end
366
+
367
+ it "errors with invalid json" do
368
+ expect {
369
+ project({"b" => nil}, json: '{"a":, "b": 2}')
370
+ }.to raise_error(StandardError)
371
+ end
372
+
373
+ it "errors with ignored unbalanced object syntax" do
374
+ expect {
375
+ project({"b" => nil}, json: '{"a": {{, "b": 2}')
376
+ }.to raise_error(StandardError)
377
+ end
378
+
379
+ it "errors with accepted unbalanced object tokens" do
380
+ expect {
381
+ project({"a" => nil}, json: '{"a": {"b": 2}')
382
+ }.to raise_error(Yajl::ParseError)
383
+ end
384
+
385
+ it "errors when projecting if an object comma is missing" do
386
+ expect {
387
+ project({"a" => nil}, json: '{"a": 1 "b": 2}')
388
+ }.to raise_error(Yajl::ParseError)
389
+ end
390
+
391
+ it "errors when building if an object comma is missing" do
392
+ expect {
393
+ project(nil, json: '{"a": {"b": 2 "c": 3}}')
394
+ }.to raise_error(Yajl::ParseError)
395
+ end
396
+
397
+ it "errors when eof instead of simple value" do
398
+ expect {
399
+ project(nil, json: '[')
400
+ }.to raise_error(Yajl::ParseError)
401
+ end
402
+
403
+ it "errors when arrays don't have a comma between elements" do
404
+ expect {
405
+ project(nil, json: '[1 2]')
406
+ }.to raise_error(Yajl::ParseError)
407
+ end
408
+
409
+ it "supports parsing empty array" do
410
+ expect(project(nil, json: '[]')).to eql([])
411
+ end
412
+
413
+ it "supports parsing empty object" do
414
+ expect(project(nil, json: '{}')).to eql({})
415
+ end
416
+
417
+ it "reads a full buffer" do
418
+ json = "[" + "1,"*2046 + "1 ]"
419
+ expect(json.size).to eql(4096)
420
+ expect(project(nil, json: json)).to eql(Array.new(2047, 1))
421
+ end
422
+
423
+ it "reads into a second buffer" do
424
+ json = "[" + "1,"*2047 + "1 ]"
425
+ expect(json.size).to eql(4098)
426
+ expect(JSON.parse(json)).to eql(Array.new(2048, 1))
427
+ expect(project(nil, json: json)).to eql(Array.new(2048, 1))
428
+ end
429
+
430
+ it "supports parsing big strings" do
431
+ json = [
432
+ "a",
433
+ "b"*10_000,
434
+ "c",
435
+ ]
436
+ expect(project(nil, over: json)).to eql(json)
437
+ end
438
+
439
+ it "supports bigger read buffers" do
440
+ json = {
441
+ "a"*10_000 => "b"*10_000
442
+ }.to_json
443
+ stream = StringIO.new(json)
444
+ expect(Yajl::Projector.new(stream, 8192).project(nil)).to have_key("a"*10_000)
445
+ end
446
+
447
+ it "errors if starting with closing object" do
448
+ expect {
449
+ project(nil, json: '}')
450
+ }.to raise_error(Yajl::ParseError)
451
+ end
452
+
453
+ it "handles objects with utf16 escape sequences as keys" do
454
+ projection = project(nil, json: '{"\ud83d\ude00": "grinning face"}')
455
+ literal = {"😀" => "grinning face"}
456
+ expect(projection).to eql(literal)
457
+ end
458
+
459
+ it "handles objects with non-ascii utf8 bytes as keys" do
460
+ expect(project(nil, json: '{"😀": "grinning face"}')).to eql({"😀" => "grinning face"})
461
+ end
462
+
463
+ it "handles strings with utf16 escape sequences as object values" do
464
+ expect(project(nil, json: '{"grinning face": "\ud83d\ude00"}')).to eql({"grinning face" => "😀"})
465
+ end
466
+
467
+ it "handles strings with utf16 escape sequences as array values" do
468
+ projection = project(nil, json: '["\ud83d\ude00"]')
469
+ puts projection.first.inspect
470
+ puts projection.first.bytes
471
+
472
+ literal = ["😀"]
473
+ puts literal.first.inspect
474
+ puts literal.first.bytes
475
+
476
+ expect(projection).to eql(literal)
477
+ end
478
+
479
+ it "handles strings with non-ascii utf8 bytes as array values" do
480
+ projection = project(nil, json: '["😀"]')
481
+ puts projection.first.inspect
482
+ puts projection.first.bytes
483
+
484
+ literal = ["😀"]
485
+ puts literal.first.inspect
486
+ puts literal.first.bytes
487
+
488
+ expect(projection).to eql(literal)
489
+ end
490
+
491
+ it "ignores strings with utf16 escape sequences" do
492
+ expect(project({"grinning face with open mouth" => nil}, json: '{"grinning face": "\ud83d\ude00", "grinning face with open mouth": "\ud83d\ude03"}')).to eql({"grinning face with open mouth" => "😃"})
493
+ end
494
+
495
+ it "handles objects whose second key has escape sequences" do
496
+ expect(project(nil, json: '{"foo": "bar", "\ud83d\ude00": "grinning face"}')).to eql({"foo" => "bar", "😀" => "grinning face"})
497
+ end
498
+ end
@@ -22,5 +22,6 @@ Gem::Specification.new do |s|
22
22
  # benchmarks
23
23
  s.add_development_dependency 'activesupport', '~> 3.1.2'
24
24
  s.add_development_dependency 'json'
25
+ s.add_development_dependency "benchmark-memory", "~> 0.1"
25
26
  end
26
27
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yajl-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.1
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian Lopez
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2017-11-07 00:00:00.000000000 Z
12
+ date: 2018-04-27 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake-compiler
@@ -67,6 +67,20 @@ dependencies:
67
67
  - - ">="
68
68
  - !ruby/object:Gem::Version
69
69
  version: '0'
70
+ - !ruby/object:Gem::Dependency
71
+ name: benchmark-memory
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: '0.1'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: '0.1'
70
84
  description:
71
85
  email: seniorlopez@gmail.com
72
86
  executables: []
@@ -221,6 +235,8 @@ files:
221
235
  - spec/parsing/fixtures_spec.rb
222
236
  - spec/parsing/large_number_spec.rb
223
237
  - spec/parsing/one_off_spec.rb
238
+ - spec/projection/project_file.rb
239
+ - spec/projection/projection.rb
224
240
  - spec/rcov.opts
225
241
  - spec/spec_helper.rb
226
242
  - tasks/compile.rake
@@ -337,5 +353,7 @@ test_files:
337
353
  - spec/parsing/fixtures_spec.rb
338
354
  - spec/parsing/large_number_spec.rb
339
355
  - spec/parsing/one_off_spec.rb
356
+ - spec/projection/project_file.rb
357
+ - spec/projection/projection.rb
340
358
  - spec/rcov.opts
341
359
  - spec/spec_helper.rb