json_pure 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. data/CHANGES +25 -0
  2. data/GPL +340 -0
  3. data/README +77 -0
  4. data/Rakefile +250 -0
  5. data/TODO +1 -0
  6. data/VERSION +1 -0
  7. data/benchmarks/benchmark.txt +133 -0
  8. data/benchmarks/benchmark_generator.rb +44 -0
  9. data/benchmarks/benchmark_parser.rb +22 -0
  10. data/benchmarks/benchmark_rails.rb +26 -0
  11. data/bin/edit_json.rb +11 -0
  12. data/data/example.json +1 -0
  13. data/data/index.html +37 -0
  14. data/data/prototype.js +2515 -0
  15. data/ext/json/ext/generator/Makefile +149 -0
  16. data/ext/json/ext/generator/extconf.rb +9 -0
  17. data/ext/json/ext/generator/generator.c +729 -0
  18. data/ext/json/ext/generator/unicode.c +184 -0
  19. data/ext/json/ext/generator/unicode.h +40 -0
  20. data/ext/json/ext/parser/Makefile +149 -0
  21. data/ext/json/ext/parser/extconf.rb +9 -0
  22. data/ext/json/ext/parser/parser.c +1551 -0
  23. data/ext/json/ext/parser/parser.rl +515 -0
  24. data/ext/json/ext/parser/unicode.c +156 -0
  25. data/ext/json/ext/parser/unicode.h +44 -0
  26. data/install.rb +26 -0
  27. data/lib/json.rb +205 -0
  28. data/lib/json/Array.xpm +21 -0
  29. data/lib/json/FalseClass.xpm +21 -0
  30. data/lib/json/Hash.xpm +21 -0
  31. data/lib/json/Key.xpm +73 -0
  32. data/lib/json/NilClass.xpm +21 -0
  33. data/lib/json/Numeric.xpm +28 -0
  34. data/lib/json/String.xpm +96 -0
  35. data/lib/json/TrueClass.xpm +21 -0
  36. data/lib/json/common.rb +184 -0
  37. data/lib/json/editor.rb +1207 -0
  38. data/lib/json/ext.rb +13 -0
  39. data/lib/json/json.xpm +1499 -0
  40. data/lib/json/pure.rb +75 -0
  41. data/lib/json/pure/generator.rb +321 -0
  42. data/lib/json/pure/parser.rb +210 -0
  43. data/lib/json/version.rb +8 -0
  44. data/tests/fixtures/fail1.json +1 -0
  45. data/tests/fixtures/fail10.json +1 -0
  46. data/tests/fixtures/fail11.json +1 -0
  47. data/tests/fixtures/fail12.json +1 -0
  48. data/tests/fixtures/fail13.json +1 -0
  49. data/tests/fixtures/fail14.json +1 -0
  50. data/tests/fixtures/fail15.json +1 -0
  51. data/tests/fixtures/fail16.json +1 -0
  52. data/tests/fixtures/fail17.json +1 -0
  53. data/tests/fixtures/fail19.json +1 -0
  54. data/tests/fixtures/fail2.json +1 -0
  55. data/tests/fixtures/fail20.json +1 -0
  56. data/tests/fixtures/fail21.json +1 -0
  57. data/tests/fixtures/fail22.json +1 -0
  58. data/tests/fixtures/fail23.json +1 -0
  59. data/tests/fixtures/fail24.json +1 -0
  60. data/tests/fixtures/fail25.json +1 -0
  61. data/tests/fixtures/fail26.json +1 -0
  62. data/tests/fixtures/fail27.json +2 -0
  63. data/tests/fixtures/fail28.json +2 -0
  64. data/tests/fixtures/fail3.json +1 -0
  65. data/tests/fixtures/fail4.json +1 -0
  66. data/tests/fixtures/fail5.json +1 -0
  67. data/tests/fixtures/fail6.json +1 -0
  68. data/tests/fixtures/fail7.json +1 -0
  69. data/tests/fixtures/fail8.json +1 -0
  70. data/tests/fixtures/fail9.json +1 -0
  71. data/tests/fixtures/pass1.json +56 -0
  72. data/tests/fixtures/pass18.json +1 -0
  73. data/tests/fixtures/pass2.json +1 -0
  74. data/tests/fixtures/pass3.json +6 -0
  75. data/tests/runner.rb +24 -0
  76. data/tests/test_json.rb +235 -0
  77. data/tests/test_json_addition.rb +94 -0
  78. data/tests/test_json_fixtures.rb +30 -0
  79. data/tests/test_json_generate.rb +81 -0
  80. data/tests/test_json_unicode.rb +55 -0
  81. data/tools/fuzz.rb +133 -0
  82. data/tools/server.rb +62 -0
  83. metadata +146 -0
