oj 2.18.5 → 3.16.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1452 -0
  3. data/README.md +53 -221
  4. data/RELEASE_NOTES.md +61 -0
  5. data/ext/oj/buf.h +54 -72
  6. data/ext/oj/cache.c +329 -0
  7. data/ext/oj/cache.h +22 -0
  8. data/ext/oj/cache8.c +61 -63
  9. data/ext/oj/cache8.h +12 -39
  10. data/ext/oj/circarray.c +38 -67
  11. data/ext/oj/circarray.h +16 -42
  12. data/ext/oj/code.c +214 -0
  13. data/ext/oj/code.h +40 -0
  14. data/ext/oj/compat.c +194 -110
  15. data/ext/oj/custom.c +1074 -0
  16. data/ext/oj/debug.c +126 -0
  17. data/ext/oj/dump.c +1276 -2494
  18. data/ext/oj/dump.h +110 -0
  19. data/ext/oj/dump_compat.c +897 -0
  20. data/ext/oj/dump_leaf.c +162 -0
  21. data/ext/oj/dump_object.c +710 -0
  22. data/ext/oj/dump_strict.c +399 -0
  23. data/ext/oj/encode.h +7 -42
  24. data/ext/oj/encoder.c +43 -0
  25. data/ext/oj/err.c +28 -53
  26. data/ext/oj/err.h +49 -46
  27. data/ext/oj/extconf.rb +33 -32
  28. data/ext/oj/fast.c +1082 -1098
  29. data/ext/oj/intern.c +313 -0
  30. data/ext/oj/intern.h +22 -0
  31. data/ext/oj/mem.c +318 -0
  32. data/ext/oj/mem.h +53 -0
  33. data/ext/oj/mimic_json.c +919 -0
  34. data/ext/oj/object.c +545 -625
  35. data/ext/oj/odd.c +158 -168
  36. data/ext/oj/odd.h +32 -58
  37. data/ext/oj/oj.c +1727 -2080
  38. data/ext/oj/oj.h +334 -259
  39. data/ext/oj/parse.c +974 -753
  40. data/ext/oj/parse.h +97 -90
  41. data/ext/oj/parser.c +1600 -0
  42. data/ext/oj/parser.h +103 -0
  43. data/ext/oj/rails.c +1478 -0
  44. data/ext/oj/rails.h +18 -0
  45. data/ext/oj/reader.c +136 -163
  46. data/ext/oj/reader.h +76 -112
  47. data/ext/oj/resolve.c +45 -94
  48. data/ext/oj/resolve.h +7 -34
  49. data/ext/oj/rxclass.c +144 -0
  50. data/ext/oj/rxclass.h +26 -0
  51. data/ext/oj/saj.c +445 -511
  52. data/ext/oj/saj2.c +584 -0
  53. data/ext/oj/saj2.h +23 -0
  54. data/ext/oj/scp.c +82 -143
  55. data/ext/oj/simd.h +10 -0
  56. data/ext/oj/sparse.c +749 -644
  57. data/ext/oj/stream_writer.c +329 -0
  58. data/ext/oj/strict.c +114 -112
  59. data/ext/oj/string_writer.c +517 -0
  60. data/ext/oj/trace.c +72 -0
  61. data/ext/oj/trace.h +55 -0
  62. data/ext/oj/usual.c +1218 -0
  63. data/ext/oj/usual.h +69 -0
  64. data/ext/oj/util.c +136 -0
  65. data/ext/oj/util.h +20 -0
  66. data/ext/oj/val_stack.c +75 -72
  67. data/ext/oj/val_stack.h +94 -127
  68. data/ext/oj/validate.c +46 -0
  69. data/ext/oj/wab.c +586 -0
  70. data/lib/oj/active_support_helper.rb +1 -3
  71. data/lib/oj/bag.rb +8 -1
  72. data/lib/oj/easy_hash.rb +21 -13
  73. data/lib/oj/error.rb +10 -12
  74. data/lib/oj/json.rb +188 -0
  75. data/lib/oj/mimic.rb +165 -26
  76. data/lib/oj/saj.rb +20 -6
  77. data/lib/oj/schandler.rb +5 -4
  78. data/lib/oj/state.rb +135 -0
  79. data/lib/oj/version.rb +2 -3
  80. data/lib/oj.rb +3 -31
  81. data/pages/Advanced.md +22 -0
  82. data/pages/Compatibility.md +25 -0
  83. data/pages/Custom.md +23 -0
  84. data/pages/Encoding.md +65 -0
  85. data/pages/InstallOptions.md +20 -0
  86. data/pages/JsonGem.md +94 -0
  87. data/pages/Modes.md +161 -0
  88. data/pages/Options.md +337 -0
  89. data/pages/Parser.md +309 -0
  90. data/pages/Rails.md +167 -0
  91. data/pages/Security.md +20 -0
  92. data/pages/WAB.md +13 -0
  93. metadata +126 -163
  94. data/ext/oj/hash.c +0 -163
  95. data/ext/oj/hash.h +0 -46
  96. data/ext/oj/hash_test.c +0 -512
  97. data/test/_test_active.rb +0 -76
  98. data/test/_test_active_mimic.rb +0 -96
  99. data/test/_test_mimic_rails.rb +0 -126
  100. data/test/activesupport_datetime_test.rb +0 -23
  101. data/test/bug.rb +0 -51
  102. data/test/bug2.rb +0 -10
  103. data/test/bug3.rb +0 -46
  104. data/test/bug_fast.rb +0 -32
  105. data/test/bug_load.rb +0 -24
  106. data/test/crash.rb +0 -111
  107. data/test/curl/curl_oj.rb +0 -46
  108. data/test/curl/get_oj.rb +0 -24
  109. data/test/curl/just_curl.rb +0 -31
  110. data/test/curl/just_oj.rb +0 -51
  111. data/test/example.rb +0 -11
  112. data/test/files.rb +0 -29
  113. data/test/foo.rb +0 -24
  114. data/test/helper.rb +0 -27
  115. data/test/io.rb +0 -48
  116. data/test/isolated/shared.rb +0 -310
  117. data/test/isolated/test_mimic_after.rb +0 -13
  118. data/test/isolated/test_mimic_alone.rb +0 -12
  119. data/test/isolated/test_mimic_as_json.rb +0 -45
  120. data/test/isolated/test_mimic_before.rb +0 -13
  121. data/test/isolated/test_mimic_define.rb +0 -28
  122. data/test/isolated/test_mimic_rails_after.rb +0 -22
  123. data/test/isolated/test_mimic_rails_before.rb +0 -21
  124. data/test/isolated/test_mimic_rails_datetime.rb +0 -27
  125. data/test/isolated/test_mimic_redefine.rb +0 -15
  126. data/test/mod.rb +0 -16
  127. data/test/perf.rb +0 -107
  128. data/test/perf_compat.rb +0 -128
  129. data/test/perf_fast.rb +0 -164
  130. data/test/perf_file.rb +0 -64
  131. data/test/perf_object.rb +0 -138
  132. data/test/perf_saj.rb +0 -109
  133. data/test/perf_scp.rb +0 -151
  134. data/test/perf_simple.rb +0 -287
  135. data/test/perf_strict.rb +0 -128
  136. data/test/rails.rb +0 -50
  137. data/test/russian.rb +0 -18
  138. data/test/sample/change.rb +0 -14
  139. data/test/sample/dir.rb +0 -19
  140. data/test/sample/doc.rb +0 -36
  141. data/test/sample/file.rb +0 -48
  142. data/test/sample/group.rb +0 -16
  143. data/test/sample/hasprops.rb +0 -16
  144. data/test/sample/layer.rb +0 -12
  145. data/test/sample/line.rb +0 -20
  146. data/test/sample/oval.rb +0 -10
  147. data/test/sample/rect.rb +0 -10
  148. data/test/sample/shape.rb +0 -35
  149. data/test/sample/text.rb +0 -20
  150. data/test/sample.rb +0 -55
  151. data/test/sample_json.rb +0 -37
  152. data/test/struct.rb +0 -29
  153. data/test/test_compat.rb +0 -398
  154. data/test/test_debian.rb +0 -53
  155. data/test/test_fast.rb +0 -458
  156. data/test/test_file.rb +0 -245
  157. data/test/test_gc.rb +0 -49
  158. data/test/test_hash.rb +0 -29
  159. data/test/test_object.rb +0 -745
  160. data/test/test_saj.rb +0 -186
  161. data/test/test_scp.rb +0 -396
  162. data/test/test_serializer.rb +0 -59
  163. data/test/test_strict.rb +0 -254
  164. data/test/test_various.rb +0 -1383
  165. data/test/test_writer.rb +0 -308
  166. data/test/write_timebars.rb +0 -31
data/ext/oj/fast.c CHANGED
@@ -1,216 +1,170 @@
1
- /* fast.c
2
- * Copyright (c) 2012, Peter Ohler
3
- * All rights reserved.
4
- *
5
- * Redistribution and use in source and binary forms, with or without
6
- * modification, are permitted provided that the following conditions are met:
7
- *
8
- * - Redistributions of source code must retain the above copyright notice, this
9
- * list of conditions and the following disclaimer.
10
- *
11
- * - Redistributions in binary form must reproduce the above copyright notice,
12
- * this list of conditions and the following disclaimer in the documentation
13
- * and/or other materials provided with the distribution.
14
- *
15
- * - Neither the name of Peter Ohler nor the names of its contributors may be
16
- * used to endorse or promote products derived from this software without
17
- * specific prior written permission.
18
- *
19
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
- */
1
+ // Copyright (c) 2012 Peter Ohler. All rights reserved.
2
+ // Licensed under the MIT License. See LICENSE file in the project root for
3
+ // license details.
30
4
 
31
5
  #if !IS_WINDOWS
32
6
  #include <sys/resource.h> // for getrlimit() on linux
33
7
  #endif
34
- #include <stdlib.h>
8
+ #include <errno.h>
9
+ #include <math.h>
35
10
  #include <stdio.h>
11
+ #include <stdlib.h>
36
12
  #include <string.h>
37
- #include <math.h>
38
- #include <errno.h>
39
13
 
40
- #include "oj.h"
14
+ #include "dump.h"
41
15
  #include "encode.h"
16
+ #include "mem.h"
17
+ #include "oj.h"
42
18
 
43
19
  // maximum to allocate on the stack, arbitrary limit
