You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by rn...@apache.org on 2013/12/24 00:31:19 UTC
[10/12] Replace ejson with jiffy
http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/ejson/src/ejson.erl
----------------------------------------------------------------------
diff --git a/src/ejson/src/ejson.erl b/src/ejson/src/ejson.erl
deleted file mode 100644
index 72bb6c1..0000000
--- a/src/ejson/src/ejson.erl
+++ /dev/null
@@ -1,168 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-% http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--module(ejson).
--export([encode/1, decode/1]).
--on_load(init/0).
-
-init() ->
- SoName = case code:priv_dir(ejson) of
- {error, bad_name} ->
- case filelib:is_dir(filename:join(["..", priv])) of
- true ->
- filename:join(["..", priv, ejson]);
- false ->
- filename:join([priv, ejson])
- end;
- Dir ->
- filename:join(Dir, ejson)
- end,
- (catch erlang:load_nif(SoName, 0)),
- case erlang:system_info(otp_release) of
- "R13B03" -> true;
- _ -> ok
- end.
-
-
-decode(undefined) ->
- throw({invalid_json, undefined});
-decode(IoList) ->
- try
- nif_decode(IoList)
- catch exit:ejson_nif_not_loaded ->
- erl_decode(IoList)
- end.
-
-encode(EJson) ->
- try
- nif_encode(EJson)
- catch exit:ejson_nif_not_loaded ->
- erl_encode(EJson)
- end.
-
-
-nif_decode(IoList) ->
- case reverse_tokens(IoList) of
- {ok, ReverseTokens} ->
- [[EJson]] = make_ejson(ReverseTokens, [[]]),
- EJson;
- Error ->
- throw({invalid_json, {Error, IoList}})
- end.
-
-
-erl_decode(IoList) ->
- try
- (mochijson2:decoder([{object_hook, fun({struct, L}) -> {L} end}]))(IoList)
- catch _Type:Error ->
- throw({invalid_json, {Error, IoList}})
- end.
-
-
-nif_encode(EJson) ->
- RevList = encode_rev(EJson),
- final_encode(lists:reverse(lists:flatten([RevList]))).
-
-
-erl_encode(EJson) ->
- Opts = [{handler, fun mochi_encode_handler/1}],
- iolist_to_binary((mochijson2:encoder(Opts))(EJson)).
-
-mochi_encode_handler({L}) when is_list(L) ->
- {struct, L};
-mochi_encode_handler(Bad) ->
- exit({json_encode, {bad_term, Bad}}).
-
-
-% Encode the json into a reverse list that's almost an iolist
-% everything in the list is the final output except for tuples with
-% {0, Strings} and {1, Floats}, which are to be converted to strings
-% inside the NIF.
-encode_rev(true) ->
- <<"true">>;
-encode_rev(false) ->
- <<"false">>;
-encode_rev(null) ->
- <<"null">>;
-encode_rev(I) when is_integer(I) ->
- list_to_binary(integer_to_list(I));
-encode_rev(S) when is_binary(S) ->
- {0, S};
-encode_rev(S) when is_atom(S) ->
- {0, list_to_binary(atom_to_list(S))};
-encode_rev(F) when is_float(F) ->
- {1, F};
-encode_rev({Props}) when is_list(Props) ->
- encode_proplist_rev(Props, [<<"{">>]);
-encode_rev(Array) when is_list(Array) ->
- encode_array_rev(Array, [<<"[">>]);
-encode_rev(Bad) ->
- throw({json_encode, {bad_term, Bad}}).
-
-
-encode_array_rev([], Acc) ->
- [<<"]">> | Acc];
-encode_array_rev([Val | Rest], [<<"[">>]) ->
- encode_array_rev(Rest, [encode_rev(Val), <<"[">>]);
-encode_array_rev([Val | Rest], Acc) ->
- encode_array_rev(Rest, [encode_rev(Val), <<",">> | Acc]).
-
-
-encode_proplist_rev([], Acc) ->
- [<<"}">> | Acc];
-encode_proplist_rev([{Key,Val} | Rest], [<<"{">>]) ->
- encode_proplist_rev(
- Rest, [encode_rev(Val), <<":">>, {0, as_binary(Key)}, <<"{">>]);
-encode_proplist_rev([{Key,Val} | Rest], Acc) ->
- encode_proplist_rev(
- Rest, [encode_rev(Val), <<":">>, {0, as_binary(Key)}, <<",">> | Acc]).
-
-as_binary(B) when is_binary(B) ->
- B;
-as_binary(A) when is_atom(A) ->
- list_to_binary(atom_to_list(A));
-as_binary(L) when is_list(L) ->
- list_to_binary(L).
-
-
-make_ejson([], Stack) ->
- Stack;
-make_ejson([0 | RevEvs], [ArrayValues, PrevValues | RestStack]) ->
- % 0 ArrayStart
- make_ejson(RevEvs, [[ArrayValues | PrevValues] | RestStack]);
-make_ejson([1 | RevEvs], Stack) ->
- % 1 ArrayEnd
- make_ejson(RevEvs, [[] | Stack]);
-make_ejson([2 | RevEvs], [ObjValues, PrevValues | RestStack]) ->
- % 2 ObjectStart
- make_ejson(RevEvs, [[{ObjValues} | PrevValues] | RestStack]);
-make_ejson([3 | RevEvs], Stack) ->
- % 3 ObjectEnd
- make_ejson(RevEvs, [[] | Stack]);
-make_ejson([{0, Value} | RevEvs], [Vals | RestStack] = _Stack) ->
- % {0, IntegerString}
- make_ejson(RevEvs, [[list_to_integer(binary_to_list(Value)) | Vals] | RestStack]);
-make_ejson([{1, Value} | RevEvs], [Vals | RestStack] = _Stack) ->
- % {1, FloatString}
- make_ejson(RevEvs, [[list_to_float(binary_to_list(Value)) | Vals] | RestStack]);
-make_ejson([{3, String} | RevEvs], [[PrevValue|RestObject] | RestStack] = _Stack) ->
- % {3 , ObjectKey}
- make_ejson(RevEvs, [[{String, PrevValue}|RestObject] | RestStack]);
-make_ejson([Value | RevEvs], [Vals | RestStack] = _Stack) ->
- make_ejson(RevEvs, [[Value | Vals] | RestStack]).
-
-
-reverse_tokens(_) ->
- exit(ejson_nif_not_loaded).
-
-final_encode(_) ->
- exit(ejson_nif_not_loaded).
http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/ejson/src/mochijson2.erl
----------------------------------------------------------------------
diff --git a/src/ejson/src/mochijson2.erl b/src/ejson/src/mochijson2.erl
deleted file mode 100644
index 954a07d..0000000
--- a/src/ejson/src/mochijson2.erl
+++ /dev/null
@@ -1,849 +0,0 @@
-%% @author Bob Ippolito <bo...@mochimedia.com>
-%% @copyright 2007 Mochi Media, Inc.
-
-%% @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, [{<<"key">>, <<"value">>}]}</li>
-%% <li>["array", 123, 12.34, true, false, null] ->
-%% [<<"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').
--export([encoder/1, encode/1]).
--export([decoder/1, decode/1]).
-
-% This is a macro to placate syntax highlighters..
--define(Q, $\").
--define(ADV_COL(S, N), S#decoder{offset=N+S#decoder.offset,
- column=N+S#decoder.column}).
--define(INC_COL(S), S#decoder{offset=1+S#decoder.offset,
- column=1+S#decoder.column}).
--define(INC_LINE(S), S#decoder{offset=1+S#decoder.offset,
- column=1,
- line=1+S#decoder.line}).
--define(INC_CHAR(S, C),
- case C of
- $\n ->
- S#decoder{column=1,
- line=1+S#decoder.line,
- offset=1+S#decoder.offset};
- _ ->
- S#decoder{column=1+S#decoder.column,
- offset=1+S#decoder.offset}
- end).
--define(IS_WHITESPACE(C),
- (C =:= $\s orelse C =:= $\t orelse C =:= $\r orelse C =:= $\n)).
-
-%% @type iolist() = [char() | binary() | iolist()]
-%% @type iodata() = iolist() | binary()
-%% @type json_string() = atom | binary()
-%% @type json_number() = integer() | float()
-%% @type json_array() = [json_term()]
-%% @type json_object() = {struct, [{json_string(), json_term()}]}
-%% @type json_iolist() = {json, iolist()}
-%% @type json_term() = json_string() | json_number() | json_array() |
-%% json_object() | json_iolist()
-
--record(encoder, {handler=null,
- utf8=false}).
-
--record(decoder, {object_hook=null,
- offset=0,
- line=1,
- column=1,
- state=null}).
-
-%% @spec encoder([encoder_option()]) -> function()
-%% @doc Create an encoder/1 with the given options.
-%% @type encoder_option() = handler_option() | utf8_option()
-%% @type utf8_option() = boolean(). Emit unicode as utf8 (default - false)
-encoder(Options) ->
- State = parse_encoder_options(Options, #encoder{}),
- fun (O) -> json_encode(O, State) end.
-
-%% @spec encode(json_term()) -> iolist()
-%% @doc Encode the given as JSON to an iolist.
-encode(Any) ->
- json_encode(Any, #encoder{}).
-
-%% @spec decoder([decoder_option()]) -> function()
-%% @doc Create a decoder/1 with the given options.
-decoder(Options) ->
- State = parse_decoder_options(Options, #decoder{}),
- fun (O) -> json_decode(O, State) end.
-
-%% @spec decode(iolist()) -> json_term()
-%% @doc Decode the given iolist to Erlang terms.
-decode(S) ->
- json_decode(S, #decoder{}).
-
-%% Internal API
-
-parse_encoder_options([], State) ->
- State;
-parse_encoder_options([{handler, Handler} | Rest], State) ->
- parse_encoder_options(Rest, State#encoder{handler=Handler});
-parse_encoder_options([{utf8, Switch} | Rest], State) ->
- parse_encoder_options(Rest, State#encoder{utf8=Switch}).
-
-parse_decoder_options([], State) ->
- State;
-parse_decoder_options([{object_hook, Hook} | Rest], State) ->
- parse_decoder_options(Rest, State#decoder{object_hook=Hook}).
-
-json_encode(true, _State) ->
- <<"true">>;
-json_encode(false, _State) ->
- <<"false">>;
-json_encode(null, _State) ->
- <<"null">>;
-json_encode(I, _State) when is_integer(I) ->
- integer_to_list(I);
-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([{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}) ->
- exit({json_encode, {bad_term, Bad}});
-json_encode(Bad, State=#encoder{handler=Handler}) ->
- json_encode(Handler(Bad), State).
-
-json_encode_array([], _State) ->
- <<"[]">>;
-json_encode_array(L, State) ->
- F = fun (O, Acc) ->
- [$,, json_encode(O, State) | Acc]
- end,
- [$, | Acc1] = lists:foldl(F, "[", L),
- lists:reverse([$\] | Acc1]).
-
-json_encode_proplist([], _State) ->
- <<"{}">>;
-json_encode_proplist(Props, State) ->
- F = fun ({K, V}, Acc) ->
- KS = json_encode_string(K, State),
- VS = json_encode(V, State),
- [$,, VS, $:, KS | Acc]
- end,
- [$, | Acc1] = lists:foldl(F, "{", Props),
- lists:reverse([$\} | Acc1]).
-
-json_encode_string(A, State) when is_atom(A) ->
- L = atom_to_list(A),
- case json_string_is_safe(L) of
- true ->
- [?Q, L, ?Q];
- false ->
- json_encode_string_unicode(xmerl_ucs:from_utf8(L), State, [?Q])
- end;
-json_encode_string(B, State) when is_binary(B) ->
- case json_bin_is_safe(B) of
- true ->
- [?Q, B, ?Q];
- false ->
- json_encode_string_unicode(xmerl_ucs:from_utf8(B), State, [?Q])
- end;
-json_encode_string(I, _State) when is_integer(I) ->
- [?Q, integer_to_list(I), ?Q];
-json_encode_string(L, State) when is_list(L) ->
- case json_string_is_safe(L) of
- true ->
- [?Q, L, ?Q];
- false ->
- json_encode_string_unicode(L, State, [?Q])
- end.
-
-json_string_is_safe([]) ->
- true;
-json_string_is_safe([C | Rest]) ->
- case C of
- ?Q ->
- false;
- $\\ ->
- false;
- $\b ->
- false;
- $\f ->
- false;
- $\n ->
- false;
- $\r ->
- false;
- $\t ->
- false;
- C when C >= 0, C < $\s; C >= 16#7f, C =< 16#10FFFF ->
- false;
- C when C < 16#7f ->
- json_string_is_safe(Rest);
- _ ->
- false
- end.
-
-json_bin_is_safe(<<>>) ->
- true;
-json_bin_is_safe(<<C, Rest/binary>>) ->
- case C of
- ?Q ->
- false;
- $\\ ->
- false;
- $\b ->
- false;
- $\f ->
- false;
- $\n ->
- false;
- $\r ->
- false;
- $\t ->
- false;
- C when C >= 0, C < $\s; C >= 16#7f ->
- false;
- C when C < 16#7f ->
- json_bin_is_safe(Rest)
- end.
-
-json_encode_string_unicode([], _State, Acc) ->
- lists:reverse([$\" | Acc]);
-json_encode_string_unicode([C | Cs], State, Acc) ->
- Acc1 = case C of
- ?Q ->
- [?Q, $\\ | Acc];
- %% Escaping solidus is only useful when trying to protect
- %% against "</script>" injection attacks which are only
- %% possible when JSON is inserted into a HTML document
- %% in-line. mochijson2 does not protect you from this, so
- %% if you do insert directly into HTML then you need to
- %% uncomment the following case or escape the output of encode.
- %%
- %% $/ ->
- %% [$/, $\\ | Acc];
- %%
- $\\ ->
- [$\\, $\\ | Acc];
- $\b ->
- [$b, $\\ | Acc];
- $\f ->
- [$f, $\\ | Acc];
- $\n ->
- [$n, $\\ | Acc];
- $\r ->
- [$r, $\\ | Acc];
- $\t ->
- [$t, $\\ | Acc];
- C when C >= 0, C < $\s ->
- [unihex(C) | Acc];
- C when C >= 16#7f, C =< 16#10FFFF, State#encoder.utf8 ->
- [xmerl_ucs:to_utf8(C) | Acc];
- C when C >= 16#7f, C =< 16#10FFFF, not State#encoder.utf8 ->
- [unihex(C) | Acc];
- C when C < 16#7f ->
- [C | Acc];
- _ ->
- exit({json_encode, {bad_char, C}})
- end,
- json_encode_string_unicode(Cs, State, Acc1).
-
-hexdigit(C) when C >= 0, C =< 9 ->
- C + $0;
-hexdigit(C) when C =< 15 ->
- C + $a - 10.
-
-unihex(C) when C < 16#10000 ->
- <<D3:4, D2:4, D1:4, D0:4>> = <<C:16>>,
- Digits = [hexdigit(D) || D <- [D3, D2, D1, D0]],
- [$\\, $u | Digits];
-unihex(C) when C =< 16#10FFFF ->
- N = C - 16#10000,
- S1 = 16#d800 bor ((N bsr 10) band 16#3ff),
- S2 = 16#dc00 bor (N band 16#3ff),
- [unihex(S1), unihex(S2)].
-
-json_decode(L, S) when is_list(L) ->
- json_decode(iolist_to_binary(L), S);
-json_decode(B, S) ->
- {Res, S1} = decode1(B, S),
- {eof, _} = tokenize(B, S1#decoder{state=trim}),
- Res.
-
-decode1(B, S=#decoder{state=null}) ->
- case tokenize(B, S#decoder{state=any}) of
- {{const, C}, S1} ->
- {C, S1};
- {start_array, S1} ->
- decode_array(B, S1);
- {start_object, S1} ->
- decode_object(B, S1)
- end.
-
-make_object(V, #decoder{object_hook=null}) ->
- V;
-make_object(V, #decoder{object_hook=Hook}) ->
- Hook(V).
-
-decode_object(B, S) ->
- decode_object(B, S#decoder{state=key}, []).
-
-decode_object(B, S=#decoder{state=key}, Acc) ->
- case tokenize(B, S) of
- {end_object, S1} ->
- V = make_object({struct, lists:reverse(Acc)}, S1),
- {V, S1#decoder{state=null}};
- {{const, K}, S1} ->
- {colon, S2} = tokenize(B, S1),
- {V, S3} = decode1(B, S2#decoder{state=null}),
- decode_object(B, S3#decoder{state=comma}, [{K, V} | Acc])
- end;
-decode_object(B, S=#decoder{state=comma}, Acc) ->
- case tokenize(B, S) of
- {end_object, S1} ->
- V = make_object({struct, lists:reverse(Acc)}, S1),
- {V, S1#decoder{state=null}};
- {comma, S1} ->
- decode_object(B, S1#decoder{state=key}, Acc)
- end.
-
-decode_array(B, S) ->
- decode_array(B, S#decoder{state=any}, []).
-
-decode_array(B, S=#decoder{state=any}, Acc) ->
- case tokenize(B, S) of
- {end_array, S1} ->
- {lists:reverse(Acc), S1#decoder{state=null}};
- {start_array, S1} ->
- {Array, S2} = decode_array(B, S1),
- decode_array(B, S2#decoder{state=comma}, [Array | Acc]);
- {start_object, S1} ->
- {Array, S2} = decode_object(B, S1),
- decode_array(B, S2#decoder{state=comma}, [Array | Acc]);
- {{const, Const}, S1} ->
- decode_array(B, S1#decoder{state=comma}, [Const | Acc])
- end;
-decode_array(B, S=#decoder{state=comma}, Acc) ->
- case tokenize(B, S) of
- {end_array, S1} ->
- {lists:reverse(Acc), S1#decoder{state=null}};
- {comma, S1} ->
- decode_array(B, S1#decoder{state=any}, Acc)
- end.
-
-tokenize_string(B, S=#decoder{offset=O}) ->
- case tokenize_string_fast(B, O) of
- {escape, O1} ->
- Length = O1 - O,
- S1 = ?ADV_COL(S, Length),
- <<_:O/binary, Head:Length/binary, _/binary>> = B,
- tokenize_string(B, S1, lists:reverse(binary_to_list(Head)));
- O1 ->
- Length = O1 - O,
- <<_:O/binary, String:Length/binary, ?Q, _/binary>> = B,
- {{const, String}, ?ADV_COL(S, Length + 1)}
- end.
-
-tokenize_string_fast(B, O) ->
- case B of
- <<_:O/binary, ?Q, _/binary>> ->
- O;
- <<_:O/binary, $\\, _/binary>> ->
- {escape, O};
- <<_:O/binary, C1, _/binary>> when C1 < 128 ->
- tokenize_string_fast(B, 1 + O);
- <<_:O/binary, C1, C2, _/binary>> when C1 >= 194, C1 =< 223,
- C2 >= 128, C2 =< 191 ->
- tokenize_string_fast(B, 2 + O);
- <<_:O/binary, C1, C2, C3, _/binary>> when C1 >= 224, C1 =< 239,
- C2 >= 128, C2 =< 191,
- C3 >= 128, C3 =< 191 ->
- tokenize_string_fast(B, 3 + O);
- <<_: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_fast(B, 4 + O);
- _ ->
- throw(invalid_utf8)
- end.
-
-tokenize_string(B, S=#decoder{offset=O}, Acc) ->
- case B of
- <<_:O/binary, ?Q, _/binary>> ->
- {{const, iolist_to_binary(lists:reverse(Acc))}, ?INC_COL(S)};
- <<_:O/binary, "\\\"", _/binary>> ->
- tokenize_string(B, ?ADV_COL(S, 2), [$\" | Acc]);
- <<_:O/binary, "\\\\", _/binary>> ->
- tokenize_string(B, ?ADV_COL(S, 2), [$\\ | Acc]);
- <<_:O/binary, "\\/", _/binary>> ->
- tokenize_string(B, ?ADV_COL(S, 2), [$/ | Acc]);
- <<_:O/binary, "\\b", _/binary>> ->
- tokenize_string(B, ?ADV_COL(S, 2), [$\b | Acc]);
- <<_:O/binary, "\\f", _/binary>> ->
- tokenize_string(B, ?ADV_COL(S, 2), [$\f | Acc]);
- <<_:O/binary, "\\n", _/binary>> ->
- tokenize_string(B, ?ADV_COL(S, 2), [$\n | Acc]);
- <<_:O/binary, "\\r", _/binary>> ->
- tokenize_string(B, ?ADV_COL(S, 2), [$\r | Acc]);
- <<_:O/binary, "\\t", _/binary>> ->
- tokenize_string(B, ?ADV_COL(S, 2), [$\t | Acc]);
- <<_:O/binary, "\\u", C3, C2, C1, C0, Rest/binary>> ->
- C = erlang:list_to_integer([C3, C2, C1, C0], 16),
- if C > 16#D7FF, C < 16#DC00 ->
- %% coalesce UTF-16 surrogate pair
- <<"\\u", D3, D2, D1, D0, _/binary>> = Rest,
- D = erlang:list_to_integer([D3,D2,D1,D0], 16),
- [CodePoint] = xmerl_ucs:from_utf16be(<<C:16/big-unsigned-integer,
- D:16/big-unsigned-integer>>),
- Acc1 = lists:reverse(xmerl_ucs:to_utf8(CodePoint), Acc),
- tokenize_string(B, ?ADV_COL(S, 12), Acc1);
- true ->
- Acc1 = lists:reverse(xmerl_ucs:to_utf8(C), Acc),
- tokenize_string(B, ?ADV_COL(S, 6), Acc1)
- end;
- <<_: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) ->
- case tokenize_number(B, sign, S, []) of
- {{int, Int}, S1} ->
- {{const, list_to_integer(Int)}, S1};
- {{float, Float}, S1} ->
- {{const, list_to_float(Float)}, S1}
- end.
-
-tokenize_number(B, sign, S=#decoder{offset=O}, []) ->
- case B of
- <<_:O/binary, $-, _/binary>> ->
- tokenize_number(B, int, ?INC_COL(S), [$-]);
- _ ->
- tokenize_number(B, int, S, [])
- end;
-tokenize_number(B, int, S=#decoder{offset=O}, Acc) ->
- case B of
- <<_:O/binary, $0, _/binary>> ->
- tokenize_number(B, frac, ?INC_COL(S), [$0 | Acc]);
- <<_:O/binary, C, _/binary>> when C >= $1 andalso C =< $9 ->
- tokenize_number(B, int1, ?INC_COL(S), [C | Acc])
- end;
-tokenize_number(B, int1, S=#decoder{offset=O}, Acc) ->
- case B of
- <<_:O/binary, C, _/binary>> when C >= $0 andalso C =< $9 ->
- tokenize_number(B, int1, ?INC_COL(S), [C | Acc]);
- _ ->
- tokenize_number(B, frac, S, Acc)
- end;
-tokenize_number(B, frac, S=#decoder{offset=O}, Acc) ->
- case B of
- <<_:O/binary, $., C, _/binary>> when C >= $0, C =< $9 ->
- tokenize_number(B, frac1, ?ADV_COL(S, 2), [C, $. | Acc]);
- <<_:O/binary, E, _/binary>> when E =:= $e orelse E =:= $E ->
- tokenize_number(B, esign, ?INC_COL(S), [$e, $0, $. | Acc]);
- _ ->
- {{int, lists:reverse(Acc)}, S}
- end;
-tokenize_number(B, frac1, S=#decoder{offset=O}, Acc) ->
- case B of
- <<_:O/binary, C, _/binary>> when C >= $0 andalso C =< $9 ->
- tokenize_number(B, frac1, ?INC_COL(S), [C | Acc]);
- <<_:O/binary, E, _/binary>> when E =:= $e orelse E =:= $E ->
- tokenize_number(B, esign, ?INC_COL(S), [$e | Acc]);
- _ ->
- {{float, lists:reverse(Acc)}, S}
- end;
-tokenize_number(B, esign, S=#decoder{offset=O}, Acc) ->
- case B of
- <<_:O/binary, C, _/binary>> when C =:= $- orelse C=:= $+ ->
- tokenize_number(B, eint, ?INC_COL(S), [C | Acc]);
- _ ->
- tokenize_number(B, eint, S, Acc)
- end;
-tokenize_number(B, eint, S=#decoder{offset=O}, Acc) ->
- case B of
- <<_:O/binary, C, _/binary>> when C >= $0 andalso C =< $9 ->
- tokenize_number(B, eint1, ?INC_COL(S), [C | Acc])
- end;
-tokenize_number(B, eint1, S=#decoder{offset=O}, Acc) ->
- case B of
- <<_:O/binary, C, _/binary>> when C >= $0 andalso C =< $9 ->
- tokenize_number(B, eint1, ?INC_COL(S), [C | Acc]);
- _ ->
- {{float, lists:reverse(Acc)}, S}
- end.
-
-tokenize(B, S=#decoder{offset=O}) ->
- case B of
- <<_:O/binary, C, _/binary>> when ?IS_WHITESPACE(C) ->
- tokenize(B, ?INC_CHAR(S, C));
- <<_:O/binary, "{", _/binary>> ->
- {start_object, ?INC_COL(S)};
- <<_:O/binary, "}", _/binary>> ->
- {end_object, ?INC_COL(S)};
- <<_:O/binary, "[", _/binary>> ->
- {start_array, ?INC_COL(S)};
- <<_:O/binary, "]", _/binary>> ->
- {end_array, ?INC_COL(S)};
- <<_:O/binary, ",", _/binary>> ->
- {comma, ?INC_COL(S)};
- <<_:O/binary, ":", _/binary>> ->
- {colon, ?INC_COL(S)};
- <<_:O/binary, "null", _/binary>> ->
- {{const, null}, ?ADV_COL(S, 4)};
- <<_:O/binary, "true", _/binary>> ->
- {{const, true}, ?ADV_COL(S, 4)};
- <<_:O/binary, "false", _/binary>> ->
- {{const, false}, ?ADV_COL(S, 5)};
- <<_:O/binary, "\"", _/binary>> ->
- tokenize_string(B, ?INC_COL(S));
- <<_:O/binary, C, _/binary>> when (C >= $0 andalso C =< $9)
- orelse C =:= $- ->
- tokenize_number(B, S);
- <<_:O/binary>> ->
- trim = S#decoder.state,
- {eof, S}
- end.
-%%
-%% Tests
-%%
--ifdef(TEST).
--include_lib("eunit/include/eunit.hrl").
-
-
-%% testing constructs borrowed from the Yaws JSON implementation.
-
-%% Create an object from a list of Key/Value pairs.
-
-obj_new() ->
- {struct, []}.
-
-is_obj({struct, Props}) ->
- F = fun ({K, _}) when is_binary(K) -> true end,
- lists:all(F, Props).
-
-obj_from_list(Props) ->
- Obj = {struct, Props},
- ?assert(is_obj(Obj)),
- Obj.
-
-%% Test for equivalence of Erlang terms.
-%% Due to arbitrary order of construction, equivalent objects might
-%% compare unequal as erlang terms, so we need to carefully recurse
-%% through aggregates (tuples and objects).
-
-equiv({struct, Props1}, {struct, Props2}) ->
- equiv_object(Props1, Props2);
-equiv(L1, L2) when is_list(L1), is_list(L2) ->
- equiv_list(L1, L2);
-equiv(N1, N2) when is_number(N1), is_number(N2) -> N1 == N2;
-equiv(B1, B2) when is_binary(B1), is_binary(B2) -> B1 == B2;
-equiv(A, A) when A =:= true orelse A =:= false orelse A =:= null -> true.
-
-%% Object representation and traversal order is unknown.
-%% Use the sledgehammer and sort property lists.
-
-equiv_object(Props1, Props2) ->
- L1 = lists:keysort(1, Props1),
- L2 = lists:keysort(1, Props2),
- Pairs = lists:zip(L1, L2),
- true = lists:all(fun({{K1, V1}, {K2, V2}}) ->
- equiv(K1, K2) and equiv(V1, V2)
- end, Pairs).
-
-%% Recursively compare tuple elements for equivalence.
-
-equiv_list([], []) ->
- true;
-equiv_list([V1 | L1], [V2 | L2]) ->
- equiv(V1, V2) andalso equiv_list(L1, L2).
-
-decode_test() ->
- [1199344435545.0, 1] = decode(<<"[1199344435545.0,1]">>),
- <<16#F0,16#9D,16#9C,16#95>> = decode([34,"\\ud835","\\udf15",34]).
-
-e2j_vec_test() ->
- test_one(e2j_test_vec(utf8), 1).
-
-test_one([], _N) ->
- %% io:format("~p tests passed~n", [N-1]),
- ok;
-test_one([{E, J} | Rest], N) ->
- %% io:format("[~p] ~p ~p~n", [N, E, J]),
- true = equiv(E, decode(J)),
- true = equiv(E, decode(encode(E))),
- test_one(Rest, 1+N).
-
-e2j_test_vec(utf8) ->
- [
- {1, "1"},
- {3.1416, "3.14160"}, %% text representation may truncate, trail zeroes
- {-1, "-1"},
- {-3.1416, "-3.14160"},
- {12.0e10, "1.20000e+11"},
- {1.234E+10, "1.23400e+10"},
- {-1.234E-10, "-1.23400e-10"},
- {10.0, "1.0e+01"},
- {123.456, "1.23456E+2"},
- {10.0, "1e1"},
- {<<"foo">>, "\"foo\""},
- {<<"foo", 5, "bar">>, "\"foo\\u0005bar\""},
- {<<"">>, "\"\""},
- {<<"\n\n\n">>, "\"\\n\\n\\n\""},
- {<<"\" \b\f\r\n\t\"">>, "\"\\\" \\b\\f\\r\\n\\t\\\"\""},
- {obj_new(), "{}"},
- {obj_from_list([{<<"foo">>, <<"bar">>}]), "{\"foo\":\"bar\"}"},
- {obj_from_list([{<<"foo">>, <<"bar">>}, {<<"baz">>, 123}]),
- "{\"foo\":\"bar\",\"baz\":123}"},
- {[], "[]"},
- {[[]], "[[]]"},
- {[1, <<"foo">>], "[1,\"foo\"]"},
-
- %% json array in a json object
- {obj_from_list([{<<"foo">>, [123]}]),
- "{\"foo\":[123]}"},
-
- %% json object in a json object
- {obj_from_list([{<<"foo">>, obj_from_list([{<<"bar">>, true}])}]),
- "{\"foo\":{\"bar\":true}}"},
-
- %% fold evaluation order
- {obj_from_list([{<<"foo">>, []},
- {<<"bar">>, obj_from_list([{<<"baz">>, true}])},
- {<<"alice">>, <<"bob">>}]),
- "{\"foo\":[],\"bar\":{\"baz\":true},\"alice\":\"bob\"}"},
-
- %% json object in a json array
- {[-123, <<"foo">>, obj_from_list([{<<"bar">>, []}]), null],
- "[-123,\"foo\",{\"bar\":[]},null]"}
- ].
-
-%% test utf8 encoding
-encoder_utf8_test() ->
- %% safe conversion case (default)
- [34,"\\u0001","\\u0442","\\u0435","\\u0441","\\u0442",34] =
- encode(<<1,"\321\202\320\265\321\201\321\202">>),
-
- %% raw utf8 output (optional)
- Enc = mochijson2:encoder([{utf8, true}]),
- [34,"\\u0001",[209,130],[208,181],[209,129],[209,130],34] =
- Enc(<<1,"\321\202\320\265\321\201\321\202">>).
-
-input_validation_test() ->
- Good = [
- {16#00A3, <<?Q, 16#C2, 16#A3, ?Q>>}, %% pound
- {16#20AC, <<?Q, 16#E2, 16#82, 16#AC, ?Q>>}, %% euro
- {16#10196, <<?Q, 16#F0, 16#90, 16#86, 16#96, ?Q>>} %% denarius
- ],
- lists:foreach(fun({CodePoint, UTF8}) ->
- Expect = list_to_binary(xmerl_ucs:to_utf8(CodePoint)),
- Expect = decode(UTF8)
- end, Good),
-
- Bad = [
- %% 2nd, 3rd, or 4th byte of a multi-byte sequence w/o leading byte
- <<?Q, 16#80, ?Q>>,
- %% missing continuations, last byte in each should be 80-BF
- <<?Q, 16#C2, 16#7F, ?Q>>,
- <<?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>>,
- %% escape characters trigger a different code path
- <<?Q, $\\, $\n, 16#80, ?Q>>
- ],
- lists:foreach(
- fun(X) ->
- ok = try decode(X) catch invalid_utf8 -> ok end,
- %% could be {ucs,{bad_utf8_character_code}} or
- %% {json_encode,{bad_char,_}}
- {'EXIT', _} = (catch encode(X))
- end, Bad).
-
-inline_json_test() ->
- ?assertEqual(<<"\"iodata iodata\"">>,
- iolist_to_binary(
- encode({json, [<<"\"iodata">>, " iodata\""]}))),
- ?assertEqual({struct, [{<<"key">>, <<"iodata iodata">>}]},
- decode(
- encode({struct,
- [{key, {json, [<<"\"iodata">>, " iodata\""]}}]}))),
- ok.
-
-big_unicode_test() ->
- UTF8Seq = list_to_binary(xmerl_ucs:to_utf8(16#0001d120)),
- ?assertEqual(
- <<"\"\\ud834\\udd20\"">>,
- iolist_to_binary(encode(UTF8Seq))),
- ?assertEqual(
- UTF8Seq,
- decode(iolist_to_binary(encode(UTF8Seq)))),
- ok.
-
-custom_decoder_test() ->
- ?assertEqual(
- {struct, [{<<"key">>, <<"value">>}]},
- (decoder([]))("{\"key\": \"value\"}")),
- F = fun ({struct, [{<<"key">>, <<"value">>}]}) -> win end,
- ?assertEqual(
- win,
- (decoder([{object_hook, F}]))("{\"key\": \"value\"}")),
- ok.
-
-atom_test() ->
- %% JSON native atoms
- [begin
- ?assertEqual(A, decode(atom_to_list(A))),
- ?assertEqual(iolist_to_binary(atom_to_list(A)),
- iolist_to_binary(encode(A)))
- end || A <- [true, false, null]],
- %% Atom to string
- ?assertEqual(
- <<"\"foo\"">>,
- iolist_to_binary(encode(foo))),
- ?assertEqual(
- <<"\"\\ud834\\udd20\"">>,
- iolist_to_binary(encode(list_to_atom(xmerl_ucs:to_utf8(16#0001d120))))),
- ok.
-
-key_encode_test() ->
- %% Some forms are accepted as keys that would not be strings in other
- %% cases
- ?assertEqual(
- <<"{\"foo\":1}">>,
- iolist_to_binary(encode({struct, [{foo, 1}]}))),
- ?assertEqual(
- <<"{\"foo\":1}">>,
- iolist_to_binary(encode({struct, [{<<"foo">>, 1}]}))),
- ?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(
- encode({struct, [{[16#0001d120], 1}]}))),
- ?assertEqual(
- <<"{\"1\":1}">>,
- iolist_to_binary(encode({struct, [{1, 1}]}))),
- ok.
-
-unsafe_chars_test() ->
- Chars = "\"\\\b\f\n\r\t",
- [begin
- ?assertEqual(false, json_string_is_safe([C])),
- ?assertEqual(false, json_bin_is_safe(<<C>>)),
- ?assertEqual(<<C>>, decode(encode(<<C>>)))
- end || C <- Chars],
- ?assertEqual(
- false,
- json_string_is_safe([16#0001d120])),
- ?assertEqual(
- false,
- json_bin_is_safe(list_to_binary(xmerl_ucs:to_utf8(16#0001d120)))),
- ?assertEqual(
- [16#0001d120],
- xmerl_ucs:from_utf8(
- binary_to_list(
- decode(encode(list_to_atom(xmerl_ucs:to_utf8(16#0001d120))))))),
- ?assertEqual(
- false,
- json_string_is_safe([16#110000])),
- ?assertEqual(
- false,
- json_bin_is_safe(list_to_binary(xmerl_ucs:to_utf8([16#110000])))),
- %% solidus can be escaped but isn't unsafe by default
- ?assertEqual(
- <<"/">>,
- decode(<<"\"\\/\"">>)),
- ok.
-
-int_test() ->
- ?assertEqual(0, decode("0")),
- ?assertEqual(1, decode("1")),
- ?assertEqual(11, decode("11")),
- ok.
-
-large_int_test() ->
- ?assertEqual(<<"-2147483649214748364921474836492147483649">>,
- iolist_to_binary(encode(-2147483649214748364921474836492147483649))),
- ?assertEqual(<<"2147483649214748364921474836492147483649">>,
- iolist_to_binary(encode(2147483649214748364921474836492147483649))),
- ok.
-
-float_test() ->
- ?assertEqual(<<"-2147483649.0">>, iolist_to_binary(encode(-2147483649.0))),
- ?assertEqual(<<"2147483648.0">>, iolist_to_binary(encode(2147483648.0))),
- ok.
-
-handler_test() ->
- ?assertEqual(
- {'EXIT',{json_encode,{bad_term,{}}}},
- catch encode({})),
- F = fun ({}) -> [] end,
- ?assertEqual(
- <<"[]">>,
- iolist_to_binary((encoder([{handler, F}]))({}))),
- ok.
-
--endif.
http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/ejson/src/mochinum.erl
----------------------------------------------------------------------
diff --git a/src/ejson/src/mochinum.erl b/src/ejson/src/mochinum.erl
deleted file mode 100644
index c52b15c..0000000
--- a/src/ejson/src/mochinum.erl
+++ /dev/null
@@ -1,354 +0,0 @@
-%% @copyright 2007 Mochi Media, Inc.
-%% @author Bob Ippolito <bo...@mochimedia.com>
-
-%% @doc Useful numeric algorithms for floats that cover some deficiencies
-%% in the math module. More interesting is digits/1, which implements
-%% the algorithm from:
-%% http://www.cs.indiana.edu/~burger/fp/index.html
-%% See also "Printing Floating-Point Numbers Quickly and Accurately"
-%% in Proceedings of the SIGPLAN '96 Conference on Programming Language
-%% Design and Implementation.
-
--module(mochinum).
--author("Bob Ippolito <bo...@mochimedia.com>").
--export([digits/1, frexp/1, int_pow/2, int_ceil/1]).
-
-%% IEEE 754 Float exponent bias
--define(FLOAT_BIAS, 1022).
--define(MIN_EXP, -1074).
--define(BIG_POW, 4503599627370496).
-
-%% External API
-
-%% @spec digits(number()) -> string()
-%% @doc Returns a string that accurately represents the given integer or float
-%% using a conservative amount of digits. Great for generating
-%% human-readable output, or compact ASCII serializations for floats.
-digits(N) when is_integer(N) ->
- integer_to_list(N);
-digits(0.0) ->
- "0.0";
-digits(Float) ->
- {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];
- _ ->
- R
- end.
-
-%% @spec frexp(F::float()) -> {Frac::float(), Exp::float()}
-%% @doc Return the fractional and exponent part of an IEEE 754 double,
-%% equivalent to the libc function of the same name.
-%% F = Frac * pow(2, Exp).
-frexp(F) ->
- frexp1(unpack(F)).
-
-%% @spec int_pow(X::integer(), N::integer()) -> Y::integer()
-%% @doc Moderately efficient way to exponentiate integers.
-%% int_pow(10, 2) = 100.
-int_pow(_X, 0) ->
- 1;
-int_pow(X, N) when N > 0 ->
- int_pow(X, N, 1).
-
-%% @spec int_ceil(F::float()) -> integer()
-%% @doc Return the ceiling of F as an integer. The ceiling is defined as
-%% F when F == trunc(F);
-%% trunc(F) when F < 0;
-%% trunc(F) + 1 when F > 0.
-int_ceil(X) ->
- T = trunc(X),
- case (X - T) of
- Pos when Pos > 0 -> T + 1;
- _ -> T
- end.
-
-
-%% Internal API
-
-int_pow(X, N, R) when N < 2 ->
- R * X;
-int_pow(X, N, R) ->
- int_pow(X * X, N bsr 1, case N band 1 of 1 -> R * X; 0 -> R end).
-
-insert_decimal(0, S) ->
- "0." ++ S;
-insert_decimal(Place, S) when Place > 0 ->
- L = length(S),
- case Place - L of
- 0 ->
- S ++ ".0";
- N when N < 0 ->
- {S0, S1} = lists:split(L + N, S),
- S0 ++ "." ++ S1;
- N when N < 6 ->
- %% More places than digits
- S ++ lists:duplicate(N, $0) ++ ".0";
- _ ->
- insert_decimal_exp(Place, S)
- end;
-insert_decimal(Place, S) when Place > -6 ->
- "0." ++ lists:duplicate(abs(Place), $0) ++ S;
-insert_decimal(Place, S) ->
- insert_decimal_exp(Place, S).
-
-insert_decimal_exp(Place, S) ->
- [C | S0] = S,
- S1 = case S0 of
- [] ->
- "0";
- _ ->
- S0
- end,
- Exp = case Place < 0 of
- true ->
- "e-";
- false ->
- "e+"
- end,
- [C] ++ "." ++ S1 ++ Exp ++ integer_to_list(abs(Place - 1)).
-
-
-digits1(Float, Exp, Frac) ->
- Round = ((Frac band 1) =:= 0),
- case Exp >= 0 of
- true ->
- BExp = 1 bsl Exp,
- case (Frac =/= ?BIG_POW) of
- true ->
- scale((Frac * BExp * 2), 2, BExp, BExp,
- Round, Round, Float);
- false ->
- scale((Frac * BExp * 4), 4, (BExp * 2), BExp,
- Round, Round, Float)
- end;
- false ->
- case (Exp =:= ?MIN_EXP) orelse (Frac =/= ?BIG_POW) of
- true ->
- scale((Frac * 2), 1 bsl (1 - Exp), 1, 1,
- Round, Round, Float);
- false ->
- scale((Frac * 4), 1 bsl (2 - Exp), 2, 1,
- Round, Round, Float)
- end
- end.
-
-scale(R, S, MPlus, MMinus, LowOk, HighOk, Float) ->
- Est = int_ceil(math:log10(abs(Float)) - 1.0e-10),
- %% Note that the scheme implementation uses a 326 element look-up table
- %% for int_pow(10, N) where we do not.
- case Est >= 0 of
- true ->
- fixup(R, S * int_pow(10, Est), MPlus, MMinus, Est,
- LowOk, HighOk);
- false ->
- Scale = int_pow(10, -Est),
- fixup(R * Scale, S, MPlus * Scale, MMinus * Scale, Est,
- LowOk, HighOk)
- end.
-
-fixup(R, S, MPlus, MMinus, K, LowOk, HighOk) ->
- TooLow = case HighOk of
- true ->
- (R + MPlus) >= S;
- false ->
- (R + MPlus) > S
- end,
- case TooLow of
- true ->
- [(K + 1) | generate(R, S, MPlus, MMinus, LowOk, HighOk)];
- false ->
- [K | generate(R * 10, S, MPlus * 10, MMinus * 10, LowOk, HighOk)]
- end.
-
-generate(R0, S, MPlus, MMinus, LowOk, HighOk) ->
- D = R0 div S,
- R = R0 rem S,
- TC1 = case LowOk of
- true ->
- R =< MMinus;
- false ->
- R < MMinus
- end,
- TC2 = case HighOk of
- true ->
- (R + MPlus) >= S;
- false ->
- (R + MPlus) > S
- end,
- case TC1 of
- false ->
- case TC2 of
- false ->
- [D | generate(R * 10, S, MPlus * 10, MMinus * 10,
- LowOk, HighOk)];
- true ->
- [D + 1]
- end;
- true ->
- case TC2 of
- false ->
- [D];
- true ->
- case R * 2 < S of
- true ->
- [D];
- false ->
- [D + 1]
- end
- end
- end.
-
-unpack(Float) ->
- <<Sign:1, Exp:11, Frac:52>> = <<Float:64/float>>,
- {Sign, Exp, Frac}.
-
-frexp1({_Sign, 0, 0}) ->
- {0.0, 0};
-frexp1({Sign, 0, Frac}) ->
- Exp = log2floor(Frac),
- <<Frac1:64/float>> = <<Sign:1, ?FLOAT_BIAS:11, (Frac-1):52>>,
- {Frac1, -(?FLOAT_BIAS) - 52 + Exp};
-frexp1({Sign, Exp, Frac}) ->
- <<Frac1:64/float>> = <<Sign:1, ?FLOAT_BIAS:11, Frac:52>>,
- {Frac1, Exp - ?FLOAT_BIAS}.
-
-log2floor(Int) ->
- log2floor(Int, 0).
-
-log2floor(0, N) ->
- N;
-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
-%%
--ifdef(TEST).
--include_lib("eunit/include/eunit.hrl").
-
-int_ceil_test() ->
- ?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() ->
- ?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() ->
- ?assertEqual("0",
- digits(0)),
- ?assertEqual("0.0",
- digits(0.0)),
- ?assertEqual("1.0",
- digits(1.0)),
- ?assertEqual("-1.0",
- digits(-1.0)),
- ?assertEqual("0.1",
- digits(0.1)),
- ?assertEqual("0.01",
- digits(0.01)),
- ?assertEqual("0.001",
- digits(0.001)),
- ?assertEqual("1.0e+6",
- digits(1000000.0)),
- ?assertEqual("0.5",
- digits(0.5)),
- ?assertEqual("4503599627370496.0",
- digits(4503599627370496.0)),
- %% small denormalized number
- %% 4.94065645841246544177e-324 =:= 5.0e-324
- <<SmallDenorm/float>> = <<0,0,0,0,0,0,0,1>>,
- ?assertEqual("5.0e-324",
- digits(SmallDenorm)),
- ?assertEqual(SmallDenorm,
- list_to_float(digits(SmallDenorm))),
- %% large denormalized number
- %% 2.22507385850720088902e-308
- <<BigDenorm/float>> = <<0,15,255,255,255,255,255,255>>,
- ?assertEqual("2.225073858507201e-308",
- digits(BigDenorm)),
- ?assertEqual(BigDenorm,
- list_to_float(digits(BigDenorm))),
- %% small normalized number
- %% 2.22507385850720138309e-308
- <<SmallNorm/float>> = <<0,16,0,0,0,0,0,0>>,
- ?assertEqual("2.2250738585072014e-308",
- digits(SmallNorm)),
- ?assertEqual(SmallNorm,
- list_to_float(digits(SmallNorm))),
- %% large normalized number
- %% 1.79769313486231570815e+308
- <<LargeNorm/float>> = <<127,239,255,255,255,255,255,255>>,
- ?assertEqual("1.7976931348623157e+308",
- 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
- ?assertEqual({0.0, 0}, frexp(0.0)),
- %% one
- ?assertEqual({0.5, 1}, frexp(1.0)),
- %% negative one
- ?assertEqual({-0.5, 1}, frexp(-1.0)),
- %% small denormalized number
- %% 4.94065645841246544177e-324
- <<SmallDenorm/float>> = <<0,0,0,0,0,0,0,1>>,
- ?assertEqual({0.5, -1073}, frexp(SmallDenorm)),
- %% large denormalized number
- %% 2.22507385850720088902e-308
- <<BigDenorm/float>> = <<0,15,255,255,255,255,255,255>>,
- ?assertEqual(
- {0.99999999999999978, -1022},
- frexp(BigDenorm)),
- %% small normalized number
- %% 2.22507385850720138309e-308
- <<SmallNorm/float>> = <<0,16,0,0,0,0,0,0>>,
- ?assertEqual({0.5, -1021}, frexp(SmallNorm)),
- %% large normalized number
- %% 1.79769313486231570815e+308
- <<LargeNorm/float>> = <<127,239,255,255,255,255,255,255>>,
- ?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.
http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/.gitignore
----------------------------------------------------------------------
diff --git a/src/jiffy/.gitignore b/src/jiffy/.gitignore
new file mode 100644
index 0000000..67903ef
--- /dev/null
+++ b/src/jiffy/.gitignore
@@ -0,0 +1,7 @@
+.jiffy.dev
+*.app
+*.beam
+*.o
+*.so
+.eunit
+deps
http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/LICENSE
----------------------------------------------------------------------
diff --git a/src/jiffy/LICENSE b/src/jiffy/LICENSE
new file mode 100644
index 0000000..6d5c254
--- /dev/null
+++ b/src/jiffy/LICENSE
@@ -0,0 +1,90 @@
+jiffy
+=====
+
+Files: c_src/* src/* test/*.t
+
+Copyright 2011 Paul J. Davis <pa...@gmail.com>
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Etap
+====
+
+Files: test/etap.erl
+
+Copyright (c) 2008-2009 Nick Gerakines <ni...@gerakines.net>
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Yajl Test Cases
+===============
+
+Files: test/cases/*.json
+
+Copyright 2010, Lloyd Hilaiel.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ 3. Neither the name of Lloyd Hilaiel nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/Makefile
----------------------------------------------------------------------
diff --git a/src/jiffy/Makefile b/src/jiffy/Makefile
new file mode 100644
index 0000000..7227ce4
--- /dev/null
+++ b/src/jiffy/Makefile
@@ -0,0 +1,47 @@
+REBAR?=./rebar
+
+
+all: build
+
+
+clean:
+ $(REBAR) clean
+ rm -rf logs
+ rm -rf .eunit
+ rm -f test/*.beam
+
+
+distclean: clean
+ git clean -fxd
+
+
+devmarker:
+ touch .jiffy.dev
+
+
+depends: devmarker
+ @if test ! -d ./deps/proper; then \
+ $(REBAR) get-deps; \
+ fi
+
+
+build: depends
+ $(REBAR) compile
+
+
+etap: test/etap.beam test/util.beam
+ prove test/*.t
+
+
+eunit:
+ $(REBAR) eunit skip_deps=true
+
+
+check: build etap eunit
+
+
+%.beam: %.erl
+ erlc -o test/ $<
+
+
+.PHONY: all clean distclean depends build etap eunit check
http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/README.md
----------------------------------------------------------------------
diff --git a/src/jiffy/README.md b/src/jiffy/README.md
new file mode 100644
index 0000000..efb5300
--- /dev/null
+++ b/src/jiffy/README.md
@@ -0,0 +1,58 @@
+Jiffy - JSON NIFs for Erlang
+============================
+
+A JSON parser as a NIF. This is a complete rewrite of the work I did
+in EEP0018 that was based on Yajl. This new version is a hand crafted
+state machine that does its best to be as quick and efficient as
+possible while not placing any constraints on the parsed JSON.
+
+Usage
+-----
+
+Jiffy is a simple API. The only thing that might catch you off guard
+is that the return type of jiffy:encode/1 is an iolist even though it
+returns a binary most of the time.
+
+A quick note on unicode. Jiffy only understands utf-8 in binaries. End
+of story. Also, there is a jiffy:encode/2 that takes a list of options
+for encoding. Currently the only supported option is `uescape`.
+
+Errors are raised as exceptions.
+
+ Eshell V5.8.2 (abort with ^G)
+ 1> jiffy:decode(<<"{\"foo\": \"bar\"}">>).
+ {[{<<"foo">>,<<"bar">>}]}
+ 2> Doc = {[{foo, [<<"bing">>, 2.3, true]}]}.
+ {[{foo,[<<"bing">>,2.3,true]}]}
+ 3> jiffy:encode(Doc).
+ <<"{\"foo\":[\"bing\",2.3,true]}">>
+
+
+Data Format
+-----------
+
+ Erlang JSON Erlang
+ ==========================================================================
+
+ null -> null -> null
+ true -> true -> true
+ false -> false -> false
+ "hi" -> [104, 105] -> [104, 105]
+ <<"hi">> -> "hi" -> <<"hi">>
+ hi -> "hi" -> <<"hi">>
+ 1 -> 1 -> 1
+ 1.25 -> 1.25 -> 1.25
+ [] -> [] -> []
+ [true, 1.0] -> [true, 1.0] -> [true, 1.0]
+ {[]} -> {} -> {[]}
+ {[{foo, bar}]} -> {"foo": "bar"} -> {[{<<"foo">>, <<"bar">>}]}
+ {[{<<"foo">>, <<"bar">>}]} -> {"foo": "bar"} -> {[{<<"foo">>, <<"bar">>}]}
+
+Improvements over EEP0018
+-------------------------
+
+Jiffy should be in all ways an improvemnt over EEP0018. It no longer
+imposes limits on the nesting depth. It is capable of encoding and
+decoding large numbers and it does quite a bit more checking for validity
+of valid UTF-8 in strings.
+
http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/c_src/decoder.c
----------------------------------------------------------------------
diff --git a/src/jiffy/c_src/decoder.c b/src/jiffy/c_src/decoder.c
new file mode 100644
index 0000000..fc892da
--- /dev/null
+++ b/src/jiffy/c_src/decoder.c
@@ -0,0 +1,914 @@
+// This file is part of Jiffy released under the MIT license.
+// See the LICENSE file for more information.
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "erl_nif.h"
+#include "jiffy.h"
+
+#define U(c) ((unsigned char) (c))
+#define ERROR(i, msg) make_error(st, env, msg)
+
+#define STACK_SIZE_INC 64
+#define NUM_BUF_LEN 32
+
+#if WINDOWS || WIN32
+#define snprintf _snprintf
+#endif
+
+enum {
+ st_value=0,
+ st_object,
+ st_array,
+ st_key,
+ st_colon,
+ st_comma,
+ st_done,
+ st_invalid
+} JsonState;
+
+enum {
+ nst_init=0,
+ nst_sign,
+ nst_mantissa,
+ nst_frac0,
+ nst_frac1,
+ nst_frac,
+ nst_esign,
+ nst_edigit
+} JsonNumState;
+
+typedef struct {
+ ErlNifEnv* env;
+ jiffy_st* atoms;
+
+ ERL_NIF_TERM arg;
+ ErlNifBinary bin;
+
+ int is_partial;
+
+ char* p;
+ unsigned char* u;
+ int i;
+ int len;
+
+ char* st_data;
+ int st_size;
+ int st_top;
+} Decoder;
+
+void
+dec_init(Decoder* d, ErlNifEnv* env, ERL_NIF_TERM arg, ErlNifBinary* bin)
+{
+ int i;
+
+ d->env = env;
+ d->atoms = enif_priv_data(env);
+ d->arg = arg;
+
+ d->is_partial = 0;
+
+ d->p = (char*) bin->data;
+ d->u = bin->data;
+ d->len = bin->size;
+ d->i = 0;
+
+ d->st_data = (char*) enif_alloc(STACK_SIZE_INC * sizeof(char));
+ d->st_size = STACK_SIZE_INC;
+ d->st_top = 0;
+
+ for(i = 0; i < d->st_size; i++) {
+ d->st_data[i] = st_invalid;
+ }
+
+ d->st_data[0] = st_value;
+ d->st_top++;
+}
+
+void
+dec_destroy(Decoder* d)
+{
+ if(d->st_data != NULL) {
+ enif_free(d->st_data);
+ }
+}
+
+ERL_NIF_TERM
+dec_error(Decoder* d, const char* atom)
+{
+ ERL_NIF_TERM pos = enif_make_int(d->env, d->i+1);
+ ERL_NIF_TERM msg = make_atom(d->env, atom);
+ ERL_NIF_TERM ret = enif_make_tuple2(d->env, pos, msg);
+ return enif_make_tuple2(d->env, d->atoms->atom_error, ret);
+}
+
+char
+dec_curr(Decoder* d)
+{
+ return d->st_data[d->st_top-1];
+}
+
+int
+dec_top(Decoder* d)
+{
+ return d->st_top;
+}
+
+void
+dec_push(Decoder* d, char val)
+{
+ char* tmp;
+ int new_sz;
+ int i;
+
+ if(d->st_top >= d->st_size) {
+ new_sz = d->st_size + STACK_SIZE_INC;
+ tmp = (char*) enif_alloc(new_sz * sizeof(char));
+ memcpy(tmp, d->st_data, d->st_size * sizeof(char));
+ enif_free(d->st_data);
+ d->st_data = tmp;
+ d->st_size = new_sz;
+ for(i = d->st_top; i < d->st_size; i++) {
+ d->st_data[i] = st_invalid;
+ }
+ }
+
+ d->st_data[d->st_top++] = val;
+}
+
+void
+dec_pop(Decoder* d, char val)
+{
+ assert(d->st_data[d->st_top-1] == val && "popped invalid state.");
+ d->st_data[d->st_top-1] = st_invalid;
+ d->st_top--;
+}
+
+int
+dec_string(Decoder* d, ERL_NIF_TERM* value)
+{
+ int has_escape = 0;
+ int num_escapes = 0;
+ int st;
+ int ulen;
+ int ui;
+ int hi;
+ int lo;
+ char* chrbuf;
+ int chrpos;
+
+ if(d->p[d->i] != '\"') {
+ return 0;
+ }
+ d->i++;
+
+ st = d->i;
+
+ while(d->i < d->len) {
+ if(d->u[d->i] < 0x20) {
+ return 0;
+ } else if(d->p[d->i] == '\"') {
+ d->i++;
+ goto parse;
+ } else if(d->p[d->i] == '\\') {
+ if(d->i+1 >= d->len) {
+ return 0;
+ }
+ has_escape = 1;
+ num_escapes += 1;
+ d->i++;
+ switch(d->p[d->i]) {
+ case '\"':
+ case '\\':
+ case '/':
+ case 'b':
+ case 'f':
+ case 'n':
+ case 'r':
+ case 't':
+ d->i++;
+ break;
+ case 'u':
+ hi = 0;
+ lo = 0;
+ d->i++;
+ if(d->i + 4 >= d->len) {
+ return 0;
+ }
+ hi = int_from_hex(&(d->u[d->i]));
+ if(hi < 0) {
+ return 0;
+ }
+ d->i += 4;
+ if(hi >= 0xD800 && hi < 0xDC00) {
+ if(d->i + 6 >= d->len) {
+ return 0;
+ }
+ if(d->p[d->i++] != '\\') {
+ return 0;
+ } else if(d->p[d->i++] != 'u') {
+ return 0;
+ }
+ lo = int_from_hex(&(d->u[d->i]));
+ if(lo < 0) {
+ return 0;
+ }
+ hi = unicode_from_pair(hi, lo);
+ if(hi < 0) {
+ return 0;
+ }
+ }
+ hi = utf8_len(hi);
+ if(hi < 0) {
+ return 0;
+ }
+ if(lo == 0) {
+ num_escapes += 5 - hi;
+ } else {
+ num_escapes += 11 - hi;
+ }
+ break;
+ default:
+ return 0;
+ }
+ } else if(d->u[d->i] < 0x80) {
+ d->i++;
+ } else {
+ ulen = utf8_validate(&(d->u[d->i]), d->len - d->i);
+ if(ulen < 0) {
+ return 0;
+ }
+ d->i += ulen;
+ }
+ }
+
+ // The goto above ensures that we only
+ // hit this when a string is not terminated
+ // correctly.
+ return 0;
+
+parse:
+ if(!has_escape) {
+ *value = enif_make_sub_binary(d->env, d->arg, st, (d->i - st - 1));
+ return 1;
+ }
+
+ hi = 0;
+ lo = 0;
+
+ ulen = (d->i - 1) - st - num_escapes;
+ chrbuf = (char*) enif_make_new_binary(d->env, ulen, value);
+ chrpos = 0;
+ ui = st;
+ while(ui < d->i - 1) {
+ if(d->p[ui] != '\\') {
+ chrbuf[chrpos++] = d->p[ui++];
+ continue;
+ }
+ ui++;
+ switch(d->p[ui]) {
+ case '\"':
+ case '\\':
+ case '/':
+ chrbuf[chrpos++] = d->p[ui];
+ ui++;
+ break;
+ case 'b':
+ chrbuf[chrpos++] = '\b';
+ ui++;
+ break;
+ case 'f':
+ chrbuf[chrpos++] = '\f';
+ ui++;
+ break;
+ case 'n':
+ chrbuf[chrpos++] = '\n';
+ ui++;
+ break;
+ case 'r':
+ chrbuf[chrpos++] = '\r';
+ ui++;
+ break;
+ case 't':
+ chrbuf[chrpos++] = '\t';
+ ui++;
+ break;
+ case 'u':
+ ui++;
+ hi = int_from_hex(&(d->u[ui]));
+ if(hi < 0) {
+ return 0;
+ }
+ if(hi >= 0xD800 && hi < 0xDC00) {
+ lo = int_from_hex(&(d->u[ui+6]));
+ if(lo < 0) {
+ return 0;
+ }
+ hi = unicode_from_pair(hi, lo);
+ ui += 10;
+ } else {
+ ui += 4;
+ }
+ hi = unicode_to_utf8(hi, (unsigned char*) chrbuf+chrpos);
+ if(hi < 0) {
+ return 0;
+ }
+ chrpos += hi;
+ break;
+ default:
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+int
+dec_number(Decoder* d, ERL_NIF_TERM* value)
+{
+ ERL_NIF_TERM num_type = d->atoms->atom_error;
+ char state = nst_init;
+ char nbuf[NUM_BUF_LEN];
+ int st = d->i;
+ int has_frac = 0;
+ int has_exp = 0;
+ double dval;
+ long lval;
+
+ while(d->i < d->len) {
+ switch(state) {
+ case nst_init:
+ switch(d->p[d->i]) {
+ case '-':
+ state = nst_sign;
+ d->i++;
+ break;
+ case '0':
+ state = nst_frac0;
+ d->i++;
+ break;
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ state = nst_mantissa;
+ d->i++;
+ break;
+ default:
+ return 0;
+ }
+ break;
+
+ case nst_sign:
+ switch(d->p[d->i]) {
+ case '0':
+ state = nst_frac0;
+ d->i++;
+ break;
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ state = nst_mantissa;
+ d->i++;
+ break;
+ default:
+ return 0;
+ }
+ break;
+
+ case nst_mantissa:
+ switch(d->p[d->i]) {
+ case '.':
+ state = nst_frac1;
+ d->i++;
+ break;
+ case 'e':
+ case 'E':
+ state = nst_esign;
+ d->i++;
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ d->i++;
+ break;
+ default:
+ goto parse;
+ }
+ break;
+
+ case nst_frac0:
+ switch(d->p[d->i]) {
+ case '.':
+ state = nst_frac1;
+ d->i++;
+ break;
+ case 'e':
+ case 'E':
+ state = nst_esign;
+ d->i++;
+ break;
+ default:
+ goto parse;
+ }
+ break;
+
+ case nst_frac1:
+ has_frac = 1;
+ switch(d->p[d->i]) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ state = nst_frac;
+ d->i++;
+ break;
+ default:
+ goto parse;
+ }
+ break;
+
+ case nst_frac:
+ switch(d->p[d->i]) {
+ case 'e':
+ case 'E':
+ state = nst_esign;
+ d->i++;
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ d->i++;
+ break;
+ default:
+ goto parse;
+ }
+ break;
+
+ case nst_esign:
+ has_exp = 1;
+ switch(d->p[d->i]) {
+ case '-':
+ case '+':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ state = nst_edigit;
+ d->i++;
+ break;
+ default:
+ return 0;
+ }
+ break;
+
+ case nst_edigit:
+ switch(d->p[d->i]) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ d->i++;
+ break;
+ default:
+ goto parse;
+ }
+ break;
+
+ default:
+ return 0;
+ }
+ }
+
+parse:
+
+ switch(state) {
+ case nst_init:
+ case nst_sign:
+ case nst_frac1:
+ case nst_esign:
+ return 0;
+ default:
+ break;
+ }
+
+ errno = 0;
+
+ if(d->i - st < NUM_BUF_LEN) {
+ memset(nbuf, 0, NUM_BUF_LEN);
+ memcpy(nbuf, &(d->p[st]), d->i - st);
+
+ if(has_frac || has_exp) {
+ dval = strtod(nbuf, NULL);
+ if(errno != ERANGE) {
+ *value = enif_make_double(d->env, dval);
+ return 1;
+ }
+ } else {
+ lval = strtol(nbuf, NULL, 10);
+ if(errno != ERANGE) {
+ *value = enif_make_int64(d->env, lval);
+ return 1;
+ }
+ }
+ }
+
+ if(!has_frac && !has_exp) {
+ num_type = d->atoms->atom_bignum;
+ } else if(!has_frac && has_exp) {
+ num_type = d->atoms->atom_bignum_e;
+ } else {
+ num_type = d->atoms->atom_bigdbl;
+ }
+
+ d->is_partial = 1;
+ *value = enif_make_sub_binary(d->env, d->arg, st, d->i - st);
+ *value = enif_make_tuple2(d->env, num_type, *value);
+ return 1;
+}
+
+ERL_NIF_TERM
+make_object(ErlNifEnv* env, ERL_NIF_TERM pairs)
+{
+ ERL_NIF_TERM ret = enif_make_list(env, 0);
+ ERL_NIF_TERM key, val;
+
+ while(enif_get_list_cell(env, pairs, &val, &pairs)) {
+ if(!enif_get_list_cell(env, pairs, &key, &pairs)) {
+ assert(0 == 1 && "Unbalanced object pairs.");
+ }
+ val = enif_make_tuple2(env, key, val);
+ ret = enif_make_list_cell(env, val, ret);
+ }
+
+ return enif_make_tuple1(env, ret);
+}
+
+ERL_NIF_TERM
+make_array(ErlNifEnv* env, ERL_NIF_TERM list)
+{
+ ERL_NIF_TERM ret = enif_make_list(env, 0);
+ ERL_NIF_TERM item;
+
+ while(enif_get_list_cell(env, list, &item, &list)) {
+ ret = enif_make_list_cell(env, item, ret);
+ }
+
+ return ret;
+}
+
+ERL_NIF_TERM
+decode(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ Decoder dec;
+ Decoder* d = &dec;
+
+ ErlNifBinary bin;
+
+ ERL_NIF_TERM objs = enif_make_list(env, 0);
+ ERL_NIF_TERM curr = enif_make_list(env, 0);
+ ERL_NIF_TERM val;
+ ERL_NIF_TERM ret;
+
+ if(argc != 1) {
+ return enif_make_badarg(env);
+ } else if(!enif_inspect_binary(env, argv[0], &bin)) {
+ return enif_make_badarg(env);
+ }
+
+ dec_init(d, env, argv[0], &bin);
+
+ //fprintf(stderr, "Parsing:\r\n");
+ while(d->i < bin.size) {
+ //fprintf(stderr, "state: %d\r\n", dec_curr(d));
+ switch(dec_curr(d)) {
+ case st_value:
+ switch(d->p[d->i]) {
+ case ' ':
+ case '\n':
+ case '\r':
+ case '\t':
+ d->i++;
+ break;
+ case 'n':
+ if(d->i + 3 >= d->len) {
+ ret = dec_error(d, "invalid_literal");
+ goto done;
+ }
+ if(memcmp(&(d->p[d->i]), "null", 4) != 0) {
+ ret = dec_error(d, "invalid_literal");
+ goto done;
+ }
+ val = d->atoms->atom_null;
+ dec_pop(d, st_value);
+ d->i += 4;
+ break;
+ case 't':
+ if(d->i + 3 >= d->len) {
+ ret = dec_error(d, "invalid_literal");
+ goto done;
+ }
+ if(memcmp(&(d->p[d->i]), "true", 4) != 0) {
+ ret = dec_error(d, "invalid_literal");
+ goto done;
+ }
+ val = d->atoms->atom_true;
+ dec_pop(d, st_value);
+ d->i += 4;
+ break;
+ case 'f':
+ if(d->i + 4 >= bin.size) {
+ ret = dec_error(d, "invalid_literal");
+ goto done;
+ }
+ if(memcmp(&(d->p[d->i]), "false", 5) != 0) {
+ ret = dec_error(d, "invalid_literal");
+ goto done;
+ }
+ val = d->atoms->atom_false;
+ dec_pop(d, st_value);
+ d->i += 5;
+ break;
+ case '\"':
+ if(!dec_string(d, &val)) {
+ ret = dec_error(d, "invalid_string");
+ goto done;
+ }
+ dec_pop(d, st_value);
+ break;
+ case '-':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if(!dec_number(d, &val)) {
+ ret = dec_error(d, "invalid_number");
+ goto done;
+ }
+ dec_pop(d, st_value);
+ break;
+ case '{':
+ dec_push(d, st_object);
+ dec_push(d, st_key);
+ objs = enif_make_list_cell(env, curr, objs);
+ curr = enif_make_list(env, 0);
+ d->i++;
+ break;
+ case '[':
+ dec_push(d, st_array);
+ dec_push(d, st_value);
+ objs = enif_make_list_cell(env, curr, objs);
+ curr = enif_make_list(env, 0);
+ d->i++;
+ break;
+ case ']':
+ if(!enif_is_empty_list(env, curr)) {
+ ret = dec_error(d, "invalid_json");
+ goto done;
+ }
+ dec_pop(d, st_value);
+ if(dec_curr(d) != st_array) {
+ ret = dec_error(d, "invalid_json");
+ goto done;
+ }
+ dec_pop(d, st_array);
+ dec_pop(d, st_value);
+ val = curr; // curr is []
+ if(!enif_get_list_cell(env, objs, &curr, &objs)) {
+ ret = dec_error(d, "internal_error");
+ goto done;
+ }
+ d->i++;
+ break;
+ default:
+ ret = dec_error(d, "invalid_json");
+ goto done;
+ }
+ if(dec_top(d) == 0) {
+ dec_push(d, st_done);
+ } else if(dec_curr(d) != st_value && dec_curr(d) != st_key) {
+ dec_push(d, st_comma);
+ curr = enif_make_list_cell(env, val, curr);
+ }
+ break;
+
+ case st_key:
+ switch(d->p[d->i]) {
+ case ' ':
+ case '\n':
+ case '\r':
+ case '\t':
+ d->i++;
+ break;
+ case '\"':
+ if(!dec_string(d, &val)) {
+ ret = dec_error(d, "invalid_string");
+ goto done;
+ }
+ dec_pop(d, st_key);
+ dec_push(d, st_colon);
+ curr = enif_make_list_cell(env, val, curr);
+ break;
+ case '}':
+ if(!enif_is_empty_list(env, curr)) {
+ ret = dec_error(d, "invalid_json");
+ goto done;
+ }
+ dec_pop(d, st_key);
+ dec_pop(d, st_object);
+ dec_pop(d, st_value);
+ val = enif_make_tuple1(env, curr);
+ if(!enif_get_list_cell(env, objs, &curr, &objs)) {
+ ret = dec_error(d, "internal_error");
+ goto done;
+ }
+ if(dec_top(d) == 0) {
+ dec_push(d, st_done);
+ } else {
+ dec_push(d, st_comma);
+ curr = enif_make_list_cell(env, val, curr);
+ }
+ d->i++;
+ break;
+ default:
+ ret = dec_error(d, "invalid_json");
+ goto done;
+ }
+ break;
+
+ case st_colon:
+ switch(d->p[d->i]) {
+ case ' ':
+ case '\n':
+ case '\r':
+ case '\t':
+ d->i++;
+ break;
+ case ':':
+ dec_pop(d, st_colon);
+ dec_push(d, st_value);
+ d->i++;
+ break;
+ default:
+ ret = dec_error(d, "invalid_json");
+ goto done;
+ }
+ break;
+
+ case st_comma:
+ switch(d->p[d->i]) {
+ case ' ':
+ case '\n':
+ case '\r':
+ case '\t':
+ d->i++;
+ break;
+ case ',':
+ dec_pop(d, st_comma);
+ switch(dec_curr(d)) {
+ case st_object:
+ dec_push(d, st_key);
+ break;
+ case st_array:
+ dec_push(d, st_value);
+ break;
+ default:
+ ret = dec_error(d, "internal_error");
+ goto done;
+ }
+ d->i++;
+ break;
+ case '}':
+ dec_pop(d, st_comma);
+ if(dec_curr(d) != st_object) {
+ ret = dec_error(d, "invalid_json");
+ goto done;
+ }
+ dec_pop(d, st_object);
+ dec_pop(d, st_value);
+ val = make_object(env, curr);
+ if(!enif_get_list_cell(env, objs, &curr, &objs)) {
+ ret = dec_error(d, "internal_error");
+ goto done;
+ }
+ if(dec_top(d) > 0) {
+ dec_push(d, st_comma);
+ curr = enif_make_list_cell(env, val, curr);
+ } else {
+ dec_push(d, st_done);
+ }
+ d->i++;
+ break;
+ case ']':
+ dec_pop(d, st_comma);
+ if(dec_curr(d) != st_array) {
+ ret = dec_error(d, "invalid_json");
+ goto done;
+ }
+ dec_pop(d, st_array);
+ dec_pop(d, st_value);
+ val = make_array(env, curr);
+ if(!enif_get_list_cell(env, objs, &curr, &objs)) {
+ ret = dec_error(d, "internal_error");
+ goto done;
+ }
+ if(dec_top(d) > 0) {
+ dec_push(d, st_comma);
+ curr = enif_make_list_cell(env, val, curr);
+ } else {
+ dec_push(d, st_done);
+ }
+ d->i++;
+ break;
+ default:
+ ret = dec_error(d, "invalid_json");
+ goto done;
+ }
+ break;
+
+ case st_done:
+ switch(d->p[d->i]) {
+ case ' ':
+ case '\n':
+ case '\r':
+ case '\t':
+ d->i++;
+ break;
+ default:
+ ret = dec_error(d, "invalid_trailing_data");
+ goto done;
+ }
+ break;
+
+ default:
+ ret = dec_error(d, "invalid_internal_state");
+ goto done;
+ }
+ }
+
+ if(dec_curr(d) != st_done) {
+ ret = dec_error(d, "truncated_json");
+ } else if(d->is_partial) {
+ ret = enif_make_tuple2(env, d->atoms->atom_partial, val);
+ } else {
+ ret = val;
+ }
+
+done:
+ dec_destroy(d);
+
+ return ret;
+}
http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/c_src/double-conversion/AUTHORS
----------------------------------------------------------------------
diff --git a/src/jiffy/c_src/double-conversion/AUTHORS b/src/jiffy/c_src/double-conversion/AUTHORS
new file mode 100644
index 0000000..ff2a5f4
--- /dev/null
+++ b/src/jiffy/c_src/double-conversion/AUTHORS
@@ -0,0 +1,12 @@
+# Below is a list of people and organizations that have contributed
+# to the double-conversion project. Names should be added to the
+# list like so:
+#
+# Name/Organization <email address>
+
+Google Inc.
+Mozilla Foundation
+
+Jeff Muizelaar <jm...@mozilla.com>
+Mike Hommey <mh...@mozilla.com>
+Martin Olsson <mn...@minimum.se>
http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/c_src/double-conversion/COPYING
----------------------------------------------------------------------
diff --git a/src/jiffy/c_src/double-conversion/COPYING b/src/jiffy/c_src/double-conversion/COPYING
new file mode 100644
index 0000000..933718a
--- /dev/null
+++ b/src/jiffy/c_src/double-conversion/COPYING
@@ -0,0 +1,26 @@
+Copyright 2006-2011, the V8 project authors. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of Google Inc. nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/c_src/double-conversion/DOWNLOADED
----------------------------------------------------------------------
diff --git a/src/jiffy/c_src/double-conversion/DOWNLOADED b/src/jiffy/c_src/double-conversion/DOWNLOADED
new file mode 100644
index 0000000..64724d8
--- /dev/null
+++ b/src/jiffy/c_src/double-conversion/DOWNLOADED
@@ -0,0 +1,3 @@
+Downloaded: 2013-02-24
+
+https://double-conversion.googlecode.com/files/double-conversion-1.1.1.tar.gz
http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/c_src/double-conversion/LICENSE
----------------------------------------------------------------------
diff --git a/src/jiffy/c_src/double-conversion/LICENSE b/src/jiffy/c_src/double-conversion/LICENSE
new file mode 100644
index 0000000..933718a
--- /dev/null
+++ b/src/jiffy/c_src/double-conversion/LICENSE
@@ -0,0 +1,26 @@
+Copyright 2006-2011, the V8 project authors. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of Google Inc. nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
http://git-wip-us.apache.org/repos/asf/couchdb/blob/2e6092e4/src/jiffy/c_src/double-conversion/README
----------------------------------------------------------------------
diff --git a/src/jiffy/c_src/double-conversion/README b/src/jiffy/c_src/double-conversion/README
new file mode 100644
index 0000000..f186b42
--- /dev/null
+++ b/src/jiffy/c_src/double-conversion/README
@@ -0,0 +1,11 @@
+http://code.google.com/p/double-conversion
+
+This project (double-conversion) provides binary-decimal and decimal-binary
+routines for IEEE doubles.
+
+The library consists of efficient conversion routines that have been extracted
+from the V8 JavaScript engine. The code has been refactored and improved so that
+it can be used more easily in other projects.
+
+There is extensive documentation in src/double-conversion.h. Other examples can
+be found in test/cctest/test-conversions.cc.