Skip to content

Commit

Permalink
Merge branch 'master' into rebarify-15688
Browse files Browse the repository at this point in the history
Conflicts:
	.gitignore
	README
	support/include.mk
	support/make_app.escript
  • Loading branch information
David Reid committed Nov 11, 2010
2 parents 9995724 + 9b8d6fe commit db6c339
Show file tree
Hide file tree
Showing 12 changed files with 647 additions and 94 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
/doc
/_test
.eunit
/docs
12 changes: 9 additions & 3 deletions README
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
MochiWeb is an Erlang library for building lightweight HTTP servers.

MochiWeb uses rebar templates to make getting started easy.
The latest version of MochiWeb is available at http://github.com/mochi/mochiweb

To create a new app use:
R12B compatibility:
The master of MochiWeb is tested with R13B04 and later. A branch compatible
with R12B is maintained separately at http://github.com/lemenkov/mochiweb
The R12B branch of that repository is mirrored in the official repository
occasionally for convenience.

To create a new mochiweb using project:
make app APPID=project_name

To create a new app in a specific directory:
To create a new mochiweb using project in a specific directory:
make app APPID=project_name PREFIX=$HOME/projects/
73 changes: 68 additions & 5 deletions src/mochijson2.erl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,38 @@
%% @doc Yet another JSON (RFC 4627) library for Erlang. mochijson2 works
%% with binaries as strings, arrays as lists (without an {array, _})
%% wrapper and it only knows how to decode UTF-8 (and ASCII).
%%
%% JSON terms are decoded as follows (javascript -> erlang):
%% <ul>
%% <li>{"key": "value"} ->
%% {struct, [{&lt;&lt;"key">>, &lt;&lt;"value">>}]}</li>
%% <li>["array", 123, 12.34, true, false, null] ->
%% [&lt;&lt;"array">>, 123, 12.34, true, false, null]
%% </li>
%% </ul>
%% <ul>
%% <li>Strings in JSON decode to UTF-8 binaries in Erlang</li>
%% <li>Objects decode to {struct, PropList}</li>
%% <li>Numbers decode to integer or float</li>
%% <li>true, false, null decode to their respective terms.</li>
%% </ul>
%% The encoder will accept the same format that the decoder will produce,
%% but will also allow additional cases for leniency:
%% <ul>
%% <li>atoms other than true, false, null will be considered UTF-8
%% strings (even as a proplist key)
%% </li>
%% <li>{json, IoList} will insert IoList directly into the output
%% with no validation
%% </li>
%% <li>{array, Array} will be encoded as Array
%% (legacy mochijson style)
%% </li>
%% <li>A non-empty raw proplist will be encoded as an object as long
%% as the first pair does not have an atom key of json, struct,
%% or array
%% </li>
%% </ul>

