json_pure 1.1.0 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES CHANGED
@@ -1,3 +1,21 @@
1
+ 2007-07-06 (1.1.1)
2
+ * Yui NARUSE <naruse@airemix.com> sent some patches to fix tests for Ruby
3
+ 1.9. I applied them and adapted some of them a bit to run both on 1.8 and
4
+ 1.9.
5
+ * Introduced a JSON.parse! method without depth checking for people who like
6
+ danger.
7
+ * Made generate and pretty_generate methods configurable by an options hash.
8
+ * Added :allow_nan option to parser and generator in order to handle NaN,
9
+ Infinity, and -Infinity correctly - if requested. Floats, which aren't numbers,
10
+ aren't valid JSON according to RFC4627, so by default an exception will be
11
+ raised if any of these symbols are encountered. Thanks to Andrea Censi
12
+ <andrea.censi@dis.uniroma1.it> for his hint about this.
13
+ * Fixed some more tests for Ruby 1.9.
14
+ * Implemented dump/load interface of Marshal as suggested in ruby-core:11405
15
+ by murphy <murphy@rubychan.de>.
16
+ * Implemented the max_nesting feature for generate methods, too.
17
+ * Added some implementations for ruby core's custom objects for
18
+ serialisation/deserialisation purposes.
1
19
  2007-05-21 (1.1.0)
2
20
  * Implemented max_nesting feature for parser to avoid stack overflows for
3
21
  data from untrusted sources. If you trust the source, you can disable it
data/Rakefile CHANGED
@@ -124,12 +124,12 @@ end
124
124
 
125
125
  desc "Benchmarking parser (pure)"
126
126
  task :benchmark_parser_pure do
127
- ruby '-I lib benchmarks/benchmark_parser.rb'
127
+ ruby '-I lib benchmarks/benchmark_parser.rb pure'
128
128
  end
129
129
 
130
130
  desc "Benchmarking generator (pure)"
131
131
  task :benchmark_generator_pure do
132
- ruby '-I lib benchmarks/benchmark_generator.rb'
132
+ ruby '-I lib benchmarks/benchmark_generator.rb pure'
133
133
  ruby 'benchmarks/benchmark_rails.rb'
134
134
  end
135
135
 
@@ -138,12 +138,12 @@ task :benchmark_pure => [ :benchmark_parser_pure, :benchmark_generator_pure ]
138
138
 
139
139
  desc "Benchmarking parser (extension)"
140
140
  task :benchmark_parser_ext => :compile do
141
- ruby '-I ext:lib benchmarks/benchmark_parser.rb'
141
+ ruby '-I ext:lib benchmarks/benchmark_parser.rb ext'
142
142
  end
143
143
 
144
144
  desc "Benchmarking generator (extension)"
145
145
  task :benchmark_generator_ext => :compile do
146
- ruby '-I ext:lib benchmarks/benchmark_generator.rb'
146
+ ruby '-I ext:lib benchmarks/benchmark_generator.rb ext'
147
147
  ruby 'benchmarks/benchmark_rails.rb'
148
148
  end
149
149
 
@@ -164,7 +164,7 @@ end
164
164
 
165
165
  desc "Create RDOC documentation"
166
166
  task :doc => [ :version, EXT_PARSER_SRC ] do
167
- sh "rdoc -m JSON -d -S -o doc #{FileList['lib/**/*.rb']} #{EXT_PARSER_SRC} #{EXT_GENERATOR_SRC}"
167
+ sh "rdoc -m JSON -S -o doc #{FileList['lib/**/*.rb']} #{EXT_PARSER_SRC} #{EXT_GENERATOR_SRC}"
168
168
  end
169
169
 
170
170
  spec_pure = Gem::Specification.new do |s|
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.1.0
1
+ 1.1.1
@@ -2,7 +2,11 @@
2
2
 
3
3
  require 'bullshit'
4
4
  $KCODE='utf8'
5
- require 'json'
5
+ if ARGV.shift == 'pure'
6
+ require 'json/pure'
7
+ else
8
+ require 'json/ext'
9
+ end
6
10
 
