xref: /5.5.2/couchdb/src/ejson/ejson.erl (revision 2e99f965)
1% Licensed under the Apache License, Version 2.0 (the "License"); you may not
2% use this file except in compliance with the License. You may obtain a copy of
3% the License at
4%
5%   http://www.apache.org/licenses/LICENSE-2.0
6%
7% Unless required by applicable law or agreed to in writing, software
8% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10% License for the specific language governing permissions and limitations under
11% the License.
12
13-module(ejson).
14-export([encode/1, decode/1]).
15-on_load(init/0).
16
17init() ->
18    SoName = case code:priv_dir(ejson) of
19    {error, bad_name} ->
20        case filelib:is_dir(filename:join(["..", priv])) of
21        true ->
22            filename:join(["..", priv, ejson]);
23        false ->
24            filename:join([priv, ejson])
25        end;
26    Dir ->
27        filename:join(Dir, ejson)
28    end,
29    (catch erlang:load_nif(SoName, 0)),
30    ok.
31
32
33decode(IoList) ->
34    try
35        nif_decode(IoList)
36    catch exit:ejson_nif_not_loaded ->
37        erl_decode(IoList)
38    end.
39
40encode(EJson) ->
41    try
42        nif_encode(EJson)
43    catch exit:ejson_nif_not_loaded ->
44        erl_encode(EJson)
45    end.
46
47
48nif_decode(IoList) ->
49    case reverse_tokens(IoList) of
50    {ok, ReverseTokens} ->
51        [[EJson]] = make_ejson(ReverseTokens, [[]]),
52        EJson;
53    Error ->
54        throw({invalid_json, {Error, IoList}})
55    end.
56
57
58erl_decode(IoList) ->
59    try
60        (mochijson2:decoder([{object_hook, fun({struct, L}) -> {L} end}]))(IoList)
61    catch _Type:Error ->
62        throw({invalid_json, {Error, IoList}})
63    end.
64
65
66nif_encode(EJson) ->
67    RevList = encode_rev(EJson),
68    final_encode(lists:reverse(lists:flatten([RevList]))).
69
70
71erl_encode(EJson) ->
72    Opts = [{handler, fun mochi_encode_handler/1}],
73    iolist_to_binary((mochijson2:encoder(Opts))(EJson)).
74
75mochi_encode_handler({L}) when is_list(L) ->
76    {struct, L};
77mochi_encode_handler(Bad) ->
78    exit({json_encode, {bad_term, Bad}}).
79
80
81% Encode the json into a reverse list that's almost an iolist
82% everything in the list is the final output except for tuples with
83% {0, Strings} and {1, Floats}, which are to be converted to strings
84% inside the NIF.
85encode_rev(true) ->
86    <<"true">>;
87encode_rev(false) ->
88    <<"false">>;
89encode_rev(null) ->
90    <<"null">>;
91encode_rev(I) when is_integer(I) ->
92    list_to_binary(integer_to_list(I));
93encode_rev(S) when is_binary(S) ->
94    {0, S};
95encode_rev(S) when is_atom(S) ->
96    {0, list_to_binary(atom_to_list(S))};
97encode_rev(F) when is_float(F) ->
98    {1, F};
99encode_rev({Props}) when is_list(Props) ->
100    encode_proplist_rev(Props, [<<"{">>]);
101encode_rev(Array) when is_list(Array) ->
102    encode_array_rev(Array, [<<"[">>]);
103encode_rev(Bad) ->
104    throw({json_encode, {bad_term, Bad}}).
105
106
107encode_array_rev([], Acc) ->
108    [<<"]">> | Acc];
109encode_array_rev([Val | Rest], [<<"[">>]) ->
110    encode_array_rev(Rest, [encode_rev(Val), <<"[">>]);
111encode_array_rev([Val | Rest], Acc) ->
112    encode_array_rev(Rest, [encode_rev(Val), <<",">> | Acc]).
113
114
115encode_proplist_rev([], Acc) ->
116    [<<"}">> | Acc];
117encode_proplist_rev([{Key,Val} | Rest], [<<"{">>]) ->
118    encode_proplist_rev(
119        Rest, [encode_rev(Val), <<":">>, {0, as_binary(Key)}, <<"{">>]);
120encode_proplist_rev([{Key,Val} | Rest], Acc) ->
121    encode_proplist_rev(
122        Rest, [encode_rev(Val), <<":">>, {0, as_binary(Key)}, <<",">> | Acc]).
123
124as_binary(B) when is_binary(B) ->
125    B;
126as_binary(A) when is_atom(A) ->
127    list_to_binary(atom_to_list(A));
128as_binary(L) when is_list(L) ->
129    list_to_binary(L).
130
131
132make_ejson([], Stack) ->
133    Stack;
134make_ejson([0 | RevEvs], [ArrayValues, PrevValues | RestStack]) ->
135    % 0 ArrayStart
136    make_ejson(RevEvs, [[ArrayValues | PrevValues] | RestStack]);
137make_ejson([1 | RevEvs], Stack) ->
138    % 1 ArrayEnd
139    make_ejson(RevEvs, [[] | Stack]);
140make_ejson([2 | RevEvs], [ObjValues, PrevValues | RestStack]) ->
141    % 2 ObjectStart
142    make_ejson(RevEvs, [[{ObjValues} | PrevValues] | RestStack]);
143make_ejson([3 | RevEvs], Stack) ->
144    % 3 ObjectEnd
145    make_ejson(RevEvs, [[] | Stack]);
146make_ejson([{0, Value} | RevEvs], [Vals | RestStack] = _Stack) ->
147    % {0, IntegerString}
148    make_ejson(RevEvs, [[list_to_integer(binary_to_list(Value)) | Vals] | RestStack]);
149make_ejson([{1, Value} | RevEvs], [Vals | RestStack] = _Stack) ->
150    % {1, FloatString}
151    make_ejson(RevEvs, [[list_to_float(binary_to_list(Value)) | Vals] | RestStack]);
152make_ejson([{3, String} | RevEvs], [[PrevValue|RestObject] | RestStack] = _Stack) ->
153    % {3 , ObjectKey}
154    make_ejson(RevEvs, [[{String, PrevValue}|RestObject] | RestStack]);
155make_ejson([Value | RevEvs], [Vals | RestStack] = _Stack) ->
156    make_ejson(RevEvs, [[Value | Vals] | RestStack]).
157
158
159reverse_tokens(_) ->
160    exit(ejson_nif_not_loaded).
161
162final_encode(_) ->
163    exit(ejson_nif_not_loaded).
164