oj 1.0.6 → 1.1.0

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

Potentially problematic release.


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

data/README.md CHANGED
@@ -24,9 +24,15 @@ A fast JSON parser and Object marshaller as a Ruby gem.
24
24
 
25
25
  ## <a name="release">Release Notes</a>
26
26
 
27
- ### Release 1.0.6
27
+ ### Release 1.1.0
28
28
 
29
- - Gave Oj::Doc a speed increase. It is now 8 times fast than JSON::Ext.
29
+ - Errors are not longer raised when comments are encountered in JSON documents.
30
+
31
+ - Oj can now mimic JSON. With some expections calling JSON.mimic_JSON will allow all JSON calls to use OJ instead of JSON. This gives a speedup of more than 2x on parsing and 5x for generating over the JSON::Ext module.
32
+
33
+ - Oj::Doc now allows a document to be left open and then closed with the Oj::Doc.close() class.
34
+
35
+ - Changed the default encoding to UTF-8 instead of the Ruby default String encoding.
30
36
 
31
37
  ## <a name="description">Description</a>
32
38
 
@@ -33,14 +33,9 @@
33
33
  #include <time.h>
34
34
  #include <stdio.h>
35
35
  #include <string.h>
36
+ #include <math.h>
36
37
 
37
38
  #include "oj.h"
38
- #if IVAR_HELPERS
39
- #include "ruby/st.h"
40
- #else
41
- #include "st.h"
42
- #endif
43
-
44
39
  #include "cache8.h"
45
40
 
46
41
  typedef unsigned long ulong;
