You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

218 lines
6.2 KiB

преди 13 години
преди 11 години
преди 10 години
преди 10 години
преди 10 години
преди 14 години
преди 14 години
  1. % This file is part of Jiffy released under the MIT license.
  2. % See the LICENSE file for more information.
  3. -module(jiffy).
  4. -export([decode/1, decode/2, encode/1, encode/2]).
  5. -define(NOT_LOADED, not_loaded(?LINE)).
  6. -compile([no_native]).
  7. -on_load(init/0).
  8. -type json_value() :: null
  9. | true
  10. | false
  11. | json_string()
  12. | json_number()
  13. | json_object()
  14. | json_array().
  15. -type json_array() :: [json_value()].
  16. -type json_string() :: atom() | binary().
  17. -type json_number() :: integer() | float().
  18. -ifdef(JIFFY_NO_MAPS).
  19. -type json_object() :: {[{json_string(),json_value()}]}.
  20. -else.
  21. -type json_object() :: {[{json_string(),json_value()}]}
  22. | #{json_string() => json_value()}.
  23. -endif.
  24. -type jiffy_decode_result() :: json_value()
  25. | {has_trailer, json_value(), binary()}.
  26. -type decode_option() :: return_maps
  27. | use_nil
  28. | return_trailer
  29. | {null_term, any()}
  30. | {bytes_per_iter, non_neg_integer()}
  31. | {bytes_per_red, non_neg_integer()}.
  32. -type encode_option() :: uescape
  33. | pretty
  34. | force_utf8
  35. | escape_forward_slashes
  36. | {bytes_per_iter, non_neg_integer()}
  37. | {bytes_per_red, non_neg_integer()}.
  38. -type decode_options() :: [decode_option()].
  39. -type encode_options() :: [encode_option()].
  40. -export_type([json_value/0, jiffy_decode_result/0]).
  41. -spec decode(iolist() | binary()) -> jiffy_decode_result().
  42. decode(Data) ->
  43. decode(Data, []).
  44. -spec decode(iolist() | binary(), decode_options()) -> json_value().
  45. decode(Data, Opts) when is_binary(Data), is_list(Opts) ->
  46. case nif_decode_init(Data, Opts) of
  47. {error, _} = Error ->
  48. throw(Error);
  49. {partial, EJson} ->
  50. finish_decode(EJson);
  51. {iter, Decoder, Val, Objs, Curr} ->
  52. decode_loop(Data, Decoder, Val, Objs, Curr);
  53. EJson ->
  54. EJson
  55. end;
  56. decode(Data, Opts) when is_list(Data) ->
  57. decode(iolist_to_binary(Data), Opts).
  58. -spec encode(json_value()) -> iolist().
  59. encode(Data) ->
  60. encode(Data, []).
  61. -spec encode(json_value(), encode_options()) -> iolist().
  62. encode(Data, Options) ->
  63. ForceUTF8 = lists:member(force_utf8, Options),
  64. case nif_encode_init(Data, Options) of
  65. {error, {invalid_string, _}} when ForceUTF8 == true ->
  66. FixedData = jiffy_utf8:fix(Data),
  67. encode(FixedData, Options -- [force_utf8]);
  68. {error, _} = Error ->
  69. throw(Error);
  70. {partial, IOData} ->
  71. finish_encode(IOData, []);
  72. {iter, Encoder, Stack, IOBuf} ->
  73. encode_loop(Data, Options, Encoder, Stack, IOBuf);
  74. IOData ->
  75. IOData
  76. end.
  77. finish_decode({bignum, Value}) ->
  78. list_to_integer(binary_to_list(Value));
  79. finish_decode({bignum_e, Value}) ->
  80. {IVal, EVal} = case string:to_integer(binary_to_list(Value)) of
  81. {I, [$e | ExpStr]} ->
  82. {E, []} = string:to_integer(ExpStr),
  83. {I, E};
  84. {I, [$E | ExpStr]} ->
  85. {E, []} = string:to_integer(ExpStr),
  86. {I, E}
  87. end,
  88. IVal * math:pow(10, EVal);
  89. finish_decode({bigdbl, Value}) ->
  90. list_to_float(binary_to_list(Value));
  91. finish_decode({Pairs}) when is_list(Pairs) ->
  92. finish_decode_obj(Pairs, []);
  93. finish_decode(Vals) when is_list(Vals) ->
  94. finish_decode_arr(Vals, []);
  95. finish_decode(Val) ->
  96. maybe_map(Val).
  97. -ifndef(JIFFY_NO_MAPS).
  98. maybe_map(Obj) when is_map(Obj) ->
  99. maps:map(fun finish_decode_map/2, Obj);
  100. maybe_map(Val) ->
  101. Val.
  102. finish_decode_map(_, V) ->
  103. finish_decode(V).
  104. -else.
  105. maybe_map(Val) ->
  106. Val.
  107. -endif.
  108. finish_decode_obj([], Acc) ->
  109. {lists:reverse(Acc)};
  110. finish_decode_obj([{K, V} | Pairs], Acc) ->
  111. finish_decode_obj(Pairs, [{K, finish_decode(V)} | Acc]).
  112. finish_decode_arr([], Acc) ->
  113. lists:reverse(Acc);
  114. finish_decode_arr([V | Vals], Acc) ->
  115. finish_decode_arr(Vals, [finish_decode(V) | Acc]).
  116. finish_encode([], Acc) ->
  117. %% No reverse! The NIF returned us
  118. %% the pieces in reverse order.
  119. Acc;
  120. finish_encode([<<_/binary>>=B | Rest], Acc) ->
  121. finish_encode(Rest, [B | Acc]);
  122. finish_encode([Val | Rest], Acc) when is_integer(Val) ->
  123. Bin = list_to_binary(integer_to_list(Val)),
  124. finish_encode(Rest, [Bin | Acc]);
  125. finish_encode([InvalidEjson | _], _) ->
  126. throw({error, {invalid_ejson, InvalidEjson}});
  127. finish_encode(_, _) ->
  128. throw({error, invalid_ejson}).
  129. init() ->
  130. PrivDir = case code:priv_dir(?MODULE) of
  131. {error, _} ->
  132. EbinDir = filename:dirname(code:which(?MODULE)),
  133. AppPath = filename:dirname(EbinDir),
  134. filename:join(AppPath, "priv");
  135. Path ->
  136. Path
  137. end,
  138. erlang:load_nif(filename:join(PrivDir, "jiffy"), 0).
  139. decode_loop(Data, Decoder, Val, Objs, Curr) ->
  140. case nif_decode_iter(Data, Decoder, Val, Objs, Curr) of
  141. {error, _} = Error ->
  142. throw(Error);
  143. {partial, EJson} ->
  144. finish_decode(EJson);
  145. {iter, NewDecoder, NewVal, NewObjs, NewCurr} ->
  146. decode_loop(Data, NewDecoder, NewVal, NewObjs, NewCurr);
  147. EJson ->
  148. EJson
  149. end.
  150. encode_loop(Data, Options, Encoder, Stack, IOBuf) ->
  151. ForceUTF8 = lists:member(force_utf8, Options),
  152. case nif_encode_iter(Encoder, Stack, IOBuf) of
  153. {error, {invalid_string, _}} when ForceUTF8 == true ->
  154. FixedData = jiffy_utf8:fix(Data),
  155. encode(FixedData, Options -- [force_utf8]);
  156. {error, _} = Error ->
  157. throw(Error);
  158. {partial, IOData} ->
  159. finish_encode(IOData, []);
  160. {iter, NewEncoder, NewStack, NewIOBuf} ->
  161. encode_loop(Data, Options, NewEncoder, NewStack, NewIOBuf);
  162. IOData ->
  163. IOData
  164. end.
  165. not_loaded(Line) ->
  166. erlang:nif_error({not_loaded, [{module, ?MODULE}, {line, Line}]}).
  167. nif_decode_init(_Data, _Opts) ->
  168. ?NOT_LOADED.
  169. nif_decode_iter(_Data, _Decoder, _, _, _) ->
  170. ?NOT_LOADED.
  171. nif_encode_init(_Data, _Options) ->
  172. ?NOT_LOADED.
  173. nif_encode_iter(_Encoder, _Stack, _IoList) ->
  174. ?NOT_LOADED.