7
11
  class BC_Generator < Bullshit::TimeCase
8
12
  include JSON
@@ -1,7 +1,11 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require 'bullshit'
4
- require 'json'
4
+ if ARGV.shift == 'pure'
5
+ require 'json/pure'
6
+ else
7
+ require 'json/ext'
8
+ end
5
9
 
6
10
  class BC_Parser < Bullshit::TimeCase
7
11
  include JSON
data/bin/edit_json.rb CHANGED
@@ -1,11 +1,11 @@
1
- #!/usr/bin/env ruby
2
- $KCODE = 'U'
3
- require 'json/editor'
4
-
5
- filename, encoding = ARGV
6
- JSON::Editor.start(encoding) do |window|
7
- if filename and File.exist?(filename)
8
- window.file_open(filename)
9
- end
10
- end
11
- # vim: set et sw=2 ts=2:
1
+ #!/usr/bin/env ruby
2
+ $KCODE = 'U'
3
+ require 'json/editor'
4
+
5
+ filename, encoding = ARGV
6
+ JSON::Editor.start(encoding) do |window|
7
+ if filename
8
+ window.file_open(filename)
9
+ end
10
+ end
11
+ # vim: set et sw=2 ts=2:
@@ -2,8 +2,8 @@ require 'mkmf'
2
2
  require 'rbconfig'
3
3
 
4
4
  if CONFIG['CC'] =~ /gcc/
5
- CONFIG['CC'] += ' -Wall -ggdb'
6
- #CONFIG['CC'] += ' -Wall'
5
+ #$CFLAGS += ' -Wall -ggdb'
6
+ $CFLAGS += ' -Wall'
7
7
  end
8
8
 
9
9
  create_makefile 'generator'
@@ -4,15 +4,22 @@
4
4
  #include "ruby.h"
5
5
  #include "st.h"
6
6
  #include "unicode.h"
7
+ #include <math.h>
8
+
9
+ #define check_max_nesting(state, depth) do { \
10
+ long current_nesting = 1 + depth; \
11
+ if (state->max_nesting != 0 && current_nesting > state->max_nesting) \
12
+ rb_raise(eNestingError, "nesting of %u is too deep", current_nesting); \
13
+ } while (0);
7
14
 
8
15
  static VALUE mJSON, mExt, mGenerator, cState, mGeneratorMethods, mObject,
9
16
  mHash, mArray, mInteger, mFloat, mString, mString_Extend,
10
17
  mTrueClass, mFalseClass, mNilClass, eGeneratorError,
11
- eCircularDatastructure;
18
+ eCircularDatastructure, eNestingError;
12
19
 
13
20
  static ID i_to_s, i_to_json, i_new, i_indent, i_space, i_space_before,
14
- i_object_nl, i_array_nl, i_check_circular, i_pack, i_unpack,
15
- i_create_id, i_extend;
21
+ i_object_nl, i_array_nl, i_check_circular, i_max_nesting,
22
+ i_allow_nan, i_pack, i_unpack, i_create_id, i_extend;
16
23
 
17
24
  typedef struct JSON_Generator_StateStruct {
18
25
  VALUE indent;
@@ -24,7 +31,9 @@ typedef struct JSON_Generator_StateStruct {
24
31
  VALUE seen;
25
32
  VALUE memo;
26
33
  VALUE depth;
34
+ long max_nesting;
27
35
  int flag;
36
+ int allow_nan;
28
37
  } JSON_Generator_State;
29
38
 
30
39
  #define GET_STATE(self) \
@@ -138,6 +147,7 @@ static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self)
138
147
  rb_str_buf_cat2(result, "}");
