capricorn 2.0.8 → 2.0.9
Sign up to get free protection for your applications and to get access to all the features.
- data/erlang/lib/capricorn/ebin/capricorn.app +2 -1
- data/erlang/lib/capricorn/include/capricorn.hrl +1 -0
- data/erlang/lib/capricorn/src/cap_cluster_gems.erl +6 -1
- data/erlang/lib/capricorn/src/cap_console_dispatcher.erl +214 -0
- data/erlang/lib/capricorn/src/cap_machine_apps.erl +25 -1
- data/erlang/lib/capricorn/src/cap_sup.erl +14 -0
- data/erlang/lib/ejson/Makefile +24 -0
- data/erlang/lib/ejson/ebin/ejson.app +9 -0
- data/erlang/lib/ejson/include/ejson.hrl +40 -0
- data/erlang/lib/ejson/rebar.config +3 -0
- data/erlang/lib/ejson/src/ejson.erl +22 -0
- data/erlang/lib/ejson/src/ejson_decode.erl +337 -0
- data/erlang/lib/ejson/src/ejson_encode.erl +124 -0
- data/erlang/lib/ejson/test/arrays.escript +47 -0
- data/erlang/lib/ejson/test/compound.escript +56 -0
- data/erlang/lib/ejson/test/literals.escript +30 -0
- data/erlang/lib/ejson/test/numbers.escript +70 -0
- data/erlang/lib/ejson/test/objects.escript +51 -0
- data/erlang/lib/ejson/test/strings.escript +49 -0
- data/erlang/lib/ejson/test/timing.escript +43 -0
- data/erlang/lib/ejson/test/timing.json +382 -0
- data/erlang/lib/ejson/vendor/mochijson2.erl +621 -0
- data/erlang/lib/ejson/vendor/rfc4627.erl +625 -0
- data/erlang/lib/misultin/LICENSE.txt +41 -0
- data/erlang/lib/misultin/Makefile +26 -0
- data/erlang/lib/misultin/README.txt +120 -0
- data/erlang/lib/misultin/ebin/misultin.app +9 -0
- data/erlang/lib/misultin/examples/misultin_compress.erl +43 -0
- data/erlang/lib/misultin/examples/misultin_echo.erl +58 -0
- data/erlang/lib/misultin/examples/misultin_file.erl +43 -0
- data/erlang/lib/misultin/examples/misultin_gen_server.erl +158 -0
- data/erlang/lib/misultin/examples/misultin_get_variable.erl +55 -0
- data/erlang/lib/misultin/examples/misultin_hello_world.erl +43 -0
- data/erlang/lib/misultin/examples/misultin_rest.erl +68 -0
- data/erlang/lib/misultin/examples/misultin_ssl.erl +51 -0
- data/erlang/lib/misultin/examples/misultin_stream.erl +55 -0
- data/erlang/lib/misultin/examples/misultin_websocket_event_example.erl +103 -0
- data/erlang/lib/misultin/examples/misultin_websocket_example.erl +95 -0
- data/erlang/lib/misultin/include/misultin.hrl +95 -0
- data/erlang/lib/misultin/make.bat +55 -0
- data/erlang/lib/misultin/priv/README.txt +12 -0
- data/erlang/lib/misultin/priv/test_certificate.pem +21 -0
- data/erlang/lib/misultin/priv/test_privkey.pem +18 -0
- data/erlang/lib/misultin/rebar.config +3 -0
- data/erlang/lib/misultin/src/misultin.app.src +9 -0
- data/erlang/lib/misultin/src/misultin.erl +338 -0
- data/erlang/lib/misultin/src/misultin_http.erl +488 -0
- data/erlang/lib/misultin/src/misultin_req.erl +280 -0
- data/erlang/lib/misultin/src/misultin_socket.erl +193 -0
- data/erlang/lib/misultin/src/misultin_utility.erl +357 -0
- data/erlang/lib/misultin/src/misultin_websocket.erl +252 -0
- data/erlang/lib/misultin/src/misultin_ws.erl +78 -0
- data/erlang/rebar.config +2 -0
- data/erlang/rel/overlay/etc/capricorn/app.config +4 -0
- data/erlang/rel/reltool.config +5 -0
- data/lib/capricorn/recipes/apache-debian.rb +1 -1
- data/lib/capricorn/recipes/centos-plesk.rb +1 -1
- data/lib/capricorn/recipes/debian-plesk95.rb +1 -2
- data/lib/capricorn/recipes/macports.rb +1 -1
- data/lib/capricorn/version.rb +1 -1
- metadata +51 -4
@@ -0,0 +1,621 @@
|
|
1
|
+
%% @author Bob Ippolito <bob@mochimedia.com>
|
2
|
+
%% @copyright 2007 Mochi Media, Inc.
|
3
|
+
|
4
|
+
%% @doc Yet another JSON (RFC 4627) library for Erlang. mochijson2 works
|
5
|
+
%% with binaries as strings, arrays as lists (without an {array, _})
|
6
|
+
%% wrapper and it only knows how to decode UTF-8 (and ASCII).
|
7
|
+
|
8
|
+
-module(mochijson2).
|
9
|
+
-author('bob@mochimedia.com').
|
10
|
+
-export([encoder/1, encode/1]).
|
11
|
+
-export([decoder/1, decode/1]).
|
12
|
+
-export([test/0]).
|
13
|
+
|
14
|
+
% This is a macro to placate syntax highlighters..
|
15
|
+
-define(Q, $\").
|
16
|
+
-define(ADV_COL(S, N), S#decoder{offset=N+S#decoder.offset,
|
17
|
+
column=N+S#decoder.column}).
|
18
|
+
-define(INC_COL(S), S#decoder{offset=1+S#decoder.offset,
|
19
|
+
column=1+S#decoder.column}).
|
20
|
+
-define(INC_LINE(S), S#decoder{offset=1+S#decoder.offset,
|
21
|
+
column=1,
|
22
|
+
line=1+S#decoder.line}).
|
23
|
+
-define(INC_CHAR(S, C),
|
24
|
+
case C of
|
25
|
+
$\n ->
|
26
|
+
S#decoder{column=1,
|
27
|
+
line=1+S#decoder.line,
|
28
|
+
offset=1+S#decoder.offset};
|
29
|
+
_ ->
|
30
|
+
S#decoder{column=1+S#decoder.column,
|
31
|
+
offset=1+S#decoder.offset}
|
32
|
+
end).
|
33
|
+
-define(IS_WHITESPACE(C),
|
34
|
+
(C =:= $\s orelse C =:= $\t orelse C =:= $\r orelse C =:= $\n)).
|
35
|
+
|
36
|
+
%% @type iolist() = [char() | binary() | iolist()]
|
37
|
+
%% @type iodata() = iolist() | binary()
|
38
|
+
%% @type json_string() = atom | binary()
|
39
|
+
%% @type json_number() = integer() | float()
|
40
|
+
%% @type json_array() = [json_term()]
|
41
|
+
%% @type json_object() = {struct, [{json_string(), json_term()}]}
|
42
|
+
%% @type json_term() = json_string() | json_number() | json_array() |
|
43
|
+
%% json_object()
|
44
|
+
|
45
|
+
-record(encoder, {handler=null}).
|
46
|
+
|
47
|
+
-record(decoder, {object_hook=null,
|
48
|
+
offset=0,
|
49
|
+
line=1,
|
50
|
+
column=1,
|
51
|
+
state=null}).
|
52
|
+
|
53
|
+
%% @spec encoder([encoder_option()]) -> function()
|
54
|
+
%% @doc Create an encoder/1 with the given options.
|
55
|
+
encoder(Options) ->
|
56
|
+
State = parse_encoder_options(Options, #encoder{}),
|
57
|
+
fun (O) -> json_encode(O, State) end.
|
58
|
+
|
59
|
+
%% @spec encode(json_term()) -> iolist()
|
60
|
+
%% @doc Encode the given as JSON to an iolist.
|
61
|
+
encode(Any) ->
|
62
|
+
json_encode(Any, #encoder{}).
|
63
|
+
|
64
|
+
%% @spec decoder([decoder_option()]) -> function()
|
65
|
+
%% @doc Create a decoder/1 with the given options.
|
66
|
+
decoder(Options) ->
|
67
|
+
State = parse_decoder_options(Options, #decoder{}),
|
68
|
+
fun (O) -> json_decode(O, State) end.
|
69
|
+
|
70
|
+
%% @spec decode(iolist()) -> json_term()
|
71
|
+
%% @doc Decode the given iolist to Erlang terms.
|
72
|
+
decode(S) ->
|
73
|
+
try json_decode(S, #decoder{})
|
74
|
+
catch
|
75
|
+
_:_ -> throw({invalid_json, S})
|
76
|
+
end.
|
77
|
+
|
78
|
+
test() ->
|
79
|
+
test_all().
|
80
|
+
|
81
|
+
%% Internal API
|
82
|
+
|
83
|
+
parse_encoder_options([], State) ->
|
84
|
+
State;
|
85
|
+
parse_encoder_options([{handler, Handler} | Rest], State) ->
|
86
|
+
parse_encoder_options(Rest, State#encoder{handler=Handler}).
|
87
|
+
|
88
|
+
parse_decoder_options([], State) ->
|
89
|
+
State;
|
90
|
+
parse_decoder_options([{object_hook, Hook} | Rest], State) ->
|
91
|
+
parse_decoder_options(Rest, State#decoder{object_hook=Hook}).
|
92
|
+
|
93
|
+
json_encode(true, _State) ->
|
94
|
+
<<"true">>;
|
95
|
+
json_encode(false, _State) ->
|
96
|
+
<<"false">>;
|
97
|
+
json_encode(null, _State) ->
|
98
|
+
<<"null">>;
|
99
|
+
json_encode(I, _State) when is_integer(I) ->
|
100
|
+
integer_to_list(I);
|
101
|
+
json_encode(F, _State) when is_float(F) ->
|
102
|
+
mochinum:digits(F);
|
103
|
+
json_encode(S, State) when is_binary(S); is_atom(S) ->
|
104
|
+
json_encode_string(S, State);
|
105
|
+
json_encode(Array, State) when is_list(Array) ->
|
106
|
+
json_encode_array(Array, State);
|
107
|
+
json_encode({Props}, State) when is_list(Props) ->
|
108
|
+
json_encode_proplist(Props, State);
|
109
|
+
json_encode(Bad, #encoder{handler=null}) ->
|
110
|
+
exit({json_encode, {bad_term, Bad}});
|
111
|
+
json_encode(Bad, State=#encoder{handler=Handler}) ->
|
112
|
+
json_encode(Handler(Bad), State).
|
113
|
+
|
114
|
+
json_encode_array([], _State) ->
|
115
|
+
<<"[]">>;
|
116
|
+
json_encode_array(L, State) ->
|
117
|
+
F = fun (O, Acc) ->
|
118
|
+
[$,, json_encode(O, State) | Acc]
|
119
|
+
end,
|
120
|
+
[$, | Acc1] = lists:foldl(F, "[", L),
|
121
|
+
lists:reverse([$\] | Acc1]).
|
122
|
+
|
123
|
+
json_encode_proplist([], _State) ->
|
124
|
+
<<"{}">>;
|
125
|
+
json_encode_proplist(Props, State) ->
|
126
|
+
F = fun ({K, V}, Acc) ->
|
127
|
+
KS = json_encode_string(K, State),
|
128
|
+
VS = json_encode(V, State),
|
129
|
+
[$,, VS, $:, KS | Acc]
|
130
|
+
end,
|
131
|
+
[$, | Acc1] = lists:foldl(F, "{", Props),
|
132
|
+
lists:reverse([$\} | Acc1]).
|
133
|
+
|
134
|
+
json_encode_string(A, _State) when is_atom(A) ->
|
135
|
+
L = atom_to_list(A),
|
136
|
+
case json_string_is_safe(L) of
|
137
|
+
true ->
|
138
|
+
[?Q, L, ?Q];
|
139
|
+
false ->
|
140
|
+
json_encode_string_unicode(xmerl_ucs:from_utf8(L), [?Q])
|
141
|
+
end;
|
142
|
+
json_encode_string(B, _State) when is_binary(B) ->
|
143
|
+
case json_bin_is_safe(B) of
|
144
|
+
true ->
|
145
|
+
[?Q, B, ?Q];
|
146
|
+
false ->
|
147
|
+
json_encode_string_unicode(xmerl_ucs:from_utf8(B), [?Q])
|
148
|
+
end;
|
149
|
+
json_encode_string(I, _State) when is_integer(I) ->
|
150
|
+
[?Q, integer_to_list(I), ?Q];
|
151
|
+
json_encode_string(L, _State) when is_list(L) ->
|
152
|
+
case json_string_is_safe(L) of
|
153
|
+
true ->
|
154
|
+
[?Q, L, ?Q];
|
155
|
+
false ->
|
156
|
+
json_encode_string_unicode(L, [?Q])
|
157
|
+
end.
|
158
|
+
|
159
|
+
json_string_is_safe([]) ->
|
160
|
+
true;
|
161
|
+
json_string_is_safe([C | Rest]) ->
|
162
|
+
case C of
|
163
|
+
?Q ->
|
164
|
+
false;
|
165
|
+
$\\ ->
|
166
|
+
false;
|
167
|
+
$\b ->
|
168
|
+
false;
|
169
|
+
$\f ->
|
170
|
+
false;
|
171
|
+
$\n ->
|
172
|
+
false;
|
173
|
+
$\r ->
|
174
|
+
false;
|
175
|
+
$\t ->
|
176
|
+
false;
|
177
|
+
C when C >= 0, C < $\s; C >= 16#7f, C =< 16#10FFFF ->
|
178
|
+
false;
|
179
|
+
C when C < 16#7f ->
|
180
|
+
json_string_is_safe(Rest);
|
181
|
+
_ ->
|
182
|
+
false
|
183
|
+
end.
|
184
|
+
|
185
|
+
json_bin_is_safe(<<>>) ->
|
186
|
+
true;
|
187
|
+
json_bin_is_safe(<<C, Rest/binary>>) ->
|
188
|
+
case C of
|
189
|
+
?Q ->
|
190
|
+
false;
|
191
|
+
$\\ ->
|
192
|
+
false;
|
193
|
+
$\b ->
|
194
|
+
false;
|
195
|
+
$\f ->
|
196
|
+
false;
|
197
|
+
$\n ->
|
198
|
+
false;
|
199
|
+
$\r ->
|
200
|
+
false;
|
201
|
+
$\t ->
|
202
|
+
false;
|
203
|
+
C when C >= 0, C < $\s; C >= 16#7f, C =< 16#10FFFF ->
|
204
|
+
false;
|
205
|
+
C when C < 16#7f ->
|
206
|
+
json_bin_is_safe(Rest);
|
207
|
+
_ ->
|
208
|
+
false
|
209
|
+
end.
|
210
|
+
|
211
|
+
json_encode_string_unicode([], Acc) ->
|
212
|
+
lists:reverse([$\" | Acc]);
|
213
|
+
json_encode_string_unicode([C | Cs], Acc) ->
|
214
|
+
Acc1 = case C of
|
215
|
+
?Q ->
|
216
|
+
[?Q, $\\ | Acc];
|
217
|
+
%% Escaping solidus is only useful when trying to protect
|
218
|
+
%% against "</script>" injection attacks which are only
|
219
|
+
%% possible when JSON is inserted into a HTML document
|
220
|
+
%% in-line. mochijson2 does not protect you from this, so
|
221
|
+
%% if you do insert directly into HTML then you need to
|
222
|
+
%% uncomment the following case or escape the output of encode.
|
223
|
+
%%
|
224
|
+
%% $/ ->
|
225
|
+
%% [$/, $\\ | Acc];
|
226
|
+
%%
|
227
|
+
$\\ ->
|
228
|
+
[$\\, $\\ | Acc];
|
229
|
+
$\b ->
|
230
|
+
[$b, $\\ | Acc];
|
231
|
+
$\f ->
|
232
|
+
[$f, $\\ | Acc];
|
233
|
+
$\n ->
|
234
|
+
[$n, $\\ | Acc];
|
235
|
+
$\r ->
|
236
|
+
[$r, $\\ | Acc];
|
237
|
+
$\t ->
|
238
|
+
[$t, $\\ | Acc];
|
239
|
+
C when C >= 0, C < $\s; C >= 16#7f, C =< 16#10FFFF ->
|
240
|
+
[unihex(C) | Acc];
|
241
|
+
C when C < 16#7f ->
|
242
|
+
[C | Acc];
|
243
|
+
_ ->
|
244
|
+
exit({json_encode, {bad_char, C}})
|
245
|
+
end,
|
246
|
+
json_encode_string_unicode(Cs, Acc1).
|
247
|
+
|
248
|
+
hexdigit(C) when C >= 0, C =< 9 ->
|
249
|
+
C + $0;
|
250
|
+
hexdigit(C) when C =< 15 ->
|
251
|
+
C + $a - 10.
|
252
|
+
|
253
|
+
unihex(C) when C < 16#10000 ->
|
254
|
+
<<D3:4, D2:4, D1:4, D0:4>> = <<C:16>>,
|
255
|
+
Digits = [hexdigit(D) || D <- [D3, D2, D1, D0]],
|
256
|
+
[$\\, $u | Digits];
|
257
|
+
unihex(C) when C =< 16#10FFFF ->
|
258
|
+
N = C - 16#10000,
|
259
|
+
S1 = 16#d800 bor ((N bsr 10) band 16#3ff),
|
260
|
+
S2 = 16#dc00 bor (N band 16#3ff),
|
261
|
+
[unihex(S1), unihex(S2)].
|
262
|
+
|
263
|
+
json_decode(L, S) when is_list(L) ->
|
264
|
+
json_decode(iolist_to_binary(L), S);
|
265
|
+
json_decode(B, S) ->
|
266
|
+
{Res, S1} = decode1(B, S),
|
267
|
+
{eof, _} = tokenize(B, S1#decoder{state=trim}),
|
268
|
+
Res.
|
269
|
+
|
270
|
+
decode1(B, S=#decoder{state=null}) ->
|
271
|
+
case tokenize(B, S#decoder{state=any}) of
|
272
|
+
{{const, C}, S1} ->
|
273
|
+
{C, S1};
|
274
|
+
{start_array, S1} ->
|
275
|
+
decode_array(B, S1);
|
276
|
+
{start_object, S1} ->
|
277
|
+
decode_object(B, S1)
|
278
|
+
end.
|
279
|
+
|
280
|
+
make_object(V, #decoder{object_hook=null}) ->
|
281
|
+
V;
|
282
|
+
make_object(V, #decoder{object_hook=Hook}) ->
|
283
|
+
Hook(V).
|
284
|
+
|
285
|
+
decode_object(B, S) ->
|
286
|
+
decode_object(B, S#decoder{state=key}, []).
|
287
|
+
|
288
|
+
decode_object(B, S=#decoder{state=key}, Acc) ->
|
289
|
+
case tokenize(B, S) of
|
290
|
+
{end_object, S1} ->
|
291
|
+
V = make_object({lists:reverse(Acc)}, S1),
|
292
|
+
{V, S1#decoder{state=null}};
|
293
|
+
{{const, K}, S1} ->
|
294
|
+
{colon, S2} = tokenize(B, S1),
|
295
|
+
{V, S3} = decode1(B, S2#decoder{state=null}),
|
296
|
+
decode_object(B, S3#decoder{state=comma}, [{K, V} | Acc])
|
297
|
+
end;
|
298
|
+
decode_object(B, S=#decoder{state=comma}, Acc) ->
|
299
|
+
case tokenize(B, S) of
|
300
|
+
{end_object, S1} ->
|
301
|
+
V = make_object({lists:reverse(Acc)}, S1),
|
302
|
+
{V, S1#decoder{state=null}};
|
303
|
+
{comma, S1} ->
|
304
|
+
decode_object(B, S1#decoder{state=key}, Acc)
|
305
|
+
end.
|
306
|
+
|
307
|
+
decode_array(B, S) ->
|
308
|
+
decode_array(B, S#decoder{state=any}, []).
|
309
|
+
|
310
|
+
decode_array(B, S=#decoder{state=any}, Acc) ->
|
311
|
+
case tokenize(B, S) of
|
312
|
+
{end_array, S1} ->
|
313
|
+
{lists:reverse(Acc), S1#decoder{state=null}};
|
314
|
+
{start_array, S1} ->
|
315
|
+
{Array, S2} = decode_array(B, S1),
|
316
|
+
decode_array(B, S2#decoder{state=comma}, [Array | Acc]);
|
317
|
+
{start_object, S1} ->
|
318
|
+
{Array, S2} = decode_object(B, S1),
|
319
|
+
decode_array(B, S2#decoder{state=comma}, [Array | Acc]);
|
320
|
+
{{const, Const}, S1} ->
|
321
|
+
decode_array(B, S1#decoder{state=comma}, [Const | Acc])
|
322
|
+
end;
|
323
|
+
decode_array(B, S=#decoder{state=comma}, Acc) ->
|
324
|
+
case tokenize(B, S) of
|
325
|
+
{end_array, S1} ->
|
326
|
+
{lists:reverse(Acc), S1#decoder{state=null}};
|
327
|
+
{comma, S1} ->
|
328
|
+
decode_array(B, S1#decoder{state=any}, Acc)
|
329
|
+
end.
|
330
|
+
|
331
|
+
tokenize_string(B, S=#decoder{offset=O}) ->
|
332
|
+
case tokenize_string_fast(B, O) of
|
333
|
+
{escape, O1} ->
|
334
|
+
Length = O1 - O,
|
335
|
+
S1 = ?ADV_COL(S, Length),
|
336
|
+
<<_:O/binary, Head:Length/binary, _/binary>> = B,
|
337
|
+
tokenize_string(B, S1, lists:reverse(binary_to_list(Head)));
|
338
|
+
O1 ->
|
339
|
+
Length = O1 - O,
|
340
|
+
<<_:O/binary, String:Length/binary, ?Q, _/binary>> = B,
|
341
|
+
{{const, String}, ?ADV_COL(S, Length + 1)}
|
342
|
+
end.
|
343
|
+
|
344
|
+
tokenize_string_fast(B, O) ->
|
345
|
+
case B of
|
346
|
+
<<_:O/binary, ?Q, _/binary>> ->
|
347
|
+
O;
|
348
|
+
<<_:O/binary, $\\, _/binary>> ->
|
349
|
+
{escape, O};
|
350
|
+
<<_:O/binary, C1, _/binary>> when C1 < 128 ->
|
351
|
+
tokenize_string_fast(B, 1 + O);
|
352
|
+
<<_:O/binary, C1, C2, _/binary>> when C1 >= 194, C1 =< 223,
|
353
|
+
C2 >= 128, C2 =< 191 ->
|
354
|
+
tokenize_string_fast(B, 2 + O);
|
355
|
+
<<_:O/binary, C1, C2, C3, _/binary>> when C1 >= 224, C1 =< 239,
|
356
|
+
C2 >= 128, C2 =< 191,
|
357
|
+
C3 >= 128, C3 =< 191 ->
|
358
|
+
tokenize_string_fast(B, 3 + O);
|
359
|
+
<<_:O/binary, C1, C2, C3, C4, _/binary>> when C1 >= 240, C1 =< 244,
|
360
|
+
C2 >= 128, C2 =< 191,
|
361
|
+
C3 >= 128, C3 =< 191,
|
362
|
+
C4 >= 128, C4 =< 191 ->
|
363
|
+
tokenize_string_fast(B, 4 + O);
|
364
|
+
_ ->
|
365
|
+
throw(invalid_utf8)
|
366
|
+
end.
|
367
|
+
|
368
|
+
tokenize_string(B, S=#decoder{offset=O}, Acc) ->
|
369
|
+
case B of
|
370
|
+
<<_:O/binary, ?Q, _/binary>> ->
|
371
|
+
{{const, iolist_to_binary(lists:reverse(Acc))}, ?INC_COL(S)};
|
372
|
+
<<_:O/binary, "\\\"", _/binary>> ->
|
373
|
+
tokenize_string(B, ?ADV_COL(S, 2), [$\" | Acc]);
|
374
|
+
<<_:O/binary, "\\\\", _/binary>> ->
|
375
|
+
tokenize_string(B, ?ADV_COL(S, 2), [$\\ | Acc]);
|
376
|
+
<<_:O/binary, "\\/", _/binary>> ->
|
377
|
+
tokenize_string(B, ?ADV_COL(S, 2), [$/ | Acc]);
|
378
|
+
<<_:O/binary, "\\b", _/binary>> ->
|
379
|
+
tokenize_string(B, ?ADV_COL(S, 2), [$\b | Acc]);
|
380
|
+
<<_:O/binary, "\\f", _/binary>> ->
|
381
|
+
tokenize_string(B, ?ADV_COL(S, 2), [$\f | Acc]);
|
382
|
+
<<_:O/binary, "\\n", _/binary>> ->
|
383
|
+
tokenize_string(B, ?ADV_COL(S, 2), [$\n | Acc]);
|
384
|
+
<<_:O/binary, "\\r", _/binary>> ->
|
385
|
+
tokenize_string(B, ?ADV_COL(S, 2), [$\r | Acc]);
|
386
|
+
<<_:O/binary, "\\t", _/binary>> ->
|
387
|
+
tokenize_string(B, ?ADV_COL(S, 2), [$\t | Acc]);
|
388
|
+
<<_:O/binary, "\\u", C3, C2, C1, C0, Rest/binary>> ->
|
389
|
+
C = erlang:list_to_integer([C3, C2, C1, C0], 16),
|
390
|
+
if C > 16#D7FF, C < 16#DC00 ->
|
391
|
+
%% coalesce UTF-16 surrogate pair
|
392
|
+
<<"\\u", D3, D2, D1, D0, _/binary>> = Rest,
|
393
|
+
D = erlang:list_to_integer([D3,D2,D1,D0], 16),
|
394
|
+
[CodePoint] = xmerl_ucs:from_utf16be(<<C:16/big-unsigned-integer,
|
395
|
+
D:16/big-unsigned-integer>>),
|
396
|
+
Acc1 = lists:reverse(xmerl_ucs:to_utf8(CodePoint), Acc),
|
397
|
+
tokenize_string(B, ?ADV_COL(S, 12), Acc1);
|
398
|
+
true ->
|
399
|
+
Acc1 = lists:reverse(xmerl_ucs:to_utf8(C), Acc),
|
400
|
+
tokenize_string(B, ?ADV_COL(S, 6), Acc1)
|
401
|
+
end;
|
402
|
+
<<_:O/binary, C, _/binary>> ->
|
403
|
+
tokenize_string(B, ?INC_CHAR(S, C), [C | Acc])
|
404
|
+
end.
|
405
|
+
|
406
|
+
tokenize_number(B, S) ->
|
407
|
+
case tokenize_number(B, sign, S, []) of
|
408
|
+
{{int, Int}, S1} ->
|
409
|
+
{{const, list_to_integer(Int)}, S1};
|
410
|
+
{{float, Float}, S1} ->
|
411
|
+
{{const, list_to_float(Float)}, S1}
|
412
|
+
end.
|
413
|
+
|
414
|
+
tokenize_number(B, sign, S=#decoder{offset=O}, []) ->
|
415
|
+
case B of
|
416
|
+
<<_:O/binary, $-, _/binary>> ->
|
417
|
+
tokenize_number(B, int, ?INC_COL(S), [$-]);
|
418
|
+
_ ->
|
419
|
+
tokenize_number(B, int, S, [])
|
420
|
+
end;
|
421
|
+
tokenize_number(B, int, S=#decoder{offset=O}, Acc) ->
|
422
|
+
case B of
|
423
|
+
<<_:O/binary, $0, _/binary>> ->
|
424
|
+
tokenize_number(B, frac, ?INC_COL(S), [$0 | Acc]);
|
425
|
+
<<_:O/binary, C, _/binary>> when C >= $1 andalso C =< $9 ->
|
426
|
+
tokenize_number(B, int1, ?INC_COL(S), [C | Acc])
|
427
|
+
end;
|
428
|
+
tokenize_number(B, int1, S=#decoder{offset=O}, Acc) ->
|
429
|
+
case B of
|
430
|
+
<<_:O/binary, C, _/binary>> when C >= $0 andalso C =< $9 ->
|
431
|
+
tokenize_number(B, int1, ?INC_COL(S), [C | Acc]);
|
432
|
+
_ ->
|
433
|
+
tokenize_number(B, frac, S, Acc)
|
434
|
+
end;
|
435
|
+
tokenize_number(B, frac, S=#decoder{offset=O}, Acc) ->
|
436
|
+
case B of
|
437
|
+
<<_:O/binary, $., C, _/binary>> when C >= $0, C =< $9 ->
|
438
|
+
tokenize_number(B, frac1, ?ADV_COL(S, 2), [C, $. | Acc]);
|
439
|
+
<<_:O/binary, E, _/binary>> when E =:= $e orelse E =:= $E ->
|
440
|
+
tokenize_number(B, esign, ?INC_COL(S), [$e, $0, $. | Acc]);
|
441
|
+
_ ->
|
442
|
+
{{int, lists:reverse(Acc)}, S}
|
443
|
+
end;
|
444
|
+
tokenize_number(B, frac1, S=#decoder{offset=O}, Acc) ->
|
445
|
+
case B of
|
446
|
+
<<_:O/binary, C, _/binary>> when C >= $0 andalso C =< $9 ->
|
447
|
+
tokenize_number(B, frac1, ?INC_COL(S), [C | Acc]);
|
448
|
+
<<_:O/binary, E, _/binary>> when E =:= $e orelse E =:= $E ->
|
449
|
+
tokenize_number(B, esign, ?INC_COL(S), [$e | Acc]);
|
450
|
+
_ ->
|
451
|
+
{{float, lists:reverse(Acc)}, S}
|
452
|
+
end;
|
453
|
+
tokenize_number(B, esign, S=#decoder{offset=O}, Acc) ->
|
454
|
+
case B of
|
455
|
+
<<_:O/binary, C, _/binary>> when C =:= $- orelse C=:= $+ ->
|
456
|
+
tokenize_number(B, eint, ?INC_COL(S), [C | Acc]);
|
457
|
+
_ ->
|
458
|
+
tokenize_number(B, eint, S, Acc)
|
459
|
+
end;
|
460
|
+
tokenize_number(B, eint, S=#decoder{offset=O}, Acc) ->
|
461
|
+
case B of
|
462
|
+
<<_:O/binary, C, _/binary>> when C >= $0 andalso C =< $9 ->
|
463
|
+
tokenize_number(B, eint1, ?INC_COL(S), [C | Acc])
|
464
|
+
end;
|
465
|
+
tokenize_number(B, eint1, S=#decoder{offset=O}, Acc) ->
|
466
|
+
case B of
|
467
|
+
<<_:O/binary, C, _/binary>> when C >= $0 andalso C =< $9 ->
|
468
|
+
tokenize_number(B, eint1, ?INC_COL(S), [C | Acc]);
|
469
|
+
_ ->
|
470
|
+
{{float, lists:reverse(Acc)}, S}
|
471
|
+
end.
|
472
|
+
|
473
|
+
tokenize(B, S=#decoder{offset=O}) ->
|
474
|
+
case B of
|
475
|
+
<<_:O/binary, C, _/binary>> when ?IS_WHITESPACE(C) ->
|
476
|
+
tokenize(B, ?INC_CHAR(S, C));
|
477
|
+
<<_:O/binary, "{", _/binary>> ->
|
478
|
+
{start_object, ?INC_COL(S)};
|
479
|
+
<<_:O/binary, "}", _/binary>> ->
|
480
|
+
{end_object, ?INC_COL(S)};
|
481
|
+
<<_:O/binary, "[", _/binary>> ->
|
482
|
+
{start_array, ?INC_COL(S)};
|
483
|
+
<<_:O/binary, "]", _/binary>> ->
|
484
|
+
{end_array, ?INC_COL(S)};
|
485
|
+
<<_:O/binary, ",", _/binary>> ->
|
486
|
+
{comma, ?INC_COL(S)};
|
487
|
+
<<_:O/binary, ":", _/binary>> ->
|
488
|
+
{colon, ?INC_COL(S)};
|
489
|
+
<<_:O/binary, "null", _/binary>> ->
|
490
|
+
{{const, null}, ?ADV_COL(S, 4)};
|
491
|
+
<<_:O/binary, "true", _/binary>> ->
|
492
|
+
{{const, true}, ?ADV_COL(S, 4)};
|
493
|
+
<<_:O/binary, "false", _/binary>> ->
|
494
|
+
{{const, false}, ?ADV_COL(S, 5)};
|
495
|
+
<<_:O/binary, "\"", _/binary>> ->
|
496
|
+
tokenize_string(B, ?INC_COL(S));
|
497
|
+
<<_:O/binary, C, _/binary>> when (C >= $0 andalso C =< $9)
|
498
|
+
orelse C =:= $- ->
|
499
|
+
tokenize_number(B, S);
|
500
|
+
<<_:O/binary>> ->
|
501
|
+
trim = S#decoder.state,
|
502
|
+
{eof, S}
|
503
|
+
end.
|
504
|
+
|
505
|
+
%% testing constructs borrowed from the Yaws JSON implementation.
|
506
|
+
|
507
|
+
%% Create an object from a list of Key/Value pairs.
|
508
|
+
|
509
|
+
obj_new() ->
|
510
|
+
{[]}.
|
511
|
+
|
512
|
+
is_obj({Props}) ->
|
513
|
+
F = fun ({K, _}) when is_binary(K) ->
|
514
|
+
true;
|
515
|
+
(_) ->
|
516
|
+
false
|
517
|
+
end,
|
518
|
+
lists:all(F, Props).
|
519
|
+
|
520
|
+
obj_from_list(Props) ->
|
521
|
+
Obj = {Props},
|
522
|
+
case is_obj(Obj) of
|
523
|
+
true -> Obj;
|
524
|
+
false -> exit({json_bad_object, Obj})
|
525
|
+
end.
|
526
|
+
|
527
|
+
%% Test for equivalence of Erlang terms.
|
528
|
+
%% Due to arbitrary order of construction, equivalent objects might
|
529
|
+
%% compare unequal as erlang terms, so we need to carefully recurse
|
530
|
+
%% through aggregates (tuples and objects).
|
531
|
+
|
532
|
+
equiv({Props1}, {Props2}) ->
|
533
|
+
equiv_object(Props1, Props2);
|
534
|
+
equiv(L1, L2) when is_list(L1), is_list(L2) ->
|
535
|
+
equiv_list(L1, L2);
|
536
|
+
equiv(N1, N2) when is_number(N1), is_number(N2) -> N1 == N2;
|
537
|
+
equiv(B1, B2) when is_binary(B1), is_binary(B2) -> B1 == B2;
|
538
|
+
equiv(true, true) -> true;
|
539
|
+
equiv(false, false) -> true;
|
540
|
+
equiv(null, null) -> true.
|
541
|
+
|
542
|
+
%% Object representation and traversal order is unknown.
|
543
|
+
%% Use the sledgehammer and sort property lists.
|
544
|
+
|
545
|
+
equiv_object(Props1, Props2) ->
|
546
|
+
L1 = lists:keysort(1, Props1),
|
547
|
+
L2 = lists:keysort(1, Props2),
|
548
|
+
Pairs = lists:zip(L1, L2),
|
549
|
+
true = lists:all(fun({{K1, V1}, {K2, V2}}) ->
|
550
|
+
equiv(K1, K2) and equiv(V1, V2)
|
551
|
+
end, Pairs).
|
552
|
+
|
553
|
+
%% Recursively compare tuple elements for equivalence.
|
554
|
+
|
555
|
+
equiv_list([], []) ->
|
556
|
+
true;
|
557
|
+
equiv_list([V1 | L1], [V2 | L2]) ->
|
558
|
+
case equiv(V1, V2) of
|
559
|
+
true ->
|
560
|
+
equiv_list(L1, L2);
|
561
|
+
false ->
|
562
|
+
false
|
563
|
+
end.
|
564
|
+
|
565
|
+
test_all() ->
|
566
|
+
[1199344435545.0, 1] = decode(<<"[1199344435545.0,1]">>),
|
567
|
+
<<16#F0,16#9D,16#9C,16#95>> = decode([34,"\\ud835","\\udf15",34]),
|
568
|
+
test_one(e2j_test_vec(utf8), 1).
|
569
|
+
|
570
|
+
test_one([], _N) ->
|
571
|
+
%% io:format("~p tests passed~n", [N-1]),
|
572
|
+
ok;
|
573
|
+
test_one([{E, J} | Rest], N) ->
|
574
|
+
%% io:format("[~p] ~p ~p~n", [N, E, J]),
|
575
|
+
true = equiv(E, decode(J)),
|
576
|
+
true = equiv(E, decode(encode(E))),
|
577
|
+
test_one(Rest, 1+N).
|
578
|
+
|
579
|
+
e2j_test_vec(utf8) ->
|
580
|
+
[
|
581
|
+
{1, "1"},
|
582
|
+
{3.1416, "3.14160"}, %% text representation may truncate, trail zeroes
|
583
|
+
{-1, "-1"},
|
584
|
+
{-3.1416, "-3.14160"},
|
585
|
+
{12.0e10, "1.20000e+11"},
|
586
|
+
{1.234E+10, "1.23400e+10"},
|
587
|
+
{-1.234E-10, "-1.23400e-10"},
|
588
|
+
{10.0, "1.0e+01"},
|
589
|
+
{123.456, "1.23456E+2"},
|
590
|
+
{10.0, "1e1"},
|
591
|
+
{<<"foo">>, "\"foo\""},
|
592
|
+
{<<"foo", 5, "bar">>, "\"foo\\u0005bar\""},
|
593
|
+
{<<"">>, "\"\""},
|
594
|
+
{<<"\n\n\n">>, "\"\\n\\n\\n\""},
|
595
|
+
{<<"\" \b\f\r\n\t\"">>, "\"\\\" \\b\\f\\r\\n\\t\\\"\""},
|
596
|
+
{obj_new(), "{}"},
|
597
|
+
{obj_from_list([{<<"foo">>, <<"bar">>}]), "{\"foo\":\"bar\"}"},
|
598
|
+
{obj_from_list([{<<"foo">>, <<"bar">>}, {<<"baz">>, 123}]),
|
599
|
+
"{\"foo\":\"bar\",\"baz\":123}"},
|
600
|
+
{[], "[]"},
|
601
|
+
{[[]], "[[]]"},
|
602
|
+
{[1, <<"foo">>], "[1,\"foo\"]"},
|
603
|
+
|
604
|
+
%% json array in a json object
|
605
|
+
{obj_from_list([{<<"foo">>, [123]}]),
|
606
|
+
"{\"foo\":[123]}"},
|
607
|
+
|
608
|
+
%% json object in a json object
|
609
|
+
{obj_from_list([{<<"foo">>, obj_from_list([{<<"bar">>, true}])}]),
|
610
|
+
"{\"foo\":{\"bar\":true}}"},
|
611
|
+
|
612
|
+
%% fold evaluation order
|
613
|
+
{obj_from_list([{<<"foo">>, []},
|
614
|
+
{<<"bar">>, obj_from_list([{<<"baz">>, true}])},
|
615
|
+
{<<"alice">>, <<"bob">>}]),
|
616
|
+
"{\"foo\":[],\"bar\":{\"baz\":true},\"alice\":\"bob\"}"},
|
617
|
+
|
618
|
+
%% json object in a json array
|
619
|
+
{[-123, <<"foo">>, obj_from_list([{<<"bar">>, []}]), null],
|
620
|
+
"[-123,\"foo\",{\"bar\":[]},null]"}
|
621
|
+
].
|