xref: /5.5.2/couchdb/src/ejson/ejson.erl (revision c065fadf)
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, validate/2]).
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    case erlang:system_info(otp_release) of
31    "R13B03" -> true;
32    _ -> ok
33    end.
34
35
36decode(IoList) ->
37    try
38        nif_decode(IoList)
39    catch exit:ejson_nif_not_loaded ->
40        erl_decode(IoList)
41    end.
42
43encode(EJson) ->
44    try
45        nif_encode(EJson)
46    catch exit:ejson_nif_not_loaded ->
47        erl_encode(EJson)
48    end.
49
50
51nif_decode(IoList) ->
52    case reverse_tokens(IoList) of
53    {ok, ReverseTokens} ->
54        [[EJson]] = make_ejson(ReverseTokens, [[]]),
55        EJson;
56    Error ->
57        throw({invalid_json, {Error, IoList}})
58    end.
59
60
61erl_decode(IoList) ->
62    try
63        (mochijson2:decoder([{object_hook, fun({struct, L}) -> {L} end}]))(IoList)
64    catch _Type:Error ->
65        throw({invalid_json, {Error, IoList}})
66    end.
67
68
69nif_encode({json, RawJson}) ->
70    RawJson;
71nif_encode(EJson) ->
72    RevList = encode_rev(EJson),
73    final_encode(lists:reverse(lists:flatten([RevList]))).
74
75
76erl_encode(EJson) ->
77    Opts = [{handler, fun mochi_encode_handler/1}],
78    iolist_to_binary((mochijson2:encoder(Opts))(EJson)).
79
80mochi_encode_handler({L}) when is_list(L) ->
81    {struct, L};
82mochi_encode_handler(Bad) ->
83    exit({json_encode, {bad_term, Bad}}).
84
85
86% Encode the json into a reverse list that's almost an iolist
87% everything in the list is the final output except for tuples with
88% {0, Strings} and {1, Floats}, which are to be converted to strings
89% inside the NIF.
90encode_rev({json, RawJson}) ->
91    RawJson;
92encode_rev(true) ->
93    <<"true">>;
94encode_rev(false) ->
95    <<"false">>;
96encode_rev(null) ->
97    <<"null">>;
98encode_rev(I) when is_integer(I) ->
99    list_to_binary(integer_to_list(I));
100encode_rev(S) when is_binary(S) ->
101    {0, S};
102encode_rev(S) when is_atom(S) ->
103    {0, list_to_binary(atom_to_list(S))};
104encode_rev(F) when is_float(F) ->
105    {1, F};
106encode_rev({Props}) when is_list(Props) ->
107    encode_proplist_rev(Props, [<<"{">>]);
108encode_rev(Array) when is_list(Array) ->
109    encode_array_rev(Array, [<<"[">>]);
110encode_rev(Bad) ->
111    throw({json_encode, {bad_term, Bad}}).
112
113
114encode_array_rev([], Acc) ->
115    [<<"]">> | Acc];
116encode_array_rev([Val | Rest], [<<"[">>]) ->
117    encode_array_rev(Rest, [encode_rev(Val), <<"[">>]);
118encode_array_rev([Val | Rest], Acc) ->
119    encode_array_rev(Rest, [encode_rev(Val), <<",">> | Acc]).
120
121
122encode_proplist_rev([], Acc) ->
123    [<<"}">> | Acc];
124encode_proplist_rev([{Key,Val} | Rest], [<<"{">>]) ->
125    encode_proplist_rev(
126        Rest, [encode_rev(Val), <<":">>, {0, as_binary(Key)}, <<"{">>]);
127encode_proplist_rev([{Key,Val} | Rest], Acc) ->
128    encode_proplist_rev(
129        Rest, [encode_rev(Val), <<":">>, {0, as_binary(Key)}, <<",">> | Acc]).
130
131as_binary(B) when is_binary(B) ->
132    B;
133as_binary(A) when is_atom(A) ->
134    list_to_binary(atom_to_list(A));
135as_binary(L) when is_list(L) ->
136    list_to_binary(L).
137
138
139make_ejson([], Stack) ->
140    Stack;
141make_ejson([0 | RevEvs], [ArrayValues, PrevValues | RestStack]) ->
142    % 0 ArrayStart
143    make_ejson(RevEvs, [[ArrayValues | PrevValues] | RestStack]);
144make_ejson([1 | RevEvs], Stack) ->
145    % 1 ArrayEnd
146    make_ejson(RevEvs, [[] | Stack]);
147make_ejson([2 | RevEvs], [ObjValues, PrevValues | RestStack]) ->
148    % 2 ObjectStart
149    make_ejson(RevEvs, [[{ObjValues} | PrevValues] | RestStack]);
150make_ejson([3 | RevEvs], Stack) ->
151    % 3 ObjectEnd
152    make_ejson(RevEvs, [[] | Stack]);
153make_ejson([{0, Value} | RevEvs], [Vals | RestStack] = _Stack) ->
154    % {0, IntegerString}
155    make_ejson(RevEvs, [[list_to_integer(binary_to_list(Value)) | Vals] | RestStack]);
156make_ejson([{1, Value} | RevEvs], [Vals | RestStack] = _Stack) ->
157    % {1, FloatString}
158    make_ejson(RevEvs, [[list_to_float(binary_to_list(Value)) | Vals] | RestStack]);
159make_ejson([{3, String} | RevEvs], [[PrevValue|RestObject] | RestStack] = _Stack) ->
160    % {3 , ObjectKey}
161    make_ejson(RevEvs, [[{String, PrevValue}|RestObject] | RestStack]);
162make_ejson([Value | RevEvs], [Vals | RestStack] = _Stack) ->
163    make_ejson(RevEvs, [[Value | Vals] | RestStack]).
164
165
166reverse_tokens(_) ->
167    exit(ejson_nif_not_loaded).
168
169final_encode(_) ->
170    exit(ejson_nif_not_loaded).
171
172validate(_, _) ->
173    exit(ejson_nif_not_loaded).
174