-module(mochijson2).
-author('bob@mochimedia.com').
Expand Down Expand Up @@ -101,10 +133,16 @@ json_encode(F, _State) when is_float(F) ->
mochinum:digits(F);
json_encode(S, State) when is_binary(S); is_atom(S) ->
json_encode_string(S, State);
json_encode(Array, State) when is_list(Array) ->
json_encode_array(Array, State);
json_encode([{K, _}|_] = Props, State) when (K =/= struct andalso
K =/= array andalso
K =/= json) ->
json_encode_proplist(Props, State);
json_encode({struct, Props}, State) when is_list(Props) ->
json_encode_proplist(Props, State);
json_encode(Array, State) when is_list(Array) ->
json_encode_array(Array, State);
json_encode({array, Array}, State) when is_list(Array) ->
json_encode_array(Array, State);
json_encode({json, IoList}, _State) ->
IoList;
json_encode(Bad, #encoder{handler=null}) ->
Expand Down Expand Up @@ -402,8 +440,22 @@ tokenize_string(B, S=#decoder{offset=O}, Acc) ->
Acc1 = lists:reverse(xmerl_ucs:to_utf8(C), Acc),
tokenize_string(B, ?ADV_COL(S, 6), Acc1)
end;
<<_:O/binary, C, _/binary>> ->
tokenize_string(B, ?INC_CHAR(S, C), [C | Acc])
<<_:O/binary, C1, _/binary>> when C1 < 128 ->
tokenize_string(B, ?INC_CHAR(S, C1), [C1 | Acc]);
<<_:O/binary, C1, C2, _/binary>> when C1 >= 194, C1 =< 223,
C2 >= 128, C2 =< 191 ->
tokenize_string(B, ?ADV_COL(S, 2), [C2, C1 | Acc]);
<<_:O/binary, C1, C2, C3, _/binary>> when C1 >= 224, C1 =< 239,
C2 >= 128, C2 =< 191,
C3 >= 128, C3 =< 191 ->
tokenize_string(B, ?ADV_COL(S, 3), [C3, C2, C1 | Acc]);
<<_:O/binary, C1, C2, C3, C4, _/binary>> when C1 >= 240, C1 =< 244,
C2 >= 128, C2 =< 191,
C3 >= 128, C3 =< 191,
C4 >= 128, C4 =< 191 ->
tokenize_string(B, ?ADV_COL(S, 4), [C4, C3, C2, C1 | Acc]);
_ ->
throw(invalid_utf8)
end.

tokenize_number(B, S) ->
Expand Down Expand Up @@ -648,7 +700,9 @@ input_validation_test() ->
<<?Q, 16#E0, 16#80,16#7F, ?Q>>,
<<?Q, 16#F0, 16#80, 16#80, 16#7F, ?Q>>,
%% we don't support code points > 10FFFF per RFC 3629
<<?Q, 16#F5, 16#80, 16#80, 16#80, ?Q>>
<<?Q, 16#F5, 16#80, 16#80, 16#80, ?Q>>,
%% escape characters trigger a different code path
<<?Q, $\\, $\n, 16#80, ?Q>>
],
lists:foreach(
fun(X) ->
Expand Down Expand Up @@ -716,6 +770,15 @@ key_encode_test() ->
?assertEqual(
<<"{\"foo\":1}">>,
iolist_to_binary(encode({struct, [{"foo", 1}]}))),
?assertEqual(
<<"{\"foo\":1}">>,
iolist_to_binary(encode([{foo, 1}]))),
?assertEqual(
<<"{\"foo\":1}">>,
iolist_to_binary(encode([{<<"foo">>, 1}]))),
?assertEqual(
<<"{\"foo\":1}">>,
iolist_to_binary(encode([{"foo", 1}]))),
?assertEqual(
<<"{\"\\ud834\\udd20\":1}">>,
iolist_to_binary(
Expand Down
77 changes: 50 additions & 27 deletions src/mochinum.erl
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,10 @@ digits(N) when is_integer(N) ->
digits(0.0) ->
"0.0";
digits(Float) ->
{Frac, Exp} = frexp(Float),
Exp1 = Exp - 53,
Frac1 = trunc(abs(Frac) * (1 bsl 53)),
[Place | Digits] = digits1(Float, Exp1, Frac1),
R = insert_decimal(Place, [$0 + D || D <- Digits]),
{Frac1, Exp1} = frexp_int(Float),
[Place0 | Digits0] = digits1(Float, Exp1, Frac1),
{Place, Digits} = transform_digits(Place0, Digits0),
R = insert_decimal(Place, Digits),
case Float < 0 of
true ->
[$- | R];
Expand Down Expand Up @@ -64,7 +63,6 @@ int_pow(X, N) when N > 0 ->
int_ceil(X) ->
T = trunc(X),
case (X - T) of
Neg when Neg < 0 -> T;
Pos when Pos > 0 -> T + 1;
_ -> T
end.
Expand Down Expand Up @@ -228,28 +226,42 @@ log2floor(Int, N) ->
log2floor(Int bsr 1, 1 + N).


transform_digits(Place, [0 | Rest]) ->
transform_digits(Place, Rest);
transform_digits(Place, Digits) ->
{Place, [$0 + D || D <- Digits]}.


frexp_int(F) ->
case unpack(F) of
{_Sign, 0, Frac} ->
{Frac, ?MIN_EXP};
{_Sign, Exp, Frac} ->
{Frac + (1 bsl 52), Exp - 53 - ?FLOAT_BIAS}
end.

%%
%% Tests
%%
-include_lib("eunit/include/eunit.hrl").
-ifdef(TEST).

int_ceil_test() ->
1 = int_ceil(0.0001),
0 = int_ceil(0.0),
1 = int_ceil(0.99),
1 = int_ceil(1.0),
-1 = int_ceil(-1.5),
-2 = int_ceil(-2.0),
?assertEqual(1, int_ceil(0.0001)),
?assertEqual(0, int_ceil(0.0)),
?assertEqual(1, int_ceil(0.99)),
?assertEqual(1, int_ceil(1.0)),
?assertEqual(-1, int_ceil(-1.5)),
?assertEqual(-2, int_ceil(-2.0)),
ok.

int_pow_test() ->
1 = int_pow(1, 1),
1 = int_pow(1, 0),
1 = int_pow(10, 0),
10 = int_pow(10, 1),
100 = int_pow(10, 2),
1000 = int_pow(10, 3),
?assertEqual(1, int_pow(1, 1)),
?assertEqual(1, int_pow(1, 0)),
?assertEqual(1, int_pow(10, 0)),
?assertEqual(10, int_pow(10, 1)),
?assertEqual(100, int_pow(10, 2)),
?assertEqual(1000, int_pow(10, 3)),
ok.

digits_test() ->
Expand All @@ -274,9 +286,9 @@ digits_test() ->
?assertEqual("4503599627370496.0",
digits(4503599627370496.0)),
%% small denormalized number
%% 4.94065645841246544177e-324
%% 4.94065645841246544177e-324 =:= 5.0e-324
<<SmallDenorm/float>> = <<0,0,0,0,0,0,0,1>>,
?assertEqual("4.9406564584124654e-324",
?assertEqual("5.0e-324",
digits(SmallDenorm)),
?assertEqual(SmallDenorm,
list_to_float(digits(SmallDenorm))),
Expand All @@ -301,31 +313,42 @@ digits_test() ->
digits(LargeNorm)),
?assertEqual(LargeNorm,
list_to_float(digits(LargeNorm))),
%% issue #10 - mochinum:frexp(math:pow(2, -1074)).
?assertEqual("5.0e-324",
digits(math:pow(2, -1074))),
ok.

frexp_test() ->
%% zero
{0.0, 0} = frexp(0.0),
?assertEqual({0.0, 0}, frexp(0.0)),
%% one
{0.5, 1} = frexp(1.0),
?assertEqual({0.5, 1}, frexp(1.0)),
%% negative one
{-0.5, 1} = frexp(-1.0),
?assertEqual({-0.5, 1}, frexp(-1.0)),
%% small denormalized number
%% 4.94065645841246544177e-324
<<SmallDenorm/float>> = <<0,0,0,0,0,0,0,1>>,
{0.5, -1073} = frexp(SmallDenorm),
?assertEqual({0.5, -1073}, frexp(SmallDenorm)),
%% large denormalized number
%% 2.22507385850720088902e-308
<<BigDenorm/float>> = <<0,15,255,255,255,255,255,255>>,
{0.99999999999999978, -1022} = frexp(BigDenorm),
?assertEqual(
{0.99999999999999978, -1022},
frexp(BigDenorm)),
%% small normalized number
%% 2.22507385850720138309e-308
<<SmallNorm/float>> = <<0,16,0,0,0,0,0,0>>,
{0.5, -1021} = frexp(SmallNorm),
?assertEqual({0.5, -1021}, frexp(SmallNorm)),
%% large normalized number
%% 1.79769313486231570815e+308
<<LargeNorm/float>> = <<127,239,255,255,255,255,255,255>>,
{0.99999999999999989, 1024} = frexp(LargeNorm),
?assertEqual(
{0.99999999999999989, 1024},
frexp(LargeNorm)),
%% issue #10 - mochinum:frexp(math:pow(2, -1074)).
?assertEqual(
{0.5, -1073},
frexp(math:pow(2, -1074))),
ok.

-endif.
2 changes: 1 addition & 1 deletion src/mochiweb.app.src
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
%% This is generated from src/mochiweb.app.src
{application, mochiweb,
[{description, "MochiMedia Web Server"},
{vsn, "1.3"},
{vsn, "1.4.0"},
{modules, []},
{registered, []},
{mod, {mochiweb_app, []}},
Expand Down
2 changes: 1 addition & 1 deletion src/mochiweb_acceptor.erl
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ init(Server, Listen, Loop) ->
{error, closed} ->
exit(normal);
{error, timeout} ->
exit(normal);
init(Server, Listen, Loop);
{error, esslaccept} ->
exit(normal);
Other ->
Expand Down
Loading

0 comments on commit db6c339

Please sign in to comment.