json-maglev- 1.5.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (139) hide show
  1. data/CHANGES +200 -0
  2. data/COPYING +58 -0
  3. data/COPYING-json-jruby +57 -0
  4. data/GPL +340 -0
  5. data/Gemfile +7 -0
  6. data/README-json-jruby.markdown +33 -0
  7. data/README.rdoc +358 -0
  8. data/Rakefile +407 -0
  9. data/TODO +1 -0
  10. data/VERSION +1 -0
  11. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkComparison.log +52 -0
  12. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_fast-autocorrelation.dat +1000 -0
  13. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_fast.dat +1001 -0
  14. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_pretty-autocorrelation.dat +900 -0
  15. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_pretty.dat +901 -0
  16. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_safe-autocorrelation.dat +1000 -0
  17. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt#generator_safe.dat +1001 -0
  18. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkExt.log +261 -0
  19. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_fast-autocorrelation.dat +1000 -0
  20. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_fast.dat +1001 -0
  21. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_pretty-autocorrelation.dat +1000 -0
  22. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_pretty.dat +1001 -0
  23. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_safe-autocorrelation.dat +1000 -0
  24. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure#generator_safe.dat +1001 -0
  25. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkPure.log +262 -0
  26. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkRails#generator-autocorrelation.dat +1000 -0
  27. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkRails#generator.dat +1001 -0
  28. data/benchmarks/data-p4-3GHz-ruby18/GeneratorBenchmarkRails.log +82 -0
  29. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkComparison.log +34 -0
  30. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkExt#parser-autocorrelation.dat +900 -0
  31. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkExt#parser.dat +901 -0
  32. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkExt.log +81 -0
  33. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkPure#parser-autocorrelation.dat +1000 -0
  34. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkPure#parser.dat +1001 -0
  35. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkPure.log +82 -0
  36. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkRails#parser-autocorrelation.dat +1000 -0
  37. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkRails#parser.dat +1001 -0
  38. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkRails.log +82 -0
  39. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkYAML#parser-autocorrelation.dat +1000 -0
  40. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkYAML#parser.dat +1001 -0
  41. data/benchmarks/data-p4-3GHz-ruby18/ParserBenchmarkYAML.log +82 -0
  42. data/benchmarks/generator2_benchmark.rb +222 -0
  43. data/benchmarks/generator_benchmark.rb +224 -0
  44. data/benchmarks/ohai.json +1216 -0
  45. data/benchmarks/ohai.ruby +1 -0
  46. data/benchmarks/parser2_benchmark.rb +251 -0
  47. data/benchmarks/parser_benchmark.rb +259 -0
  48. data/bin/edit_json.rb +9 -0
  49. data/bin/prettify_json.rb +48 -0
  50. data/data/example.json +1 -0
  51. data/data/index.html +38 -0
  52. data/data/prototype.js +4184 -0
  53. data/ext/json/ext/generator/extconf.rb +20 -0
  54. data/ext/json/ext/generator/generator.c +1414 -0
  55. data/ext/json/ext/generator/generator.h +196 -0
  56. data/ext/json/ext/parser/extconf.rb +16 -0
  57. data/ext/json/ext/parser/parser.c +1996 -0
  58. data/ext/json/ext/parser/parser.h +82 -0
  59. data/ext/json/ext/parser/parser.rl +853 -0
  60. data/install.rb +26 -0
  61. data/java/lib/bytelist-1.0.6.jar +0 -0
  62. data/java/lib/jcodings.jar +0 -0
  63. data/java/src/json/ext/ByteListTranscoder.java +167 -0
  64. data/java/src/json/ext/Generator.java +437 -0
  65. data/java/src/json/ext/GeneratorMethods.java +232 -0
  66. data/java/src/json/ext/GeneratorService.java +43 -0
  67. data/java/src/json/ext/GeneratorState.java +473 -0
  68. data/java/src/json/ext/OptionsReader.java +119 -0
  69. data/java/src/json/ext/Parser.java +2314 -0
  70. data/java/src/json/ext/Parser.rl +844 -0
  71. data/java/src/json/ext/ParserService.java +35 -0
  72. data/java/src/json/ext/RuntimeInfo.java +121 -0
  73. data/java/src/json/ext/StringDecoder.java +166 -0
  74. data/java/src/json/ext/StringEncoder.java +106 -0
  75. data/java/src/json/ext/Utils.java +89 -0
  76. data/json-java.gemspec +22 -0
  77. data/json.gemspec +41 -0
  78. data/json_pure.gemspec +46 -0
  79. data/lib/json.rb +62 -0
  80. data/lib/json/Array.xpm +21 -0
  81. data/lib/json/FalseClass.xpm +21 -0
  82. data/lib/json/Hash.xpm +21 -0
  83. data/lib/json/Key.xpm +73 -0
  84. data/lib/json/NilClass.xpm +21 -0
  85. data/lib/json/Numeric.xpm +28 -0
  86. data/lib/json/String.xpm +96 -0
  87. data/lib/json/TrueClass.xpm +21 -0
  88. data/lib/json/add/core.rb +243 -0
  89. data/lib/json/add/rails.rb +8 -0
  90. data/lib/json/common.rb +423 -0
  91. data/lib/json/editor.rb +1369 -0
  92. data/lib/json/ext.rb +28 -0
  93. data/lib/json/json.xpm +1499 -0
  94. data/lib/json/pure.rb +15 -0
  95. data/lib/json/pure/generator.rb +442 -0
  96. data/lib/json/pure/parser.rb +320 -0
  97. data/lib/json/version.rb +8 -0
  98. data/tests/fixtures/fail1.json +1 -0
  99. data/tests/fixtures/fail10.json +1 -0
  100. data/tests/fixtures/fail11.json +1 -0
  101. data/tests/fixtures/fail12.json +1 -0
  102. data/tests/fixtures/fail13.json +1 -0
  103. data/tests/fixtures/fail14.json +1 -0
  104. data/tests/fixtures/fail18.json +1 -0
  105. data/tests/fixtures/fail19.json +1 -0
  106. data/tests/fixtures/fail2.json +1 -0
  107. data/tests/fixtures/fail20.json +1 -0
  108. data/tests/fixtures/fail21.json +1 -0
  109. data/tests/fixtures/fail22.json +1 -0
  110. data/tests/fixtures/fail23.json +1 -0
  111. data/tests/fixtures/fail24.json +1 -0
  112. data/tests/fixtures/fail25.json +1 -0
  113. data/tests/fixtures/fail27.json +2 -0
  114. data/tests/fixtures/fail28.json +2 -0
  115. data/tests/fixtures/fail3.json +1 -0
  116. data/tests/fixtures/fail4.json +1 -0
  117. data/tests/fixtures/fail5.json +1 -0
  118. data/tests/fixtures/fail6.json +1 -0
  119. data/tests/fixtures/fail7.json +1 -0
  120. data/tests/fixtures/fail8.json +1 -0
  121. data/tests/fixtures/fail9.json +1 -0
  122. data/tests/fixtures/pass1.json +56 -0
  123. data/tests/fixtures/pass15.json +1 -0
  124. data/tests/fixtures/pass16.json +1 -0
  125. data/tests/fixtures/pass17.json +1 -0
  126. data/tests/fixtures/pass2.json +1 -0
  127. data/tests/fixtures/pass26.json +1 -0
  128. data/tests/fixtures/pass3.json +6 -0
  129. data/tests/setup_variant.rb +11 -0
  130. data/tests/test_json.rb +424 -0
  131. data/tests/test_json_addition.rb +167 -0
  132. data/tests/test_json_encoding.rb +65 -0
  133. data/tests/test_json_fixtures.rb +35 -0
  134. data/tests/test_json_generate.rb +180 -0
  135. data/tests/test_json_string_matching.rb +40 -0
  136. data/tests/test_json_unicode.rb +72 -0
  137. data/tools/fuzz.rb +139 -0
  138. data/tools/server.rb +61 -0
  139. metadata +265 -0
