Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

183 lignes
5.1 KiB

il y a 7 ans
  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. | dedupe_keys
  30. | copy_strings
  31. | {null_term, any()}
  32. | {bytes_per_iter, non_neg_integer()}
  33. | {bytes_per_red, non_neg_integer()}.
  34. -type encode_option() :: uescape
  35. | pretty
  36. | force_utf8
  37. | use_nil
  38. | escape_forward_slashes
  39. | {bytes_per_iter, non_neg_integer()}
  40. | {bytes_per_red, non_neg_integer()}.
  41. -type decode_options() :: [decode_option()].
  42. -type encode_options() :: [encode_option()].
  43. -export_type([json_value/0, jiffy_decode_result/0]).
  44. -spec decode(iolist() | binary()) -> jiffy_decode_result().
  45. decode(Data) ->
  46. decode(Data, []).
  47. -spec decode(iolist() | binary(), decode_options()) -> jiffy_decode_result().
  48. decode(Data, Opts) when is_binary(Data), is_list(Opts) ->
  49. case nif_decode(Data, Opts) of
  50. {error, _} = Error ->
  51. throw(Error);
  52. {partial, EJson} ->
  53. finish_decode(EJson);
  54. EJson ->
  55. EJson
  56. end;
  57. decode(Data, Opts) when is_list(Data) ->
  58. decode(iolist_to_binary(Data), Opts).
  59. -spec encode(json_value()) -> iodata().
  60. encode(Data) ->
  61. encode(Data, []).
  62. -spec encode(json_value(), encode_options()) -> iodata().
  63. encode(Data, Options) ->
  64. ForceUTF8 = lists:member(force_utf8, Options),
  65. case nif_encode(Data, Options) of
  66. {error, {invalid_string, _}} when ForceUTF8 == true ->
  67. FixedData = jiffy_utf8:fix(Data),
  68. encode(FixedData, Options -- [force_utf8]);
  69. {error, {invalid_object_member_key, _}} when ForceUTF8 == true ->
  70. FixedData = jiffy_utf8:fix(Data),
  71. encode(FixedData, Options -- [force_utf8]);
  72. {error, _} = Error ->
  73. throw(Error);
  74. {partial, IOData} ->
  75. finish_encode(IOData, []);
  76. IOData ->
  77. IOData
  78. end.
  79. finish_decode({bignum, Value}) ->
  80. list_to_integer(binary_to_list(Value));
  81. finish_decode({bignum_e, Value}) ->
  82. {IVal, EVal} = case string:to_integer(binary_to_list(Value)) of
  83. {I, [$e | ExpStr]} ->
  84. {E, []} = string:to_integer(ExpStr),
  85. {I, E};
  86. {I, [$E | ExpStr]} ->
  87. {E, []} = string:to_integer(ExpStr),
  88. {I, E}
  89. end,
  90. IVal * math:pow(10, EVal);
  91. finish_decode({bigdbl, Value}) ->
  92. list_to_float(binary_to_list(Value));
  93. finish_decode({Pairs}) when is_list(Pairs) ->
  94. finish_decode_obj(Pairs, []);
  95. finish_decode(Vals) when is_list(Vals) ->
  96. finish_decode_arr(Vals, []);
  97. finish_decode(Val) ->
  98. maybe_map(Val).
  99. -ifndef(JIFFY_NO_MAPS).
  100. maybe_map(Obj) when is_map(Obj) ->
  101. maps:map(fun finish_decode_map/2, Obj);
  102. maybe_map(Val) ->
  103. Val.
  104. finish_decode_map(_, V) ->
  105. finish_decode(V).
  106. -else.
  107. maybe_map(Val) ->
  108. Val.
  109. -endif.
  110. finish_decode_obj([], Acc) ->
  111. {lists:reverse(Acc)};
  112. finish_decode_obj([{K, V} | Pairs], Acc) ->
  113. finish_decode_obj(Pairs, [{K, finish_decode(V)} | Acc]).
  114. finish_decode_arr([], Acc) ->
  115. lists:reverse(Acc);
  116. finish_decode_arr([V | Vals], Acc) ->
  117. finish_decode_arr(Vals, [finish_decode(V) | Acc]).
  118. finish_encode([], Acc) ->
  119. %% No reverse! The NIF returned us
  120. %% the pieces in reverse order.
  121. Acc;
  122. finish_encode([<<_/binary>>=B | Rest], Acc) ->
  123. finish_encode(Rest, [B | Acc]);
  124. finish_encode([Val | Rest], Acc) when is_integer(Val) ->
  125. Bin = list_to_binary(integer_to_list(Val)),
  126. finish_encode(Rest, [Bin | Acc]);
  127. finish_encode([InvalidEjson | _], _) ->
  128. throw({error, {invalid_ejson, InvalidEjson}});
  129. finish_encode(_, _) ->
  130. throw({error, invalid_ejson}).
  131. init() ->
  132. PrivDir = case code:priv_dir(?MODULE) of
  133. {error, _} ->
  134. EbinDir = filename:dirname(code:which(?MODULE)),
  135. AppPath = filename:dirname(EbinDir),
  136. filename:join(AppPath, "priv");
  137. Path ->
  138. Path
  139. end,
  140. erlang:load_nif(filename:join(PrivDir, "jiffy"), 0).
  141. not_loaded(Line) ->
  142. erlang:nif_error({not_loaded, [{module, ?MODULE}, {line, Line}]}).
  143. nif_decode(_Data, _Opts) ->
  144. ?NOT_LOADED.
  145. nif_encode(_Data, _Options) ->
  146. ?NOT_LOADED.