44
- #define SMALL_XML 65536
45
- #define MAX_STACK 100
46
- //#define BATCH_SIZE (4096 / sizeof(struct _Leaf) - 1)
47
- #define BATCH_SIZE 100
48
-
49
- typedef struct _Batch {
50
- struct _Batch *next;
51
- int next_avail;
52
- struct _Leaf leaves[BATCH_SIZE];
20
+ #define SMALL_JSON 65536
21
+ #define MAX_STACK 100
22
+ // #define BATCH_SIZE (4096 / sizeof(struct _leaf) - 1)
23
+ #define BATCH_SIZE 100
24
+
25
+ // Support for compaction
26
+ #ifdef HAVE_RB_GC_MARK_MOVABLE
27
+ #define mark rb_gc_mark_movable
28
+ #else
29
+ #define mark rb_gc_mark
30
+ #endif
31
+
32
+ typedef struct _batch {
33
+ struct _batch *next;
34
+ int next_avail;
35
+ struct _leaf leaves[BATCH_SIZE];
53
36
  } *Batch;
54
37
 
55
- typedef struct _Doc {
56
- Leaf data;
57
- Leaf *where; // points to current location
58
- Leaf where_path[MAX_STACK]; // points to head of path
59
- char *json;
60
- unsigned long size; // number of leaves/branches in the doc
61
- VALUE self;
62
- Batch batches;
63
- struct _Batch batch0;
38
+ typedef struct _doc {
39
+ Leaf data;
40
+ Leaf *where; // points to current location
41
+ Leaf where_path[MAX_STACK]; // points to head of path
42
+ char *json;
43
+ unsigned long size; // number of leaves/branches in the doc
44
+ VALUE self;
45
+ Batch batches;
46
+ struct _batch batch0;
64
47
  } *Doc;
65
48
 
66
- typedef struct _ParseInfo {
67
- char *str; /* buffer being read from */
68
- char *s; /* current position in buffer */
69
- Doc doc;
70
- void *stack_min;
49
+ typedef struct _parseInfo {
50
+ char *str; // buffer being read from
51
+ char *s; // current position in buffer
52
+ Doc doc;
53
+ void *stack_min;
71
54
  } *ParseInfo;
72
55
 
73
- static void leaf_init(Leaf leaf, int type);
74
- static Leaf leaf_new(Doc doc, int type);
75
- static void leaf_append_element(Leaf parent, Leaf element);
76
- static VALUE leaf_value(Doc doc, Leaf leaf);
77
- static void leaf_fixnum_value(Leaf leaf);
78
- static void leaf_float_value(Leaf leaf);
79
- static VALUE leaf_array_value(Doc doc, Leaf leaf);
80
- static VALUE leaf_hash_value(Doc doc, Leaf leaf);
81
-
82
- static Leaf read_next(ParseInfo pi);
83
- static Leaf read_obj(ParseInfo pi);
84
- static Leaf read_array(ParseInfo pi);
85
- static Leaf read_str(ParseInfo pi);
86
- static Leaf read_num(ParseInfo pi);
87
- static Leaf read_true(ParseInfo pi);
88
- static Leaf read_false(ParseInfo pi);
89
- static Leaf read_nil(ParseInfo pi);
90
- static void next_non_white(ParseInfo pi);
91
- static char* read_quoted_value(ParseInfo pi);
92
- static void skip_comment(ParseInfo pi);
93
-
94
- static VALUE protect_open_proc(VALUE x);
95
- static VALUE parse_json(VALUE clas, char *json, int given, int allocated);
96
- static void each_leaf(Doc doc, VALUE self);
97
- static int move_step(Doc doc, const char *path, int loc);
98
- static Leaf get_doc_leaf(Doc doc, const char *path);
99
- static Leaf get_leaf(Leaf *stack, Leaf *lp, const char *path);
100
- static void each_value(Doc doc, Leaf leaf);
101
-
102
- static void doc_init(Doc doc);
103
- static void doc_free(Doc doc);
104
- static VALUE doc_open(VALUE clas, VALUE str);
105
- static VALUE doc_open_file(VALUE clas, VALUE filename);
106
- static VALUE doc_where(VALUE self);
107
- static VALUE doc_local_key(VALUE self);
108
- static VALUE doc_home(VALUE self);
109
- static VALUE doc_type(int argc, VALUE *argv, VALUE self);
110
- static VALUE doc_fetch(int argc, VALUE *argv, VALUE self);
111
- static VALUE doc_each_leaf(int argc, VALUE *argv, VALUE self);
112
- static VALUE doc_move(VALUE self, VALUE str);
113
- static VALUE doc_each_child(int argc, VALUE *argv, VALUE self);
114
- static VALUE doc_each_value(int argc, VALUE *argv, VALUE self);
115
- static VALUE doc_dump(int argc, VALUE *argv, VALUE self);
116
- static VALUE doc_size(VALUE self);
117
-
118
- VALUE oj_doc_class = 0;
56
+ static void leaf_init(Leaf leaf, int type);
57
+ static Leaf leaf_new(Doc doc, int type);
58
+ static void leaf_append_element(Leaf parent, Leaf element);
59
+ static VALUE leaf_value(Doc doc, Leaf leaf);
60
+ static void leaf_fixnum_value(Leaf leaf);
61
+ static void leaf_float_value(Leaf leaf);
62
+ static VALUE leaf_array_value(Doc doc, Leaf leaf);
63
+ static VALUE leaf_hash_value(Doc doc, Leaf leaf);
64
+
65
+ static Leaf read_next(ParseInfo pi);
66
+ static Leaf read_obj(ParseInfo pi);
67
+ static Leaf read_array(ParseInfo pi);
68
+ static Leaf read_str(ParseInfo pi);
69
+ static Leaf read_num(ParseInfo pi);
70
+ static Leaf read_true(ParseInfo pi);
71
+ static Leaf read_false(ParseInfo pi);
72
+ static Leaf read_nil(ParseInfo pi);
73
+ static void next_non_white(ParseInfo pi);
74
+ static char *read_quoted_value(ParseInfo pi);
75
+ static void skip_comment(ParseInfo pi);
76
+
77
+ static VALUE protect_open_proc(VALUE x);
78
+ static VALUE parse_json(VALUE clas, char *json, bool given);
79
+ static void each_leaf(Doc doc, VALUE self);
80
+ static int move_step(Doc doc, const char *path, int loc);
81
+ static Leaf get_doc_leaf(Doc doc, const char *path);
82
+ static Leaf get_leaf(Leaf *stack, Leaf *lp, const char *path);
83
+ static void each_value(Doc doc, Leaf leaf);
84
+
85
+ VALUE oj_doc_class = Qundef;
119
86
 
120
87
  // This is only for CentOS 5.4 with Ruby 1.9.3-p0.
121
- #ifdef NEEDS_STPCPY
88
+ #ifndef HAVE_STPCPY
122
89
  char *stpcpy(char *dest, const char *src) {
123
- size_t cnt = strlen(src);
124
-
90
+ size_t cnt = strlen(src);
91
+
125
92
  strcpy(dest, src);
126
93
 
127
94
  return dest + cnt;
128
95
  }
129
96
  #endif
130
97
 
131
- inline static void
132
- next_non_white(ParseInfo pi) {
98
+ inline static void next_non_white(ParseInfo pi) {
133
99
  for (; 1; pi->s++) {
134
- switch(*pi->s) {
135
- case ' ':
136
- case '\t':
137
- case '\f':
138
- case '\n':
139
- case '\r':
140
- break;
141
- case '/':
142
- skip_comment(pi);
143
- break;
144
- default:
145
- return;
146
- }
100
+ switch (*pi->s) {
101
+ case ' ':
102
+ case '\t':
103
+ case '\f':
104
+ case '\n':
105
+ case '\r': break;
106
+ case '/': skip_comment(pi); break;
107
+ default: return;
108
+ }
147
109
  }
148
110
  }
149
111
 
150
- inline static char*
151
- ulong_fill(char *s, size_t num) {
152
- char buf[32];
153
- char *b = buf + sizeof(buf) - 1;
112
+ inline static char *ulong_fill(char *s, size_t num) {
113
+ char buf[32];
114
+ char *b = buf + sizeof(buf) - 1;
154
115
 
155
116
  *b-- = '\0';
156
- for (; 0 < num; num /= 10, b--) {
157
- *b = (num % 10) + '0';
158
- }
159
- b++;
117
+ b = oj_longlong_to_string((long long)num, false, b);
160
118
  if ('\0' == *b) {
161
- b--;
162
- *b = '0';
119
+ b--;
120
+ *b = '0';
163
121
  }
164
122
  for (; '\0' != *b; b++, s++) {
165
- *s = *b;
123
+ *s = *b;
166
124
  }
167
125
  return s;
168
126
  }
169
127
 
170
- inline static void
171
- leaf_init(Leaf leaf, int type) {
172
- leaf->next = 0;
173
- leaf->rtype = type;
128
+ inline static void leaf_init(Leaf leaf, int type) {
129
+ leaf->next = 0;
130
+ leaf->rtype = type;
174
131
  leaf->parent_type = T_NONE;
175
132
  switch (type) {
176
133
  case T_ARRAY:
177
134
  case T_HASH:
178
- leaf->elements = 0;
179
- leaf->value_type = COL_VAL;
180
- break;
135
+ leaf->elements = 0;
136
+ leaf->value_type = COL_VAL;
137
+ break;
181
138
  case T_NIL:
182
- leaf->value = Qnil;
183
- leaf->value_type = RUBY_VAL;
184
- break;
139
+ leaf->value = Qnil;
140
+ leaf->value_type = RUBY_VAL;
141
+ break;
185
142
  case T_TRUE:
186
- leaf->value = Qtrue;
187
- leaf->value_type = RUBY_VAL;
188
- break;
143
+ leaf->value = Qtrue;
144
+ leaf->value_type = RUBY_VAL;
145
+ break;
189
146
  case T_FALSE:
190
- leaf->value = Qfalse;
191
- leaf->value_type = RUBY_VAL;
192
- break;
147
+ leaf->value = Qfalse;
148
+ leaf->value_type = RUBY_VAL;
149
+ break;
193
150
  case T_FIXNUM:
194
151
  case T_FLOAT:
195
152
  case T_STRING:
196
- default:
197
- leaf->value_type = STR_VAL;
198
- break;
153
+ default: leaf->value_type = STR_VAL; break;
199
154
  }
200
155
  }
201
156
 
202
- inline static Leaf
203
- leaf_new(Doc doc, int type) {
204
- Leaf leaf;
157
+ inline static Leaf leaf_new(Doc doc, int type) {
158
+ Leaf leaf;
205
159
 
206
160
  if (0 == doc->batches || BATCH_SIZE == doc->batches->next_avail) {
207
- Batch b = ALLOC(struct _Batch);
161
+ Batch b = OJ_R_ALLOC(struct _batch);
208
162
 
209
- // Initializes all leaves with a NO_VAL value_type
210
- memset(b, 0, sizeof(struct _Batch));
211
- b->next = doc->batches;
212
- doc->batches = b;
213
- b->next_avail = 0;
163
+ // Initializes all leaves with a NO_VAL value_type
164
+ memset(b, 0, sizeof(struct _batch));
165
+ b->next = doc->batches;
166
+ doc->batches = b;
167
+ b->next_avail = 0;
214
168
  }
215
169
  leaf = &doc->batches->leaves[doc->batches->next_avail];
216
170
  doc->batches->next_avail++;
@@ -219,93 +173,70 @@ leaf_new(Doc doc, int type) {
219
173
  return leaf;
220
174
  }
221
175
 
222
- inline static void
223
- leaf_append_element(Leaf parent, Leaf element) {
176
+ inline static void leaf_append_element(Leaf parent, Leaf element) {
224
177
  if (0 == parent->elements) {
225
- parent->elements = element;
226
- element->next = element;
178
+ parent->elements = element;
179
+ element->next = element;
227
180
  } else {
228
- element->next = parent->elements->next;
229
- parent->elements->next = element;
230
- parent->elements = element;
181
+ element->next = parent->elements->next;
182
+ parent->elements->next = element;
183
+ parent->elements = element;
231
184
  }
232
185
  }
233
186
 
234
- static VALUE
235
- leaf_value(Doc doc, Leaf leaf) {
187
+ static VALUE leaf_value(Doc doc, Leaf leaf) {
236
188
  if (RUBY_VAL != leaf->value_type) {
237
- switch (leaf->rtype) {
238
- case T_NIL:
239
- leaf->value = Qnil;
240
- break;
241
- case T_TRUE:
242
- leaf->value = Qtrue;
243
- break;
244
- case T_FALSE:
245
- leaf->value = Qfalse;
246
- break;
247
- case T_FIXNUM:
248
- leaf_fixnum_value(leaf);
249
- break;
250
- case T_FLOAT:
251
- leaf_float_value(leaf);
252
- break;
253
- case T_STRING:
254
- leaf->value = rb_str_new2(leaf->str);
255
- leaf->value = oj_encode(leaf->value);
256
- leaf->value_type = RUBY_VAL;
257
- break;
258
- case T_ARRAY:
259
- return leaf_array_value(doc, leaf);
260
- break;
261
- case T_HASH:
262
- return leaf_hash_value(doc, leaf);
263
- break;
264
- default:
265
- rb_raise(rb_const_get_at(Oj, rb_intern("Error")), "Unexpected type %02x.", leaf->rtype);
266
- break;
267
- }
189
+ switch (leaf->rtype) {
190
+ case T_NIL: leaf->value = Qnil; break;
191
+ case T_TRUE: leaf->value = Qtrue; break;
192
+ case T_FALSE: leaf->value = Qfalse; break;
193
+ case T_FIXNUM: leaf_fixnum_value(leaf); break;
194
+ case T_FLOAT: leaf_float_value(leaf); break;
195
+ case T_STRING:
196
+ leaf->value = rb_utf8_str_new_cstr(leaf->str);
197
+ leaf->value_type = RUBY_VAL;
198
+ break;
199
+ case T_ARRAY: return leaf_array_value(doc, leaf); break;
200
+ case T_HASH: return leaf_hash_value(doc, leaf); break;
201
+ default: rb_raise(rb_const_get_at(Oj, rb_intern("Error")), "Unexpected type %02x.", leaf->rtype); break;
202
+ }
268
203
  }
269
204
  return leaf->value;
270
205
  }
271
206
 
272
- inline static Doc
273
- self_doc(VALUE self) {
274
- Doc doc = DATA_PTR(self);
207
+ inline static Doc self_doc(VALUE self) {
208
+ Doc doc = DATA_PTR(self);
275
209
 
276
210
  if (0 == doc) {
277
- rb_raise(rb_eIOError, "Document already closed or not open.");
211
+ rb_raise(rb_eIOError, "Document already closed or not open.");
278
212
  }
279
213
  return doc;
280
214
  }
281
215
 
282
- static void
283
- skip_comment(ParseInfo pi) {
284
- pi->s++; // skip first /
216
+ static void skip_comment(ParseInfo pi) {
217
+ pi->s++; // skip first /
285
218
  if ('*' == *pi->s) {
286
- pi->s++;
287
- for (; '\0' != *pi->s; pi->s++) {
288
- if ('*' == *pi->s && '/' == *(pi->s + 1)) {
289
- pi->s++;
290
- return;
291
- } else if ('\0' == *pi->s) {
292
- raise_error("comment not terminated", pi->str, pi->s);
293
- }
294
- }
219
+ pi->s++;
220
+ for (; '\0' != *pi->s; pi->s++) {
221
+ if ('*' == *pi->s && '/' == *(pi->s + 1)) {
222
+ pi->s++;
223
+ return;
224
+ } else if ('\0' == *pi->s) {
225
+ raise_error("comment not terminated", pi->str, pi->s);
226
+ }
227
+ }
295
228
  } else if ('/' == *pi->s) {
296
- for (; 1; pi->s++) {
297
- switch (*pi->s) {
298
- case '\n':
299
- case '\r':
300
- case '\f':
301
- case '\0':
302
- return;
303
- default:
304
- break;
305
- }
306
- }
229
+ for (; 1; pi->s++) {
230
+ switch (*pi->s) {
231
+ case '\n':
232
+ case '\r':
233
+ case '\f':
234
+ case '\0': return;
235
+ default: break;
236
+ }
237
+ }
307
238
  } else {
308
- raise_error("invalid comment", pi->str, pi->s);
239
+ raise_error("invalid comment", pi->str, pi->s);
309
240
  }
310
241
  }
311
242
 
@@ -315,100 +246,87 @@ skip_comment(ParseInfo pi) {
315
246
  #define NUM_MAX (FIXNUM_MAX >> 8)
316
247
  #endif
317
248
 
249
+ static void leaf_fixnum_value(Leaf leaf) {
250
+ char *s = leaf->str;
251
+ int64_t n = 0;
252
+ int neg = 0;
253
+ int big = 0;
318
254
 
319
- static void
320
- leaf_fixnum_value(Leaf leaf) {
321
- char *s = leaf->str;
322
- int64_t n = 0;
323
- int neg = 0;
324
- int big = 0;
325
-
326
255
  if ('-' == *s) {
327
- s++;
328
- neg = 1;
256
+ s++;
257
+ neg = 1;
329
258
  } else if ('+' == *s) {
330
- s++;
259
+ s++;
331
260
  }
332
261
  for (; '0' <= *s && *s <= '9'; s++) {
333
- n = n * 10 + (*s - '0');
334
- if (NUM_MAX <= n) {
335
- big = 1;
336
- }
262
+ n = n * 10 + (*s - '0');
263
+ if (NUM_MAX <= n) {
264
+ big = 1;
265
+ }
337
266
  }
338
267
  if (big) {
339
- char c = *s;
340
-
341
- *s = '\0';
342
- leaf->value = rb_cstr_to_inum(leaf->str, 10, 0);
343
- *s = c;
268
+ char c = *s;
269
+
270
+ *s = '\0';
271
+ leaf->value = rb_cstr_to_inum(leaf->str, 10, 0);
272
+ *s = c;
344
273
  } else {
345
- if (neg) {
346
- n = -n;
347
- }
348
- leaf->value = rb_ll2inum(n);
274
+ if (neg) {
275
+ n = -n;
276
+ }
277
+ leaf->value = rb_ll2inum(n);
349
278
  }
350
279
  leaf->value_type = RUBY_VAL;
351
280
  }
352
281
 
353
- static void
354
- leaf_float_value(Leaf leaf) {
355
- leaf->value = rb_float_new(rb_cstr_to_dbl(leaf->str, 1));
282
+ static void leaf_float_value(Leaf leaf) {
283
+ leaf->value = rb_float_new(rb_cstr_to_dbl(leaf->str, 1));
356
284
  leaf->value_type = RUBY_VAL;
357
285
  }
358
286
 
359
- static VALUE
360
- leaf_array_value(Doc doc, Leaf leaf) {
361
- VALUE a = rb_ary_new();
287
+ static VALUE leaf_array_value(Doc doc, Leaf leaf) {
288
+ volatile VALUE a = rb_ary_new();
362
289
 
363
290
  if (0 != leaf->elements) {
364
- Leaf first = leaf->elements->next;
365
- Leaf e = first;
291
+ Leaf first = leaf->elements->next;
292
+ Leaf e = first;
366
293
 
367
- do {
368
- rb_ary_push(a, leaf_value(doc, e));
369
- e = e->next;
370
- } while (e != first);
294
+ do {
295
+ rb_ary_push(a, leaf_value(doc, e));
296
+ e = e->next;
297
+ } while (e != first);
371
298
  }
372
299
  return a;
373
300
  }
374
301
 
375
- static VALUE
376
- leaf_hash_value(Doc doc, Leaf leaf) {
377
- VALUE h = rb_hash_new();
302
+ static VALUE leaf_hash_value(Doc doc, Leaf leaf) {
303
+ volatile VALUE h = rb_hash_new();
378
304
 
379
305
  if (0 != leaf->elements) {
380
- Leaf first = leaf->elements->next;
381
- Leaf e = first;
382
- VALUE key;
306
+ Leaf first = leaf->elements->next;
307
+ Leaf e = first;
308
+ volatile VALUE key;
383
309
 
384
- do {
385
- key = rb_str_new2(e->key);
386
- key = oj_encode(key);
387
- rb_hash_aset(h, key, leaf_value(doc, e));
388
- e = e->next;
389
- } while (e != first);
310
+ do {
311
+ key = rb_utf8_str_new_cstr(e->key);
312
+ rb_hash_aset(h, key, leaf_value(doc, e));
313
+ e = e->next;
314
+ } while (e != first);
390
315
  }
391
316
  return h;
392
317
  }
393
318
 
394
- static Leaf
395
- read_next(ParseInfo pi) {
396
- Leaf leaf = 0;
319
+ static Leaf read_next(ParseInfo pi) {
320
+ Leaf leaf = 0;
397
321
 
398
- if ((void*)&leaf < pi->stack_min) {
399
- rb_raise(rb_eSysStackError, "JSON is too deeply nested");
322
+ if ((void *)&leaf < pi->stack_min) {
323
+ rb_raise(rb_eSysStackError, "JSON is too deeply nested");
400
324
  }
401
- next_non_white(pi); // skip white space
325
+ next_non_white(pi); // skip white space
402
326
  switch (*pi->s) {
403
- case '{':
404
- leaf = read_obj(pi);
405
- break;
406
- case '[':
407
- leaf = read_array(pi);
408
- break;
409
- case '"':
410
- leaf = read_str(pi);
411
- break;
327
+ case '{': leaf = read_obj(pi); break;
328
+ case '[': leaf = read_array(pi); break;
329
+ case '"': leaf = read_str(pi); break;
412
330
  case '+':
413
331
  case '-':
414
332
  case '0':
@@ -420,695 +338,740 @@ read_next(ParseInfo pi) {
420
338
  case '6':
421
339
  case '7':
422
340
  case '8':
423
- case '9':
424
- leaf = read_num(pi);
425
- break;
426
- case 't':
427
- leaf = read_true(pi);
428
- break;
429
- case 'f':
430
- leaf = read_false(pi);
431
- break;
432
- case 'n':
433
- leaf = read_nil(pi);
434
- break;
341
+ case '9': leaf = read_num(pi); break;
342
+ case 't': leaf = read_true(pi); break;
343
+ case 'f': leaf = read_false(pi); break;
344
+ case 'n': leaf = read_nil(pi); break;
435
345
  case '\0':
436
- default:
437
- break; // returns 0
346
+ default: break; // returns 0
438
347
  }
439
348
  pi->doc->size++;
440
349
 
441
350
  return leaf;
442
351
  }
443
352
 
444
- static Leaf
445
- read_obj(ParseInfo pi) {
446
- Leaf h = leaf_new(pi->doc, T_HASH);
447
- char *end;
448
- const char *key = 0;
449
- Leaf val = 0;
353
+ static Leaf read_obj(ParseInfo pi) {
354
+ Leaf h = leaf_new(pi->doc, T_HASH);
355
+ char *end;
356
+ const char *key = 0;
357
+ Leaf val = 0;
450
358
 
451
359
  pi->s++;
452
360
  next_non_white(pi);
453
361
  if ('}' == *pi->s) {
454
- pi->s++;
455
- return h;
362
+ pi->s++;
363
+ return h;
456
364
  }
457
365
  while (1) {
458
- next_non_white(pi);
459
- key = 0;
460
- val = 0;
461
- if ('"' != *pi->s || 0 == (key = read_quoted_value(pi))) {
462
- raise_error("unexpected character", pi->str, pi->s);
463
- }
464
- next_non_white(pi);
465
- if (':' == *pi->s) {
466
- pi->s++;
467
- } else {
468
- raise_error("invalid format, expected :", pi->str, pi->s);
469
- }
470
- if (0 == (val = read_next(pi))) {
471
- //printf("*** '%s'\n", pi->s);
472
- raise_error("unexpected character", pi->str, pi->s);
473
- }
474
- end = pi->s;
475
- val->key = key;
476
- val->parent_type = T_HASH;
477
- leaf_append_element(h, val);
478
- next_non_white(pi);
479
- if ('}' == *pi->s) {
480
- pi->s++;
481
- *end = '\0';
482
- break;
483
- } else if (',' == *pi->s) {
484
- pi->s++;
485
- } else {
486
- //printf("*** '%s'\n", pi->s);
487
- raise_error("invalid format, expected , or } while in an object", pi->str, pi->s);
488
- }
489
- *end = '\0';
366
+ next_non_white(pi);
367
+ key = 0;
368
+ val = 0;
369
+ if ('"' != *pi->s || 0 == (key = read_quoted_value(pi))) {
370
+ raise_error("unexpected character", pi->str, pi->s);
371
+ }
372
+ next_non_white(pi);
373
+ if (':' == *pi->s) {
374
+ pi->s++;
375
+ } else {
376
+ raise_error("invalid format, expected :", pi->str, pi->s);
377
+ }
378
+ if (0 == (val = read_next(pi))) {
379
+ // printf("*** '%s'\n", pi->s);
380
+ raise_error("unexpected character", pi->str, pi->s);
381
+ }
382
+ end = pi->s;
383
+ val->key = key;
384
+ val->parent_type = T_HASH;
385
+ leaf_append_element(h, val);
386
+ next_non_white(pi);
387
+ if ('}' == *pi->s) {
388
+ pi->s++;
389
+ *end = '\0';
390
+ break;
391
+ } else if (',' == *pi->s) {
392
+ pi->s++;
393
+ } else {
394
+ // printf("*** '%s'\n", pi->s);
395
+ raise_error("invalid format, expected , or } while in an object", pi->str, pi->s);
396
+ }
397
+ *end = '\0';
490
398
  }
491
399
  return h;
492
400
  }
493
401
 
494
- static Leaf
495
- read_array(ParseInfo pi) {
496
- Leaf a = leaf_new(pi->doc, T_ARRAY);
497
- Leaf e;
498
- char *end;
499
- int cnt = 0;
402
+ static Leaf read_array(ParseInfo pi) {
403
+ Leaf a = leaf_new(pi->doc, T_ARRAY);
404
+ Leaf e;
405
+ char *end;
406
+ int cnt = 0;
500
407
 
501
408
  pi->s++;
502
409
  next_non_white(pi);
503
410
  if (']' == *pi->s) {
504
- pi->s++;
505
- return a;
411
+ pi->s++;
412
+ return a;
506
413
  }
507
414
  while (1) {
508
- next_non_white(pi);
509
- if (0 == (e = read_next(pi))) {
510
- raise_error("unexpected character", pi->str, pi->s);
511
- }
512
- cnt++;
513
- e->index = cnt;
514
- e->parent_type = T_ARRAY;
515
- leaf_append_element(a, e);
516
- end = pi->s;
517
- next_non_white(pi);
518
- if (',' == *pi->s) {
519
- pi->s++;
520
- } else if (']' == *pi->s) {
521
- pi->s++;
522
- *end = '\0';
523
- break;
524
- } else {
525
- raise_error("invalid format, expected , or ] while in an array", pi->str, pi->s);
526
- }
527
- *end = '\0';
415
+ next_non_white(pi);
416
+ if (0 == (e = read_next(pi))) {
417
+ raise_error("unexpected character", pi->str, pi->s);
418
+ }
419
+ cnt++;
420
+ e->index = cnt;
421
+ e->parent_type = T_ARRAY;
422
+ leaf_append_element(a, e);
423
+ end = pi->s;
424
+ next_non_white(pi);
425
+ if (',' == *pi->s) {
426
+ pi->s++;
427
+ } else if (']' == *pi->s) {
428
+ pi->s++;
429
+ *end = '\0';
430
+ break;
431
+ } else {
432
+ raise_error("invalid format, expected , or ] while in an array", pi->str, pi->s);
433
+ }
434
+ *end = '\0';
528
435
  }
529
436
  return a;
530
437
  }
531
438
 
532
- static Leaf
533
- read_str(ParseInfo pi) {
534
- Leaf leaf = leaf_new(pi->doc, T_STRING);
439
+ static Leaf read_str(ParseInfo pi) {
440
+ Leaf leaf = leaf_new(pi->doc, T_STRING);
535
441
 
536
442
  leaf->str = read_quoted_value(pi);
537
443
 
538
444
  return leaf;
539
445
  }
540
446
 
541
- static Leaf
542
- read_num(ParseInfo pi) {
543
- char *start = pi->s;
544
- int type = T_FIXNUM;
545
- Leaf leaf;
447
+ static Leaf read_num(ParseInfo pi) {
448
+ char *start = pi->s;
449
+ int type = T_FIXNUM;
450
+ Leaf leaf;
546
451
 
547
452
  if ('-' == *pi->s) {
548
- pi->s++;
453
+ pi->s++;
549
454
  }
550
455
  // digits
551
456
  for (; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
552
457
  }
553
458
  if ('.' == *pi->s) {
554
- type = T_FLOAT;
555
- pi->s++;
556
- for (; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
557
- }
459
+ type = T_FLOAT;
460
+ pi->s++;
461
+ for (; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
462
+ }
558
463
  }
559
464
  if ('e' == *pi->s || 'E' == *pi->s) {
560
- pi->s++;
561
- if ('-' == *pi->s || '+' == *pi->s) {
562
- pi->s++;
563
- }
564
- for (; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
565
- }
566
- }
567
- leaf = leaf_new(pi->doc, type);
465
+ pi->s++;
466
+ if ('-' == *pi->s || '+' == *pi->s) {
467
+ pi->s++;
468
+ }
469
+ for (; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
470
+ }
471
+ }
472
+ leaf = leaf_new(pi->doc, type);
568
473
  leaf->str = start;
569
474
 
570
475
  return leaf;
571
476
  }
572
477
 
573
- static Leaf
574
- read_true(ParseInfo pi) {
575
- Leaf leaf = leaf_new(pi->doc, T_TRUE);
478
+ static Leaf read_true(ParseInfo pi) {
479
+ Leaf leaf = leaf_new(pi->doc, T_TRUE);
576
480
 
577
481
  pi->s++;
578
482
  if ('r' != *pi->s || 'u' != *(pi->s + 1) || 'e' != *(pi->s + 2)) {
579
- raise_error("invalid format, expected 'true'", pi->str, pi->s);
483
+ raise_error("invalid format, expected 'true'", pi->str, pi->s);
580
484
  }
581
485
  pi->s += 3;
582
486
 
583
487
  return leaf;
584
488
  }
585
489
 
586
- static Leaf
587
- read_false(ParseInfo pi) {
588
- Leaf leaf = leaf_new(pi->doc, T_FALSE);
490
+ static Leaf read_false(ParseInfo pi) {
491
+ Leaf leaf = leaf_new(pi->doc, T_FALSE);
589
492
 
590
493
  pi->s++;
591
494
  if ('a' != *pi->s || 'l' != *(pi->s + 1) || 's' != *(pi->s + 2) || 'e' != *(pi->s + 3)) {
592
- raise_error("invalid format, expected 'false'", pi->str, pi->s);
495
+ raise_error("invalid format, expected 'false'", pi->str, pi->s);
593
496
  }
594
497
  pi->s += 4;
595
498
 
596
499
  return leaf;
597
500
  }
598
501
 
599
- static Leaf
600
- read_nil(ParseInfo pi) {
601
- Leaf leaf = leaf_new(pi->doc, T_NIL);
502
+ static Leaf read_nil(ParseInfo pi) {
503
+ Leaf leaf = leaf_new(pi->doc, T_NIL);
602
504
 
603
505
  pi->s++;
604
506
  if ('u' != *pi->s || 'l' != *(pi->s + 1) || 'l' != *(pi->s + 2)) {
605
- raise_error("invalid format, expected 'nil'", pi->str, pi->s);
507
+ raise_error("invalid format, expected 'nil'", pi->str, pi->s);
606
508
  }
607
509
  pi->s += 3;
608
510
 
609
511
  return leaf;
610
512
  }
611
513
 
612
- static uint32_t
613
- read_4hex(ParseInfo pi, const char *h) {
614
- uint32_t b = 0;
615
- int i;
514
+ static uint32_t read_4hex(ParseInfo pi, const char *h) {
515
+ uint32_t b = 0;
516
+ int i;
616
517
 
617
518
  for (i = 0; i < 4; i++, h++) {
618
- b = b << 4;
619
- if ('0' <= *h && *h <= '9') {
620
- b += *h - '0';
621
- } else if ('A' <= *h && *h <= 'F') {
622
- b += *h - 'A' + 10;
623
- } else if ('a' <= *h && *h <= 'f') {
624
- b += *h - 'a' + 10;
625
- } else {
626
- raise_error("invalid hex character", pi->str, pi->s);
627
- }
519
+ b = b << 4;
520
+ if ('0' <= *h && *h <= '9') {
521
+ b += *h - '0';
522
+ } else if ('A' <= *h && *h <= 'F') {
523
+ b += *h - 'A' + 10;
524
+ } else if ('a' <= *h && *h <= 'f') {
525
+ b += *h - 'a' + 10;
526
+ } else {
527
+ raise_error("invalid hex character", pi->str, pi->s);
528
+ }
628
529
  }
629
530
  return b;
630
531
  }
631
532
 
632
- static char*
633
- unicode_to_chars(ParseInfo pi, char *t, uint32_t code) {
533
+ static char *unicode_to_chars(ParseInfo pi, char *t, uint32_t code) {
634
534
  if (0x0000007F >= code) {
635
- *t++ = (char)code;
535
+ *t++ = (char)code;
636
536
  } else if (0x000007FF >= code) {
637
- *t++ = 0xC0 | (code >> 6);
638
- *t++ = 0x80 | (0x3F & code);
537
+ *t++ = 0xC0 | (code >> 6);
538
+ *t++ = 0x80 | (0x3F & code);
639
539
  } else if (0x0000FFFF >= code) {
640
- *t++ = 0xE0 | (code >> 12);
641
- *t++ = 0x80 | ((code >> 6) & 0x3F);
642
- *t++ = 0x80 | (0x3F & code);
540
+ *t++ = 0xE0 | (code >> 12);
541
+ *t++ = 0x80 | ((code >> 6) & 0x3F);
542
+ *t++ = 0x80 | (0x3F & code);
643
543
  } else if (0x001FFFFF >= code) {
644
- *t++ = 0xF0 | (code >> 18);
645
- *t++ = 0x80 | ((code >> 12) & 0x3F);
646
- *t++ = 0x80 | ((code >> 6) & 0x3F);
647
- *t++ = 0x80 | (0x3F & code);
544
+ *t++ = 0xF0 | (code >> 18);
545
+ *t++ = 0x80 | ((code >> 12) & 0x3F);
546
+ *t++ = 0x80 | ((code >> 6) & 0x3F);
547
+ *t++ = 0x80 | (0x3F & code);
648
548
  } else if (0x03FFFFFF >= code) {
649
- *t++ = 0xF8 | (code >> 24);
650
- *t++ = 0x80 | ((code >> 18) & 0x3F);
651
- *t++ = 0x80 | ((code >> 12) & 0x3F);
652
- *t++ = 0x80 | ((code >> 6) & 0x3F);
653
- *t++ = 0x80 | (0x3F & code);
549
+ *t++ = 0xF8 | (code >> 24);
550
+ *t++ = 0x80 | ((code >> 18) & 0x3F);
551
+ *t++ = 0x80 | ((code >> 12) & 0x3F);
552
+ *t++ = 0x80 | ((code >> 6) & 0x3F);
553
+ *t++ = 0x80 | (0x3F & code);
654
554
  } else if (0x7FFFFFFF >= code) {
655
- *t++ = 0xFC | (code >> 30);
656
- *t++ = 0x80 | ((code >> 24) & 0x3F);
657
- *t++ = 0x80 | ((code >> 18) & 0x3F);
658
- *t++ = 0x80 | ((code >> 12) & 0x3F);
659
- *t++ = 0x80 | ((code >> 6) & 0x3F);
660
- *t++ = 0x80 | (0x3F & code);
555
+ *t++ = 0xFC | (code >> 30);
556
+ *t++ = 0x80 | ((code >> 24) & 0x3F);
557
+ *t++ = 0x80 | ((code >> 18) & 0x3F);
558
+ *t++ = 0x80 | ((code >> 12) & 0x3F);
559
+ *t++ = 0x80 | ((code >> 6) & 0x3F);
560
+ *t++ = 0x80 | (0x3F & code);
661
561
  } else {
662
- raise_error("invalid Unicode character", pi->str, pi->s);
562
+ raise_error("invalid Unicode character", pi->str, pi->s);
663
563
  }
664
564
  return t;
665
565
  }
666
566
 
667
- /* Assume the value starts immediately and goes until the quote character is
668
- * reached again. Do not read the character after the terminating quote.
669
- */
670
- static char*
671
- read_quoted_value(ParseInfo pi) {
672
- char *value = 0;
673
- char *h = pi->s; // head
674
- char *t = h; // tail
675
-
676
- h++; // skip quote character
567
+ // Assume the value starts immediately and goes until the quote character is
568
+ // reached again. Do not read the character after the terminating quote.
569
+ static char *read_quoted_value(ParseInfo pi) {
570
+ char *value = 0;
571
+ char *h = pi->s; // head
572
+ char *t = h; // tail
573
+
574
+ h++; // skip quote character
677
575
  t++;
678
576
  value = h;
679
577
  for (; '"' != *h; h++, t++) {
680
- if ('\0' == *h) {
681
- pi->s = h;
682
- raise_error("quoted string not terminated", pi->str, pi->s);
683
- } else if ('\\' == *h) {
684
- h++;
685
- switch (*h) {
686
- case 'n': *t = '\n'; break;
687
- case 'r': *t = '\r'; break;
688
- case 't': *t = '\t'; break;
689
- case 'f': *t = '\f'; break;
690
- case 'b': *t = '\b'; break;
691
- case '"': *t = '"'; break;
692
- case '/': *t = '/'; break;
693
- case '\\': *t = '\\'; break;
694
- case 'u': {
695
- uint32_t code;
696
-
697
- h++;
698
- code = read_4hex(pi, h);
699
- h += 3;
700
- if (0x0000D800 <= code && code <= 0x0000DFFF) {
701
- uint32_t c1 = (code - 0x0000D800) & 0x000003FF;
702
- uint32_t c2;
703
-
704
- h++;
705
- if ('\\' != *h || 'u' != *(h + 1)) {
706
- pi->s = h;
707
- raise_error("invalid escaped character", pi->str, pi->s);
708
- }
709
- h += 2;
710
- c2 = read_4hex(pi, h);
711
- h += 3;
712
- c2 = (c2 - 0x0000DC00) & 0x000003FF;
713
- code = ((c1 << 10) | c2) + 0x00010000;
714
- }
715
- t = unicode_to_chars(pi, t, code);
716
- t--;
717
- break;
718
- }
719
- default:
720
- pi->s = h;
721
- raise_error("invalid escaped character", pi->str, pi->s);
722
- break;
723
- }
724
- } else if (t != h) {
725
- *t = *h;
726
- }
727
- }
728
- *t = '\0'; // terminate value
578
+ if ('\0' == *h) {
579
+ pi->s = h;
580
+ raise_error("quoted string not terminated", pi->str, pi->s);
581
+ } else if ('\\' == *h) {
582
+ h++;
583
+ switch (*h) {
584
+ case 'n': *t = '\n'; break;
585
+ case 'r': *t = '\r'; break;
586
+ case 't': *t = '\t'; break;
587
+ case 'f': *t = '\f'; break;
588
+ case 'b': *t = '\b'; break;
589
+ case '"': *t = '"'; break;
590
+ case '/': *t = '/'; break;
591
+ case '\\': *t = '\\'; break;
592
+ case 'u': {
593
+ uint32_t code;
594
+
595
+ h++;
596
+ code = read_4hex(pi, h);
597
+ h += 3;
598
+ if (0x0000D800 <= code && code <= 0x0000DFFF) {
599
+ uint32_t c1 = (code - 0x0000D800) & 0x000003FF;
600
+ uint32_t c2;
601
+
602
+ h++;
603
+ if ('\\' != *h || 'u' != *(h + 1)) {
604
+ pi->s = h;
605
+ raise_error("invalid escaped character", pi->str, pi->s);
606
+ }
607
+ h += 2;
608
+ c2 = read_4hex(pi, h);
609
+ h += 3;
610
+ c2 = (c2 - 0x0000DC00) & 0x000003FF;
611
+ code = ((c1 << 10) | c2) + 0x00010000;
612
+ }
613
+ t = unicode_to_chars(pi, t, code);
614
+ t--;
615
+ break;
616
+ }
617
+ default:
618
+ pi->s = h;
619
+ raise_error("invalid escaped character", pi->str, pi->s);
620
+ break;
621
+ }
622
+ } else if (t != h) {
623
+ *t = *h;
624
+ }
625
+ }
626
+ *t = '\0'; // terminate value
729
627
  pi->s = h + 1;
730
628
 
731
629
  return value;
732
630
  }
733
631
 
734
632
  // doc support functions
735
- inline static void
736
- doc_init(Doc doc) {
737
- memset(doc, 0, sizeof(struct _Doc));
738
- doc->where = doc->where_path;
739
- doc->self = Qundef;
633
+ inline static void doc_init(Doc doc) {
634
+ memset(doc, 0, sizeof(struct _doc));
635
+ doc->where = doc->where_path;
636
+ doc->self = Qundef;
740
637
  doc->batches = &doc->batch0;
741
638
  }
742
639
 
743
- static void
744
- doc_free(Doc doc) {
640
+ static void doc_free(Doc doc) {
745
641
  if (0 != doc) {
746
- Batch b;
642
+ Batch b;
747
643
 
748
- while (0 != (b = doc->batches)) {
749
- doc->batches = doc->batches->next;
750
- if (&doc->batch0 != b) {
751
- xfree(b);
752
- }
753
- }
754
- //xfree(f);
644
+ while (0 != (b = doc->batches)) {
645
+ doc->batches = doc->batches->next;
646
+ if (&doc->batch0 != b) {
647
+ OJ_R_FREE(b);
648
+ }
649
+ }
650
+ OJ_R_FREE(doc->json);
651
+ OJ_R_FREE(doc);
755
652
  }
756
653
  }
757
654
 
758
- static VALUE
759
- protect_open_proc(VALUE x) {
760
- ParseInfo pi = (ParseInfo)x;
655
+ static VALUE protect_open_proc(VALUE x) {
656
+ ParseInfo pi = (ParseInfo)x;
761
657
 
762
- pi->doc->data = read_next(pi); // parse
658
+ pi->doc->data = read_next(pi); // parse
763
659
  *pi->doc->where = pi->doc->data;
764
- pi->doc->where = pi->doc->where_path;
660
+ pi->doc->where = pi->doc->where_path;
765
661
  if (rb_block_given_p()) {
766
- return rb_yield(pi->doc->self); // caller processing
662
+ return rb_yield(pi->doc->self); // caller processing
767
663
  }
768
664
  return Qnil;
769
665
  }
770
666
 
771
- static void
772
- free_doc_cb(void *x) {
773
- Doc doc = (Doc)x;
667
+ static void free_doc_cb(void *x) {
668
+ Doc doc = (Doc)x;
774
669
 
775
670
  if (0 != doc) {
776
- xfree(doc->json);
777
- doc_free(doc);
671
+ doc_free(doc);
778
672
  }
779
673
  }
780
674
 
781
- static VALUE
782
- parse_json(VALUE clas, char *json, int given, int allocated) {
783
- struct _ParseInfo pi;
784
- VALUE result = Qnil;
785
- Doc doc;
786
- int ex = 0;
675
+ static void mark_leaf(Leaf leaf) {
676
+ if (NULL != leaf) {
677
+ switch (leaf->value_type) {
678
+ case COL_VAL:
679
+ if (NULL != leaf->elements) {
680
+ Leaf first = leaf->elements->next;
681
+ Leaf e = first;
682
+
683
+ do {
684
+ mark_leaf(e);
685
+ e = e->next;
686
+ } while (e != first);
687
+ }
688
+ break;
689
+ case RUBY_VAL: mark(leaf->value); break;
690
+
691
+ default: break;
692
+ }
693
+ }
694
+ }
787
695
 
788
- if (given) {
789
- doc = ALLOCA_N(struct _Doc, 1);
790
- } else {
791
- doc = ALLOC_N(struct _Doc, 1);
696
+ static void mark_doc(void *ptr) {
697
+ if (NULL != ptr) {
698
+ Doc doc = (Doc)ptr;
699
+
700
+ mark(doc->self);
701
+ mark_leaf(doc->data);
702
+ }
703
+ }
704
+ #ifdef HAVE_RB_GC_MARK_MOVABLE
705
+ static void compact_leaf(Leaf leaf) {
706
+ switch (leaf->value_type) {
707
+ case COL_VAL:
708
+ if (NULL != leaf->elements) {
709
+ Leaf first = leaf->elements->next;
710
+ Leaf e = first;
711
+
712
+ do {
713
+ compact_leaf(e);
714
+ e = e->next;
715
+ } while (e != first);
716
+ }
717
+ break;
718
+ case RUBY_VAL: leaf->value = rb_gc_location(leaf->value); break;
719
+
720
+ default: break;
721
+ }
722
+ }
723
+
724
+ static void compact_doc(void *ptr) {
725
+ Doc doc = (Doc)ptr;
726
+
727
+ if (doc) {
728
+ doc->self = rb_gc_location(doc->self);
729
+ compact_leaf(doc->data);
792
730
  }
793
- /* skip UTF-8 BOM if present */
731
+ }
732
+ #endif
733
+
734
+ static const rb_data_type_t oj_doc_type = {
735
+ "Oj/doc",
736
+ {
737
+ mark_doc,
738
+ free_doc_cb,
739
+ NULL,
740
+ #ifdef HAVE_RB_GC_MARK_MOVABLE
741
+ compact_doc,
742
+ #endif
743
+ },
744
+ 0,
745
+ 0,
746
+ };
747
+
748
+ static VALUE parse_json(VALUE clas, char *json, bool given) {
749
+ struct _parseInfo pi;
750
+ volatile VALUE result = Qnil;
751
+ Doc doc;
752
+ int ex = 0;
753
+ volatile VALUE self;
754
+
755
+ doc = OJ_R_ALLOC_N(struct _doc, 1);
756
+
757
+ // skip UTF-8 BOM if present
794
758
  if (0xEF == (uint8_t)*json && 0xBB == (uint8_t)json[1] && 0xBF == (uint8_t)json[2]) {
795
- pi.str = json + 3;
759
+ pi.str = json + 3;
796
760
  } else {
797
- pi.str = json;
761
+ pi.str = json;
798
762
  }
799
763
  pi.s = pi.str;
800
764
  doc_init(doc);
801
765
  pi.doc = doc;
802
- #if IS_WINDOWS
803
- pi.stack_min = (void*)((char*)&pi - (512 * 1024)); // assume a 1M stack and give half to ruby
766
+ #if IS_WINDOWS || !defined(HAVE_GETRLIMIT)
767
+ // assume a 1M stack and give half to ruby
768
+ pi.stack_min = (void *)((char *)&pi - (512L * 1024L));
804
769
  #else
805
770
  {
806
- struct rlimit lim;
771
+ struct rlimit lim;
807
772
 
808
- if (0 == getrlimit(RLIMIT_STACK, &lim)) {
809
- pi.stack_min = (void*)((char*)&lim - (lim.rlim_cur / 4 * 3)); // let 3/4ths of the stack be used only
810
- } else {
811
- pi.stack_min = 0; // indicates not to check stack limit
812
- }
773
+ if (0 == getrlimit(RLIMIT_STACK, &lim) && RLIM_INFINITY != lim.rlim_cur) {
774
+ // let 3/4ths of the stack be used only
775
+ pi.stack_min = (void *)((char *)&lim - (lim.rlim_cur / 4 * 3));
776
+ } else {
777
+ pi.stack_min = 0; // indicates not to check stack limit
778
+ }
813
779
  }
814
780
  #endif
815
- // last arg is free func void* func(void*)
816
- #if HAS_DATA_OBJECT_WRAP
817
- doc->self = rb_data_object_wrap(clas, doc, 0, free_doc_cb);
818
- #else
819
- doc->self = rb_data_object_alloc(clas, doc, 0, free_doc_cb);
820
- #endif
821
- rb_gc_register_address(&doc->self);
822
781
  doc->json = json;
823
- DATA_PTR(doc->self) = doc;
824
- result = rb_protect(protect_open_proc, (VALUE)&pi, &ex);
782
+ self = TypedData_Wrap_Struct(clas, &oj_doc_type, doc);
783
+ doc->self = self;
784
+ result = rb_protect(protect_open_proc, (VALUE)&pi, &ex);
825
785
  if (given || 0 != ex) {
826
- rb_gc_unregister_address(&doc->self);
827
- DATA_PTR(doc->self) = 0;
828
- doc_free(pi.doc);
829
- if (allocated && 0 != ex) { // will jump so caller will not free
830
- xfree(json);
831
- }
786
+ DATA_PTR(doc->self) = NULL;
787
+ // TBD is this needed?
788
+ /*
789
+ doc_free(pi.doc);
790
+ if (0 != ex) { // will jump so caller will not free
791
+ OJ_R_FREE(json);
792
+ }
793
+ */
832
794
  } else {
833
- result = doc->self;
795
+ result = doc->self;
834
796
  }
835
797
  if (0 != ex) {
836
- rb_jump_tag(ex);
798
+ rb_jump_tag(ex);
837
799
  }
838
800
  return result;
839
801
  }
840
802
 
841
- static Leaf
842
- get_doc_leaf(Doc doc, const char *path) {
843
- Leaf leaf = *doc->where;
803
+ static Leaf get_doc_leaf(Doc doc, const char *path) {
804
+ Leaf leaf = *doc->where;
844
805
 
845
806
  if (0 != doc->data && 0 != path) {
846
- Leaf stack[MAX_STACK];
847
- Leaf *lp;
848
-
849
- if ('/' == *path) {
850
- path++;
851
- *stack = doc->data;
852
- lp = stack;
853
- } else if (doc->where == doc->where_path) {
854
- *stack = doc->data;
855
- lp = stack;
856
- } else {
857
- size_t cnt = doc->where - doc->where_path;
858
-
859
- if (MAX_STACK <= cnt) {
860
- rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
861
- }
862
- memcpy(stack, doc->where_path, sizeof(Leaf) * (cnt + 1));
863
- lp = stack + cnt;
864
- }
865
- return get_leaf(stack, lp, path);
807
+ Leaf stack[MAX_STACK];
808
+ Leaf *lp;
809
+
810
+ if ('/' == *path) {
811
+ path++;
812
+ *stack = doc->data;
813
+ lp = stack;
814
+ } else if (doc->where == doc->where_path) {
815
+ *stack = doc->data;
816
+ lp = stack;
817
+ } else {
818
+ size_t cnt = doc->where - doc->where_path;
819
+
820
+ if (MAX_STACK <= cnt) {
821
+ rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
822
+ }
823
+ memcpy(stack, doc->where_path, sizeof(Leaf) * (cnt + 1));
824
+ lp = stack + cnt;
825
+ }
826
+ return get_leaf(stack, lp, path);
866
827
  }
867
828
  return leaf;
868
829
  }
869
830
 
870
- static const char*
871
- next_slash(const char *s) {
831
+ static const char *next_slash(const char *s) {
872
832
  for (; '\0' != *s; s++) {
873
- if ('\\' == *s) {
874
- s++;
875
- if ('\0' == *s) {
876
- break;
877
- }
878
- } else if ('/' == *s) {
879
- return s;
880
- }
833
+ if ('\\' == *s) {
834
+ s++;
835
+ if ('\0' == *s) {
836
+ break;
837
+ }
838
+ } else if ('/' == *s) {
839
+ return s;
840
+ }
881
841
  }
882
842
  return NULL;
883
843
  }
884
844
 
885
- static bool
886
- key_match(const char *pat, const char *key, int plen) {
845
+ static bool key_match(const char *pat, const char *key, int plen) {
887
846
  for (; 0 < plen; plen--, pat++, key++) {
888
- if ('\\' == *pat) {
889
- plen--;
890
- pat++;
891
- }
892
- if (*pat != *key) {
893
- return false;
894
- }
847
+ if ('\\' == *pat) {
848
+ plen--;
849
+ pat++;
850
+ }
851
+ if (*pat != *key) {
852
+ return false;
853
+ }
895
854
  }
896
855
  return '\0' == *key;
897
856
  }
898
857
 
899
- static Leaf
900
- get_leaf(Leaf *stack, Leaf *lp, const char *path) {
901
- Leaf leaf = *lp;
858
+ static Leaf get_leaf(Leaf *stack, Leaf *lp, const char *path) {
859
+ Leaf leaf = *lp;
902
860
 
903
861
  if (MAX_STACK <= lp - stack) {
904
- rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
862
+ rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
905
863
  }
906
864
  if ('\0' != *path) {
907
- if ('.' == *path && '.' == *(path + 1)) {
908
- path += 2;
909
- if ('/' == *path) {
910
- path++;
911
- }
912
- if (stack < lp) {
913
- leaf = get_leaf(stack, lp - 1, path);
914
- } else {
915
- return 0;
916
- }
917
- } else if (COL_VAL == leaf->value_type && 0 != leaf->elements) {
918
- Leaf first = leaf->elements->next;
919
- Leaf e = first;
920
- int type = leaf->rtype;
921
-
922
- leaf = 0;
923
- if (T_ARRAY == type) {
924
- int cnt = 0;
925
-
926
- for (; '0' <= *path && *path <= '9'; path++) {
927
- cnt = cnt * 10 + (*path - '0');
928
- }
929
- if ('/' == *path) {
930
- path++;
931
- }
932
- do {
933
- if (1 >= cnt) {
934
- lp++;
935
- *lp = e;
936
- leaf = get_leaf(stack, lp, path);
937
- break;
938
- }
939
- cnt--;
940
- e = e->next;
941
- } while (e != first);
942
- } else if (T_HASH == type) {
943
- const char *key = path;
944
- const char *slash = next_slash(path);
945
- int klen;
946
-
947
- if (0 == slash) {
948
- klen = (int)strlen(key);
949
- path += klen;
950
- } else {
951
- klen = (int)(slash - key);
952
- path += klen + 1;
953
- }
954
- do {
955
- if (key_match(key, e->key, klen)) {
956
- lp++;
957
- *lp = e;
958
- leaf = get_leaf(stack, lp, path);
959
- break;
960
- }
961
- e = e->next;
962
- } while (e != first);
963
- }
964
- }
865
+ if ('.' == *path && '.' == *(path + 1)) {
866
+ path += 2;
867
+ if ('/' == *path) {
868
+ path++;
869
+ }
870
+ if (stack < lp) {
871
+ leaf = get_leaf(stack, lp - 1, path);
872
+ } else {
873
+ return 0;
874
+ }
875
+ } else if (NULL == leaf->elements) {
876
+ leaf = NULL;
877
+ } else if (STR_VAL == leaf->value_type || RUBY_VAL == leaf->value_type) {
878
+ // We are trying to get a children of a leaf, which
879
+ // doesn't exist.
880
+ leaf = NULL;
881
+ } else if (COL_VAL == leaf->value_type) {
882
+ Leaf first = leaf->elements->next;
883
+ Leaf e = first;
884
+ int type = leaf->rtype;
885
+
886
+ leaf = NULL;
887
+ if (T_ARRAY == type) {
888
+ int cnt = 0;
889
+
890
+ for (; '0' <= *path && *path <= '9'; path++) {
891
+ cnt = cnt * 10 + (*path - '0');
892
+ }
893
+ if ('/' == *path) {
894
+ path++;
895
+ }
896
+ do {
897
+ if (1 >= cnt) {
898
+ lp++;
899
+ *lp = e;
900
+ leaf = get_leaf(stack, lp, path);
901
+ break;
902
+ }
903
+ cnt--;
904
+ e = e->next;
905
+ } while (e != first);
906
+ } else if (T_HASH == type) {
907
+ const char *key = path;
908
+ const char *slash = next_slash(path);
909
+ int klen;
910
+
911
+ leaf = NULL;
912
+ if (0 == slash) {
913
+ klen = (int)strlen(key);
914
+ path += klen;
915
+ } else {
916
+ klen = (int)(slash - key);
917
+ path += klen + 1;
918
+ }
919
+ do {
920
+ if (key_match(key, e->key, klen)) {
921
+ lp++;
922
+ *lp = e;
923
+ leaf = get_leaf(stack, lp, path);
924
+ break;
925
+ }
926
+ e = e->next;
927
+ } while (e != first);
928
+ }
929
+ }
965
930
  }
966
931
  return leaf;
967
932
  }
968
933
 
969
- static void
970
- each_leaf(Doc doc, VALUE self) {
934
+ static void each_leaf(Doc doc, VALUE self) {
971
935
  if (COL_VAL == (*doc->where)->value_type) {
972
- if (0 != (*doc->where)->elements) {
973
- Leaf first = (*doc->where)->elements->next;
974
- Leaf e = first;
975
-
976
- doc->where++;
977
- if (MAX_STACK <= doc->where - doc->where_path) {
978
- rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
979
- }
980
- do {
981
- *doc->where = e;
982
- each_leaf(doc, self);
983
- e = e->next;
984
- } while (e != first);
985
- doc->where--;
986
- }
936
+ if (0 != (*doc->where)->elements) {
937
+ Leaf first = (*doc->where)->elements->next;
938
+ Leaf e = first;
939
+
940
+ doc->where++;
941
+ if (MAX_STACK <= doc->where - doc->where_path) {
942
+ rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
943
+ }
944
+ do {
945
+ *doc->where = e;
946
+ each_leaf(doc, self);
947
+ e = e->next;
948
+ } while (e != first);
949
+ doc->where--;
950
+ }
987
951
  } else {
988
- rb_yield(self);
952
+ rb_yield(self);
989
953
  }
990
954
  }
991
955
 
992
- static int
993
- move_step(Doc doc, const char *path, int loc) {
956
+ static int move_step(Doc doc, const char *path, int loc) {
994
957
  if (MAX_STACK <= doc->where - doc->where_path) {
995
- rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
958
+ rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
996
959
  }
997
960
  if ('\0' == *path) {
998
- loc = 0;
961
+ loc = 0;
999
962
  } else {
1000
- Leaf leaf;
1001
-
1002
- if (0 == doc->where || 0 == (leaf = *doc->where)) {
1003
- printf("*** Internal error at %s\n", path);
1004
- return loc;
1005
- }
1006
- if ('.' == *path && '.' == *(path + 1)) {
1007
- Leaf init = *doc->where;
1008
-
1009
- path += 2;
1010
- if (doc->where == doc->where_path) {
1011
- return loc;
1012
- }
1013
- if ('/' == *path) {
1014
- path++;
1015
- }
1016
- *doc->where = 0;
1017
- doc->where--;
1018
- loc = move_step(doc, path, loc + 1);
1019
- if (0 != loc) {
1020
- *doc->where = init;
1021
- doc->where++;
1022
- }
1023
- } else if (COL_VAL == leaf->value_type && 0 != leaf->elements) {
1024
- Leaf first = leaf->elements->next;
1025
- Leaf e = first;
1026
-
1027
- if (T_ARRAY == leaf->rtype) {
1028
- int cnt = 0;
1029
-
1030
- for (; '0' <= *path && *path <= '9'; path++) {
1031
- cnt = cnt * 10 + (*path - '0');
1032
- }
1033
- if ('/' == *path) {
1034
- path++;
1035
- } else if ('\0' != *path) {
1036
- return loc;
1037
- }
1038
- do {
1039
- if (1 >= cnt) {
1040
- doc->where++;
1041
- *doc->where = e;
1042
- loc = move_step(doc, path, loc + 1);
1043
- if (0 != loc) {
1044
- *doc->where = 0;
1045
- doc->where--;
1046
- }
1047
- break;
1048
- }
1049
- cnt--;
1050
- e = e->next;
1051
- } while (e != first);
1052
- } else if (T_HASH == leaf->rtype) {
1053
- const char *key = path;
1054
- const char *slash = next_slash(path);
1055
- int klen;
1056
-
1057
- if (0 == slash) {
1058
- klen = (int)strlen(key);
1059
- path += klen;
1060
- } else {
1061
- klen = (int)(slash - key);
1062
- path += klen + 1;
1063
- }
1064
- do {
1065
- if (key_match(key, e->key, klen)) {
1066
- doc->where++;
1067
- *doc->where = e;
1068
- loc = move_step(doc, path, loc + 1);
1069
- if (0 != loc) {
1070
- *doc->where = 0;
1071
- doc->where--;
1072
- }
1073
- break;
1074
- }
1075
- e = e->next;
1076
- } while (e != first);
1077
- }
1078
- }
963
+ Leaf leaf;
964
+
965
+ if (0 == doc->where || 0 == (leaf = *doc->where)) {
966
+ printf("*** Internal error at %s\n", path);
967
+ return loc;
968
+ }
969
+ if ('.' == *path && '.' == *(path + 1)) {
970
+ Leaf init = *doc->where;
971
+
972
+ path += 2;
973
+ if (doc->where == doc->where_path) {
974
+ return loc;
975
+ }
976
+ if ('/' == *path) {
977
+ path++;
978
+ }
979
+ *doc->where = 0;
980
+ doc->where--;
981
+ loc = move_step(doc, path, loc + 1);
982
+ if (0 != loc) {
983
+ *doc->where = init;
984
+ doc->where++;
985
+ }
986
+ } else if (COL_VAL == leaf->value_type && 0 != leaf->elements) {
987
+ Leaf first = leaf->elements->next;
988
+ Leaf e = first;
989
+
990
+ if (T_ARRAY == leaf->rtype) {
991
+ int cnt = 0;
992
+
993
+ for (; '0' <= *path && *path <= '9'; path++) {
994
+ cnt = cnt * 10 + (*path - '0');
995
+ }
996
+ if ('/' == *path) {
997
+ path++;
998
+ } else if ('\0' != *path) {
999
+ return loc;
1000
+ }
1001
+ do {
1002
+ if (1 >= cnt) {
1003
+ doc->where++;
1004
+ *doc->where = e;
1005
+ loc = move_step(doc, path, loc + 1);
1006
+ if (0 != loc) {
1007
+ *doc->where = 0;
1008
+ doc->where--;
1009
+ }
1010
+ break;
1011
+ }
1012
+ cnt--;
1013
+ e = e->next;
1014
+ } while (e != first);
1015
+ } else if (T_HASH == leaf->rtype) {
1016
+ const char *key = path;
1017
+ const char *slash = next_slash(path);
1018
+ int klen;
1019
+
1020
+ if (0 == slash) {
1021
+ klen = (int)strlen(key);
1022
+ path += klen;
1023
+ } else {
1024
+ klen = (int)(slash - key);
1025
+ path += klen + 1;
1026
+ }
1027
+ do {
1028
+ if (key_match(key, e->key, klen)) {
1029
+ doc->where++;
1030
+ *doc->where = e;
1031
+ loc = move_step(doc, path, loc + 1);
1032
+ if (0 != loc) {
1033
+ *doc->where = 0;
1034
+ doc->where--;
1035
+ }
1036
+ break;
1037
+ }
1038
+ e = e->next;
1039
+ } while (e != first);
1040
+ }
1041
+ }
1079
1042
  }
1080
1043
  return loc;
1081
1044
  }
1082
1045
 
1083
- static void
1084
- each_value(Doc doc, Leaf leaf) {
1046
+ static void each_value(Doc doc, Leaf leaf) {
1085
1047
  if (COL_VAL == leaf->value_type) {
1086
- if (0 != leaf->elements) {
1087
- Leaf first = leaf->elements->next;
1088
- Leaf e = first;
1089
-
1090
- do {
1091
- each_value(doc, e);
1092
- e = e->next;
1093
- } while (e != first);
1094
- }
1048
+ if (0 != leaf->elements) {
1049
+ Leaf first = leaf->elements->next;
1050
+ Leaf e = first;
1051
+
1052
+ do {
1053
+ each_value(doc, e);
1054
+ e = e->next;
1055
+ } while (e != first);
1056
+ }
1095
1057
  } else {
1096
- rb_yield(leaf_value(doc, leaf));
1058
+ rb_yield(leaf_value(doc, leaf));
1097
1059
  }
1098
1060
  }
1099
1061
 
1100
1062
  // doc functions
1101
1063
 
1102
- /* call-seq: open(json) { |doc| ... } => Object
1064
+ /* @overload open(json) { |doc| ... } => Object
1103
1065
  *
1104
1066
  * Parses a JSON document String and then yields to the provided block if one
1105
1067
  * is given with an instance of the Oj::Doc as the single yield parameter. If
1106
1068
  * a block is not given then an Oj::Doc instance is returned and must be
1107
1069
  * closed with a call to the #close() method when no longer needed.
1108
1070
  *
1109
- * @param [String] json JSON document string
1071
+ * @param [String] json JSON document string
1110
1072
  * @yieldparam [Oj::Doc] doc parsed JSON document
1111
- * @yieldreturn [Object] returns the result of the yield as the result of the method call
1073
+ * @yieldreturn [Object] returns the result of the yield as the result of the
1074
+ * method call
1112
1075
  * @example
1113
1076
  * Oj::Doc.open('[1,2,3]') { |doc| doc.size() } #=> 4
1114
1077
  * # or as an alternative
@@ -1116,40 +1079,38 @@ each_value(Doc doc, Leaf leaf) {
1116
1079
  * doc.size() #=> 4
1117
1080
  * doc.close()
1118
1081
  */
1119
- static VALUE
1120
- doc_open(VALUE clas, VALUE str) {
1121
- char *json;
1122
- size_t len;
1123
- VALUE obj;
1124
- int given = rb_block_given_p();
1125
- int allocate;
1082
+ static VALUE doc_open(VALUE clas, VALUE str) {
1083
+ char *json;
1084
+ size_t len;
1085
+ volatile VALUE obj;
1086
+ int given = rb_block_given_p();
1126
1087
 
1127
1088
  Check_Type(str, T_STRING);
1128
- len = RSTRING_LEN(str) + 1;
1129
- allocate = (SMALL_XML < len || !given);
1130
- if (allocate) {
1131
- json = ALLOC_N(char, len);
1132
- } else {
1133
- json = ALLOCA_N(char, len);
1134
- }
1089
+ len = RSTRING_LEN(str) + 1;
1090
+ json = OJ_R_ALLOC_N(char, len);
1091
+
1135
1092
  memcpy(json, StringValuePtr(str), len);
1136
- obj = parse_json(clas, json, given, allocate);
1137
- if (given && allocate) {
1138
- xfree(json);
1093
+ obj = parse_json(clas, json, given);
1094
+ // TBD is this needed
1095
+ /*
1096
+ if (given) {
1097
+ OJ_R_FREE(json);
1139
1098
  }
1099
+ */
1140
1100
  return obj;
1141
1101
  }
1142
1102
 
1143
- /* call-seq: open_file(filename) { |doc| ... } => Object
1103
+ /* @overload open_file(filename) { |doc| ... } => Object
1144
1104
  *
1145
1105
  * Parses a JSON document from a file and then yields to the provided block if
1146
1106
  * one is given with an instance of the Oj::Doc as the single yield
1147
1107
  * parameter. If a block is not given then an Oj::Doc instance is returned and
1148
1108
  * must be closed with a call to the #close() method when no longer needed.
1149
1109
  *
1150
- * @param [String] filename name of file that contains a JSON document
1110
+ * @param [String] filename name of file that contains a JSON document
1151
1111
  * @yieldparam [Oj::Doc] doc parsed JSON document
1152
- * @yieldreturn [Object] returns the result of the yield as the result of the method call
1112
+ * @yieldreturn [Object] returns the result of the yield as the result of the
1113
+ * method call
1153
1114
  * @example
1154
1115
  * File.open('array.json', 'w') { |f| f.write('[1,2,3]') }
1155
1116
  * Oj::Doc.open_file(filename) { |doc| doc.size() } #=> 4
@@ -1158,63 +1119,59 @@ doc_open(VALUE clas, VALUE str) {
1158
1119
  * doc.size() #=> 4
1159
1120
  * doc.close()
1160
1121
  */
1161
- static VALUE
1162
- doc_open_file(VALUE clas, VALUE filename) {
1163
- char *path;
1164
- char *json;
1165
- FILE *f;
1166
- size_t len;
1167
- VALUE obj;
1168
- int given = rb_block_given_p();
1169
- int allocate;
1170
-
1171
- Check_Type(filename, T_STRING);
1122
+ static VALUE doc_open_file(VALUE clas, VALUE filename) {
1123
+ char *path;
1124
+ char *json;
1125
+ FILE *f;
1126
+ size_t len;
1127
+ volatile VALUE obj;
1128
+ int given = rb_block_given_p();
1129
+
1172
1130
  path = StringValuePtr(filename);
1173
1131
  if (0 == (f = fopen(path, "r"))) {
1174
- rb_raise(rb_eIOError, "%s", strerror(errno));
1132
+ rb_raise(rb_eIOError, "%s", strerror(errno));
1175
1133
  }
1176
1134
  fseek(f, 0, SEEK_END);
1177
- len = ftell(f);
1178
- allocate = (SMALL_XML < len || !given);
1179
- if (allocate) {
1180
- json = ALLOC_N(char, len + 1);
1181
- } else {
1182
- json = ALLOCA_N(char, len + 1);
1183
- }
1135
+ len = ftell(f);
1136
+ json = OJ_R_ALLOC_N(char, len + 1);
1137
+
1184
1138
  fseek(f, 0, SEEK_SET);
1185
1139
  if (len != fread(json, 1, len, f)) {
1186
- fclose(f);
1187
- rb_raise(rb_const_get_at(Oj, rb_intern("LoadError")),
1188
- "Failed to read %lu bytes from %s.", (unsigned long)len, path);
1140
+ fclose(f);
1141
+ rb_raise(rb_const_get_at(Oj, rb_intern("LoadError")),
1142
+ "Failed to read %lu bytes from %s.",
1143
+ (unsigned long)len,
1144
+ path);
1189
1145
  }
1190
1146
  fclose(f);
1191
1147
  json[len] = '\0';
1192
- obj = parse_json(clas, json, given, allocate);
1193
- if (given && allocate) {
1194
- xfree(json);
1148
+ obj = parse_json(clas, json, given);
1149
+ // TBD is this needed
1150
+ /*
1151
+ if (given) {
1152
+ OJ_R_FREE(json);
1195
1153
  }
1154
+ */
1196
1155
  return obj;
1197
1156
  }
1198
1157
 
1199
- static int
1200
- esc_strlen(const char *s) {
1201
- int cnt = 0;
1158
+ static int esc_strlen(const char *s) {
1159
+ int cnt = 0;
1202
1160
 
1203
1161
  for (; '\0' != *s; s++, cnt++) {
1204
- if ('/' == *s) {
1205
- cnt++;
1206
- }
1162
+ if ('/' == *s) {
1163
+ cnt++;
1164
+ }
1207
1165
  }
1208
1166
  return cnt;
1209
1167
  }
1210
1168
 
1211
- static char*
1212
- append_key(char *p, const char *key) {
1169
+ static char *append_key(char *p, const char *key) {
1213
1170
  for (; '\0' != *key; p++, key++) {
1214
- if ('/' == *key) {
1215
- *p++ = '\\';
1216
- }
1217
- *p = *key;
1171
+ if ('/' == *key) {
1172
+ *p++ = '\\';
1173
+ }
1174
+ *p = *key;
1218
1175
  }
1219
1176
  return p;
1220
1177
  }
@@ -1223,169 +1180,201 @@ append_key(char *p, const char *key) {
1223
1180
  * @see Oj::Doc.open
1224
1181
  */
1225
1182
 
1226
- /* call-seq: where?() => String
1183
+ /* @overload where() => String
1227
1184
  *
1228
1185
  * Returns a String that describes the absolute path to the current location
1229
1186
  * in the JSON document.
1230
1187
  */
1231
- static VALUE
1232
- doc_where(VALUE self) {
1233
- Doc doc = self_doc(self);
1188
+ static VALUE doc_where(VALUE self) {
1189
+ Doc doc = self_doc(self);
1234
1190
 
1235
1191
  if (0 == *doc->where_path || doc->where == doc->where_path) {
1236
- return oj_slash_string;
1192
+ return oj_slash_string;
1237
1193
  } else {
1238
- Leaf *lp;
1239
- Leaf leaf;
1240
- size_t size = 3; // leading / and terminating \0
1241
- char *path;
1242
- char *p;
1243
-
1244
- for (lp = doc->where_path; lp <= doc->where; lp++) {
1245
- leaf = *lp;
1246
- if (T_HASH == leaf->parent_type) {
1247
- size += esc_strlen((*lp)->key) + 1;
1248
- } else if (T_ARRAY == leaf->parent_type) {
1249
- size += ((*lp)->index < 100) ? 3 : 11;
1250
- }
1251
- }
1252
- path = ALLOCA_N(char, size);
1253
- p = path;
1254
- for (lp = doc->where_path; lp <= doc->where; lp++) {
1255
- leaf = *lp;
1256
- if (T_HASH == leaf->parent_type) {
1257
- p = append_key(p, (*lp)->key);
1258
- } else if (T_ARRAY == leaf->parent_type) {
1259
- p = ulong_fill(p, (*lp)->index);
1260
- }
1261
- *p++ = '/';
1262
- }
1263
- *--p = '\0';
1264
- return rb_str_new(path, p - path);
1194
+ Leaf *lp;
1195
+ Leaf leaf;
1196
+ size_t size = 3; // leading / and terminating \0
1197
+ char *path;
1198
+ char *p;
1199
+
1200
+ for (lp = doc->where_path; lp <= doc->where; lp++) {
1201
+ leaf = *lp;
1202
+ if (T_HASH == leaf->parent_type) {
1203
+ size += esc_strlen((*lp)->key) + 1;
1204
+ } else if (T_ARRAY == leaf->parent_type) {
1205
+ size += ((*lp)->index < 100) ? 3 : 11;
1206
+ }
1207
+ }
1208
+ path = ALLOCA_N(char, size);
1209
+ p = path;
1210
+ for (lp = doc->where_path; lp <= doc->where; lp++) {
1211
+ leaf = *lp;
1212
+ if (T_HASH == leaf->parent_type) {
1213
+ p = append_key(p, (*lp)->key);
1214
+ } else if (T_ARRAY == leaf->parent_type) {
1215
+ p = ulong_fill(p, (*lp)->index);
1216
+ }
1217
+ *p++ = '/';
1218
+ }
1219
+ *--p = '\0';
1220
+
1221
+ return rb_str_new(path, p - path);
1265
1222
  }
1266
1223
  }
1267
1224
 
1268
- /* call-seq: local_key() => String, Fixnum, nil
1225
+ /* @overload where?() => String
1226
+ * @deprecated
1227
+ * Returns a String that describes the absolute path to the current location
1228
+ * in the JSON document.
1229
+ */
1230
+ static VALUE doc_where_q(VALUE self) {
1231
+ return doc_where(self);
1232
+ }
1233
+
1234
+ /* @overload path() => String
1235
+ *
1236
+ * Returns a String that describes the absolute path to the current location
1237
+ * in the JSON document.
1238
+ */
1239
+ static VALUE doc_path(VALUE self) {
1240
+ return doc_where(self);
1241
+ }
1242
+
1243
+ /* @overload local_key() => String, Fixnum, nil
1269
1244
  *
1270
1245
  * Returns the final key to the current location.
1271
1246
  * @example
1272
- * Oj::Doc.open('[1,2,3]') { |doc| doc.move('/2'); doc.local_key() } #=> 2
1273
- * Oj::Doc.open('{"one":3}') { |doc| doc.move('/one'); doc.local_key() } #=> "one"
1274
- * Oj::Doc.open('[1,2,3]') { |doc| doc.local_key() } #=> nil
1247
+ * Oj::Doc.open('[1,2,3]') { |doc| doc.move('/2'); doc.local_key() } #=> 2
1248
+ * Oj::Doc.open('{"one":3}') { |doc| doc.move('/one'); doc.local_key() } #=>
1249
+ * "one" Oj::Doc.open('[1,2,3]') { |doc| doc.local_key() }
1250
+ * #=> nil
1275
1251
  */
1276
- static VALUE
1277
- doc_local_key(VALUE self) {
1278
- Doc doc = self_doc(self);
1279
- Leaf leaf = *doc->where;
1280
- VALUE key = Qnil;
1252
+ static VALUE doc_local_key(VALUE self) {
1253
+ Doc doc = self_doc(self);
1254
+ Leaf leaf = *doc->where;
1255
+ volatile VALUE key = Qnil;
1281
1256
 
1282
1257
  if (T_HASH == leaf->parent_type) {
1283
- key = rb_str_new2(leaf->key);
1284
- key = oj_encode(key);
1258
+ key = rb_utf8_str_new_cstr(leaf->key);
1285
1259
  } else if (T_ARRAY == leaf->parent_type) {
1286
- key = LONG2NUM(leaf->index);
1260
+ key = LONG2NUM(leaf->index);
1287
1261
  }
1288
1262
  return key;
1289
1263
  }
1290
1264
 
1291
- /* call-seq: home() => nil
1265
+ /* @overload home() => nil
1292
1266
  *
1293
1267
  * Moves the document marker or location to the hoot or home position. The
1294
1268
  * same operation can be performed with a Oj::Doc.move('/').
1295
1269
  * @example
1296
- * Oj::Doc.open('[1,2,3]') { |doc| doc.move('/2'); doc.home(); doc.where? } #=> '/'
1270
+ * Oj::Doc.open('[1,2,3]') { |doc| doc.move('/2'); doc.home(); doc.where? }
1271
+ * #=> '/'
1297
1272
  */
1298
- static VALUE
1299
- doc_home(VALUE self) {
1300
- Doc doc = self_doc(self);
1273
+ static VALUE doc_home(VALUE self) {
1274
+ Doc doc = self_doc(self);
1301
1275
 
1302
1276
  *doc->where_path = doc->data;
1303
- doc->where = doc->where_path;
1277
+ doc->where = doc->where_path;
1304
1278
 
1305
1279
  return oj_slash_string;
1306
1280
  }
1307
1281
 
1308
- /* call-seq: type(path=nil) => Class
1282
+ /* @overload type(path=nil) => Class
1309
1283
  *
1310
1284
  * Returns the Class of the data value at the location identified by the path
1311
1285
  * or the current location if the path is nil or not provided. This method
1312
1286
  * does not create the Ruby Object at the location specified so the overhead
1313
1287
  * is low.
1314
- * @param [String] path path to the location to get the type of if provided
1288
+ * @param [String] path path to the location to get the type of if provided
1315
1289
  * @example
1316
1290
  * Oj::Doc.open('[1,2]') { |doc| doc.type() } #=> Array
1317
1291
  * Oj::Doc.open('[1,2]') { |doc| doc.type('/1') } #=> Fixnum
1318
1292
  */
1319
- static VALUE
1320
- doc_type(int argc, VALUE *argv, VALUE self) {
1321
- Doc doc = self_doc(self);
1322
- Leaf leaf;
1323
- const char *path = 0;
1324
- VALUE type = Qnil;
1293
+ static VALUE doc_type(int argc, VALUE *argv, VALUE self) {
1294
+ Doc doc = self_doc(self);
1295
+ Leaf leaf;
1296
+ const char *path = 0;
1297
+ VALUE type = Qnil;
1325
1298
 
1326
1299
  if (1 <= argc) {
1327
- Check_Type(*argv, T_STRING);
1328
- path = StringValuePtr(*argv);
1300
+ path = StringValuePtr(*argv);
1329
1301
  }
1330
1302
  if (0 != (leaf = get_doc_leaf(doc, path))) {
1331
- switch (leaf->rtype) {
1332
- case T_NIL: type = rb_cNilClass; break;
1333
- case T_TRUE: type = rb_cTrueClass; break;
1334
- case T_FALSE: type = rb_cFalseClass; break;
1335
- case T_STRING: type = rb_cString; break;
1336
- #ifdef RUBY_INTEGER_UNIFICATION
1337
- case T_FIXNUM: type = rb_cInteger; break;
1338
- #else
1339
- case T_FIXNUM: type = rb_cFixnum; break;
1340
- #endif
1341
- case T_FLOAT: type = rb_cFloat; break;
1342
- case T_ARRAY: type = rb_cArray; break;
1343
- case T_HASH: type = rb_cHash; break;
1344
- default: break;
1345
- }
1303
+ switch (leaf->rtype) {
1304
+ case T_NIL: type = rb_cNilClass; break;
1305
+ case T_TRUE: type = rb_cTrueClass; break;
1306
+ case T_FALSE: type = rb_cFalseClass; break;
1307
+ case T_STRING: type = rb_cString; break;
1308
+ case T_FIXNUM: type = rb_cInteger; break;
1309
+ case T_FLOAT: type = rb_cFloat; break;
1310
+ case T_ARRAY: type = rb_cArray; break;
1311
+ case T_HASH: type = rb_cHash; break;
1312
+ default: break;
1313
+ }
1346
1314
  }
1347
1315
  return type;
1348
1316
  }
1349
1317
 
1350
- /* call-seq: fetch(path=nil) => nil, true, false, Fixnum, Float, String, Array, Hash
1318
+ /* @overload fetch(path=nil,default=nil) => nil, true, false, Fixnum, Float, String, Array,
1319
+ * Hash
1351
1320
  *
1352
1321
  * Returns the value at the location identified by the path or the current
1353
1322
  * location if the path is nil or not provided. This method will create and
1354
1323
  * return an Array or Hash if that is the type of Object at the location
1355
1324
  * specified. This is more expensive than navigating to the leaves of the JSON
1356
- * document.
1357
- * @param [String] path path to the location to get the type of if provided
1325
+ * document. If a default is provided that is used if no value if found.
1326
+ * @param [String] path path to the location to get the type of if provided
1358
1327
  * @example
1359
1328
  * Oj::Doc.open('[1,2]') { |doc| doc.fetch() } #=> [1, 2]
1360
1329
  * Oj::Doc.open('[1,2]') { |doc| doc.fetch('/1') } #=> 1
1361
1330
  */
1362
- static VALUE
1363
- doc_fetch(int argc, VALUE *argv, VALUE self) {
1364
- Doc doc;
1365
- Leaf leaf;
1366
- VALUE val = Qnil;
1367
- const char *path = 0;
1331
+ static VALUE doc_fetch(int argc, VALUE *argv, VALUE self) {
1332
+ Doc doc;
1333
+ Leaf leaf;
1334
+ volatile VALUE val = Qnil;
1335
+ const char *path = 0;
1368
1336
 
1369
1337
  doc = self_doc(self);
1370
1338
  if (1 <= argc) {
1371
- Check_Type(*argv, T_STRING);
1372
- path = StringValuePtr(*argv);
1373
- if (2 == argc) {
1374
- val = argv[1];
1375
- }
1339
+ path = StringValuePtr(*argv);
1340
+ if (2 == argc) {
1341
+ val = argv[1];
1342
+ }
1376
1343
  }
1377
1344
  if (0 != (leaf = get_doc_leaf(doc, path))) {
1378
- val = leaf_value(doc, leaf);
1345
+ val = leaf_value(doc, leaf);
1379
1346
  }
1380
1347
  return val;
1381
1348
  }
1382
1349
 
1383
- /* call-seq: each_leaf(path=nil) => nil
1350
+ /* @overload exists?(path) => true, false
1351
+ *
1352
+ * Returns true if the value at the location identified by the path exists.
1353
+ * @param [String] path path to the location
1354
+ * @example
1355
+ * Oj::Doc.open('[1,2]') { |doc| doc.exists?('/1') } #=> true
1356
+ * Oj::Doc.open('[1,2]') { |doc| doc.exists?('/3') } #=> false
1357
+ */
1358
+ static VALUE doc_exists(VALUE self, VALUE str) {
1359
+ Doc doc;
1360
+ Leaf leaf;
1361
+
1362
+ doc = self_doc(self);
1363
+ if (0 != (leaf = get_doc_leaf(doc, StringValuePtr(str)))) {
1364
+ if (NULL != leaf) {
1365
+ return Qtrue;
1366
+ }
1367
+ }
1368
+ return Qfalse;
1369
+ }
1370
+
1371
+ /* @overload each_leaf(path=nil) => nil
1384
1372
  *
1385
1373
  * Yields to the provided block for each leaf node with the identified
1386
1374
  * location of the JSON document as the root. The parameter passed to the
1387
1375
  * block on yield is the Doc instance after moving to the child location.
1388
- * @param [String] path if provided it identified the top of the branch to process the leaves of
1376
+ * @param [String] path if provided it identified the top of the branch to
1377
+ * process the leaves of
1389
1378
  * @yieldparam [Doc] Doc at the child location
1390
1379
  * @example
1391
1380
  * Oj::Doc.open('[3,[2,1]]') { |doc|
@@ -1395,73 +1384,71 @@ doc_fetch(int argc, VALUE *argv, VALUE self) {
1395
1384
  * }
1396
1385
  * #=> ["/1" => 3, "/2/1" => 2, "/2/2" => 1]
1397
1386
  */
1398
- static VALUE
1399
- doc_each_leaf(int argc, VALUE *argv, VALUE self) {
1387
+ static VALUE doc_each_leaf(int argc, VALUE *argv, VALUE self) {
1400
1388
  if (rb_block_given_p()) {
1401
- Leaf save_path[MAX_STACK];
1402
- Doc doc = self_doc(self);
1403
- const char *path = 0;
1404
- size_t wlen;
1405
-
1406
- wlen = doc->where - doc->where_path;
1407
- if (0 < wlen) {
1408
- memcpy(save_path, doc->where_path, sizeof(Leaf) * (wlen + 1));
1409
- }
1410
- if (1 <= argc) {
1411
- Check_Type(*argv, T_STRING);
1412
- path = StringValuePtr(*argv);
1413
- if ('/' == *path) {
1414
- doc->where = doc->where_path;
1415
- path++;
1416
- }
1417
- if (0 != move_step(doc, path, 1)) {
1418
- if (0 < wlen) {
1419
- memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
1420
- }
1421
- return Qnil;
1422
- }
1423
- }
1424
- each_leaf(doc, self);
1425
- if (0 < wlen) {
1426
- memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
1427
- }
1389
+ Leaf save_path[MAX_STACK];
1390
+ Doc doc = self_doc(self);
1391
+ const char *path = 0;
1392
+ size_t wlen;
1393
+
1394
+ wlen = doc->where - doc->where_path;
1395
+ if (0 < wlen) {
1396
+ memcpy(save_path, doc->where_path, sizeof(Leaf) * (wlen + 1));
1397
+ }
1398
+ if (1 <= argc) {
1399
+ path = StringValuePtr(*argv);
1400
+ if ('/' == *path) {
1401
+ doc->where = doc->where_path;
1402
+ path++;
1403
+ }
1404
+ if (0 != move_step(doc, path, 1)) {
1405
+ if (0 < wlen) {
1406
+ memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
1407
+ }
1408
+ return Qnil;
1409
+ }
1410
+ }
1411
+ each_leaf(doc, self);
1412
+ if (0 < wlen) {
1413
+ memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
1414
+ }
1428
1415
  }
1429
1416
  return Qnil;
1430
1417
  }
1431
1418
 
1432
- /* call-seq: move(path) => nil
1419
+ /* @overload move(path) => nil
1433
1420
  *
1434
1421
  * Moves the document marker to the path specified. The path can an absolute
1435
1422
  * path or a relative path.
1436
- * @param [String] path path to the location to move to
1423
+ * @param [String] path path to the location to move to
1437
1424
  * @example
1438
- * Oj::Doc.open('{"one":[1,2]') { |doc| doc.move('/one/2'); doc.where? } #=> "/one/2"
1425
+ * Oj::Doc.open('{"one":[1,2]') { |doc| doc.move('/one/2'); doc.where? } #=>
1426
+ * "/one/2"
1439
1427
  */
1440
- static VALUE
1441
- doc_move(VALUE self, VALUE str) {
1442
- Doc doc = self_doc(self);
1443
- const char *path;
1444
- int loc;
1428
+ static VALUE doc_move(VALUE self, VALUE str) {
1429
+ Doc doc = self_doc(self);
1430
+ const char *path;
1431
+ int loc;
1445
1432
 
1446
- Check_Type(str, T_STRING);
1447
1433
  path = StringValuePtr(str);
1448
1434
  if ('/' == *path) {
1449
- doc->where = doc->where_path;
1450
- path++;
1435
+ doc->where = doc->where_path;
1436
+ path++;
1451
1437
  }
1452
1438
  if (0 != (loc = move_step(doc, path, 1))) {
1453
- rb_raise(rb_eArgError, "Failed to locate element %d of the path %s.", loc, path);
1439
+ rb_raise(rb_eArgError, "Failed to locate element %d of the path %s.", loc, path);
1454
1440
  }
1455
1441
  return Qnil;
1456
1442
  }
1457
1443
 
1458
- /* call-seq: each_child(path=nil) { |doc| ... } => nil
1444
+ /* @overload each_child(path=nil) { |doc| ... } => nil
1459
1445
  *
1460
1446
  * Yields to the provided block for each immediate child node with the
1461
1447
  * identified location of the JSON document as the root. The parameter passed
1462
1448
  * to the block on yield is the Doc instance after moving to the child
1463
1449
  * location.
1464
- * @param [String] path if provided it identified the top of the branch to process the chilren of
1450
+ * @param [String] path if provided it identified the top of the branch to
1451
+ * process the children of
1465
1452
  * @yieldparam [Doc] Doc at the child location
1466
1453
  * @example
1467
1454
  * Oj::Doc.open('[3,[2,1]]') { |doc|
@@ -1471,57 +1458,62 @@ doc_move(VALUE self, VALUE str) {
1471
1458
  * }
1472
1459
  * #=> ["/2/1", "/2/2"]
1473
1460
  */
1474
- static VALUE
1475
- doc_each_child(int argc, VALUE *argv, VALUE self) {
1461
+ static VALUE doc_each_child(int argc, VALUE *argv, VALUE self) {
1476
1462
  if (rb_block_given_p()) {
1477
- Leaf save_path[MAX_STACK];
1478
- Doc doc = self_doc(self);
1479
- const char *path = 0;
1480
- size_t wlen;
1481
-
1482
- wlen = doc->where - doc->where_path;
1483
- if (0 < wlen) {
1484
- memcpy(save_path, doc->where_path, sizeof(Leaf) * (wlen + 1));
1485
- }
1486
- if (1 <= argc) {
1487
- Check_Type(*argv, T_STRING);
1488
- path = StringValuePtr(*argv);
1489
- if ('/' == *path) {
1490
- doc->where = doc->where_path;
1491
- path++;
1492
- }
1493
- if (0 != move_step(doc, path, 1)) {
1494
- if (0 < wlen) {
1495
- memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
1496
- }
1497
- return Qnil;
1498
- }
1499
- }
1500
- if (COL_VAL == (*doc->where)->value_type && 0 != (*doc->where)->elements) {
1501
- Leaf first = (*doc->where)->elements->next;
1502
- Leaf e = first;
1503
-
1504
- doc->where++;
1505
- do {
1506
- *doc->where = e;
1507
- rb_yield(self);
1508
- e = e->next;
1509
- } while (e != first);
1510
- }
1511
- if (0 < wlen) {
1512
- memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
1513
- }
1463
+ Leaf save_path[MAX_STACK];
1464
+ Doc doc = self_doc(self);
1465
+ const char *path = 0;
1466
+ size_t wlen;
1467
+ Leaf *where_orig = doc->where;
1468
+
1469
+ wlen = doc->where - doc->where_path;
1470
+ if (0 < wlen) {
1471
+ memcpy(save_path, doc->where_path, sizeof(Leaf) * (wlen + 1));
1472
+ }
1473
+ if (1 <= argc) {
1474
+ path = StringValuePtr(*argv);
1475
+ if ('/' == *path) {
1476
+ doc->where = doc->where_path;
1477
+ path++;
1478
+ }
1479
+ if (0 != move_step(doc, path, 1)) {
1480
+ if (0 < wlen) {
1481
+ memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
1482
+ }
1483
+ doc->where = where_orig;
1484
+ return Qnil;
1485
+ }
1486
+ }
1487
+ if (NULL == doc->where || NULL == *doc->where) {
1488
+ return Qnil;
1489
+ }
1490
+ if (COL_VAL == (*doc->where)->value_type && 0 != (*doc->where)->elements) {
1491
+ Leaf first = (*doc->where)->elements->next;
1492
+ Leaf e = first;
1493
+
1494
+ doc->where++;
1495
+ do {
1496
+ *doc->where = e;
1497
+ rb_yield(self);
1498
+ e = e->next;
1499
+ } while (e != first);
1500
+ }
1501
+ if (0 < wlen) {
1502
+ memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
1503
+ }
1504
+ doc->where = where_orig;
1514
1505
  }
1515
1506
  return Qnil;
1516
1507
  }
1517
1508
 
1518
- /* call-seq: each_value(path=nil) { |val| ... } => nil
1509
+ /* @overload each_value(path=nil) { |val| ... } => nil
1519
1510
  *
1520
1511
  * Yields to the provided block for each leaf value in the identified location
1521
1512
  * of the JSON document. The parameter passed to the block on yield is the
1522
1513
  * value of the leaf. Only those leaves below the element specified by the
1523
1514
  * path parameter are processed.
1524
- * @param [String] path if provided it identified the top of the branch to process the leaf values of
1515
+ * @param [String] path if provided it identified the top of the branch to
1516
+ * process the leaf values of
1525
1517
  * @yieldparam [Object] val each leaf value
1526
1518
  * @example
1527
1519
  * Oj::Doc.open('[3,[2,1]]') { |doc|
@@ -1530,7 +1522,7 @@ doc_each_child(int argc, VALUE *argv, VALUE self) {
1530
1522
  * result
1531
1523
  * }
1532
1524
  * #=> [3, 2, 1]
1533
- *
1525
+ *
1534
1526
  * Oj::Doc.open('[3,[2,1]]') { |doc|
1535
1527
  * result = []
1536
1528
  * doc.each_value('/2') { |v| result << v }
@@ -1538,79 +1530,73 @@ doc_each_child(int argc, VALUE *argv, VALUE self) {
1538
1530
  * }
1539
1531
  * #=> [2, 1]
1540
1532
  */
1541
- static VALUE
1542
- doc_each_value(int argc, VALUE *argv, VALUE self) {
1533
+ static VALUE doc_each_value(int argc, VALUE *argv, VALUE self) {
1543
1534
  if (rb_block_given_p()) {
1544
- Doc doc = self_doc(self);
1545
- const char *path = 0;
1546
- Leaf leaf;
1547
-
1548
- if (1 <= argc) {
1549
- Check_Type(*argv, T_STRING);
1550
- path = StringValuePtr(*argv);
1551
- }
1552
- if (0 != (leaf = get_doc_leaf(doc, path))) {
1553
- each_value(doc, leaf);
1554
- }
1535
+ Doc doc = self_doc(self);
1536
+ const char *path = 0;
1537
+ Leaf leaf;
1538
+
1539
+ if (1 <= argc) {
1540
+ path = StringValuePtr(*argv);
1541
+ }
1542
+ if (0 != (leaf = get_doc_leaf(doc, path))) {
1543
+ each_value(doc, leaf);
1544
+ }
1555
1545
  }
1556
1546
  return Qnil;
1557
1547
  }
1558
1548
 
1559
- /* call-seq: dump(path=nil) => String
1549
+ /* @overload dump(path, filename)
1560
1550
  *
1561
1551
  * Dumps the document or nodes to a new JSON document. It uses the default
1562
1552
  * options for generating the JSON.
1563
- * @param [String] path if provided it identified the top of the branch to dump to JSON
1564
- * @param [String] filename if provided it is the filename to write the output to
1553
+ * @param path [String] if provided it identified the top of the branch to
1554
+ * dump to JSON
1555
+ * @param filename [String] if provided it is the filename to write the output
1556
+ * to
1565
1557
  * @example
1566
1558
  * Oj::Doc.open('[3,[2,1]]') { |doc|
1567
1559
  * doc.dump('/2')
1568
1560
  * }
1569
1561
  * #=> "[2,1]"
1570
1562
  */
1571
- static VALUE
1572
- doc_dump(int argc, VALUE *argv, VALUE self) {
1573
- Doc doc = self_doc(self);
1574
- Leaf leaf;
1575
- const char *path = 0;
1576
- const char *filename = 0;
1563
+ static VALUE doc_dump(int argc, VALUE *argv, VALUE self) {
1564
+ Doc doc = self_doc(self);
1565
+ Leaf leaf;
1566
+ const char *path = 0;
1567
+ const char *filename = 0;
1577
1568
 
1578
1569
  if (1 <= argc) {
1579
- if (Qnil != *argv) {
1580
- Check_Type(*argv, T_STRING);
1581
- path = StringValuePtr(*argv);
1582
- }
1583
- if (2 <= argc) {
1584
- Check_Type(argv[1], T_STRING);
1585
- filename = StringValuePtr(argv[1]);
1586
- }
1570
+ if (Qnil != *argv) {
1571
+ path = StringValuePtr(*argv);
1572
+ }
1573
+ if (2 <= argc) {
1574
+ filename = StringValuePtr(argv[1]);
1575
+ }
1587
1576
  }
1588
1577
  if (0 != (leaf = get_doc_leaf(doc, path))) {
1589
- VALUE rjson;
1590
-
1591
- if (0 == filename) {
1592
- char buf[4096];
1593
- struct _Out out;
1594
-
1595
- out.buf = buf;
1596
- out.end = buf + sizeof(buf) - 10;
1597
- out.allocated = 0;
1598
- out.omit_nil = oj_default_options.dump_opts.omit_nil;
1599
- oj_dump_leaf_to_json(leaf, &oj_default_options, &out);
1600
- rjson = rb_str_new2(out.buf);
1601
- if (out.allocated) {
1602
- xfree(out.buf);
1603
- }
1604
- } else {
1605
- oj_write_leaf_to_file(leaf, filename, &oj_default_options);
1606
- rjson = Qnil;
1607
- }
1608
- return rjson;
1578
+ volatile VALUE rjson;
1579
+
1580
+ if (0 == filename) {
1581
+ struct _out out;
1582
+
1583
+ oj_out_init(&out);
1584
+
1585
+ out.omit_nil = oj_default_options.dump_opts.omit_nil;
1586
+ oj_dump_leaf_to_json(leaf, &oj_default_options, &out);
1587
+ rjson = rb_str_new2(out.buf);
1588
+
1589
+ oj_out_free(&out);
1590
+ } else {
1591
+ oj_write_leaf_to_file(leaf, filename, &oj_default_options);
1592
+ rjson = Qnil;
1593
+ }
1594
+ return rjson;
1609
1595
  }
1610
1596
  return Qnil;
1611
1597
  }
1612
1598
 
1613
- /* call-seq: size() => Fixnum
1599
+ /* @overload size() => Fixnum
1614
1600
  *
1615
1601
  * Returns the number of nodes in the JSON document where a node is any one of
1616
1602
  * the basic JSON components.
@@ -1618,12 +1604,13 @@ doc_dump(int argc, VALUE *argv, VALUE self) {
1618
1604
  * @example
1619
1605
  * Oj::Doc.open('[1,2,3]') { |doc| doc.size() } #=> 4
1620
1606
  */
1621
- static VALUE
1622
- doc_size(VALUE self) {
1623
- return ULONG2NUM(((Doc)DATA_PTR(self))->size);
1607
+ static VALUE doc_size(VALUE self) {
1608
+ Doc d;
1609
+ TypedData_Get_Struct(self, struct _doc, &oj_doc_type, d);
1610
+ return ULONG2NUM(d->size);
1624
1611
  }
1625
1612
 
1626
- /* call-seq: close() => nil
1613
+ /* @overload close() => nil
1627
1614
  *
1628
1615
  * Closes an open document. No further calls to the document will be valid
1629
1616
  * after closing.
@@ -1632,16 +1619,13 @@ doc_size(VALUE self) {
1632
1619
  * doc.size() #=> 4
1633
1620
  * doc.close()
1634
1621
  */
1635
- static VALUE
1636
- doc_close(VALUE self) {
1637
- Doc doc = self_doc(self);
1622
+ static VALUE doc_close(VALUE self) {
1623
+ Doc doc = self_doc(self);
1638
1624
 
1639
1625
  rb_gc_unregister_address(&doc->self);
1640
- DATA_PTR(doc->self) = 0;
1626
+ DATA_PTR(doc->self) = NULL;
1641
1627
  if (0 != doc) {
1642
- xfree(doc->json);
1643
- doc_free(doc);
1644
- xfree(doc);
1628
+ doc_free(doc);
1645
1629
  }
1646
1630
  return Qnil;
1647
1631
  }
@@ -1650,8 +1634,7 @@ doc_close(VALUE self) {
1650
1634
  Oj = rb_define_module("Oj");
1651
1635
  #endif
1652
1636
 
1653
- static VALUE
1654
- doc_not_implemented(VALUE self) {
1637
+ static VALUE doc_not_implemented(VALUE self) {
1655
1638
  rb_raise(rb_eNotImpError, "Not implemented.");
1656
1639
  return Qnil;
1657
1640
  }
@@ -1663,21 +1646,21 @@ doc_not_implemented(VALUE self) {
1663
1646
  * extracted. Once the document is closed the document can not longer be
1664
1647
  * accessed. This allows the parsing and data extraction to be extremely fast
1665
1648
  * compared to other JSON parses.
1666
- *
1649
+ *
1667
1650
  * An Oj::Doc class is not created directly but the _open()_ class method is
1668
1651
  * used to open a document and the yield parameter to the block of the #open()
1669
1652
  * call is the Doc instance. The Doc instance can be moved across, up, and
1670
1653
  * down the JSON document. At each element the data associated with the
1671
1654
  * element can be extracted. It is also possible to just provide a path to the
1672
1655
  * data to be extracted and retrieve the data in that manner.
1673
- *
1656
+ *
1674
1657
  * For many of the methods a path is used to describe the location of an
1675
1658
  * element. Paths follow a subset of the XPath syntax. The slash ('/')
1676
1659
  * character is the separator. Each step in the path identifies the next
1677
1660
  * branch to take through the document. A JSON object will expect a key string
1678
1661
  * while an array will expect a positive index. A .. step indicates a move up
1679
1662
  * the JSON document.
1680
- *
1663
+ *
1681
1664
  * @example
1682
1665
  * json = %{[
1683
1666
  * {
@@ -1691,28 +1674,29 @@ doc_not_implemented(VALUE self) {
1691
1674
  * ]}
1692
1675
  * # move and get value
1693
1676
  * Oj::Doc.open(json) do |doc|
1694
- * doc.move('/1/two')
1695
- * # doc location is now at the 'two' element of the hash that is the first element of the array.
1696
- * doc.fetch()
1697
- * end
1677
+ * doc.move('/1/two')
1678
+ * # doc location is now at the 'two' element of the hash that is the first
1679
+ * element of the array. doc.fetch() end
1698
1680
  * #=> 2
1699
- *
1700
- * # Now try again using a path to Oj::Doc.fetch() directly and not using a block.
1701
- * doc = Oj::Doc.open(json)
1702
- * doc.fetch('/2/three') #=> 3
1703
- * doc.close()
1681
+ *
1682
+ * # Now try again using a path to Oj::Doc.fetch() directly and not using a
1683
+ * block. doc = Oj::Doc.open(json) doc.fetch('/2/three') #=> 3 doc.close()
1704
1684
  */
1705
- void
1706
- oj_init_doc() {
1685
+ void oj_init_doc(void) {
1707
1686
  oj_doc_class = rb_define_class_under(Oj, "Doc", rb_cObject);
1687
+ rb_gc_register_address(&oj_doc_class);
1688
+ rb_undef_alloc_func(oj_doc_class);
1708
1689
  rb_define_singleton_method(oj_doc_class, "open", doc_open, 1);
1709
1690
  rb_define_singleton_method(oj_doc_class, "open_file", doc_open_file, 1);
1710
1691
  rb_define_singleton_method(oj_doc_class, "parse", doc_open, 1);
1711
- rb_define_method(oj_doc_class, "where?", doc_where, 0);
1692
+ rb_define_method(oj_doc_class, "where?", doc_where_q, 0);
1693
+ rb_define_method(oj_doc_class, "where", doc_where, 0);
1694
+ rb_define_method(oj_doc_class, "path", doc_path, 0);
1712
1695
  rb_define_method(oj_doc_class, "local_key", doc_local_key, 0);
1713
1696
  rb_define_method(oj_doc_class, "home", doc_home, 0);
1714
1697
  rb_define_method(oj_doc_class, "type", doc_type, -1);
1715
1698
  rb_define_method(oj_doc_class, "fetch", doc_fetch, -1);
1699
+ rb_define_method(oj_doc_class, "exists?", doc_exists, 1);
1716
1700
  rb_define_method(oj_doc_class, "each_leaf", doc_each_leaf, -1);
1717
1701
  rb_define_method(oj_doc_class, "move", doc_move, 1);
1718
1702
  rb_define_method(oj_doc_class, "each_child", doc_each_child, -1);