json 1.1.0 → 1.1.1

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

Potentially problematic release.


This version of json might be problematic. Click here for more details.

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
@@ -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");