data/install.rb ADDED
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rbconfig'
4
+ require 'fileutils'
5
+ include FileUtils::Verbose
6
+
7
+ include Config
8
+
9
+ bindir = CONFIG["bindir"]
10
+ cd 'bin' do
11
+ filename = 'edit_json.rb'
12
+ #install(filename, bindir)
13
+ end
14
+ sitelibdir = CONFIG["sitelibdir"]
15
+ cd 'lib' do
16
+ install('json.rb', sitelibdir)
17
+ mkdir_p File.join(sitelibdir, 'json')
18
+ for file in Dir['json/**/*.{rb,xpm}']
19
+ d = File.join(sitelibdir, file)
20
+ mkdir_p File.dirname(d)
21
+ install(file, d)
22
+ end
23
+ install(File.join('json', 'editor.rb'), File.join(sitelibdir,'json'))
24
+ install(File.join('json', 'json.xpm'), File.join(sitelibdir,'json'))
25
+ end
26
+ warn " *** Installed PURE ruby library."
Binary file
Binary file
@@ -0,0 +1,167 @@
1
+ /*
2
+ * This code is copyrighted work by Daniel Luz <dev at mernen dot com>.
3
+ *
4
+ * Distributed under the Ruby and GPLv2 licenses; see COPYING and GPL files
5
+ * for details.
6
+ */
7
+ package json.ext;
8
+
9
+ import org.jruby.exceptions.RaiseException;
10
+ import org.jruby.runtime.ThreadContext;
11
+ import org.jruby.util.ByteList;
12
+
13
+ /**
14
+ * A class specialized in transcoding a certain String format into another,
15
+ * using UTF-8 ByteLists as both input and output.
16
+ */
17
+ abstract class ByteListTranscoder {
18
+ protected final ThreadContext context;
19
+
20
+ protected ByteList src;
21
+ protected int srcEnd;
22
+ /** Position where the last read character started */
23
+ protected int charStart;
24
+ /** Position of the next character to read */
25
+ protected int pos;
26
+
27
+ private ByteList out;
28
+ /**
29
+ * When a character that can be copied straight into the output is found,
30
+ * its index is stored on this variable, and copying is delayed until
31
+ * the sequence of characters that can be copied ends.
32
+ *
33
+ * <p>The variable stores -1 when not in a plain sequence.
34
+ */
35
+ private int quoteStart = -1;
36
+
37
+ protected ByteListTranscoder(ThreadContext context) {
38
+ this.context = context;
39
+ }
40
+
41
+ protected void init(ByteList src, ByteList out) {
42
+ this.init(src, 0, src.length(), out);
43
+ }
44
+
45
+ protected void init(ByteList src, int start, int end, ByteList out) {
46
+ this.src = src;
47
+ this.pos = start;
48
+ this.charStart = start;
49
+ this.srcEnd = end;
50
+ this.out = out;
51
+ }
52
+
53
+ /**
54
+ * Returns whether there are any characters left to be read.
55
+ */
56
+ protected boolean hasNext() {
57
+ return pos < srcEnd;
58
+ }
59
+
60
+ /**
61
+ * Returns the next character in the buffer.
62
+ */
63
+ private char next() {
64
+ return src.charAt(pos++);
65
+ }
66
+
67
+ /**
68
+ * Reads an UTF-8 character from the input and returns its code point,
69
+ * while advancing the input position.
70
+ *
71
+ * <p>Raises an {@link #invalidUtf8()} exception if an invalid byte
72
+ * is found.
73
+ */
74
+ protected int readUtf8Char() {
75
+ charStart = pos;
76
+ char head = next();
77
+ if (head <= 0x7f) { // 0b0xxxxxxx (ASCII)
78
+ return head;
79
+ }
80
+ if (head <= 0xbf) { // 0b10xxxxxx
81
+ throw invalidUtf8(); // tail byte with no head
82
+ }
83
+ if (head <= 0xdf) { // 0b110xxxxx
84
+ ensureMin(1);
85
+ int cp = ((head & 0x1f) << 6)
86
+ | nextPart();
87
+ if (cp < 0x0080) throw invalidUtf8();
88
+ return cp;
89
+ }
90
+ if (head <= 0xef) { // 0b1110xxxx
91
+ ensureMin(2);
92
+ int cp = ((head & 0x0f) << 12)
93
+ | (nextPart() << 6)
94
+ | nextPart();
95
+ if (cp < 0x0800) throw invalidUtf8();
96
+ return cp;
97
+ }
98
+ if (head <= 0xf7) { // 0b11110xxx
99
+ ensureMin(3);
100
+ int cp = ((head & 0x07) << 18)
101
+ | (nextPart() << 12)
102
+ | (nextPart() << 6)
103
+ | nextPart();
104
+ if (!Character.isValidCodePoint(cp)) throw invalidUtf8();
105
+ return cp;
106
+ }
107
+ // 0b11111xxx?
108
+ throw invalidUtf8();
109
+ }
110
+
111
+ /**
112
+ * Throws a GeneratorError if the input list doesn't have at least this
113
+ * many bytes left.
114
+ */
115
+ protected void ensureMin(int n) {
116
+ if (pos + n > srcEnd) throw incompleteUtf8();
117
+ }
118
+
119
+ /**
120
+ * Reads the next byte of a multi-byte UTF-8 character and returns its
121
+ * contents (lower 6 bits).
122
+ *
123
+ * <p>Throws a GeneratorError if the byte is not a valid tail.
124
+ */
125
+ private int nextPart() {
126
+ char c = next();
127
+ // tail bytes must be 0b10xxxxxx
128
+ if ((c & 0xc0) != 0x80) throw invalidUtf8();
129
+ return c & 0x3f;
130
+ }
131
+
132
+
133
+ protected void quoteStart() {
134
+ if (quoteStart == -1) quoteStart = charStart;
135
+ }
136
+
137
+ /**
138
+ * When in a sequence of characters that can be copied directly,
139
+ * interrupts the sequence and copies it to the output buffer.
140
+ *
141
+ * @param endPos The offset until which the direct character quoting should
142
+ * occur. You may pass {@link #pos} to quote until the most
143
+ * recently read character, or {@link #charStart} to quote
144
+ * until the character before it.
145
+ */
146
+ protected void quoteStop(int endPos) {
147
+ if (quoteStart != -1) {
148
+ out.append(src, quoteStart, endPos - quoteStart);
149
+ quoteStart = -1;
150
+ }
151
+ }
152
+
153
+ protected void append(int b) {
154
+ out.append(b);
155
+ }
156
+
157
+ protected void append(byte[] origin, int start, int length) {
158
+ out.append(origin, start, length);
159
+ }
160
+
161
+
162
+ protected abstract RaiseException invalidUtf8();
163
+
164
+ protected RaiseException incompleteUtf8() {
165
+ return invalidUtf8();
166
+ }
167
+ }
@@ -0,0 +1,437 @@
1
+ /*
2
+ * This code is copyrighted work by Daniel Luz <dev at mernen dot com>.
3
+ *
4
+ * Distributed under the Ruby and GPLv2 licenses; see COPYING and GPL files
5
+ * for details.
6
+ */
7
+ package json.ext;
8
+
9
+ import org.jruby.Ruby;
10
+ import org.jruby.RubyArray;
11
+ import org.jruby.RubyBignum;
12
+ import org.jruby.RubyBoolean;
13
+ import org.jruby.RubyClass;
14
+ import org.jruby.RubyFixnum;
15
+ import org.jruby.RubyFloat;
16
+ import org.jruby.RubyHash;
17
+ import org.jruby.RubyNumeric;
18
+ import org.jruby.RubyString;
19
+ import org.jruby.runtime.ThreadContext;
20
+ import org.jruby.runtime.builtin.IRubyObject;
21
+ import org.jruby.util.ByteList;
22
+
23
+ public final class Generator {
24
+ private Generator() {
25
+ throw new RuntimeException();
26
+ }
27
+
28
+ /**
29
+ * Encodes the given object as a JSON string, using the given handler.
30
+ */
31
+ static <T extends IRubyObject> RubyString
32
+ generateJson(ThreadContext context, T object,
33
+ Handler<? super T> handler, IRubyObject[] args) {
34
+ Session session = new Session(context, args.length > 0 ? args[0]
35
+ : null);
36
+ return session.infect(handler.generateNew(session, object));
37
+ }
38
+
39
+ /**
40
+ * Encodes the given object as a JSON string, detecting the appropriate handler
41
+ * for the given object.
42
+ */
43
+ static <T extends IRubyObject> RubyString
44
+ generateJson(ThreadContext context, T object, IRubyObject[] args) {
45
+ Handler<? super T> handler = getHandlerFor(context.getRuntime(), object);
46
+ return generateJson(context, object, handler, args);
47
+ }
48
+
49
+ /**
50
+ * Encodes the given object as a JSON string, using the appropriate
51
+ * handler if one is found or calling #to_json if not.
52
+ */
53
+ public static <T extends IRubyObject> RubyString
54
+ generateJson(ThreadContext context, T object,
55
+ GeneratorState config) {
56
+ Session session = new Session(context, config);
57
+ Handler<? super T> handler = getHandlerFor(context.getRuntime(), object);
58
+ return handler.generateNew(session, object);
59
+ }
60
+
61
+ /**
62
+ * Returns the best serialization handler for the given object.
63
+ */
64
+ // Java's generics can't handle this satisfactorily, so I'll just leave
65
+ // the best I could get and ignore the warnings
66
+ @SuppressWarnings("unchecked")
67
+ private static <T extends IRubyObject>
68
+ Handler<? super T> getHandlerFor(Ruby runtime, T object) {
69
+ RubyClass metaClass = object.getMetaClass();
70
+ if (metaClass == runtime.getString()) return (Handler)STRING_HANDLER;
71
+ if (metaClass == runtime.getFixnum()) return (Handler)FIXNUM_HANDLER;
72
+ if (metaClass == runtime.getHash()) return (Handler)HASH_HANDLER;
73
+ if (metaClass == runtime.getArray()) return (Handler)ARRAY_HANDLER;
74
+ if (object.isNil()) return (Handler)NIL_HANDLER;
75
+ if (object == runtime.getTrue()) return (Handler)TRUE_HANDLER;
76
+ if (object == runtime.getFalse()) return (Handler)FALSE_HANDLER;
77
+ if (metaClass == runtime.getFloat()) return (Handler)FLOAT_HANDLER;
78
+ if (metaClass == runtime.getBignum()) return (Handler)BIGNUM_HANDLER;
79
+ return GENERIC_HANDLER;
80
+ }
81
+
82
+
83
+ /* Generator context */
84
+
85
+ /**
86
+ * A class that concentrates all the information that is shared by
87
+ * generators working on a single session.
88
+ *
89
+ * <p>A session is defined as the process of serializing a single root
90
+ * object; any handler directly called by container handlers (arrays and
91
+ * hashes/objects) shares this object with its caller.
92
+ *
93
+ * <p>Note that anything called indirectly (via {@link GENERIC_HANDLER})
94
+ * won't be part of the session.
95
+ */
96
+ static class Session {
97
+ private final ThreadContext context;
98
+ private GeneratorState state;
99
+ private IRubyObject possibleState;
100
+ private RuntimeInfo info;
101
+ private StringEncoder stringEncoder;
102
+
103
+ private boolean tainted = false;
104
+ private boolean untrusted = false;
105
+
106
+ Session(ThreadContext context, GeneratorState state) {
107
+ this.context = context;
108
+ this.state = state;
109
+ }
110
+
111
+ Session(ThreadContext context, IRubyObject possibleState) {
112
+ this.context = context;
113
+ this.possibleState = possibleState == null || possibleState.isNil()
114
+ ? null : possibleState;
115
+ }
116
+
117
+ public ThreadContext getContext() {
118
+ return context;
119
+ }
120
+
121
+ public Ruby getRuntime() {
122
+ return context.getRuntime();
123
+ }
124
+
125
+ public GeneratorState getState() {
126
+ if (state == null) {
127
+ state = GeneratorState.fromState(context, getInfo(), possibleState);
128
+ }
129
+ return state;
130
+ }
131
+
132
+ public RuntimeInfo getInfo() {
133
+ if (info == null) info = RuntimeInfo.forRuntime(getRuntime());
134
+ return info;
135
+ }
136
+
137
+ public StringEncoder getStringEncoder() {
138
+ if (stringEncoder == null) {
139
+ stringEncoder = new StringEncoder(context, getState().asciiOnly());
140
+ }
141
+ return stringEncoder;
142
+ }
143
+
144
+ public void infectBy(IRubyObject object) {
145
+ if (object.isTaint()) tainted = true;
146
+ if (object.isUntrusted()) untrusted = true;
147
+ }
148
+
149
+ public <T extends IRubyObject> T infect(T object) {
150
+ if (tainted) object.setTaint(true);
151
+ if (untrusted) object.setUntrusted(true);
152
+ return object;
153
+ }
154
+ }
155
+
156
+
157
+ /* Handler base classes */
158
+
159
+ private static abstract class Handler<T extends IRubyObject> {
160
+ /**
161
+ * Returns an estimative of how much space the serialization of the
162
+ * given object will take. Used for allocating enough buffer space
163
+ * before invoking other methods.
164
+ */
165
+ int guessSize(Session session, T object) {
166
+ return 4;
167
+ }
168
+
169
+ RubyString generateNew(Session session, T object) {
170
+ ByteList buffer = new ByteList(guessSize(session, object));
171
+ generate(session, object, buffer);
172
+ return RubyString.newString(session.getRuntime(), buffer);
173
+ }
174
+
175
+ abstract void generate(Session session, T object, ByteList buffer);
176
+ }
177
+
178
+ /**
179
+ * A handler that returns a fixed keyword regardless of the passed object.
180
+ */
181
+ private static class KeywordHandler<T extends IRubyObject>
182
+ extends Handler<T> {
183
+ private final ByteList keyword;
184
+
185
+ private KeywordHandler(String keyword) {
186
+ this.keyword = new ByteList(ByteList.plain(keyword), false);
187
+ }
188
+
189
+ @Override
190
+ int guessSize(Session session, T object) {
191
+ return keyword.length();
192
+ }
193
+
194
+ @Override
195
+ RubyString generateNew(Session session, T object) {
196
+ return RubyString.newStringShared(session.getRuntime(), keyword);
197
+ }
198
+
199
+ @Override
200
+ void generate(Session session, T object, ByteList buffer) {
201
+ buffer.append(keyword);
202
+ }
203
+ }
204
+
205
+
206
+ /* Handlers */
207
+
208
+ static final Handler<RubyBignum> BIGNUM_HANDLER =
209
+ new Handler<RubyBignum>() {
210
+ @Override
211
+ void generate(Session session, RubyBignum object, ByteList buffer) {
212
+ // JRUBY-4751: RubyBignum.to_s() returns generic object
213
+ // representation (fixed in 1.5, but we maintain backwards
214
+ // compatibility; call to_s(IRubyObject[]) then
215
+ buffer.append(((RubyString)object.to_s(IRubyObject.NULL_ARRAY)).getByteList());
216
+ }
217
+ };
218
+
219
+ static final Handler<RubyFixnum> FIXNUM_HANDLER =
220
+ new Handler<RubyFixnum>() {
221
+ @Override
222
+ void generate(Session session, RubyFixnum object, ByteList buffer) {
223
+ buffer.append(object.to_s().getByteList());
224
+ }
225
+ };
226
+
227
+ static final Handler<RubyFloat> FLOAT_HANDLER =
228
+ new Handler<RubyFloat>() {
229
+ @Override
230
+ void generate(Session session, RubyFloat object, ByteList buffer) {
231
+ double value = RubyFloat.num2dbl(object);
232
+
233
+ if (Double.isInfinite(value) || Double.isNaN(value)) {
234
+ if (!session.getState().allowNaN()) {
235
+ throw Utils.newException(session.getContext(),
236
+ Utils.M_GENERATOR_ERROR,
237
+ object + " not allowed in JSON");
238
+ }
239
+ }
240
+ buffer.append(((RubyString)object.to_s()).getByteList());
241
+ }
242
+ };
243
+
244
+ static final Handler<RubyArray> ARRAY_HANDLER =
245
+ new Handler<RubyArray>() {
246
+ @Override
247
+ int guessSize(Session session, RubyArray object) {
248
+ GeneratorState state = session.getState();
249
+ int depth = state.getDepth();
250
+ int perItem =
251
+ 4 // prealloc
252
+ + (depth + 1) * state.getIndent().length() // indent
253
+ + 1 + state.getArrayNl().length(); // ',' arrayNl
254
+ return 2 + object.size() * perItem;
255
+ }
256
+
257
+ @Override
258
+ void generate(Session session, RubyArray object, ByteList buffer) {
259
+ ThreadContext context = session.getContext();
260
+ Ruby runtime = context.getRuntime();
261
+ GeneratorState state = session.getState();
262
+ int depth = state.increaseDepth();
263
+
264
+ ByteList indentUnit = state.getIndent();
265
+ byte[] shift = Utils.repeat(indentUnit, depth);
266
+
267
+ ByteList arrayNl = state.getArrayNl();
268
+ byte[] delim = new byte[1 + arrayNl.length()];
269
+ delim[0] = ',';
270
+ System.arraycopy(arrayNl.unsafeBytes(), arrayNl.begin(), delim, 1,
271
+ arrayNl.length());
272
+
273
+ session.infectBy(object);
274
+
275
+ buffer.append((byte)'[');
276
+ buffer.append(arrayNl);
277
+ boolean firstItem = true;
278
+ for (int i = 0, t = object.getLength(); i < t; i++) {
279
+ IRubyObject element = object.eltInternal(i);
280
+ session.infectBy(element);
281
+ if (firstItem) {
282
+ firstItem = false;
283
+ } else {
284
+ buffer.append(delim);
285
+ }
286
+ buffer.append(shift);
287
+ Handler<IRubyObject> handler = getHandlerFor(runtime, element);
288
+ handler.generate(session, element, buffer);
289
+ }
290
+
291
+ state.decreaseDepth();
292
+ if (arrayNl.length() != 0) {
293
+ buffer.append(arrayNl);
294
+ buffer.append(shift, 0, state.getDepth() * indentUnit.length());
295
+ }
296
+
297
+ buffer.append((byte)']');
298
+ }
299
+ };
300
+
301
+ static final Handler<RubyHash> HASH_HANDLER =
302
+ new Handler<RubyHash>() {
303
+ @Override
304
+ int guessSize(Session session, RubyHash object) {
305
+ GeneratorState state = session.getState();
306
+ int perItem =
307
+ 12 // key, colon, comma
308
+ + (state.getDepth() + 1) * state.getIndent().length()
309
+ + state.getSpaceBefore().length()
310
+ + state.getSpace().length();
311
+ return 2 + object.size() * perItem;
312
+ }
313
+
314
+ @Override
315
+ void generate(final Session session, RubyHash object,
316
+ final ByteList buffer) {
317
+ ThreadContext context = session.getContext();
318
+ final Ruby runtime = context.getRuntime();
319
+ final GeneratorState state = session.getState();
320
+ final int depth = state.increaseDepth();
321
+
322
+ final ByteList objectNl = state.getObjectNl();
323
+ final byte[] indent = Utils.repeat(state.getIndent(), depth);
324
+ final ByteList spaceBefore = state.getSpaceBefore();
325
+ final ByteList space = state.getSpace();
326
+
327
+ buffer.append((byte)'{');
328
+ buffer.append(objectNl);
329
+ object.visitAll(new RubyHash.Visitor() {
330
+ private boolean firstPair = true;
331
+
332
+ @Override
333
+ public void visit(IRubyObject key, IRubyObject value) {
334
+ if (firstPair) {
335
+ firstPair = false;
336
+ } else {
337
+ buffer.append((byte)',');
338
+ buffer.append(objectNl);
339
+ }
340
+ if (objectNl.length() != 0) buffer.append(indent);
341
+
342
+ STRING_HANDLER.generate(session, key.asString(), buffer);
343
+ session.infectBy(key);
344
+
345
+ buffer.append(spaceBefore);
346
+ buffer.append((byte)':');
347
+ buffer.append(space);
348
+
349
+ Handler<IRubyObject> valueHandler = getHandlerFor(runtime, value);
350
+ valueHandler.generate(session, value, buffer);
351
+ session.infectBy(value);
352
+ }
353
+ });
354
+ state.decreaseDepth();
355
+ if (objectNl.length() != 0) {
356
+ buffer.append(objectNl);
357
+ buffer.append(Utils.repeat(state.getIndent(), state.getDepth()));
358
+ }
359
+ buffer.append((byte)'}');
360
+ }
361
+ };
362
+
363
+ static final Handler<RubyString> STRING_HANDLER =
364
+ new Handler<RubyString>() {
365
+ @Override
366
+ int guessSize(Session session, RubyString object) {
367
+ // for most applications, most strings will be just a set of
368
+ // printable ASCII characters without any escaping, so let's
369
+ // just allocate enough space for that + the quotes
370
+ return 2 + object.getByteList().length();
371
+ }
372
+
373
+ @Override
374
+ void generate(Session session, RubyString object, ByteList buffer) {
375
+ RuntimeInfo info = session.getInfo();
376
+ RubyString src;
377
+
378
+ if (info.encodingsSupported() &&
379
+ object.encoding(session.getContext()) != info.utf8.get()) {
380
+ src = (RubyString)object.encode(session.getContext(),
381
+ info.utf8.get());
382
+ } else {
383
+ src = object;
384
+ }
385
+
386
+ session.getStringEncoder().encode(src.getByteList(), buffer);
387
+ }
388
+ };
389
+
390
+ static final Handler<RubyBoolean> TRUE_HANDLER =
391
+ new KeywordHandler<RubyBoolean>("true");
392
+ static final Handler<RubyBoolean> FALSE_HANDLER =
393
+ new KeywordHandler<RubyBoolean>("false");
394
+ static final Handler<IRubyObject> NIL_HANDLER =
395
+ new KeywordHandler<IRubyObject>("null");
396
+
397
+ /**
398
+ * The default handler (<code>Object#to_json</code>): coerces the object
399
+ * to string using <code>#to_s</code>, and serializes that string.
400
+ */
401
+ static final Handler<IRubyObject> OBJECT_HANDLER =
402
+ new Handler<IRubyObject>() {
403
+ @Override
404
+ RubyString generateNew(Session session, IRubyObject object) {
405
+ RubyString str = object.asString();
406
+ return STRING_HANDLER.generateNew(session, str);
407
+ }
408
+
409
+ @Override
410
+ void generate(Session session, IRubyObject object, ByteList buffer) {
411
+ RubyString str = object.asString();
412
+ STRING_HANDLER.generate(session, str, buffer);
413
+ }
414
+ };
415
+
416
+ /**
417
+ * A handler that simply calls <code>#to_json(state)</code> on the
418
+ * given object.
419
+ */
420
+ static final Handler<IRubyObject> GENERIC_HANDLER =
421
+ new Handler<IRubyObject>() {
422
+ @Override
423
+ RubyString generateNew(Session session, IRubyObject object) {
424
+ IRubyObject result =
425
+ object.callMethod(session.getContext(), "to_json",
426
+ new IRubyObject[] {session.getState()});
427
+ if (result instanceof RubyString) return (RubyString)result;
428
+ throw session.getRuntime().newTypeError("to_json must return a String");
429
+ }
430
+
431
+ @Override
432
+ void generate(Session session, IRubyObject object, ByteList buffer) {
433
+ RubyString result = generateNew(session, object);
434
+ buffer.append(result.getByteList());
435
+ }
436
+ };
437
+ }