capricorn 2.0.8 → 2.0.9

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.
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
+ ].