oj 3.10.6 → 3.12.0

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