@@ -341,8 +336,35 @@ static void
341
336
  dump_float(VALUE obj, Out out) {
342
337
  char buf[64];
343
338
  char *b;
344
- int cnt = sprintf(buf, "%0.16g", RFLOAT_VALUE(obj)); // used sprintf due to bug in snprintf
339
+ double d = RFLOAT_VALUE(obj);
340
+ int cnt;
345
341
 
342
+ switch (fpclassify(d)) {
343
+ case FP_NAN:
344
+ printf("*** nan\n");
345
+ cnt = sprintf(buf, "%0.16g", d); // used sprintf due to bug in snprintf
346
+ break;
347
+ case FP_INFINITE:
348
+ b = buf;
349
+ cnt = 8;
350
+ if (d < 0.0) {
351
+ *b++ = '-';
352
+ cnt++;
353
+ }
354
+ strcpy(b, "Infinity");
355
+ break;
356
+ case FP_ZERO:
357
+ b = buf;
358
+ *b++ = '0';
359
+ *b++ = '.';
360
+ *b++ = '0';
361
+ *b++ = '\0';
362
+ cnt = 3;
363
+ break;
364
+ default:
365
+ cnt = sprintf(buf, "%0.16g", d); // used sprintf due to bug in snprintf
366
+ break;
367
+ }
346
368
  if (out->end - out->cur <= (long)cnt) {
347
369
  grow(out, cnt);
348
370
  }
@@ -520,12 +542,30 @@ dump_array(VALUE a, int depth, Out out) {
520
542
  if (0 < id) {
521
543
  *out->cur++ = ',';
522
544
  }
523
- size = d2 * out->indent + 2;
545
+ if (0 == out->opts->dump_opts) {
546
+ size = d2 * out->indent + 2;
547
+ } else {
548
+ size = d2 * out->opts->dump_opts->indent_size + out->opts->dump_opts->array_size + 1;
549
+ }
524
550
  for (; 0 < cnt; cnt--, np++) {
525
551
  if (out->end - out->cur <= (long)size) {
526
552
  grow(out, size);
527
553
  }
528
- fill_indent(out, d2);
554
+ if (0 == out->opts->dump_opts) {
555
+ fill_indent(out, d2);
556
+ } else {
557
+ if (0 < out->opts->dump_opts->array_size) {
558
+ strcpy(out->cur, out->opts->dump_opts->array_nl);
559
+ out->cur += out->opts->dump_opts->array_size;
560
+ }
561
+ if (0 < out->opts->dump_opts->indent_size) {
562
+ int i;
563
+ for (i = d2; 0 < i; i--) {
564
+ strcpy(out->cur, out->opts->dump_opts->indent);
565
+ out->cur += out->opts->dump_opts->indent_size;
566
+ }
567
+ }
568
+ }
529
569
  dump_val(*np, d2, out);
530
570
  if (1 < cnt) {
531
571
  *out->cur++ = ',';
@@ -535,7 +575,22 @@ dump_array(VALUE a, int depth, Out out) {
535
575
  if (out->end - out->cur <= (long)size) {
536
576
  grow(out, size);
537
577
  }
538
- fill_indent(out, depth);
578
+ if (0 == out->opts->dump_opts) {
579
+ fill_indent(out, depth);
580
+ } else {
581
+ //printf("*** d2: %u indent: %u '%s'\n", d2, out->opts->dump_opts->indent_size, out->opts->dump_opts->indent);
582
+ if (0 < out->opts->dump_opts->array_size) {
583
+ strcpy(out->cur, out->opts->dump_opts->array_nl);
584
+ out->cur += out->opts->dump_opts->array_size;
585
+ }
586
+ if (0 < out->opts->dump_opts->indent_size) {
587
+ int i;
588
+ for (i = depth; 0 < i; i--) {
589
+ strcpy(out->cur, out->opts->dump_opts->indent);
590
+ out->cur += out->opts->dump_opts->indent_size;
591
+ }
592
+ }
593
+ }
539
594
  *out->cur++ = ']';
540
595
  }
541
596
  *out->cur = '\0';
@@ -544,19 +599,51 @@ dump_array(VALUE a, int depth, Out out) {
544
599
  static int
545
600
  hash_cb_strict(VALUE key, VALUE value, Out out) {
546
601
  int depth = out->depth;
547
- long size = depth * out->indent + 1;
602
+ long size;
548
603
 
549
- if (out->end - out->cur <= size) {
550
- grow(out, size);
604
+ if (rb_type(key) != T_STRING) {
605
+ rb_raise(rb_eTypeError, "In :strict mode all Hash keys must be Strings.");
551
606
  }
552
- fill_indent(out, depth);
553
- if (rb_type(key) == T_STRING) {
607
+ if (0 == out->opts->dump_opts) {
608
+ size = depth * out->indent + 1;
609
+ if (out->end - out->cur <= size) {
610
+ grow(out, size);
611
+ }
612
+ fill_indent(out, depth);
554
613
  dump_str_comp(key, out);
555
614
  *out->cur++ = ':';
556
- dump_val(value, depth, out);
557
615
  } else {
558
- rb_raise(rb_eTypeError, "In :strict mode all Hash keys must be Strings.");
616
+ size = depth * out->opts->dump_opts->indent_size + out->opts->dump_opts->hash_size + 1;
617
+ if (out->end - out->cur <= size) {
618
+ grow(out, size);
619
+ }
620
+ if (0 < out->opts->dump_opts->hash_size) {
621
+ strcpy(out->cur, out->opts->dump_opts->hash_nl);
622
+ out->cur += out->opts->dump_opts->hash_size;
623
+ }
624
+ if (0 < out->opts->dump_opts->indent_size) {
625
+ int i;
626
+ for (i = depth; 0 < i; i--) {
627
+ strcpy(out->cur, out->opts->dump_opts->indent);
628
+ out->cur += out->opts->dump_opts->indent_size;
629
+ }
630
+ }
631
+ dump_str_comp(key, out);
632
+ size = out->opts->dump_opts->before_size + out->opts->dump_opts->after_size + 2;
633
+ if (out->end - out->cur <= size) {
634
+ grow(out, size);
635
+ }
636
+ if (0 < out->opts->dump_opts->before_size) {
637
+ strcpy(out->cur, out->opts->dump_opts->before_sep);
638
+ out->cur += out->opts->dump_opts->before_size;
639
+ }
640
+ *out->cur++ = ':';
641
+ if (0 < out->opts->dump_opts->after_size) {
642
+ strcpy(out->cur, out->opts->dump_opts->after_sep);
643
+ out->cur += out->opts->dump_opts->after_size;
644
+ }
559
645
  }
646
+ dump_val(value, depth, out);
560
647
  out->depth = depth;
561
648
  *out->cur++ = ',';
562
649
 
@@ -566,12 +653,31 @@ hash_cb_strict(VALUE key, VALUE value, Out out) {
566
653
  static int
567
654
  hash_cb_compat(VALUE key, VALUE value, Out out) {
568
655
  int depth = out->depth;
569
- long size = depth * out->indent + 1;
656
+ long size;
570
657
 
571
- if (out->end - out->cur <= size) {
572
- grow(out, size);
658
+ if (0 == out->opts->dump_opts) {
659
+ size = depth * out->indent + 1;
660
+ if (out->end - out->cur <= size) {
661
+ grow(out, size);
662
+ }
663
+ fill_indent(out, depth);
664
+ } else {
665
+ size = depth * out->opts->dump_opts->indent_size + out->opts->dump_opts->hash_size + 1;
666
+ if (out->end - out->cur <= size) {
667
+ grow(out, size);
668
+ }
669
+ if (0 < out->opts->dump_opts->hash_size) {
670
+ strcpy(out->cur, out->opts->dump_opts->hash_nl);
671
+ out->cur += out->opts->dump_opts->hash_size;
672
+ }
673
+ if (0 < out->opts->dump_opts->indent_size) {
674
+ int i;
675
+ for (i = depth; 0 < i; i--) {
676
+ strcpy(out->cur, out->opts->dump_opts->indent);
677
+ out->cur += out->opts->dump_opts->indent_size;
678
+ }
679
+ }
573
680
  }
574
- fill_indent(out, depth);
575
681
  switch (rb_type(key)) {
576
682
  case T_STRING:
577
683
  dump_str_comp(key, out);
@@ -583,7 +689,23 @@ hash_cb_compat(VALUE key, VALUE value, Out out) {
583
689
  rb_raise(rb_eTypeError, "In :strict mode all Hash keys must be Strings.");
584
690
  break;
585
691
  }
586
- *out->cur++ = ':';
692
+ if (0 == out->opts->dump_opts) {
693
+ *out->cur++ = ':';
694
+ } else {
695
+ size = out->opts->dump_opts->before_size + out->opts->dump_opts->after_size + 2;
696
+ if (out->end - out->cur <= size) {
697
+ grow(out, size);
698
+ }
699
+ if (0 < out->opts->dump_opts->before_size) {
700
+ strcpy(out->cur, out->opts->dump_opts->before_sep);
701
+ out->cur += out->opts->dump_opts->before_size;
702
+ }
703
+ *out->cur++ = ':';
704
+ if (0 < out->opts->dump_opts->after_size) {
705
+ strcpy(out->cur, out->opts->dump_opts->after_sep);
706
+ out->cur += out->opts->dump_opts->after_size;
707
+ }
708
+ }
587
709
  dump_val(value, depth, out);
588
710
  out->depth = depth;
589
711
  *out->cur++ = ',';
@@ -696,10 +818,28 @@ dump_hash(VALUE obj, int depth, int mode, Out out) {
696
818
  if (',' == *(out->cur - 1)) {
697
819
  out->cur--; // backup to overwrite last comma
698
820
  }
699
- if (out->end - out->cur <= (long)size) {
700
- grow(out, size);
821
+ if (0 == out->opts->dump_opts) {
822
+ if (out->end - out->cur <= (long)size) {
823
+ grow(out, size);
824
+ }
825
+ fill_indent(out, depth);
826
+ } else {
827
+ size = depth * out->opts->dump_opts->indent_size + out->opts->dump_opts->hash_size + 1;
828
+ if (out->end - out->cur <= (long)size) {
829
+ grow(out, size);
830
+ }
831
+ if (0 < out->opts->dump_opts->hash_size) {
832
+ strcpy(out->cur, out->opts->dump_opts->hash_nl);
833
+ out->cur += out->opts->dump_opts->hash_size;
834
+ }
835
+ if (0 < out->opts->dump_opts->indent_size) {
836
+ int i;
837
+ for (i = depth; 0 < i; i--) {
838
+ strcpy(out->cur, out->opts->dump_opts->indent);
839
+ out->cur += out->opts->dump_opts->indent_size;
840
+ }
841
+ }
701
842
  }
702
- fill_indent(out, depth);
703
843
  *out->cur++ = '}';
704
844
  }
705
845
  *out->cur = '\0';
@@ -1034,7 +1174,7 @@ dump_val(VALUE obj, int depth, Out out) {
1034
1174
  default: dump_sym_obj(obj, out); break;
1035
1175
  }
1036
1176
  break;
1037
- case T_ARRAY: dump_array(obj, depth, out); break;
1177
+ case T_ARRAY: dump_array(obj, depth, out); break;
1038
1178
  case T_HASH: dump_hash(obj, depth, out->opts->mode, out); break;
1039
1179
  case T_CLASS:
1040
1180
  switch (out->opts->mode) {
@@ -36,6 +36,8 @@
36
36
 
37
37
  #include "oj.h"
38
38
 
39
+ // maximum to allocate on the stack, arbitrary limit
40
+ #define SMALL_XML 65536
39
41
  #define MAX_STACK 100
40
42
  //#define BATCH_SIZE (4096 / sizeof(struct _Leaf) - 1)
41
43
  #define BATCH_SIZE 100
@@ -55,6 +57,7 @@ typedef struct _Doc {
55
57
  #else
56
58
  void *encoding;
57
59
  #endif
60
+ char *json;
58
61
  unsigned long size; // number of leaves/branches in the doc
59
62
  VALUE self;
60
63
  Batch batches;
@@ -86,9 +89,10 @@ static Leaf read_false(ParseInfo pi);
86
89
  static Leaf read_nil(ParseInfo pi);
87
90
  static void next_non_white(ParseInfo pi);
88
91
  static char* read_quoted_value(ParseInfo pi);
92
+ static void skip_comment(ParseInfo pi);
89
93
 
90
94
  static VALUE protect_open_proc(VALUE x);
91
- static VALUE parse_json(VALUE clas, char *json);
95
+ static VALUE parse_json(VALUE clas, char *json, int given, int allocated);
92
96
  static void each_leaf(Doc doc, VALUE self);
93
97
  static int move_step(Doc doc, const char *path, int loc);
94
98
  static Leaf get_doc_leaf(Doc doc, const char *path);
@@ -123,6 +127,9 @@ next_non_white(ParseInfo pi) {
123
127
  case '\n':
124
128
  case '\r':
125
129
  break;
130
+ case '/':
131
+ skip_comment(pi);
132
+ break;
126
133
  default:
127
134
  return;
128
135
  }
@@ -270,6 +277,46 @@ leaf_value(Doc doc, Leaf leaf) {
270
277
  return leaf->value;
271
278
  }
272
279
 
280
+ inline static Doc
281
+ self_doc(VALUE self) {
282
+ Doc doc = DATA_PTR(self);
283
+
284
+ if (0 == doc) {
285
+ rb_raise(rb_eIOError, "Document already closed or not open.");
286
+ }
287
+ return doc;
288
+ }
289
+
290
+ static void
291
+ skip_comment(ParseInfo pi) {
292
+ pi->s++; // skip first /
293
+ if ('*' == *pi->s) {
294
+ pi->s++;
295
+ for (; '\0' != *pi->s; pi->s++) {
296
+ if ('*' == *pi->s && '/' == *(pi->s + 1)) {
297
+ pi->s++;
298
+ return;
299
+ } else if ('\0' == *pi->s) {
300
+ raise_error("comment not terminated", pi->str, pi->s);
301
+ }
302
+ }
303
+ } else if ('/' == *pi->s) {
304
+ for (; 1; pi->s++) {
305
+ switch (*pi->s) {
306
+ case '\n':
307
+ case '\r':
308
+ case '\f':
309
+ case '\0':
310
+ return;
311
+ default:
312
+ break;
313
+ }
314
+ }
315
+ } else {
316
+ raise_error("invalid comment", pi->str, pi->s);
317
+ }
318
+ }
319
+
273
320
  #ifdef RUBINIUS
274
321
  #define NUM_MAX 0x07FFFFFF
275
322
  #else
@@ -733,6 +780,7 @@ doc_init(Doc doc) {
733
780
  doc->encoding = 0;
734
781
  #endif
735
782
  doc->size = 0;
783
+ doc->json = 0;
736
784
  doc->batches = &doc->batch0;
737
785
  doc->batch0.next = 0;
738
786
  doc->batch0.next_avail = 0;
@@ -760,30 +808,41 @@ protect_open_proc(VALUE x) {
760
808
  pi->doc->data = read_next(pi); // parse
761
809
  *pi->doc->where = pi->doc->data;
762
810
  pi->doc->where = pi->doc->where_path;
763
-
764
- return rb_yield(pi->doc->self); // caller processing
811
+ if (rb_block_given_p()) {
812
+ return rb_yield(pi->doc->self); // caller processing
813
+ }
814
+ return Qnil;
765
815
  }
766
816
 
767
817
  static VALUE
768
- parse_json(VALUE clas, char *json) {
818
+ parse_json(VALUE clas, char *json, int given, int allocated) {
769
819
  struct _ParseInfo pi;
770
820
  VALUE result = Qnil;
771
- struct _Doc doc;
821
+ Doc doc;
772
822
  int ex = 0;
773
823
 
774
- if (!rb_block_given_p()) {
775
- rb_raise(rb_eArgError, "Block or Proc is required.");
824
+ if (given) {
825
+ doc = ALLOCA_N(struct _Doc, 1);
826
+ } else {
827
+ doc = ALLOC_N(struct _Doc, 1);
776
828
  }
777
829
  pi.str = json;
778
830
  pi.s = pi.str;
779
- doc_init(&doc);
780
- pi.doc = &doc;
781
- doc.self = rb_obj_alloc(clas);
782
- DATA_PTR(doc.self) = pi.doc;
831
+ doc_init(doc);
832
+ pi.doc = doc;
833
+ doc->self = rb_obj_alloc(clas);
834
+ doc->json = json;
835
+ DATA_PTR(doc->self) = doc;
783
836
  result = rb_protect(protect_open_proc, (VALUE)&pi, &ex);
784
- DATA_PTR(doc.self) = 0;
785
- doc_free(pi.doc);
786
- //xfree(pi.str);
837
+ if (given || 0 != ex) {
838
+ DATA_PTR(doc->self) = 0;
839
+ doc_free(pi.doc);
840
+ if (allocated) {
841
+ xfree(json);
842
+ }
843
+ } else {
844
+ result = doc->self;
845
+ }
787
846
  if (0 != ex) {
788
847
  rb_jump_tag(ex);
789
848
  }
@@ -1014,32 +1073,51 @@ each_value(Doc doc, Leaf leaf) {
1014
1073
 
1015
1074
  /* call-seq: open(json) { |doc| ... } => Object
1016
1075
  *
1017
- * Parses a JSON document String and then yields to the provided block with an
1018
- * instance of the Oj::Doc as the single yield parameter.
1076
+ * Parses a JSON document String and then yields to the provided block if one
1077
+ * is given with an instance of the Oj::Doc as the single yield parameter. If
1078
+ * a block is not given then an Oj::Doc instance is returned and must be
1079
+ * closed with a call to the #close() method when no longer needed.
1019
1080
  *
1020
1081
  * @param [String] json JSON document string
1021
1082
  * @yieldparam [Oj::Doc] doc parsed JSON document
1022
1083
  * @yieldreturn [Object] returns the result of the yield as the result of the method call
1023
1084
  * @example
1024
1085
  * Oj::Doc.open('[1,2,3]') { |doc| doc.size() } #=> 4
1086
+ * # or as an alternative
1087
+ * doc = Oj::Doc.open('[1,2,3]')
1088
+ * doc.size() #=> 4
1089
+ * doc.close()
1025
1090
  */
1026
1091
  static VALUE
1027
1092
  doc_open(VALUE clas, VALUE str) {
1028
1093
  char *json;
1029
1094
  size_t len;
1095
+ VALUE obj;
1096
+ int given = rb_block_given_p();
1097
+ int allocate;
1030
1098
 
1031
1099
  Check_Type(str, T_STRING);
1032
1100
  len = RSTRING_LEN(str) + 1;
1033
- json = ALLOCA_N(char, len);
1101
+ allocate = (SMALL_XML < len || !given);
1102
+ if (allocate) {
1103
+ json = ALLOC_N(char, len);
1104
+ } else {
1105
+ json = ALLOCA_N(char, len);
1106
+ }
1034
1107
  memcpy(json, StringValuePtr(str), len);
1035
-
1036
- return parse_json(clas, json);
1108
+ obj = parse_json(clas, json, given, allocate);
1109
+ if (given && allocate) {
1110
+ xfree(json);
1111
+ }
1112
+ return obj;
1037
1113
  }
1038
1114
 
1039
1115
  /* call-seq: open_file(filename) { |doc| ... } => Object
1040
1116
  *
1041
- * Parses a JSON document from a file and then yields to the provided block
1042
- * with an instance of the Oj::Doc as the single yield parameter.
1117
+ * Parses a JSON document from a file and then yields to the provided block if
1118
+ * one is given with an instance of the Oj::Doc as the single yield
1119
+ * parameter. If a block is not given then an Oj::Doc instance is returned and
1120
+ * must be closed with a call to the #close() method when no longer needed.
1043
1121
  *
1044
1122
  * @param [String] filename name of file that contains a JSON document
1045
1123
  * @yieldparam [Oj::Doc] doc parsed JSON document
@@ -1047,6 +1125,10 @@ doc_open(VALUE clas, VALUE str) {
1047
1125
  * @example
1048
1126
  * File.open('array.json', 'w') { |f| f.write('[1,2,3]') }
1049
1127
  * Oj::Doc.open_file(filename) { |doc| doc.size() } #=> 4
1128
+ * # or as an alternative
1129
+ * doc = Oj::Doc.open_file(filename)
1130
+ * doc.size() #=> 4
1131
+ * doc.close()
1050
1132
  */
1051
1133
  static VALUE
1052
1134
  doc_open_file(VALUE clas, VALUE filename) {
@@ -1054,6 +1136,9 @@ doc_open_file(VALUE clas, VALUE filename) {
1054
1136
  char *json;
1055
1137
  FILE *f;
1056
1138
  size_t len;
1139
+ VALUE obj;
1140
+ int given = rb_block_given_p();
1141
+ int allocate;
1057
1142
 
1058
1143
  Check_Type(filename, T_STRING);
1059
1144
  path = StringValuePtr(filename);
@@ -1062,7 +1147,12 @@ doc_open_file(VALUE clas, VALUE filename) {
1062
1147
  }
1063
1148
  fseek(f, 0, SEEK_END);
1064
1149
  len = ftell(f);
1065
- json = ALLOCA_N(char, len + 1);
1150
+ allocate = (SMALL_XML < len || !given);
1151
+ if (allocate) {
1152
+ json = ALLOC_N(char, len + 1);
1153
+ } else {
1154
+ json = ALLOCA_N(char, len + 1);
1155
+ }
1066
1156
  fseek(f, 0, SEEK_SET);
1067
1157
  if (len != fread(json, 1, len, f)) {
1068
1158
  fclose(f);
@@ -1070,8 +1160,11 @@ doc_open_file(VALUE clas, VALUE filename) {
1070
1160
  }
1071
1161
  fclose(f);
1072
1162
  json[len] = '\0';
1073
-
1074
- return parse_json(clas, json);
1163
+ obj = parse_json(clas, json, given, allocate);
1164
+ if (given && allocate) {
1165
+ xfree(json);
1166
+ }
1167
+ return obj;
1075
1168
  }
1076
1169
 
1077
1170
  /* Document-method: parse
@@ -1085,7 +1178,7 @@ doc_open_file(VALUE clas, VALUE filename) {
1085
1178
  */
1086
1179
  static VALUE
1087
1180
  doc_where(VALUE self) {
1088
- Doc doc = DATA_PTR(self);
1181
+ Doc doc = self_doc(self);
1089
1182
 
1090
1183
  if (0 == *doc->where_path || doc->where == doc->where_path) {
1091
1184
  return oj_slash_string;
@@ -1116,7 +1209,7 @@ doc_where(VALUE self) {
1116
1209
  *p++ = '/';
1117
1210
  }
1118
1211
  *--p = '\0';
1119
- return rb_str_new2(path);
1212
+ return rb_str_new(path, p - path);
1120
1213
  }
1121
1214
  }
1122
1215
 
@@ -1130,7 +1223,7 @@ doc_where(VALUE self) {
1130
1223
  */
1131
1224
  static VALUE
1132
1225
  doc_local_key(VALUE self) {
1133
- Doc doc = DATA_PTR(self);
1226
+ Doc doc = self_doc(self);
1134
1227
  Leaf leaf = *doc->where;
1135
1228
  VALUE key = Qnil;
1136
1229
 
@@ -1156,7 +1249,7 @@ doc_local_key(VALUE self) {
1156
1249
  */
1157
1250
  static VALUE
1158
1251
  doc_home(VALUE self) {
1159
- Doc doc = DATA_PTR(self);
1252
+ Doc doc = self_doc(self);
1160
1253
 
1161
1254
  *doc->where_path = doc->data;
1162
1255
  doc->where = doc->where_path;
@@ -1177,7 +1270,7 @@ doc_home(VALUE self) {
1177
1270
  */
1178
1271
  static VALUE
1179
1272
  doc_type(int argc, VALUE *argv, VALUE self) {
1180
- Doc doc = DATA_PTR(self);
1273
+ Doc doc = self_doc(self);
1181
1274
  Leaf leaf;
1182
1275
  const char *path = 0;
1183
1276
  VALUE type = Qnil;
@@ -1216,7 +1309,7 @@ doc_type(int argc, VALUE *argv, VALUE self) {
1216
1309
  */
1217
1310
  static VALUE
1218
1311
  doc_fetch(int argc, VALUE *argv, VALUE self) {
1219
- Doc doc = DATA_PTR(self);
1312
+ Doc doc = self_doc(self);
1220
1313
  Leaf leaf;
1221
1314
  VALUE val = Qnil;
1222
1315
  const char *path = 0;
@@ -1253,7 +1346,7 @@ static VALUE
1253
1346
  doc_each_leaf(int argc, VALUE *argv, VALUE self) {
1254
1347
  if (rb_block_given_p()) {
1255
1348
  Leaf save_path[MAX_STACK];
1256
- Doc doc = DATA_PTR(self);
1349
+ Doc doc = self_doc(self);
1257
1350
  const char *path = 0;
1258
1351
  size_t wlen;
1259
1352
 
@@ -1287,7 +1380,7 @@ doc_each_leaf(int argc, VALUE *argv, VALUE self) {
1287
1380
  */
1288
1381
  static VALUE
1289
1382
  doc_move(VALUE self, VALUE str) {
1290
- Doc doc = DATA_PTR(self);
1383
+ Doc doc = self_doc(self);
1291
1384
  const char *path;
1292
1385
  int loc;
1293
1386
 
@@ -1323,7 +1416,7 @@ static VALUE
1323
1416
  doc_each_child(int argc, VALUE *argv, VALUE self) {
1324
1417
  if (rb_block_given_p()) {
1325
1418
  Leaf save_path[MAX_STACK];
1326
- Doc doc = DATA_PTR(self);
1419
+ Doc doc = self_doc(self);
1327
1420
  const char *path = 0;
1328
1421
  size_t wlen;
1329
1422
 
@@ -1383,7 +1476,7 @@ doc_each_child(int argc, VALUE *argv, VALUE self) {
1383
1476
  static VALUE
1384
1477
  doc_each_value(int argc, VALUE *argv, VALUE self) {
1385
1478
  if (rb_block_given_p()) {
1386
- Doc doc = DATA_PTR(self);
1479
+ Doc doc = self_doc(self);
1387
1480
  const char *path = 0;
1388
1481
  Leaf leaf;
1389
1482
 
@@ -1412,7 +1505,7 @@ doc_each_value(int argc, VALUE *argv, VALUE self) {
1412
1505
  */
1413
1506
  static VALUE
1414
1507
  doc_dump(int argc, VALUE *argv, VALUE self) {
1415
- Doc doc = DATA_PTR(self);
1508
+ Doc doc = self_doc(self);
1416
1509
  Leaf leaf;
1417
1510
  const char *path = 0;
1418
1511
  const char *filename = 0;
@@ -1457,6 +1550,26 @@ doc_size(VALUE self) {
1457
1550
  return ULONG2NUM(((Doc)DATA_PTR(self))->size);
1458
1551
  }
1459
1552
 
1553
+ /* call-seq: close() => nil
1554
+ *
1555
+ * Closes an open document. No further calls to the document will be valid
1556
+ * after closing.
1557
+ * @example
1558
+ * doc = Oj::Doc.open('[1,2,3]')
1559
+ * doc.size() #=> 4
1560
+ * doc.close()
1561
+ */
1562
+ static VALUE
1563
+ doc_close(VALUE self) {
1564
+ Doc doc = self_doc(self);
1565
+
1566
+ DATA_PTR(doc->self) = 0;
1567
+ xfree(doc->json);
1568
+ doc_free(doc);
1569
+
1570
+ return Qnil;
1571
+ }
1572
+
1460
1573
  /* Document-class: Oj::Doc
1461
1574
  *
1462
1575
  * The Doc class is used to parse and navigate a JSON document. The model it
@@ -1498,8 +1611,10 @@ doc_size(VALUE self) {
1498
1611
  * end
1499
1612
  * #=> 2
1500
1613
  *
1501
- * # Now try again using a path to Oj::Doc.fetch() directly.
1502
- * Oj::Doc.open(json) { |doc| doc.fetch('/2/three') } #=> 3
1614
+ * # Now try again using a path to Oj::Doc.fetch() directly and not using a block.
1615
+ * doc = Oj::Doc.open(json)
1616
+ * doc.fetch('/2/three') #=> 3
1617
+ * doc.close()
1503
1618
  */
1504
1619
  void
1505
1620
  oj_init_doc() {
@@ -1518,4 +1633,5 @@ oj_init_doc() {
1518
1633
  rb_define_method(oj_doc_class, "each_value", doc_each_value, -1);
1519
1634
  rb_define_method(oj_doc_class, "dump", doc_dump, -1);
1520
1635
  rb_define_method(oj_doc_class, "size", doc_size, 0);
1636
+ rb_define_method(oj_doc_class, "close", doc_close, 0);
1521
1637
  }