capricorn 2.0.8 → 2.0.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. data/erlang/lib/capricorn/ebin/capricorn.app +2 -1
  2. data/erlang/lib/capricorn/include/capricorn.hrl +1 -0
  3. data/erlang/lib/capricorn/src/cap_cluster_gems.erl +6 -1
  4. data/erlang/lib/capricorn/src/cap_console_dispatcher.erl +214 -0
  5. data/erlang/lib/capricorn/src/cap_machine_apps.erl +25 -1
  6. data/erlang/lib/capricorn/src/cap_sup.erl +14 -0
  7. data/erlang/lib/ejson/Makefile +24 -0
  8. data/erlang/lib/ejson/ebin/ejson.app +9 -0
  9. data/erlang/lib/ejson/include/ejson.hrl +40 -0
  10. data/erlang/lib/ejson/rebar.config +3 -0
  11. data/erlang/lib/ejson/src/ejson.erl +22 -0
  12. data/erlang/lib/ejson/src/ejson_decode.erl +337 -0
  13. data/erlang/lib/ejson/src/ejson_encode.erl +124 -0
  14. data/erlang/lib/ejson/test/arrays.escript +47 -0
  15. data/erlang/lib/ejson/test/compound.escript +56 -0
  16. data/erlang/lib/ejson/test/literals.escript +30 -0
  17. data/erlang/lib/ejson/test/numbers.escript +70 -0
  18. data/erlang/lib/ejson/test/objects.escript +51 -0
  19. data/erlang/lib/ejson/test/strings.escript +49 -0
  20. data/erlang/lib/ejson/test/timing.escript +43 -0
  21. data/erlang/lib/ejson/test/timing.json +382 -0
  22. data/erlang/lib/ejson/vendor/mochijson2.erl +621 -0
  23. data/erlang/lib/ejson/vendor/rfc4627.erl +625 -0
  24. data/erlang/lib/misultin/LICENSE.txt +41 -0
  25. data/erlang/lib/misultin/Makefile +26 -0
  26. data/erlang/lib/misultin/README.txt +120 -0
  27. data/erlang/lib/misultin/ebin/misultin.app +9 -0
  28. data/erlang/lib/misultin/examples/misultin_compress.erl +43 -0
  29. data/erlang/lib/misultin/examples/misultin_echo.erl +58 -0
  30. data/erlang/lib/misultin/examples/misultin_file.erl +43 -0
  31. data/erlang/lib/misultin/examples/misultin_gen_server.erl +158 -0
  32. data/erlang/lib/misultin/examples/misultin_get_variable.erl +55 -0
  33. data/erlang/lib/misultin/examples/misultin_hello_world.erl +43 -0
  34. data/erlang/lib/misultin/examples/misultin_rest.erl +68 -0
  35. data/erlang/lib/misultin/examples/misultin_ssl.erl +51 -0
  36. data/erlang/lib/misultin/examples/misultin_stream.erl +55 -0
  37. data/erlang/lib/misultin/examples/misultin_websocket_event_example.erl +103 -0
  38. data/erlang/lib/misultin/examples/misultin_websocket_example.erl +95 -0
  39. data/erlang/lib/misultin/include/misultin.hrl +95 -0
  40. data/erlang/lib/misultin/make.bat +55 -0
  41. data/erlang/lib/misultin/priv/README.txt +12 -0
  42. data/erlang/lib/misultin/priv/test_certificate.pem +21 -0
  43. data/erlang/lib/misultin/priv/test_privkey.pem +18 -0
  44. data/erlang/lib/misultin/rebar.config +3 -0
  45. data/erlang/lib/misultin/src/misultin.app.src +9 -0
  46. data/erlang/lib/misultin/src/misultin.erl +338 -0
  47. data/erlang/lib/misultin/src/misultin_http.erl +488 -0
  48. data/erlang/lib/misultin/src/misultin_req.erl +280 -0
  49. data/erlang/lib/misultin/src/misultin_socket.erl +193 -0
  50. data/erlang/lib/misultin/src/misultin_utility.erl +357 -0
  51. data/erlang/lib/misultin/src/misultin_websocket.erl +252 -0
  52. data/erlang/lib/misultin/src/misultin_ws.erl +78 -0
  53. data/erlang/rebar.config +2 -0
  54. data/erlang/rel/overlay/etc/capricorn/app.config +4 -0
  55. data/erlang/rel/reltool.config +5 -0
  56. data/lib/capricorn/recipes/apache-debian.rb +1 -1
  57. data/lib/capricorn/recipes/centos-plesk.rb +1 -1
  58. data/lib/capricorn/recipes/debian-plesk95.rb +1 -2
  59. data/lib/capricorn/recipes/macports.rb +1 -1
  60. data/lib/capricorn/version.rb +1 -1
  61. 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
+ ].