multi_json 1.15.0 → 1.16.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +100 -103
- data/CONTRIBUTING.md +1 -1
- data/LICENSE.md +1 -1
- data/README.md +19 -27
- data/lib/multi_json/adapter.rb +21 -14
- data/lib/multi_json/adapter_error.rb +5 -7
- data/lib/multi_json/adapters/gson.rb +2 -3
- data/lib/multi_json/adapters/jr_jackson.rb +3 -3
- data/lib/multi_json/adapters/json_gem.rb +26 -3
- data/lib/multi_json/adapters/json_pure.rb +2 -6
- data/lib/multi_json/adapters/oj.rb +31 -31
- data/lib/multi_json/adapters/ok_json.rb +3 -3
- data/lib/multi_json/adapters/yajl.rb +3 -3
- data/lib/multi_json/convertible_hash_keys.rb +29 -21
- data/lib/multi_json/options.rb +23 -13
- data/lib/multi_json/options_cache.rb +43 -19
- data/lib/multi_json/parse_error.rb +8 -8
- data/lib/multi_json/vendor/okjson.rb +145 -206
- data/lib/multi_json/version.rb +2 -2
- data/lib/multi_json.rb +59 -48
- metadata +15 -51
- data/lib/multi_json/adapters/json_common.rb +0 -23
- data/lib/multi_json/adapters/nsjsonserialization.rb +0 -35
@@ -1,5 +1,3 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
#
|
3
1
|
# Copyright 2011, 2012 Keith Rarick
|
4
2
|
#
|
5
3
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
@@ -22,17 +20,16 @@
|
|
22
20
|
|
23
21
|
# See https://github.com/kr/okjson for updates.
|
24
22
|
|
25
|
-
require
|
23
|
+
require "stringio"
|
26
24
|
|
27
25
|
module MultiJson
|
28
26
|
# Some parts adapted from
|
29
27
|
# https://golang.org/src/encoding/json/decode.go and
|
30
28
|
# https://golang.org/src/unicode/utf8/utf8.go
|
31
29
|
module OkJson
|
32
|
-
Upstream =
|
30
|
+
Upstream = "45"
|
33
31
|
extend self
|
34
32
|
|
35
|
-
|
36
33
|
# Decodes a json document in string s and
|
37
34
|
# returns the corresponding ruby value.
|
38
35
|
# String s must be valid UTF-8. If you have
|
@@ -44,13 +41,11 @@ module MultiJson
|
|
44
41
|
def decode(s)
|
45
42
|
ts = lex(s)
|
46
43
|
v, ts = textparse(ts)
|
47
|
-
|
48
|
-
|
49
|
-
end
|
44
|
+
raise Error, "trailing garbage" unless ts.empty?
|
45
|
+
|
50
46
|
v
|
51
47
|
end
|
52
48
|
|
53
|
-
|
54
49
|
# Encodes x into a json text. It may contain only
|
55
50
|
# Array, Hash, String, Numeric, true, false, nil.
|
56
51
|
# (Note, this list excludes Symbol.)
|
@@ -62,172 +57,140 @@ module MultiJson
|
|
62
57
|
# Strings contained in x must be valid UTF-8.
|
63
58
|
def encode(x)
|
64
59
|
case x
|
65
|
-
when Hash
|
66
|
-
when Array
|
60
|
+
when Hash then objenc(x)
|
61
|
+
when Array then arrenc(x)
|
67
62
|
else
|
68
|
-
raise Error,
|
63
|
+
raise Error, "root value must be an Array or a Hash"
|
69
64
|
end
|
70
65
|
end
|
71
66
|
|
72
|
-
|
73
67
|
def valenc(x)
|
74
68
|
case x
|
75
|
-
when Hash
|
76
|
-
when Array
|
77
|
-
when String
|
69
|
+
when Hash then objenc(x)
|
70
|
+
when Array then arrenc(x)
|
71
|
+
when String then strenc(x)
|
78
72
|
when Numeric then numenc(x)
|
79
|
-
when true
|
80
|
-
when false
|
81
|
-
when nil
|
73
|
+
when true then "true"
|
74
|
+
when false then "false"
|
75
|
+
when nil then "null"
|
82
76
|
else
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
raise Error, "cannot encode #{x.class}: #{x.inspect}"
|
87
|
-
end
|
77
|
+
raise Error, "cannot encode #{x.class}: #{x.inspect}" unless x.respond_to?(:to_json)
|
78
|
+
|
79
|
+
x.to_json
|
88
80
|
end
|
89
81
|
end
|
90
82
|
|
91
|
-
|
92
|
-
private
|
93
|
-
|
83
|
+
private
|
94
84
|
|
95
85
|
# Parses a "json text" in the sense of RFC 4627.
|
96
86
|
# Returns the parsed value and any trailing tokens.
|
97
87
|
# Note: this is almost the same as valparse,
|
98
88
|
# except that it does not accept atomic values.
|
99
89
|
def textparse(ts)
|
100
|
-
if ts.length <= 0
|
101
|
-
raise Error, 'empty'
|
102
|
-
end
|
90
|
+
raise Error, "empty" if ts.length <= 0
|
103
91
|
|
104
92
|
typ, _, val = ts[0]
|
105
93
|
case typ
|
106
|
-
when
|
107
|
-
when
|
94
|
+
when "{" then objparse(ts)
|
95
|
+
when "[" then arrparse(ts)
|
108
96
|
else
|
109
97
|
raise Error, "unexpected #{val.inspect}"
|
110
98
|
end
|
111
99
|
end
|
112
100
|
|
113
|
-
|
114
101
|
# Parses a "value" in the sense of RFC 4627.
|
115
102
|
# Returns the parsed value and any trailing tokens.
|
116
103
|
def valparse(ts)
|
117
|
-
if ts.length <= 0
|
118
|
-
raise Error, 'empty'
|
119
|
-
end
|
104
|
+
raise Error, "empty" if ts.length <= 0
|
120
105
|
|
121
106
|
typ, _, val = ts[0]
|
122
107
|
case typ
|
123
|
-
when
|
124
|
-
when
|
125
|
-
when :val
|
108
|
+
when "{" then objparse(ts)
|
109
|
+
when "[" then arrparse(ts)
|
110
|
+
when :val, :str then [val, ts[1..]]
|
126
111
|
else
|
127
112
|
raise Error, "unexpected #{val.inspect}"
|
128
113
|
end
|
129
114
|
end
|
130
115
|
|
131
|
-
|
132
116
|
# Parses an "object" in the sense of RFC 4627.
|
133
117
|
# Returns the parsed value and any trailing tokens.
|
134
118
|
def objparse(ts)
|
135
|
-
ts = eat(
|
119
|
+
ts = eat("{", ts)
|
136
120
|
obj = {}
|
137
121
|
|
138
|
-
if ts[0][0] ==
|
139
|
-
return obj, ts[1..-1]
|
140
|
-
end
|
122
|
+
return obj, ts[1..] if ts[0][0] == "}"
|
141
123
|
|
142
124
|
k, v, ts = pairparse(ts)
|
143
125
|
obj[k] = v
|
144
126
|
|
145
|
-
if ts[0][0] ==
|
146
|
-
return obj, ts[1..-1]
|
147
|
-
end
|
127
|
+
return obj, ts[1..] if ts[0][0] == "}"
|
148
128
|
|
149
129
|
loop do
|
150
|
-
ts = eat(
|
130
|
+
ts = eat(",", ts)
|
151
131
|
|
152
132
|
k, v, ts = pairparse(ts)
|
153
133
|
obj[k] = v
|
154
134
|
|
155
|
-
if ts[0][0] ==
|
156
|
-
return obj, ts[1..-1]
|
157
|
-
end
|
135
|
+
return obj, ts[1..] if ts[0][0] == "}"
|
158
136
|
end
|
159
137
|
end
|
160
138
|
|
161
|
-
|
162
139
|
# Parses a "member" in the sense of RFC 4627.
|
163
140
|
# Returns the parsed values and any trailing tokens.
|
164
141
|
def pairparse(ts)
|
165
|
-
(typ, _, k)
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
ts = eat(
|
142
|
+
(typ, _, k) = ts[0]
|
143
|
+
ts = ts[1..]
|
144
|
+
raise Error, "unexpected #{k.inspect}" if typ != :str
|
145
|
+
|
146
|
+
ts = eat(":", ts)
|
170
147
|
v, ts = valparse(ts)
|
171
148
|
[k, v, ts]
|
172
149
|
end
|
173
150
|
|
174
|
-
|
175
151
|
# Parses an "array" in the sense of RFC 4627.
|
176
152
|
# Returns the parsed value and any trailing tokens.
|
177
153
|
def arrparse(ts)
|
178
|
-
ts = eat(
|
154
|
+
ts = eat("[", ts)
|
179
155
|
arr = []
|
180
156
|
|
181
|
-
if ts[0][0] ==
|
182
|
-
return arr, ts[1..-1]
|
183
|
-
end
|
157
|
+
return arr, ts[1..] if ts[0][0] == "]"
|
184
158
|
|
185
159
|
v, ts = valparse(ts)
|
186
160
|
arr << v
|
187
161
|
|
188
|
-
if ts[0][0] ==
|
189
|
-
return arr, ts[1..-1]
|
190
|
-
end
|
162
|
+
return arr, ts[1..] if ts[0][0] == "]"
|
191
163
|
|
192
164
|
loop do
|
193
|
-
ts = eat(
|
165
|
+
ts = eat(",", ts)
|
194
166
|
|
195
167
|
v, ts = valparse(ts)
|
196
168
|
arr << v
|
197
169
|
|
198
|
-
if ts[0][0] ==
|
199
|
-
return arr, ts[1..-1]
|
200
|
-
end
|
170
|
+
return arr, ts[1..] if ts[0][0] == "]"
|
201
171
|
end
|
202
172
|
end
|
203
173
|
|
204
|
-
|
205
174
|
def eat(typ, ts)
|
206
|
-
if ts[0][0] != typ
|
207
|
-
raise Error, "expected #{typ} (got #{ts[0].inspect})"
|
208
|
-
end
|
209
|
-
ts[1..-1]
|
210
|
-
end
|
175
|
+
raise Error, "expected #{typ} (got #{ts[0].inspect})" if ts[0][0] != typ
|
211
176
|
|
177
|
+
ts[1..]
|
178
|
+
end
|
212
179
|
|
213
180
|
# Scans s and returns a list of json tokens,
|
214
181
|
# excluding white space (as defined in RFC 4627).
|
215
182
|
def lex(s)
|
216
183
|
ts = []
|
217
|
-
|
184
|
+
until s.empty?
|
218
185
|
typ, lexeme, val = tok(s)
|
219
|
-
if typ
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
ts << [typ, lexeme, val]
|
224
|
-
end
|
225
|
-
s = s[lexeme.length..-1]
|
186
|
+
raise Error, "invalid character at #{s[0, 10].inspect}" if typ.nil?
|
187
|
+
|
188
|
+
ts << [typ, lexeme, val] if typ != :space
|
189
|
+
s = s[lexeme.length..]
|
226
190
|
end
|
227
191
|
ts
|
228
192
|
end
|
229
193
|
|
230
|
-
|
231
194
|
# Scans the first token in s and
|
232
195
|
# returns a 3-element list, or nil
|
233
196
|
# if s does not begin with a valid token.
|
@@ -243,62 +206,58 @@ module MultiJson
|
|
243
206
|
# it is the lexeme.
|
244
207
|
def tok(s)
|
245
208
|
case s[0]
|
246
|
-
when
|
247
|
-
when
|
248
|
-
when
|
249
|
-
when
|
250
|
-
when
|
251
|
-
when
|
252
|
-
when
|
253
|
-
when
|
254
|
-
when
|
255
|
-
when
|
256
|
-
when Spc,
|
209
|
+
when "{" then ["{", s[0, 1], s[0, 1]]
|
210
|
+
when "}" then ["}", s[0, 1], s[0, 1]]
|
211
|
+
when ":" then [":", s[0, 1], s[0, 1]]
|
212
|
+
when "," then [",", s[0, 1], s[0, 1]]
|
213
|
+
when "[" then ["[", s[0, 1], s[0, 1]]
|
214
|
+
when "]" then ["]", s[0, 1], s[0, 1]]
|
215
|
+
when "n" then nulltok(s)
|
216
|
+
when "t" then truetok(s)
|
217
|
+
when "f" then falsetok(s)
|
218
|
+
when '"' then strtok(s)
|
219
|
+
when Spc, "\t", "\n", "\r" then [:space, s[0, 1], s[0, 1]]
|
257
220
|
else
|
258
221
|
numtok(s)
|
259
222
|
end
|
260
223
|
end
|
261
224
|
|
225
|
+
def nulltok(s) = (s[0, 4] == "null") ? [:val, "null", nil] : []
|
262
226
|
|
263
|
-
def
|
264
|
-
def truetok(s); s[0,4] == 'true' ? [:val, 'true', true] : [] end
|
265
|
-
def falsetok(s); s[0,5] == 'false' ? [:val, 'false', false] : [] end
|
227
|
+
def truetok(s) = (s[0, 4] == "true") ? [:val, "true", true] : []
|
266
228
|
|
229
|
+
def falsetok(s) = (s[0, 5] == "false") ? [:val, "false", false] : []
|
267
230
|
|
268
231
|
def numtok(s)
|
269
232
|
m = /(-?(?:[1-9][0-9]+|[0-9]))([.][0-9]+)?([eE][+-]?[0-9]+)?/.match(s)
|
270
|
-
if m
|
233
|
+
if m&.begin(0)&.zero?
|
271
234
|
if !m[2] && !m[3]
|
272
235
|
[:val, m[0], Integer(m[0])]
|
273
236
|
elsif m[2]
|
274
237
|
[:val, m[0], Float(m[0])]
|
275
238
|
else
|
276
|
-
[:val, m[0], Integer(m[1])*(10**m[3][1
|
239
|
+
[:val, m[0], Integer(m[1]) * (10**m[3][1..].to_i(10))]
|
277
240
|
end
|
278
241
|
else
|
279
242
|
[]
|
280
243
|
end
|
281
244
|
end
|
282
245
|
|
283
|
-
|
284
246
|
def strtok(s)
|
285
|
-
m =
|
286
|
-
|
287
|
-
|
288
|
-
end
|
247
|
+
m = %r{"([^"\\]|\\["/\\bfnrt]|\\u[0-9a-fA-F]{4})*"}.match(s)
|
248
|
+
raise Error, "invalid string literal at #{abbrev(s)}" unless m
|
249
|
+
|
289
250
|
[:str, m[0], unquote(m[0])]
|
290
251
|
end
|
291
252
|
|
292
|
-
|
293
253
|
def abbrev(s)
|
294
|
-
t = s[0,10]
|
295
|
-
p = t[
|
296
|
-
t = t[0,p] if p
|
297
|
-
t
|
298
|
-
|
254
|
+
t = s[0, 10]
|
255
|
+
p = t["`"]
|
256
|
+
t = t[0, p] if p
|
257
|
+
t += "..." if t.length < s.length
|
258
|
+
"`" + t + "`"
|
299
259
|
end
|
300
260
|
|
301
|
-
|
302
261
|
# Converts a quoted json string literal q into a UTF-8-encoded string.
|
303
262
|
# The rules are different than for Ruby, so we cannot use eval.
|
304
263
|
# Unquote will raise an error if q contains control characters.
|
@@ -306,47 +265,42 @@ module MultiJson
|
|
306
265
|
q = q[1...-1]
|
307
266
|
a = q.dup # allocate a big enough string
|
308
267
|
# In ruby >= 1.9, a[w] is a codepoint, not a byte.
|
309
|
-
if rubydoesenc?
|
310
|
-
|
311
|
-
|
312
|
-
r, w = 0, 0
|
268
|
+
a.force_encoding("UTF-8") if rubydoesenc?
|
269
|
+
r = 0
|
270
|
+
w = 0
|
313
271
|
while r < q.length
|
314
272
|
c = q[r]
|
315
|
-
if c ==
|
273
|
+
if c == "\\"
|
316
274
|
r += 1
|
317
|
-
if r >= q.length
|
318
|
-
raise Error, "string literal ends with a \"\\\": \"#{q}\""
|
319
|
-
end
|
275
|
+
raise Error, "string literal ends with a \"\\\": \"#{q}\"" if r >= q.length
|
320
276
|
|
321
277
|
case q[r]
|
322
|
-
when
|
278
|
+
when '"', "\\", "/", "'"
|
323
279
|
a[w] = q[r]
|
324
280
|
r += 1
|
325
281
|
w += 1
|
326
|
-
when
|
282
|
+
when "b", "f", "n", "r", "t"
|
327
283
|
a[w] = Unesc[q[r]]
|
328
284
|
r += 1
|
329
285
|
w += 1
|
330
|
-
when
|
286
|
+
when "u"
|
331
287
|
r += 1
|
332
288
|
uchar = begin
|
333
|
-
hexdec4(q[r,4])
|
289
|
+
hexdec4(q[r, 4])
|
334
290
|
rescue RuntimeError => e
|
335
|
-
raise Error, "invalid escape sequence \\u#{q[r,4]}: #{e}"
|
291
|
+
raise Error, "invalid escape sequence \\u#{q[r, 4]}: #{e}"
|
336
292
|
end
|
337
293
|
r += 4
|
338
|
-
if surrogate?
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
r += 6
|
345
|
-
end
|
294
|
+
if surrogate?(uchar) && (q.length >= r + 6)
|
295
|
+
uchar1 = hexdec4(q[r + 2, 4])
|
296
|
+
uchar = subst(uchar, uchar1)
|
297
|
+
if uchar != Ucharerr
|
298
|
+
# A valid pair; consume.
|
299
|
+
r += 6
|
346
300
|
end
|
347
301
|
end
|
348
302
|
if rubydoesenc?
|
349
|
-
a[w] =
|
303
|
+
a[w] = "" << uchar
|
350
304
|
w += 1
|
351
305
|
else
|
352
306
|
w += ucharenc(a, w, uchar)
|
@@ -354,7 +308,7 @@ module MultiJson
|
|
354
308
|
else
|
355
309
|
raise Error, "invalid escape char #{q[r]} in \"#{q}\""
|
356
310
|
end
|
357
|
-
elsif c ==
|
311
|
+
elsif c == '"' || c < Spc
|
358
312
|
raise Error, "invalid character in string literal \"#{q}\""
|
359
313
|
else
|
360
314
|
# Copy anything else byte-for-byte.
|
@@ -367,10 +321,9 @@ module MultiJson
|
|
367
321
|
w += 1
|
368
322
|
end
|
369
323
|
end
|
370
|
-
a[0,w]
|
324
|
+
a[0, w]
|
371
325
|
end
|
372
326
|
|
373
|
-
|
374
327
|
# Encodes unicode character u as UTF-8
|
375
328
|
# bytes in string a at position i.
|
376
329
|
# Returns the number of bytes written.
|
@@ -379,65 +332,56 @@ module MultiJson
|
|
379
332
|
a[i] = (u & 0xff).chr
|
380
333
|
1
|
381
334
|
elsif u <= Uchar2max
|
382
|
-
a[i+0] = (Utag2 | ((u>>6)&0xff)).chr
|
383
|
-
a[i+1] = (Utagx | (u&Umaskx)).chr
|
335
|
+
a[i + 0] = (Utag2 | ((u >> 6) & 0xff)).chr
|
336
|
+
a[i + 1] = (Utagx | (u & Umaskx)).chr
|
384
337
|
2
|
385
338
|
elsif u <= Uchar3max
|
386
|
-
a[i+0] = (Utag3 | ((u>>12)&0xff)).chr
|
387
|
-
a[i+1] = (Utagx | ((u>>6)&Umaskx)).chr
|
388
|
-
a[i+2] = (Utagx | (u&Umaskx)).chr
|
339
|
+
a[i + 0] = (Utag3 | ((u >> 12) & 0xff)).chr
|
340
|
+
a[i + 1] = (Utagx | ((u >> 6) & Umaskx)).chr
|
341
|
+
a[i + 2] = (Utagx | (u & Umaskx)).chr
|
389
342
|
3
|
390
343
|
else
|
391
|
-
a[i+0] = (Utag4 | ((u>>18)&0xff)).chr
|
392
|
-
a[i+1] = (Utagx | ((u>>12)&Umaskx)).chr
|
393
|
-
a[i+2] = (Utagx | ((u>>6)&Umaskx)).chr
|
394
|
-
a[i+3] = (Utagx | (u&Umaskx)).chr
|
344
|
+
a[i + 0] = (Utag4 | ((u >> 18) & 0xff)).chr
|
345
|
+
a[i + 1] = (Utagx | ((u >> 12) & Umaskx)).chr
|
346
|
+
a[i + 2] = (Utagx | ((u >> 6) & Umaskx)).chr
|
347
|
+
a[i + 3] = (Utagx | (u & Umaskx)).chr
|
395
348
|
4
|
396
349
|
end
|
397
350
|
end
|
398
351
|
|
399
|
-
|
400
352
|
def hexdec4(s)
|
401
|
-
if s.length != 4
|
402
|
-
raise Error, 'short'
|
403
|
-
end
|
404
|
-
(nibble(s[0])<<12) | (nibble(s[1])<<8) | (nibble(s[2])<<4) | nibble(s[3])
|
405
|
-
end
|
353
|
+
raise Error, "short" if s.length != 4
|
406
354
|
|
355
|
+
(nibble(s[0]) << 12) | (nibble(s[1]) << 8) | (nibble(s[2]) << 4) | nibble(s[3])
|
356
|
+
end
|
407
357
|
|
408
358
|
def subst(u1, u2)
|
409
|
-
|
410
|
-
return ((u1-Usurr1)<<10) | (u2-Usurr2) + Usurrself
|
411
|
-
end
|
412
|
-
return Ucharerr
|
413
|
-
end
|
359
|
+
return ((u1 - Usurr1) << 10) | ((u2 - Usurr2) + Usurrself) if u1 >= Usurr1 && u1 < Usurr2 && u2 >= Usurr2 && u2 < Usurr3
|
414
360
|
|
361
|
+
Ucharerr
|
362
|
+
end
|
415
363
|
|
416
364
|
def surrogate?(u)
|
417
|
-
|
365
|
+
u >= Usurr1 && u < Usurr3
|
418
366
|
end
|
419
367
|
|
420
|
-
|
421
368
|
def nibble(c)
|
422
|
-
if
|
423
|
-
elsif
|
424
|
-
elsif
|
369
|
+
if c >= "0" && c <= "9" then c.ord - "0".ord
|
370
|
+
elsif c >= "a" && c <= "z" then c.ord - "a".ord + 10
|
371
|
+
elsif c >= "A" && c <= "Z" then c.ord - "A".ord + 10
|
425
372
|
else
|
426
373
|
raise Error, "invalid hex code #{c}"
|
427
374
|
end
|
428
375
|
end
|
429
376
|
|
430
|
-
|
431
377
|
def objenc(x)
|
432
|
-
|
378
|
+
"{" + x.map { |k, v| keyenc(k) + ":" + valenc(v) }.join(",") + "}"
|
433
379
|
end
|
434
380
|
|
435
|
-
|
436
381
|
def arrenc(a)
|
437
|
-
|
382
|
+
"[" + a.map { |x| valenc(x) }.join(",") + "]"
|
438
383
|
end
|
439
384
|
|
440
|
-
|
441
385
|
def keyenc(k)
|
442
386
|
case k
|
443
387
|
when String then strenc(k)
|
@@ -446,37 +390,34 @@ module MultiJson
|
|
446
390
|
end
|
447
391
|
end
|
448
392
|
|
449
|
-
|
450
393
|
def strenc(s)
|
451
394
|
t = StringIO.new
|
452
|
-
t.putc(
|
395
|
+
t.putc('"')
|
453
396
|
r = 0
|
454
397
|
|
455
398
|
while r < s.length
|
456
399
|
case s[r]
|
457
|
-
when
|
458
|
-
when
|
459
|
-
when
|
460
|
-
when
|
461
|
-
when
|
462
|
-
when
|
463
|
-
when
|
400
|
+
when '"' then t.print('\\"')
|
401
|
+
when "\\" then t.print("\\\\")
|
402
|
+
when "\b" then t.print('\\b')
|
403
|
+
when "\f" then t.print('\\f')
|
404
|
+
when "\n" then t.print('\\n')
|
405
|
+
when "\r" then t.print('\\r')
|
406
|
+
when "\t" then t.print('\\t')
|
464
407
|
else
|
465
408
|
c = s[r]
|
466
409
|
# In ruby >= 1.9, s[r] is a codepoint, not a byte.
|
467
410
|
if rubydoesenc?
|
468
411
|
begin
|
469
412
|
# c.ord will raise an error if c is invalid UTF-8
|
470
|
-
if c.ord < Spc.ord
|
471
|
-
c = "\\u%04x" % [c.ord]
|
472
|
-
end
|
413
|
+
c = "\\u%04x" % [c.ord] if c.ord < Spc.ord
|
473
414
|
t.write(c)
|
474
415
|
rescue
|
475
416
|
t.write(Ustrerr)
|
476
417
|
end
|
477
418
|
elsif c < Spc
|
478
419
|
t.write("\\u%04x" % c)
|
479
|
-
elsif
|
420
|
+
elsif c >= Spc && c <= "~"
|
480
421
|
t.putc(c)
|
481
422
|
else
|
482
423
|
n = ucharcopy(t, s, r) # ensure valid UTF-8 output
|
@@ -485,18 +426,15 @@ module MultiJson
|
|
485
426
|
end
|
486
427
|
r += 1
|
487
428
|
end
|
488
|
-
t.putc(
|
429
|
+
t.putc('"')
|
489
430
|
t.string
|
490
431
|
end
|
491
432
|
|
492
|
-
|
493
433
|
def numenc(x)
|
494
|
-
if (
|
495
|
-
raise Error, "Numeric cannot be represented: #{x}"
|
496
|
-
end
|
497
|
-
"#{x}"
|
498
|
-
end
|
434
|
+
raise Error, "Numeric cannot be represented: #{x}" if (x.nan? || x.infinite? rescue false)
|
499
435
|
|
436
|
+
x.to_s
|
437
|
+
end
|
500
438
|
|
501
439
|
# Copies the valid UTF-8 bytes of a single character
|
502
440
|
# from string s at position i to I/O object t, and
|
@@ -518,12 +456,14 @@ module MultiJson
|
|
518
456
|
raise Utf8Error if c0 < Utag2 # unexpected continuation byte?
|
519
457
|
|
520
458
|
raise Utf8Error if n < 2 # need continuation byte
|
521
|
-
|
522
|
-
|
459
|
+
|
460
|
+
c1 = s[i + 1].ord
|
461
|
+
raise Utf8Error if c1 < Utagx || c1 >= Utag2
|
523
462
|
|
524
463
|
# 2-byte, 11-bit sequence?
|
525
464
|
if c0 < Utag3
|
526
|
-
raise Utf8Error if ((c0&Umask2)<<6 | (c1&Umaskx)) <= Uchar1max
|
465
|
+
raise Utf8Error if (((c0 & Umask2) << 6) | (c1 & Umaskx)) <= Uchar1max
|
466
|
+
|
527
467
|
t.putc(c0)
|
528
468
|
t.putc(c1)
|
529
469
|
return 2
|
@@ -532,13 +472,14 @@ module MultiJson
|
|
532
472
|
# need second continuation byte
|
533
473
|
raise Utf8Error if n < 3
|
534
474
|
|
535
|
-
c2 = s[i+2].ord
|
536
|
-
raise Utf8Error if c2 < Utagx ||
|
475
|
+
c2 = s[i + 2].ord
|
476
|
+
raise Utf8Error if c2 < Utagx || c2 >= Utag2
|
537
477
|
|
538
478
|
# 3-byte, 16-bit sequence?
|
539
479
|
if c0 < Utag4
|
540
|
-
u = (c0&Umask3)<<12 | (c1&Umaskx)<<6 | (c2&Umaskx)
|
480
|
+
u = ((c0 & Umask3) << 12) | ((c1 & Umaskx) << 6) | (c2 & Umaskx)
|
541
481
|
raise Utf8Error if u <= Uchar2max
|
482
|
+
|
542
483
|
t.putc(c0)
|
543
484
|
t.putc(c1)
|
544
485
|
t.putc(c2)
|
@@ -547,13 +488,15 @@ module MultiJson
|
|
547
488
|
|
548
489
|
# need third continuation byte
|
549
490
|
raise Utf8Error if n < 4
|
550
|
-
|
551
|
-
|
491
|
+
|
492
|
+
c3 = s[i + 3].ord
|
493
|
+
raise Utf8Error if c3 < Utagx || c3 >= Utag2
|
552
494
|
|
553
495
|
# 4-byte, 21-bit sequence?
|
554
496
|
if c0 < Utag5
|
555
|
-
u = (c0&Umask4)<<18 | (c1&Umaskx)<<12 | (c2&Umaskx)<<6 | (c3&Umaskx)
|
497
|
+
u = ((c0 & Umask4) << 18) | ((c1 & Umaskx) << 12) | ((c2 & Umaskx) << 6) | (c3 & Umaskx)
|
556
498
|
raise Utf8Error if u <= Uchar3max
|
499
|
+
|
557
500
|
t.putc(c0)
|
558
501
|
t.putc(c1)
|
559
502
|
t.putc(c2)
|
@@ -564,23 +507,19 @@ module MultiJson
|
|
564
507
|
raise Utf8Error
|
565
508
|
rescue Utf8Error
|
566
509
|
t.write(Ustrerr)
|
567
|
-
|
510
|
+
1
|
568
511
|
end
|
569
512
|
|
570
|
-
|
571
513
|
def rubydoesenc?
|
572
514
|
::String.method_defined?(:force_encoding)
|
573
515
|
end
|
574
516
|
|
575
|
-
|
576
517
|
class Utf8Error < ::StandardError
|
577
518
|
end
|
578
519
|
|
579
|
-
|
580
520
|
class Error < ::StandardError
|
581
521
|
end
|
582
522
|
|
583
|
-
|
584
523
|
Utagx = 0b1000_0000
|
585
524
|
Utag2 = 0b1100_0000
|
586
525
|
Utag3 = 0b1110_0000
|
@@ -590,9 +529,9 @@ module MultiJson
|
|
590
529
|
Umask2 = 0b0001_1111
|
591
530
|
Umask3 = 0b0000_1111
|
592
531
|
Umask4 = 0b0000_0111
|
593
|
-
Uchar1max = (1<<7) - 1
|
594
|
-
Uchar2max = (1<<11) - 1
|
595
|
-
Uchar3max = (1<<16) - 1
|
532
|
+
Uchar1max = (1 << 7) - 1
|
533
|
+
Uchar2max = (1 << 11) - 1
|
534
|
+
Uchar3max = (1 << 16) - 1
|
596
535
|
Ucharerr = 0xFFFD # unicode "replacement char"
|
597
536
|
Ustrerr = "\xef\xbf\xbd" # unicode "replacement char"
|
598
537
|
Usurrself = 0x10000
|
@@ -600,7 +539,7 @@ module MultiJson
|
|
600
539
|
Usurr2 = 0xdc00
|
601
540
|
Usurr3 = 0xe000
|
602
541
|
|
603
|
-
Spc =
|
604
|
-
Unesc = {
|
542
|
+
Spc = " "[0]
|
543
|
+
Unesc = {"b" => "\b", "f" => "\f", "n" => "\n", "r" => "\r", "t" => "\t"}
|
605
544
|
end
|
606
545
|
end
|