139
148
  } else {
140
149
  GET_STATE(Vstate);
150
+ check_max_nesting(state, depth);
141
151
  if (state->check_circular) {
142
152
  VALUE self_id = rb_obj_id(self);
143
153
  if (RTEST(rb_hash_aref(state->seen, self_id))) {
@@ -162,6 +172,7 @@ inline static VALUE mArray_json_transfrom(VALUE self, VALUE Vstate, VALUE Vdepth
162
172
  VALUE delim = rb_str_new2(",");
163
173
  GET_STATE(Vstate);
164
174
 
175
+ check_max_nesting(state, depth);
165
176
  if (state->check_circular) {
166
177
  VALUE self_id = rb_obj_id(self);
167
178
  rb_hash_aset(state->seen, self_id, Qtrue);
@@ -170,6 +181,7 @@ inline static VALUE mArray_json_transfrom(VALUE self, VALUE Vstate, VALUE Vdepth
170
181
  shift = rb_str_times(state->indent, LONG2FIX(depth + 1));
171
182
 
172
183
  rb_str_buf_cat2(result, "[");
184
+ OBJ_INFECT(result, self);
173
185
  rb_str_buf_append(result, state->array_nl);
174
186
  for (i = 0; i < len; i++) {
175
187
  VALUE element = RARRAY_PTR(self)[i];
@@ -190,6 +202,7 @@ inline static VALUE mArray_json_transfrom(VALUE self, VALUE Vstate, VALUE Vdepth
190
202
  rb_hash_delete(state->seen, self_id);
191
203
  } else {
192
204
  result = rb_str_buf_new(len);
205
+ OBJ_INFECT(result, self);
193
206
  if (RSTRING_LEN(state->array_nl)) rb_str_append(delim, state->array_nl);
194
207
  shift = rb_str_times(state->indent, LONG2FIX(depth + 1));
195
208
 
@@ -228,6 +241,7 @@ static VALUE mArray_to_json(int argc, VALUE *argv, VALUE self) {
228
241
  long i, len = RARRAY_LEN(self);
229
242
  result = rb_str_buf_new(2 + 2 * len);
230
243
  rb_str_buf_cat2(result, "[");
244
+ OBJ_INFECT(result, self);
231
245
  for (i = 0; i < len; i++) {
232
246
  VALUE element = RARRAY_PTR(self)[i];
233
247
  OBJ_INFECT(result, element);
@@ -259,7 +273,28 @@ static VALUE mInteger_to_json(int argc, VALUE *argv, VALUE self)
259
273
  */
260
274
  static VALUE mFloat_to_json(int argc, VALUE *argv, VALUE self)
261
275
  {
262
- return rb_funcall(self, i_to_s, 0);
276
+ JSON_Generator_State *state = NULL;
277
+ VALUE Vstate, rest, tmp;
278
+ double value = RFLOAT(self)->value;
279
+ rb_scan_args(argc, argv, "01*", &Vstate, &rest);
280
+ if (!NIL_P(Vstate)) Data_Get_Struct(Vstate, JSON_Generator_State, state);
281
+ if (isinf(value)) {
282
+ if (!state || state->allow_nan) {
283
+ return rb_funcall(self, i_to_s, 0);
284
+ } else {
285
+ tmp = rb_funcall(self, i_to_s, 0);
286
+ rb_raise(eGeneratorError, "%s not allowed in JSON", StringValueCStr(tmp));
287
+ }
288
+ } else if (isnan(value)) {
289
+ if (!state || state->allow_nan) {
290
+ return rb_funcall(self, i_to_s, 0);
291
+ } else {
292
+ tmp = rb_funcall(self, i_to_s, 0);
293
+ rb_raise(eGeneratorError, "%s not allowed in JSON", StringValueCStr(tmp));
294
+ }
295
+ } else {
296
+ return rb_funcall(self, i_to_s, 0);
297
+ }
263
298
  }
264
299
 
265
300
  /*
@@ -403,6 +438,92 @@ static VALUE cState_s_allocate(VALUE klass)
403
438
  return Data_Wrap_Struct(klass, State_mark, -1, state);
404
439
  }
405
440
 
441
+ /*
442
+ * call-seq: configure(opts)
443
+ *
444
+ * Configure this State instance with the Hash _opts_, and return
445
+ * itself.
446
+ */
447
+ static inline VALUE cState_configure(VALUE self, VALUE opts)
448
+ {
449
+ VALUE tmp;
450
+ GET_STATE(self);
451
+ tmp = rb_convert_type(opts, T_HASH, "Hash", "to_hash");
452
+ if (NIL_P(tmp)) tmp = rb_convert_type(opts, T_HASH, "Hash", "to_h");
453
+ if (NIL_P(tmp)) {
454
+ rb_raise(rb_eArgError, "opts has to be hash like or convertable into a hash");
455
+ }
456
+ opts = tmp;
457
+ tmp = rb_hash_aref(opts, ID2SYM(i_indent));
458
+ if (RTEST(tmp)) {
459
+ Check_Type(tmp, T_STRING);
460
+ state->indent = tmp;
461
+ }
462
+ tmp = rb_hash_aref(opts, ID2SYM(i_space));
463
+ if (RTEST(tmp)) {
464
+ Check_Type(tmp, T_STRING);
465
+ state->space = tmp;
466
+ }
467
+ tmp = rb_hash_aref(opts, ID2SYM(i_space_before));
468
+ if (RTEST(tmp)) {
469
+ Check_Type(tmp, T_STRING);
470
+ state->space_before = tmp;
471
+ }
472
+ tmp = rb_hash_aref(opts, ID2SYM(i_array_nl));
473
+ if (RTEST(tmp)) {
474
+ Check_Type(tmp, T_STRING);
475
+ state->array_nl = tmp;
476
+ }
477
+ tmp = rb_hash_aref(opts, ID2SYM(i_object_nl));
478
+ if (RTEST(tmp)) {
479
+ Check_Type(tmp, T_STRING);
480
+ state->object_nl = tmp;
481
+ }
482
+ tmp = ID2SYM(i_check_circular);
483
+ if (st_lookup(RHASH(opts)->tbl, tmp, 0)) {
484
+ tmp = rb_hash_aref(opts, ID2SYM(i_check_circular));
485
+ state->check_circular = RTEST(tmp);
486
+ } else {
487
+ state->check_circular = 1;
488
+ }
489
+ tmp = ID2SYM(i_max_nesting);
490
+ state->max_nesting = 19;
491
+ if (st_lookup(RHASH(opts)->tbl, tmp, 0)) {
492
+ VALUE max_nesting = rb_hash_aref(opts, tmp);
493
+ if (RTEST(max_nesting)) {
494
+ Check_Type(max_nesting, T_FIXNUM);
495
+ state->max_nesting = FIX2LONG(max_nesting);
496
+ } else {
497
+ state->max_nesting = 0;
498
+ }
499
+ }
500
+ tmp = rb_hash_aref(opts, ID2SYM(i_allow_nan));
501
+ state->allow_nan = RTEST(tmp);
502
+ return self;
503
+ }
504
+
505
+ /*
506
+ * call-seq: to_h
507
+ *
508
+ * Returns the configuration instance variables as a hash, that can be
509
+ * passed to the configure method.
510
+ */
511
+ static VALUE cState_to_h(VALUE self)
512
+ {
513
+ VALUE result = rb_hash_new();
514
+ GET_STATE(self);
515
+ rb_hash_aset(result, ID2SYM(i_indent), state->indent);
516
+ rb_hash_aset(result, ID2SYM(i_space), state->space);
517
+ rb_hash_aset(result, ID2SYM(i_space_before), state->space_before);
518
+ rb_hash_aset(result, ID2SYM(i_object_nl), state->object_nl);
519
+ rb_hash_aset(result, ID2SYM(i_array_nl), state->array_nl);
520
+ rb_hash_aset(result, ID2SYM(i_check_circular), state->check_circular ? Qtrue : Qfalse);
521
+ rb_hash_aset(result, ID2SYM(i_allow_nan), state->allow_nan ? Qtrue : Qfalse);
522
+ rb_hash_aset(result, ID2SYM(i_max_nesting), LONG2FIX(state->max_nesting));
523
+ return result;
524
+ }
525
+
526
+
406
527
  /*
407
528
  * call-seq: new(opts = {})
408
529
  *
@@ -417,6 +538,9 @@ static VALUE cState_s_allocate(VALUE klass)
417
538
  * * *array_nl*: a string that is put at the end of a JSON array (default: ''),
418
539
  * * *check_circular*: true if checking for circular data structures
419
540
  * should be done, false (the default) otherwise.
541
+ * * *allow_nan*: true if NaN, Infinity, and -Infinity should be
542
+ * generated, otherwise an exception is thrown, if these values are
543
+ * encountered. This options defaults to false.
420
544
  */
421
545
  static VALUE cState_initialize(int argc, VALUE *argv, VALUE self)
422
546
  {
@@ -424,53 +548,17 @@ static VALUE cState_initialize(int argc, VALUE *argv, VALUE self)
424
548
  GET_STATE(self);
425
549
 
426
550
  rb_scan_args(argc, argv, "01", &opts);
551
+ state->indent = rb_str_new2("");
552
+ state->space = rb_str_new2("");
553
+ state->space_before = rb_str_new2("");
554
+ state->array_nl = rb_str_new2("");
555
+ state->object_nl = rb_str_new2("");
427
556
  if (NIL_P(opts)) {
428
- state->indent = rb_str_new2("");
429
- state->space = rb_str_new2("");
430
- state->space_before = rb_str_new2("");
431
- state->array_nl = rb_str_new2("");
432
- state->object_nl = rb_str_new2("");
433
- state->check_circular = 0;
557
+ state->check_circular = 1;
558
+ state->allow_nan = 0;
559
+ state->max_nesting = 19;
434
560
  } else {
435
- VALUE tmp;
436
- opts = rb_convert_type(opts, T_HASH, "Hash", "to_hash");
437
- tmp = rb_hash_aref(opts, ID2SYM(i_indent));
438
- if (RTEST(tmp)) {
439
- Check_Type(tmp, T_STRING);
440
- state->indent = tmp;
441
- } else {
442
- state->indent = rb_str_new2("");
443
- }
444
- tmp = rb_hash_aref(opts, ID2SYM(i_space));
445
- if (RTEST(tmp)) {
446
- Check_Type(tmp, T_STRING);
447
- state->space = tmp;
448
- } else {
449
- state->space = rb_str_new2("");
450
- }
451
- tmp = rb_hash_aref(opts, ID2SYM(i_space_before));
452
- if (RTEST(tmp)) {
453
- Check_Type(tmp, T_STRING);
454
- state->space_before = tmp;
455
- } else {
456
- state->space_before = rb_str_new2("");
457
- }
458
- tmp = rb_hash_aref(opts, ID2SYM(i_array_nl));
459
- if (RTEST(tmp)) {
460
- Check_Type(tmp, T_STRING);
461
- state->array_nl = tmp;
462
- } else {
463
- state->array_nl = rb_str_new2("");
464
- }
465
- tmp = rb_hash_aref(opts, ID2SYM(i_object_nl));
466
- if (RTEST(tmp)) {
467
- Check_Type(tmp, T_STRING);
468
- state->object_nl = tmp;
469
- } else {
470
- state->object_nl = rb_str_new2("");
471
- }
472
- tmp = rb_hash_aref(opts, ID2SYM(i_check_circular));
473
- state->check_circular = RTEST(tmp);
561
+ cState_configure(self, opts);
474
562
  }
475
563
  state->seen = rb_hash_new();
476
564
  state->memo = Qnil;
@@ -616,7 +704,7 @@ static VALUE cState_array_nl_set(VALUE self, VALUE array_nl)
616
704
  }
617
705
 
618
706
  /*
619
- * call-seq: check_circular?(object)
707
+ * call-seq: check_circular?
620
708
  *
621
709
  * Returns true, if circular data structures should be checked,
622
710
  * otherwise returns false.
@@ -627,6 +715,44 @@ static VALUE cState_check_circular_p(VALUE self)
627
715
  return state->check_circular ? Qtrue : Qfalse;
628
716
  }
629
717
 
718
+ /*
719
+ * call-seq: max_nesting
720
+ *
721
+ * This integer returns the maximum level of data structure nesting in
722
+ * the generated JSON, max_nesting = 0 if no maximum is checked.
723
+ */
724
+ static VALUE cState_max_nesting(VALUE self)
725
+ {
726
+ GET_STATE(self);
727
+ return LONG2FIX(state->max_nesting);
728
+ }
729
+
730
+ /*
731
+ * call-seq: max_nesting=(depth)
732
+ *
733
+ * This sets the maximum level of data structure nesting in the generated JSON
734
+ * to the integer depth, max_nesting = 0 if no maximum should be checked.
735
+ */
736
+ static VALUE cState_max_nesting_set(VALUE self, VALUE depth)
737
+ {
738
+ GET_STATE(self);
739
+ Check_Type(depth, T_FIXNUM);
740
+ state->max_nesting = FIX2LONG(depth);
741
+ return Qnil;
742
+ }
743
+
744
+ /*
745
+ * call-seq: allow_nan?
746
+ *
747
+ * Returns true, if NaN, Infinity, and -Infinity should be generated, otherwise
748
+ * returns false.
749
+ */
750
+ static VALUE cState_allow_nan_p(VALUE self)
751
+ {
752
+ GET_STATE(self);
753
+ return state->allow_nan ? Qtrue : Qfalse;
754
+ }
755
+
630
756
  /*
631
757
  * call-seq: seen?(object)
632
758
  *
@@ -668,6 +794,7 @@ void Init_generator()
668
794
  mGenerator = rb_define_module_under(mExt, "Generator");
669
795
  eGeneratorError = rb_path2class("JSON::GeneratorError");
670
796
  eCircularDatastructure = rb_path2class("JSON::CircularDatastructure");
797
+ eNestingError = rb_path2class("JSON::NestingError");
671
798
  cState = rb_define_class_under(mGenerator, "State", rb_cObject);
672
799
  rb_define_alloc_func(cState, cState_s_allocate);
673
800
  rb_define_singleton_method(cState, "from_state", cState_from_state_s, 1);
@@ -684,9 +811,15 @@ void Init_generator()
684
811
  rb_define_method(cState, "array_nl", cState_array_nl, 0);
685
812
  rb_define_method(cState, "array_nl=", cState_array_nl_set, 1);
686
813
  rb_define_method(cState, "check_circular?", cState_check_circular_p, 0);
814
+ rb_define_method(cState, "max_nesting", cState_max_nesting, 0);
815
+ rb_define_method(cState, "max_nesting=", cState_max_nesting_set, 1);
816
+ rb_define_method(cState, "allow_nan?", cState_allow_nan_p, 0);
687
817
  rb_define_method(cState, "seen?", cState_seen_p, 1);
688
818
  rb_define_method(cState, "remember", cState_remember, 1);
689
819
  rb_define_method(cState, "forget", cState_forget, 1);
820
+ rb_define_method(cState, "configure", cState_configure, 1);
821
+ rb_define_method(cState, "to_h", cState_to_h, 0);
822
+
690
823
  mGeneratorMethods = rb_define_module_under(mGenerator, "GeneratorMethods");
691
824
  mObject = rb_define_module_under(mGeneratorMethods, "Object");
692
825
  rb_define_method(mObject, "to_json", mObject_to_json, -1);
@@ -721,6 +854,8 @@ void Init_generator()
721
854
  i_object_nl = rb_intern("object_nl");
722
855
  i_array_nl = rb_intern("array_nl");
723
856
  i_check_circular = rb_intern("check_circular");
857
+ i_max_nesting = rb_intern("max_nesting");
858
+ i_allow_nan = rb_intern("allow_nan");
724
859
  i_pack = rb_intern("pack");
725
860
  i_unpack = rb_intern("unpack");
726
861
  i_create_id = rb_intern("create_id");