@@ -0,0 +1,149 @@
1
+
2
+ SHELL = /bin/sh
3
+
4
+ #### Start of system configuration section. ####
5
+
6
+ srcdir = .
7
+ topdir = /usr/lib/ruby/1.8/i686-linux
8
+ hdrdir = $(topdir)
9
+ VPATH = $(srcdir):$(topdir):$(hdrdir)
10
+ prefix = $(DESTDIR)/usr
11
+ exec_prefix = $(prefix)
12
+ sitedir = $(DESTDIR)/usr/lib/ruby/site_ruby
13
+ rubylibdir = $(libdir)/ruby/$(ruby_version)
14
+ docdir = $(datarootdir)/doc/$(PACKAGE)
15
+ dvidir = $(docdir)
16
+ datarootdir = $(prefix)/share
17
+ archdir = $(rubylibdir)/$(arch)
18
+ sbindir = $(exec_prefix)/sbin
19
+ psdir = $(docdir)
20
+ localedir = $(datarootdir)/locale
21
+ htmldir = $(docdir)
22
+ datadir = $(DESTDIR)/usr/share
23
+ includedir = $(prefix)/include
24
+ infodir = $(DESTDIR)/usr/share/info
25
+ sysconfdir = $(DESTDIR)/etc
26
+ mandir = $(DESTDIR)/usr/share/man
27
+ libdir = $(exec_prefix)/lib
28
+ sharedstatedir = $(prefix)/com
29
+ oldincludedir = $(DESTDIR)/usr/include
30
+ pdfdir = $(docdir)
31
+ sitearchdir = $(sitelibdir)/$(sitearch)
32
+ bindir = $(exec_prefix)/bin
33
+ localstatedir = $(DESTDIR)/var/lib
34
+ sitelibdir = $(sitedir)/$(ruby_version)
35
+ libexecdir = $(exec_prefix)/libexec
36
+
37
+ CC = i686-pc-linux-gnu-gcc -Wall -ggdb
38
+ LIBRUBY = $(LIBRUBY_SO)
39
+ LIBRUBY_A = lib$(RUBY_SO_NAME)-static.a
40
+ LIBRUBYARG_SHARED = -Wl,-R -Wl,$(libdir) -L$(libdir) -L. -l$(RUBY_SO_NAME)
41
+ LIBRUBYARG_STATIC = -l$(RUBY_SO_NAME)-static
42
+
43
+ RUBY_EXTCONF_H =
44
+ CFLAGS = -fPIC -march=pentium4 -O2 -pipe -ggdb -fPIC
45
+ INCFLAGS = -I. -I$(topdir) -I$(hdrdir) -I$(srcdir)
46
+ CPPFLAGS =
47
+ CXXFLAGS = $(CFLAGS)
48
+ DLDFLAGS =
49
+ LDSHARED = $(CC) -shared
50
+ AR = i686-pc-linux-gnu-ar
51
+ EXEEXT =
52
+
53
+ RUBY_INSTALL_NAME = ruby18
54
+ RUBY_SO_NAME = ruby18
55
+ arch = i686-linux
56
+ sitearch = i686-linux
57
+ ruby_version = 1.8
58
+ ruby = /usr/bin/ruby18
59
+ RUBY = $(ruby)
60
+ RM = rm -f
61
+ MAKEDIRS = mkdir -p
62
+ INSTALL = /bin/install -c
63
+ INSTALL_PROG = $(INSTALL) -m 0755
64
+ INSTALL_DATA = $(INSTALL) -m 644
65
+ COPY = cp
66
+
67
+ #### End of system configuration section. ####
68
+
69
+ preload =
70
+
71
+ libpath = $(libdir)
72
+ LIBPATH = -L'$(libdir)' -Wl,-R'$(libdir)'
73
+ DEFFILE =
74
+
75
+ CLEANFILES =
76
+ DISTCLEANFILES =
77
+
78
+ extout =
79
+ extout_prefix =
80
+ target_prefix =
81
+ LOCAL_LIBS =
82
+ LIBS = $(LIBRUBYARG_SHARED) -ldl -lcrypt -lm -lc
83
+ SRCS = unicode.c generator.c
84
+ OBJS = unicode.o generator.o
85
+ TARGET = generator
86
+ DLLIB = $(TARGET).so
87
+ EXTSTATIC =
88
+ STATIC_LIB =
89
+
90
+ RUBYCOMMONDIR = $(sitedir)$(target_prefix)
91
+ RUBYLIBDIR = $(sitelibdir)$(target_prefix)
92
+ RUBYARCHDIR = $(sitearchdir)$(target_prefix)
93
+
94
+ TARGET_SO = $(DLLIB)
95
+ CLEANLIBS = $(TARGET).so $(TARGET).il? $(TARGET).tds $(TARGET).map
96
+ CLEANOBJS = *.o *.a *.s[ol] *.pdb *.exp *.bak
97
+
98
+ all: $(DLLIB)
99
+ static: $(STATIC_LIB)
100
+
101
+ clean:
102
+ @-$(RM) $(CLEANLIBS) $(CLEANOBJS) $(CLEANFILES)
103
+
104
+ distclean: clean
105
+ @-$(RM) Makefile $(RUBY_EXTCONF_H) conftest.* mkmf.log
106
+ @-$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES)
107
+
108
+ realclean: distclean
109
+ install: install-so install-rb
110
+
111
+ install-so: $(RUBYARCHDIR)
112
+ install-so: $(RUBYARCHDIR)/$(DLLIB)
113
+ $(RUBYARCHDIR)/$(DLLIB): $(DLLIB)
114
+ $(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR)
115
+ install-rb: pre-install-rb install-rb-default
116
+ install-rb-default: pre-install-rb-default
117
+ pre-install-rb: Makefile
118
+ pre-install-rb-default: Makefile
119
+ $(RUBYARCHDIR):
120
+ $(MAKEDIRS) $@
121
+
122
+ site-install: site-install-so site-install-rb
123
+ site-install-so: install-so
124
+ site-install-rb: install-rb
125
+
126
+ .SUFFIXES: .c .m .cc .cxx .cpp .C .o
127
+
128
+ .cc.o:
129
+ $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $<
130
+
131
+ .cxx.o:
132
+ $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $<
133
+
134
+ .cpp.o:
135
+ $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $<
136
+
137
+ .C.o:
138
+ $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $<
139
+
140
+ .c.o:
141
+ $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) -c $<
142
+
143
+ $(DLLIB): $(OBJS)
144
+ @-$(RM) $@
145
+ $(LDSHARED) $(DLDFLAGS) $(LIBPATH) -o $@ $(OBJS) $(LOCAL_LIBS) $(LIBS)
146
+
147
+
148
+
149
+ $(OBJS): ruby.h defines.h
@@ -0,0 +1,9 @@
1
+ require 'mkmf'
2
+ require 'rbconfig'
3
+
4
+ if CONFIG['CC'] =~ /gcc/
5
+ CONFIG['CC'] += ' -Wall -ggdb'
6
+ #CONFIG['CC'] += ' -Wall'
7
+ end
8
+
9
+ create_makefile 'generator'
@@ -0,0 +1,729 @@
1
+ /* vim: set cin et sw=4 ts=4: */
2
+
3
+ #include <string.h>
4
+ #include "ruby.h"
5
+ #include "st.h"
6
+ #include "unicode.h"
7
+
8
+ static VALUE mJSON, mExt, mGenerator, cState, mGeneratorMethods, mObject,
9
+ mHash, mArray, mInteger, mFloat, mString, mString_Extend,
10
+ mTrueClass, mFalseClass, mNilClass, eGeneratorError,
11
+ eCircularDatastructure;
12
+
13
+ static ID i_to_s, i_to_json, i_new, i_indent, i_space, i_space_before,
14
+ i_object_nl, i_array_nl, i_check_circular, i_pack, i_unpack,
15
+ i_create_id, i_extend;
16
+
17
+ typedef struct JSON_Generator_StateStruct {
18
+ VALUE indent;
19
+ VALUE space;
20
+ VALUE space_before;
21
+ VALUE object_nl;
22
+ VALUE array_nl;
23
+ int check_circular;
24
+ VALUE seen;
25
+ VALUE memo;
26
+ VALUE depth;
27
+ int flag;
28
+ } JSON_Generator_State;
29
+
30
+ #define GET_STATE(self) \
31
+ JSON_Generator_State *state; \
32
+ Data_Get_Struct(self, JSON_Generator_State, state);
33
+ #define FUL(string) RSTRING(string)->len
34
+
35
+ /*
36
+ * Document-module: JSON::Ext::Generator
37
+ *
38
+ * This is the JSON generator implemented as a C extension. It can be
39
+ * configured to be used by setting
40
+ *
41
+ * JSON.generator = JSON::Ext::Generator
42
+ *
43
+ * with the method generator= in JSON.
44
+ *
45
+ */
46
+
47
+ static int hash_to_json_state_i(VALUE key, VALUE value, VALUE Vstate)
48
+ {
49
+ VALUE json, buf, Vdepth;
50
+ GET_STATE(Vstate);
51
+ buf = state->memo;
52
+ Vdepth = state->depth;
53
+
54
+ if (key == Qundef) return ST_CONTINUE;
55
+ if (state->flag) {
56
+ state->flag = 0;
57
+ rb_str_buf_cat2(buf, ",");
58
+ if (FUL(state->object_nl)) rb_str_buf_append(buf, state->object_nl);
59
+ }
60
+ if (FUL(state->object_nl)) {
61
+ rb_str_buf_append(buf, rb_str_times(state->indent, Vdepth));
62
+ }
63
+ json = rb_funcall(rb_funcall(key, i_to_s, 0), i_to_json, 2, Vstate, Vdepth);
64
+ rb_str_buf_append(buf, json);
65
+ OBJ_INFECT(buf, json);
66
+ if (FUL(state->space_before)) {
67
+ rb_str_buf_append(buf, state->space_before);
68
+ }
69
+ rb_str_buf_cat2(buf, ":");
70
+ if (FUL(state->space)) rb_str_buf_append(buf, state->space);
71
+ json = rb_funcall(value, i_to_json, 2, Vstate, Vdepth);
72
+ state->flag = 1;
73
+ rb_str_buf_append(buf, json);
74
+ OBJ_INFECT(buf, json);
75
+ state->depth = Vdepth;
76
+ state->memo = buf;
77
+ return ST_CONTINUE;
78
+ }
79
+
80
+ inline static VALUE mHash_json_transfrom(VALUE self, VALUE Vstate, VALUE Vdepth) {
81
+ long depth, len = RHASH(self)->tbl->num_entries;
82
+ VALUE result;
83
+ GET_STATE(Vstate);
84
+
85
+ depth = 1 + FIX2LONG(Vdepth);
86
+ result = rb_str_buf_new(len);
87
+ state->memo = result;
88
+ state->depth = LONG2FIX(depth);
89
+ state->flag = 0;
90
+ rb_str_buf_cat2(result, "{");
91
+ if (FUL(state->object_nl)) rb_str_buf_append(result, state->object_nl);
92
+ rb_hash_foreach(self, hash_to_json_state_i, Vstate);
93
+ if (FUL(state->object_nl)) rb_str_buf_append(result, state->object_nl);
94
+ if (FUL(state->object_nl)) {
95
+ rb_str_buf_append(result, rb_str_times(state->indent, Vdepth));
96
+ }
97
+ rb_str_buf_cat2(result, "}");
98
+ return result;
99
+ }
100
+
101
+ static int hash_to_json_i(VALUE key, VALUE value, VALUE buf)
102
+ {
103
+ VALUE tmp;
104
+
105
+ if (key == Qundef) return ST_CONTINUE;
106
+ if (RSTRING(buf)->len > 1) rb_str_buf_cat2(buf, ",");
107
+ tmp = rb_funcall(rb_funcall(key, i_to_s, 0), i_to_json, 0);
108
+ rb_str_buf_append(buf, tmp);
109
+ OBJ_INFECT(buf, tmp);
110
+ rb_str_buf_cat2(buf, ":");
111
+ tmp = rb_funcall(value, i_to_json, 0);
112
+ rb_str_buf_append(buf, tmp);
113
+ OBJ_INFECT(buf, tmp);
114
+
115
+ return ST_CONTINUE;
116
+ }
117
+
118
+ /*
119
+ * call-seq: to_json(state = nil, depth = 0)
120
+ *
121
+ * Returns a JSON string containing a JSON object, that is unparsed from
122
+ * this Hash instance.
123
+ * _state_ is a JSON::State object, that can also be used to configure the
124
+ * produced JSON string output further.
125
+ * _depth_ is used to find out nesting depth, to indent accordingly.
126
+ */
127
+ static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self)
128
+ {
129
+ VALUE Vstate, Vdepth, result;
130
+ long depth;
131
+
132
+ rb_scan_args(argc, argv, "02", &Vstate, &Vdepth);
133
+ depth = NIL_P(Vdepth) ? 0 : FIX2LONG(Vdepth);
134
+ if (NIL_P(Vstate)) {
135
+ long len = RHASH(self)->tbl->num_entries;
136
+ result = rb_str_buf_new(len);
137
+ rb_str_buf_cat2(result, "{");
138
+ rb_hash_foreach(self, hash_to_json_i, result);
139
+ rb_str_buf_cat2(result, "}");
140
+ } else {
141
+ GET_STATE(Vstate);
142
+ if (state->check_circular) {
143
+ VALUE self_id = rb_obj_id(self);
144
+ if (RTEST(rb_hash_aref(state->seen, self_id))) {
145
+ rb_raise(eCircularDatastructure,
146
+ "circular data structures not supported!");
147
+ }
148
+ rb_hash_aset(state->seen, self_id, Qtrue);
149
+ result = mHash_json_transfrom(self, Vstate, LONG2FIX(depth));
150
+ rb_hash_delete(state->seen, self_id);
151
+ } else {
152
+ result = mHash_json_transfrom(self, Vstate, LONG2FIX(depth));
153
+ }
154
+ }
155
+ OBJ_INFECT(result, self);
156
+ return result;
157
+ }
158
+
159
+ inline static VALUE mArray_json_transfrom(VALUE self, VALUE Vstate, VALUE Vdepth) {
160
+ long i, len = RARRAY(self)->len;
161
+ VALUE shift, result;
162
+ long depth = NIL_P(Vdepth) ? 0 : FIX2LONG(Vdepth);
163
+ VALUE delim = rb_str_new2(",");
164
+ GET_STATE(Vstate);
165
+
166
+ if (state->check_circular) {
167
+ VALUE self_id = rb_obj_id(self);
168
+ rb_hash_aset(state->seen, self_id, Qtrue);
169
+ result = rb_str_buf_new(len);
170
+ if (FUL(state->array_nl)) rb_str_append(delim, state->array_nl);
171
+ shift = rb_str_times(state->indent, LONG2FIX(depth + 1));
172
+
173
+ rb_str_buf_cat2(result, "[");
174
+ rb_str_buf_append(result, state->array_nl);
175
+ for (i = 0; i < len; i++) {
176
+ VALUE element = RARRAY(self)->ptr[i];
177
+ if (RTEST(rb_hash_aref(state->seen, rb_obj_id(element)))) {
178
+ rb_raise(eCircularDatastructure,
179
+ "circular data structures not supported!");
180
+ }
181
+ OBJ_INFECT(result, element);
182
+ if (i > 0) rb_str_buf_append(result, delim);
183
+ rb_str_buf_append(result, shift);
184
+ rb_str_buf_append(result, rb_funcall(element, i_to_json, 2, Vstate, LONG2FIX(depth + 1)));
185
+ }
186
+ if (FUL(state->array_nl)) {
187
+ rb_str_buf_append(result, state->array_nl);
188
+ rb_str_buf_append(result, rb_str_times(state->indent, LONG2FIX(depth)));
189
+ }
190
+ rb_str_buf_cat2(result, "]");
191
+ rb_hash_delete(state->seen, self_id);
192
+ } else {
193
+ result = rb_str_buf_new(len);
194
+ if (FUL(state->array_nl)) rb_str_append(delim, state->array_nl);
195
+ shift = rb_str_times(state->indent, LONG2FIX(depth + 1));
196
+
197
+ rb_str_buf_cat2(result, "[");
198
+ rb_str_buf_append(result, state->array_nl);
199
+ for (i = 0; i < len; i++) {
200
+ VALUE element = RARRAY(self)->ptr[i];
201
+ OBJ_INFECT(result, element);
202
+ if (i > 0) rb_str_buf_append(result, delim);
203
+ rb_str_buf_append(result, shift);
204
+ rb_str_buf_append(result, rb_funcall(element, i_to_json, 2, Vstate, LONG2FIX(depth + 1)));
205
+ }
206
+ rb_str_buf_append(result, state->array_nl);
207
+ if (FUL(state->array_nl)) {
208
+ rb_str_buf_append(result, rb_str_times(state->indent, LONG2FIX(depth)));
209
+ }
210
+ rb_str_buf_cat2(result, "]");
211
+ }
212
+ return result;
213
+ }
214
+
215
+ /*
216
+ * call-seq: to_json(state = nil, depth = 0)
217
+ *
218
+ * Returns a JSON string containing a JSON array, that is unparsed from
219
+ * this Array instance.
220
+ * _state_ is a JSON::State object, that can also be used to configure the
221
+ * produced JSON string output further.
222
+ * _depth_ is used to find out nesting depth, to indent accordingly.
223
+ */
224
+ static VALUE mArray_to_json(int argc, VALUE *argv, VALUE self) {
225
+ VALUE Vstate, Vdepth, result;
226
+
227
+ rb_scan_args(argc, argv, "02", &Vstate, &Vdepth);
228
+ if (NIL_P(Vstate)) {
229
+ long i, len = RARRAY(self)->len;
230
+ result = rb_str_buf_new(2 + 2 * len);
231
+ rb_str_buf_cat2(result, "[");
232
+ for (i = 0; i < len; i++) {
233
+ VALUE element = RARRAY(self)->ptr[i];
234
+ OBJ_INFECT(result, element);
235
+ if (i > 0) rb_str_buf_cat2(result, ",");
236
+ rb_str_buf_append(result, rb_funcall(element, i_to_json, 0));
237
+ }
238
+ rb_str_buf_cat2(result, "]");
239
+ } else {
240
+ result = mArray_json_transfrom(self, Vstate, Vdepth);
241
+ }
242
+ OBJ_INFECT(result, self);
243
+ return result;
244
+ }
245
+
246
+ /*
247
+ * call-seq: to_json(*)
248
+ *
249
+ * Returns a JSON string representation for this Integer number.
250
+ */
251
+ static VALUE mInteger_to_json(int argc, VALUE *argv, VALUE self)
252
+ {
253
+ return rb_funcall(self, i_to_s, 0);
254
+ }
255
+
256
+ /*
257
+ * call-seq: to_json(*)
258
+ *
259
+ * Returns a JSON string representation for this Float number.
260
+ */
261
+ static VALUE mFloat_to_json(int argc, VALUE *argv, VALUE self)
262
+ {
263
+ return rb_funcall(self, i_to_s, 0);
264
+ }
265
+
266
+ /*
267
+ * call-seq: String.included(modul)
268
+ *
269
+ * Extends _modul_ with the String::Extend module.
270
+ */
271
+ static VALUE mString_included_s(VALUE self, VALUE modul) {
272
+ return rb_funcall(modul, i_extend, 1, mString_Extend);
273
+ }
274
+
275
+ /*
276
+ * call-seq: to_json(*)
277
+ *
278
+ * This string should be encoded with UTF-8 A call to this method
279
+ * returns a JSON string encoded with UTF16 big endian characters as
280
+ * \u????.
281
+ */
282
+ static VALUE mString_to_json(int argc, VALUE *argv, VALUE self)
283
+ {
284
+ VALUE result = rb_str_buf_new(RSTRING(self)->len);
285
+ rb_str_buf_cat2(result, "\"");
286
+ JSON_convert_UTF8_to_JSON(result, self, strictConversion);
287
+ rb_str_buf_cat2(result, "\"");
288
+ return result;
289
+ }
290
+
291
+ /*
292
+ * call-seq: to_json_raw_object()
293
+ *
294
+ * This method creates a raw object hash, that can be nested into
295
+ * other data structures and will be unparsed as a raw string. This
296
+ * method should be used, if you want to convert raw strings to JSON
297
+ * instead of UTF-8 strings, e. g. binary data.
298
+ */
299
+ static VALUE mString_to_json_raw_object(VALUE self) {
300
+ VALUE ary;
301
+ VALUE result = rb_hash_new();
302
+ rb_hash_aset(result, rb_funcall(mJSON, i_create_id, 0), rb_class_name(rb_obj_class(self)));
303
+ ary = rb_funcall(self, i_unpack, 1, rb_str_new2("C*"));
304
+ rb_hash_aset(result, rb_str_new2("raw"), ary);
305
+ return result;
306
+ }
307
+
308
+ /*
309
+ * call-seq: to_json_raw(*args)
310
+ *
311
+ * This method creates a JSON text from the result of a call to
312
+ * to_json_raw_object of this String.
313
+ */
314
+ static VALUE mString_to_json_raw(int argc, VALUE *argv, VALUE self) {
315
+ VALUE obj = mString_to_json_raw_object(self);
316
+ Check_Type(obj, T_HASH);
317
+ return mHash_to_json(argc, argv, obj);
318
+ }
319
+
320
+ /*
321
+ * call-seq: json_create(o)
322
+ *
323
+ * Raw Strings are JSON Objects (the raw bytes are stored in an array for the
324
+ * key "raw"). The Ruby String can be created by this module method.
325
+ */
326
+ static VALUE mString_Extend_json_create(VALUE self, VALUE o) {
327
+ VALUE ary;
328
+ Check_Type(o, T_HASH);
329
+ ary = rb_hash_aref(o, rb_str_new2("raw"));
330
+ return rb_funcall(ary, i_pack, 1, rb_str_new2("C*"));
331
+ }
332
+
333
+ /*
334
+ * call-seq: to_json(state = nil, depth = 0)
335
+ *
336
+ * Returns a JSON string for true: 'true'.
337
+ */
338
+ static VALUE mTrueClass_to_json(int argc, VALUE *argv, VALUE self)
339
+ {
340
+ return rb_str_new2("true");
341
+ }
342
+
343
+ /*
344
+ * call-seq: to_json(state = nil, depth = 0)
345
+ *
346
+ * Returns a JSON string for false: 'false'.
347
+ */
348
+ static VALUE mFalseClass_to_json(int argc, VALUE *argv, VALUE self)
349
+ {
350
+ return rb_str_new2("false");
351
+ }
352
+
353
+ /*
354
+ * call-seq: to_json(state = nil, depth = 0)
355
+ *
356
+ */
357
+ static VALUE mNilClass_to_json(int argc, VALUE *argv, VALUE self)
358
+ {
359
+ return rb_str_new2("null");
360
+ }
361
+
362
+ /*
363
+ * call-seq: to_json(*)
364
+ *
365
+ * Converts this object to a string (calling #to_s), converts
366
+ * it to a JSON string, and returns the result. This is a fallback, if no
367
+ * special method #to_json was defined for some object.
368
+ */
369
+ static VALUE mObject_to_json(int argc, VALUE *argv, VALUE self)
370
+ {
371
+ VALUE string = rb_funcall(self, i_to_s, 0);
372
+ Check_Type(string, T_STRING);
373
+ return mString_to_json(argc, argv, string);
374
+ }
375
+
376
+ /*
377
+ * Document-class: JSON::Ext::Generator::State
378
+ *
379
+ * This class is used to create State instances, that are use to hold data
380
+ * while generating a JSON text from a a Ruby data structure.
381
+ */
382
+
383
+ static void State_mark(JSON_Generator_State *state)
384
+ {
385
+ rb_gc_mark_maybe(state->indent);
386
+ rb_gc_mark_maybe(state->space);
387
+ rb_gc_mark_maybe(state->space_before);
388
+ rb_gc_mark_maybe(state->object_nl);
389
+ rb_gc_mark_maybe(state->array_nl);
390
+ rb_gc_mark_maybe(state->seen);
391
+ rb_gc_mark_maybe(state->memo);
392
+ rb_gc_mark_maybe(state->depth);
393
+ }
394
+
395
+ static JSON_Generator_State *State_allocate()
396
+ {
397
+ JSON_Generator_State *state = ALLOC(JSON_Generator_State);
398
+ return state;
399
+ }
400
+
401
+ static VALUE cState_s_allocate(VALUE klass)
402
+ {
403
+ JSON_Generator_State *state = State_allocate();
404
+ return Data_Wrap_Struct(klass, State_mark, -1, state);
405
+ }
406
+
407
+ /*
408
+ * call-seq: new(opts = {})
409
+ *
410
+ * Instantiates a new State object, configured by _opts_.
411
+ *
412
+ * _opts_ can have the following keys:
413
+ *
414
+ * * *indent*: a string used to indent levels (default: ''),
415
+ * * *space*: a string that is put after, a : or , delimiter (default: ''),
416
+ * * *space_before*: a string that is put before a : pair delimiter (default: ''),
417
+ * * *object_nl*: a string that is put at the end of a JSON object (default: ''),
418
+ * * *array_nl*: a string that is put at the end of a JSON array (default: ''),
419
+ * * *check_circular*: true if checking for circular data structures
420
+ * should be done, false (the default) otherwise.
421
+ */
422
+ static VALUE cState_initialize(int argc, VALUE *argv, VALUE self)
423
+ {
424
+ VALUE opts;
425
+ GET_STATE(self);
426
+
427
+ rb_scan_args(argc, argv, "01", &opts);
428
+ if (NIL_P(opts)) {
429
+ state->indent = rb_str_new2("");
430
+ state->space = rb_str_new2("");
431
+ state->space_before = rb_str_new2("");
432
+ state->array_nl = rb_str_new2("");
433
+ state->object_nl = rb_str_new2("");
434
+ state->check_circular = 0;
435
+ } else {
436
+ VALUE tmp;
437
+ opts = rb_convert_type(opts, T_HASH, "Hash", "to_hash");
438
+ tmp = rb_hash_aref(opts, ID2SYM(i_indent));
439
+ if (RTEST(tmp)) {
440
+ Check_Type(tmp, T_STRING);
441
+ state->indent = tmp;
442
+ } else {
443
+ state->indent = rb_str_new2("");
444
+ }
445
+ tmp = rb_hash_aref(opts, ID2SYM(i_space));
446
+ if (RTEST(tmp)) {
447
+ Check_Type(tmp, T_STRING);
448
+ state->space = tmp;
449
+ } else {
450
+ state->space = rb_str_new2("");
451
+ }
452
+ tmp = rb_hash_aref(opts, ID2SYM(i_space_before));
453
+ if (RTEST(tmp)) {
454
+ Check_Type(tmp, T_STRING);
455
+ state->space_before = tmp;
456
+ } else {
457
+ state->space_before = rb_str_new2("");
458
+ }
459
+ tmp = rb_hash_aref(opts, ID2SYM(i_array_nl));
460
+ if (RTEST(tmp)) {
461
+ Check_Type(tmp, T_STRING);
462
+ state->array_nl = tmp;
463
+ } else {
464
+ state->array_nl = rb_str_new2("");
465
+ }
466
+ tmp = rb_hash_aref(opts, ID2SYM(i_object_nl));
467
+ if (RTEST(tmp)) {
468
+ Check_Type(tmp, T_STRING);
469
+ state->object_nl = tmp;
470
+ } else {
471
+ state->object_nl = rb_str_new2("");
472
+ }
473
+ tmp = rb_hash_aref(opts, ID2SYM(i_check_circular));
474
+ state->check_circular = RTEST(tmp);
475
+ }
476
+ state->seen = rb_hash_new();
477
+ state->memo = Qnil;
478
+ state->depth = INT2FIX(0);
479
+ return self;
480
+ }
481
+
482
+ /*
483
+ * call-seq: from_state(opts)
484
+ *
485
+ * Creates a State object from _opts_, which ought to be Hash to create a
486
+ * new State instance configured by _opts_, something else to create an
487
+ * unconfigured instance. If _opts_ is a State object, it is just returned.
488
+ */
489
+ static VALUE cState_from_state_s(VALUE self, VALUE opts)
490
+ {
491
+ if (rb_obj_is_kind_of(opts, self)) {
492
+ return opts;
493
+ } else if (rb_obj_is_kind_of(opts, rb_cHash)) {
494
+ return rb_funcall(self, i_new, 1, opts);
495
+ } else {
496
+ return rb_funcall(self, i_new, 0);
497
+ }
498
+ }
499
+
500
+ /*
501
+ * call-seq: indent()
502
+ *
503
+ * This string is used to indent levels in the JSON text.
504
+ */
505
+ static VALUE cState_indent(VALUE self)
506
+ {
507
+ GET_STATE(self);
508
+ return state->indent;
509
+ }
510
+
511
+ /*
512
+ * call-seq: indent=(indent)
513
+ *
514
+ * This string is used to indent levels in the JSON text.
515
+ */
516
+ static VALUE cState_indent_set(VALUE self, VALUE indent)
517
+ {
518
+ GET_STATE(self);
519
+ Check_Type(indent, T_STRING);
520
+ return state->indent = indent;
521
+ }
522
+
523
+ /*
524
+ * call-seq: space()
525
+ *
526
+ * This string is used to insert a space between the tokens in a JSON
527
+ * string.
528
+ */
529
+ static VALUE cState_space(VALUE self)
530
+ {
531
+ GET_STATE(self);
532
+ return state->space;
533
+ }
534
+
535
+ /*
536
+ * call-seq: space=(space)
537
+ *
538
+ * This string is used to insert a space between the tokens in a JSON
539
+ * string.
540
+ */
541
+ static VALUE cState_space_set(VALUE self, VALUE space)
542
+ {
543
+ GET_STATE(self);
544
+ Check_Type(space, T_STRING);
545
+ return state->space = space;
546
+ }
547
+
548
+ /*
549
+ * call-seq: space_before()
550
+ *
551
+ * This string is used to insert a space before the ':' in JSON objects.
552
+ */
553
+ static VALUE cState_space_before(VALUE self)
554
+ {
555
+ GET_STATE(self);
556
+ return state->space_before;
557
+ }
558
+
559
+ /*
560
+ * call-seq: space_before=(space_before)
561
+ *
562
+ * This string is used to insert a space before the ':' in JSON objects.
563
+ */
564
+ static VALUE cState_space_before_set(VALUE self, VALUE space_before)
565
+ {
566
+ GET_STATE(self);
567
+ Check_Type(space_before, T_STRING);
568
+ return state->space_before = space_before;
569
+ }
570
+
571
+ /*
572
+ * call-seq: object_nl()
573
+ *
574
+ * This string is put at the end of a line that holds a JSON object (or
575
+ * Hash).
576
+ */
577
+ static VALUE cState_object_nl(VALUE self)
578
+ {
579
+ GET_STATE(self);
580
+ return state->object_nl;
581
+ }
582
+
583
+ /*
584
+ * call-seq: object_nl=(object_nl)
585
+ *
586
+ * This string is put at the end of a line that holds a JSON object (or
587
+ * Hash).
588
+ */
589
+ static VALUE cState_object_nl_set(VALUE self, VALUE object_nl)
590
+ {
591
+ GET_STATE(self);
592
+ Check_Type(object_nl, T_STRING);
593
+ return state->object_nl = object_nl;
594
+ }
595
+
596
+ /*
597
+ * call-seq: array_nl()
598
+ *
599
+ * This string is put at the end of a line that holds a JSON array.
600
+ */
601
+ static VALUE cState_array_nl(VALUE self)
602
+ {
603
+ GET_STATE(self);
604
+ return state->array_nl;
605
+ }
606
+
607
+ /*
608
+ * call-seq: array_nl=(array_nl)
609
+ *
610
+ * This string is put at the end of a line that holds a JSON array.
611
+ */
612
+ static VALUE cState_array_nl_set(VALUE self, VALUE array_nl)
613
+ {
614
+ GET_STATE(self);
615
+ Check_Type(array_nl, T_STRING);
616
+ return state->array_nl = array_nl;
617
+ }
618
+
619
+ /*
620
+ * call-seq: check_circular?(object)
621
+ *
622
+ * Returns true, if circular data structures should be checked,
623
+ * otherwise returns false.
624
+ */
625
+ static VALUE cState_check_circular_p(VALUE self)
626
+ {
627
+ GET_STATE(self);
628
+ return state->check_circular ? Qtrue : Qfalse;
629
+ }
630
+
631
+ /*
632
+ * call-seq: seen?(object)
633
+ *
634
+ * Returns _true_, if _object_ was already seen during this generating run.
635
+ */
636
+ static VALUE cState_seen_p(VALUE self, VALUE object)
637
+ {
638
+ GET_STATE(self);
639
+ return rb_hash_aref(state->seen, rb_obj_id(object));
640
+ }
641
+
642
+ /*
643
+ * call-seq: remember(object)
644
+ *
645
+ * Remember _object_, to find out if it was already encountered (if a cyclic
646
+ * data structure is rendered).
647
+ */
648
+ static VALUE cState_remember(VALUE self, VALUE object)
649
+ {
650
+ GET_STATE(self);
651
+ return rb_hash_aset(state->seen, rb_obj_id(object), Qtrue);
652
+ }
653
+
654
+ /*
655
+ * call-seq: forget(object)
656
+ *
657
+ * Forget _object_ for this generating run.
658
+ */
659
+ static VALUE cState_forget(VALUE self, VALUE object)
660
+ {
661
+ GET_STATE(self);
662
+ return rb_hash_delete(state->seen, rb_obj_id(object));
663
+ }
664
+
665
+ void Init_generator()
666
+ {
667
+ mJSON = rb_define_module("JSON");
668
+ mExt = rb_define_module_under(mJSON, "Ext");
669
+ mGenerator = rb_define_module_under(mExt, "Generator");
670
+ eGeneratorError = rb_path2class("JSON::GeneratorError");
671
+ eCircularDatastructure = rb_path2class("JSON::CircularDatastructure");
672
+ cState = rb_define_class_under(mGenerator, "State", rb_cObject);
673
+ rb_define_alloc_func(cState, cState_s_allocate);
674
+ rb_define_singleton_method(cState, "from_state", cState_from_state_s, 1);
675
+ rb_define_method(cState, "initialize", cState_initialize, -1);
676
+
677
+ rb_define_method(cState, "indent", cState_indent, 0);
678
+ rb_define_method(cState, "indent=", cState_indent_set, 1);
679
+ rb_define_method(cState, "space", cState_space, 0);
680
+ rb_define_method(cState, "space=", cState_space_set, 1);
681
+ rb_define_method(cState, "space_before", cState_space_before, 0);
682
+ rb_define_method(cState, "space_before=", cState_space_before_set, 1);
683
+ rb_define_method(cState, "object_nl", cState_object_nl, 0);
684
+ rb_define_method(cState, "object_nl=", cState_object_nl_set, 1);
685
+ rb_define_method(cState, "array_nl", cState_array_nl, 0);
686
+ rb_define_method(cState, "array_nl=", cState_array_nl_set, 1);
687
+ rb_define_method(cState, "check_circular?", cState_check_circular_p, 0);
688
+ rb_define_method(cState, "seen?", cState_seen_p, 1);
689
+ rb_define_method(cState, "remember", cState_remember, 1);
690
+ rb_define_method(cState, "forget", cState_forget, 1);
691
+ mGeneratorMethods = rb_define_module_under(mGenerator, "GeneratorMethods");
692
+ mObject = rb_define_module_under(mGeneratorMethods, "Object");
693
+ rb_define_method(mObject, "to_json", mObject_to_json, -1);
694
+ mHash = rb_define_module_under(mGeneratorMethods, "Hash");
695
+ rb_define_method(mHash, "to_json", mHash_to_json, -1);
696
+ mArray = rb_define_module_under(mGeneratorMethods, "Array");
697
+ rb_define_method(mArray, "to_json", mArray_to_json, -1);
698
+ mInteger = rb_define_module_under(mGeneratorMethods, "Integer");
699
+ rb_define_method(mInteger, "to_json", mInteger_to_json, -1);
700
+ mFloat = rb_define_module_under(mGeneratorMethods, "Float");
701
+ rb_define_method(mFloat, "to_json", mFloat_to_json, -1);
702
+ mString = rb_define_module_under(mGeneratorMethods, "String");
703
+ rb_define_singleton_method(mString, "included", mString_included_s, 1);
704
+ rb_define_method(mString, "to_json", mString_to_json, -1);
705
+ rb_define_method(mString, "to_json_raw", mString_to_json_raw, -1);
706
+ rb_define_method(mString, "to_json_raw_object", mString_to_json_raw_object, 0);
707
+ mString_Extend = rb_define_module_under(mString, "Extend");
708
+ rb_define_method(mString_Extend, "json_create", mString_Extend_json_create, 1);
709
+ mTrueClass = rb_define_module_under(mGeneratorMethods, "TrueClass");
710
+ rb_define_method(mTrueClass, "to_json", mTrueClass_to_json, -1);
711
+ mFalseClass = rb_define_module_under(mGeneratorMethods, "FalseClass");
712
+ rb_define_method(mFalseClass, "to_json", mFalseClass_to_json, -1);
713
+ mNilClass = rb_define_module_under(mGeneratorMethods, "NilClass");
714
+ rb_define_method(mNilClass, "to_json", mNilClass_to_json, -1);
715
+
716
+ i_to_s = rb_intern("to_s");
717
+ i_to_json = rb_intern("to_json");
718
+ i_new = rb_intern("new");
719
+ i_indent = rb_intern("indent");
720
+ i_space = rb_intern("space");
721
+ i_space_before = rb_intern("space_before");
722
+ i_object_nl = rb_intern("object_nl");
723
+ i_array_nl = rb_intern("array_nl");
724
+ i_check_circular = rb_intern("check_circular");
725
+ i_pack = rb_intern("pack");
726
+ i_unpack = rb_intern("unpack");
727
+ i_create_id = rb_intern("create_id");
728
+ i_extend = rb_intern("extend");
729
+ }