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.

166 lines
4.6 KiB

  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. -on_load(init/0).
  7. decode(Data) ->
  8. decode(Data, []).
  9. decode(Data, Opts) when is_binary(Data), is_list(Opts) ->
  10. case nif_decode_init(Data, Opts) of
  11. {error, _} = Error ->
  12. throw(Error);
  13. {partial, EJson} ->
  14. finish_decode(EJson);
  15. {iter, Decoder, Val, Objs, Curr} ->
  16. decode_loop(Data, Decoder, Val, Objs, Curr);
  17. EJson ->
  18. EJson
  19. end;
  20. decode(Data, Opts) when is_list(Data) ->
  21. decode(iolist_to_binary(Data), Opts).
  22. encode(Data) ->
  23. encode(Data, []).
  24. encode(Data, Options) ->
  25. ForceUTF8 = lists:member(force_utf8, Options),
  26. case nif_encode_init(Data, Options) of
  27. {error, {invalid_string, _}} when ForceUTF8 == true ->
  28. FixedData = jiffy_utf8:fix(Data),
  29. encode(FixedData, Options -- [force_utf8]);
  30. {error, _} = Error ->
  31. throw(Error);
  32. {partial, IOData} ->
  33. finish_encode(IOData, []);
  34. {iter, Encoder, Stack, IOBuf} ->
  35. encode_loop(Data, Options, Encoder, Stack, IOBuf);
  36. IOData ->
  37. IOData
  38. end.
  39. finish_decode({bignum, Value}) ->
  40. list_to_integer(binary_to_list(Value));
  41. finish_decode({bignum_e, Value}) ->
  42. {IVal, EVal} = case string:to_integer(binary_to_list(Value)) of
  43. {I, [$e | ExpStr]} ->
  44. {E, []} = string:to_integer(ExpStr),
  45. {I, E};
  46. {I, [$E | ExpStr]} ->
  47. {E, []} = string:to_integer(ExpStr),
  48. {I, E}
  49. end,
  50. IVal * math:pow(10, EVal);
  51. finish_decode({bigdbl, Value}) ->
  52. list_to_float(binary_to_list(Value));
  53. finish_decode({Pairs}) when is_list(Pairs) ->
  54. finish_decode_obj(Pairs, []);
  55. finish_decode(Vals) when is_list(Vals) ->
  56. finish_decode_arr(Vals, []);
  57. finish_decode(Val) ->
  58. maybe_map(Val).
  59. -ifndef(JIFFY_NO_MAPS).
  60. maybe_map(Obj) when is_map(Obj) ->
  61. maps:map(fun finish_decode_map/2, Obj);
  62. maybe_map(Val) ->
  63. Val.
  64. finish_decode_map(_, V) ->
  65. finish_decode(V).
  66. -else.
  67. maybe_map(Val) ->
  68. Val.
  69. -endif.
  70. finish_decode_obj([], Acc) ->
  71. {lists:reverse(Acc)};
  72. finish_decode_obj([{K, V} | Pairs], Acc) ->
  73. finish_decode_obj(Pairs, [{K, finish_decode(V)} | Acc]).
  74. finish_decode_arr([], Acc) ->
  75. lists:reverse(Acc);
  76. finish_decode_arr([V | Vals], Acc) ->
  77. finish_decode_arr(Vals, [finish_decode(V) | Acc]).
  78. finish_encode([], Acc) ->
  79. %% No reverse! The NIF returned us
  80. %% the pieces in reverse order.
  81. Acc;
  82. finish_encode([<<_/binary>>=B | Rest], Acc) ->
  83. finish_encode(Rest, [B | Acc]);
  84. finish_encode([Val | Rest], Acc) when is_integer(Val) ->
  85. Bin = list_to_binary(integer_to_list(Val)),
  86. finish_encode(Rest, [Bin | Acc]);
  87. finish_encode([InvalidEjson | _], _) ->
  88. throw({error, {invalid_ejson, InvalidEjson}});
  89. finish_encode(_, _) ->
  90. throw({error, invalid_ejson}).
  91. init() ->
  92. PrivDir = case code:priv_dir(?MODULE) of
  93. {error, _} ->
  94. EbinDir = filename:dirname(code:which(?MODULE)),
  95. AppPath = filename:dirname(EbinDir),
  96. filename:join(AppPath, "priv");
  97. Path ->
  98. Path
  99. end,
  100. erlang:load_nif(filename:join(PrivDir, "jiffy"), 0).
  101. decode_loop(Data, Decoder, Val, Objs, Curr) ->
  102. case nif_decode_iter(Data, Decoder, Val, Objs, Curr) of
  103. {error, _} = Error ->
  104. throw(Error);
  105. {partial, EJson} ->
  106. finish_decode(EJson);
  107. {iter, NewDecoder, NewVal, NewObjs, NewCurr} ->
  108. decode_loop(Data, NewDecoder, NewVal, NewObjs, NewCurr);
  109. EJson ->
  110. EJson
  111. end.
  112. encode_loop(Data, Options, Encoder, Stack, IOBuf) ->
  113. ForceUTF8 = lists:member(force_utf8, Options),
  114. case nif_encode_iter(Encoder, Stack, IOBuf) of
  115. {error, {invalid_string, _}} when ForceUTF8 == true ->
  116. FixedData = jiffy_utf8:fix(Data),
  117. encode(FixedData, Options -- [force_utf8]);
  118. {error, _} = Error ->
  119. throw(Error);
  120. {partial, IOData} ->
  121. finish_encode(IOData, []);
  122. {iter, NewEncoder, NewStack, NewIOBuf} ->
  123. encode_loop(Data, Options, NewEncoder, NewStack, NewIOBuf);
  124. IOData ->
  125. IOData
  126. end.
  127. not_loaded(Line) ->
  128. erlang:nif_error({not_loaded, [{module, ?MODULE}, {line, Line}]}).
  129. nif_decode_init(_Data, _Opts) ->
  130. ?NOT_LOADED.
  131. nif_decode_iter(_Data, _Decoder, _, _, _) ->
  132. ?NOT_LOADED.
  133. nif_encode_init(_Data, _Options) ->
  134. ?NOT_LOADED.
  135. nif_encode_iter(_Encoder, _Stack, _IoList) ->
  136. ?NOT_LOADED.