jsone_encode.erl 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. %%% @doc JSON encoding module
  2. %%% @private
  3. %%% @end
  4. %%%
  5. %%% Copyright (c) 2013-2015, Takeru Ohta <phjgt308@gmail.com>
  6. %%%
  7. %%% The MIT License
  8. %%%
  9. %%% Permission is hereby granted, free of charge, to any person obtaining a copy
  10. %%% of this software and associated documentation files (the "Software"), to deal
  11. %%% in the Software without restriction, including without limitation the rights
  12. %%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  13. %%% copies of the Software, and to permit persons to whom the Software is
  14. %%% furnished to do so, subject to the following conditions:
  15. %%%
  16. %%% The above copyright notice and this permission notice shall be included in
  17. %%% all copies or substantial portions of the Software.
  18. %%%
  19. %%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20. %%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21. %%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  22. %%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23. %%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24. %%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  25. %%% THE SOFTWARE.
  26. %%%
  27. %%%---------------------------------------------------------------------------------------
  28. -module(jsone_encode).
  29. %%--------------------------------------------------------------------------------
  30. %% Exported API
  31. %%--------------------------------------------------------------------------------
  32. -export([encode/1, encode/2]).
  33. %%--------------------------------------------------------------------------------
  34. %% Macros & Records & Types
  35. %%--------------------------------------------------------------------------------
  36. -define(ERROR(Function, Args), {error, {badarg, [{?MODULE, Function, Args, [{line, ?LINE}]}]}}).
  37. -define(IS_REDUNDANT_UTF8(B1, B2, FirstBitN), (B1 =:= 0 andalso B2 < (1 bsl (FirstBitN + 1)))).
  38. -define(HEX(N, I), (binary:at(<<"0123456789abcdef">>, (N bsr (I * 4)) band 2#1111))).
  39. -define(UNICODE_TO_HEX(Code), ?HEX(Code, 3), ?HEX(Code, 2), ?HEX(Code, 1), ?HEX(Code, 0)).
  40. -define(IS_STR(X), is_binary(X); is_atom(X)).
  41. -type encode_result() :: {ok, binary()} | {error, {Reason::term(), [erlang:stack_item()]}}.
  42. -type next() :: {array_values, [jsone:json_value()]}
  43. | {object_value, jsone:json_value(), jsone:json_object_members()}
  44. | {object_members, jsone:json_object_members()}.
  45. -record(encode_opt_v1, {
  46. native_utf8 = false :: boolean(),
  47. space = 0 :: non_neg_integer(),
  48. indent = 0 :: non_neg_integer()
  49. }).
  50. -define(OPT, #encode_opt_v1).
  51. -type opt() :: #encode_opt_v1{}.
  52. %%--------------------------------------------------------------------------------
  53. %% Exported Functions
  54. %%--------------------------------------------------------------------------------
  55. -spec encode(jsone:json_value()) -> encode_result().
  56. encode(Value) ->
  57. encode(Value, []).
  58. -spec encode(jsone:json_value(), [jsone:encode_option()]) -> encode_result().
  59. encode(Value, Options) ->
  60. Opt = parse_options(Options),
  61. value(Value, [], <<"">>, Opt).
  62. %%--------------------------------------------------------------------------------
  63. %% Internal Functions
  64. %%--------------------------------------------------------------------------------
  65. -spec next([next()], binary(), opt()) -> encode_result().
  66. next([], Buf, _) -> {ok, Buf};
  67. next(Level = [Next | Nexts], Buf, Opt) ->
  68. case Next of
  69. {array_values, Values} ->
  70. case Values of
  71. [] -> array_values(Values, Nexts, Buf, Opt);
  72. _ -> array_values(Values, Nexts, pp_newline_or_space(<<Buf/binary, $,>>, Level, Opt), Opt)
  73. end;
  74. {object_value, Value, Members} ->
  75. object_value(Value, Members, Nexts, pp_space(<<Buf/binary, $:>>, Opt), Opt);
  76. {object_members, Members} ->
  77. case Members of
  78. [] -> object_members(Members, Nexts, Buf, Opt);
  79. _ -> object_members(Members, Nexts, pp_newline_or_space(<<Buf/binary, $,>>, Level, Opt), Opt)
  80. end
  81. end.
  82. -spec value(jsone:json_value(), [next()], binary(), opt()) -> encode_result().
  83. value(null, Nexts, Buf, Opt) -> next(Nexts, <<Buf/binary, "null">>, Opt);
  84. value(false, Nexts, Buf, Opt) -> next(Nexts, <<Buf/binary, "false">>, Opt);
  85. value(true, Nexts, Buf, Opt) -> next(Nexts, <<Buf/binary, "true">>, Opt);
  86. value(Value, Nexts, Buf, Opt) when is_integer(Value) -> next(Nexts, <<Buf/binary, (integer_to_binary(Value))/binary>>, Opt);
  87. value(Value, Nexts, Buf, Opt) when is_float(Value) -> next(Nexts, <<Buf/binary, (float_to_binary(Value))/binary>>, Opt);
  88. value(Value, Nexts, Buf, Opt) when ?IS_STR(Value) -> string(Value, Nexts, Buf, Opt);
  89. value({Value}, Nexts, Buf, Opt) -> object(Value, Nexts, Buf, Opt);
  90. value([{}], Nexts, Buf, Opt) -> object([], Nexts, Buf, Opt);
  91. value([{_, _}|_] = Value, Nexts, Buf, Opt) -> object(Value, Nexts, Buf, Opt);
  92. value(Value, Nexts, Buf, Opt) when is_map(Value) -> object(maps:to_list(Value), Nexts, Buf, Opt);
  93. value(Value, Nexts, Buf, Opt) when is_list(Value) -> array(Value, Nexts, Buf, Opt);
  94. value(Value, Nexts, Buf, Opt) -> ?ERROR(value, [Value, Nexts, Buf, Opt]).
  95. -spec string(jsone:json_string(), [next()], binary(), opt()) -> encode_result().
  96. string(<<Str/binary>>, Nexts, Buf, Opt) ->
  97. escape_string(Str, Nexts, <<Buf/binary, $">>, Opt);
  98. string(Str, Nexts, Buf, Opt) ->
  99. string(atom_to_binary(Str, utf8), Nexts, Buf, Opt).
  100. -spec escape_string(binary(), [next()], binary(), opt()) -> encode_result().
  101. escape_string(<<"">>, Nexts, Buf, Opt) -> next(Nexts, <<Buf/binary, $">>, Opt);
  102. escape_string(<<$", Str/binary>>, Nexts, Buf, Opt) -> escape_string(Str, Nexts, <<Buf/binary, $\\, $">>, Opt);
  103. escape_string(<<$\/, Str/binary>>, Nexts, Buf, Opt) -> escape_string(Str, Nexts, <<Buf/binary, $\\, $\/>>, Opt);
  104. escape_string(<<$\\, Str/binary>>, Nexts, Buf, Opt) -> escape_string(Str, Nexts, <<Buf/binary, $\\, $\\>>, Opt);
  105. escape_string(<<$\b, Str/binary>>, Nexts, Buf, Opt) -> escape_string(Str, Nexts, <<Buf/binary, $\\, $b>>, Opt);
  106. escape_string(<<$\f, Str/binary>>, Nexts, Buf, Opt) -> escape_string(Str, Nexts, <<Buf/binary, $\\, $f>>, Opt);
  107. escape_string(<<$\n, Str/binary>>, Nexts, Buf, Opt) -> escape_string(Str, Nexts, <<Buf/binary, $\\, $n>>, Opt);
  108. escape_string(<<$\r, Str/binary>>, Nexts, Buf, Opt) -> escape_string(Str, Nexts, <<Buf/binary, $\\, $r>>, Opt);
  109. escape_string(<<$\t, Str/binary>>, Nexts, Buf, Opt) -> escape_string(Str, Nexts, <<Buf/binary, $\\, $t>>, Opt);
  110. escape_string(<<0:1, C:7, Str/binary>>, Nexts, Buf, Opt) -> escape_string(Str, Nexts, <<Buf/binary, C>>, Opt);
  111. escape_string(<<2#110:3, B1:5, 2#10:2, B2:6, Str/binary>>, Nexts, Buf, Opt) when not ?IS_REDUNDANT_UTF8(B1, B2, 5) ->
  112. case Opt?OPT.native_utf8 of
  113. false ->
  114. Unicode = (B1 bsl 6) + B2,
  115. escape_unicode_char(Str, Unicode, Nexts, Buf, Opt);
  116. true ->
  117. unicode_char(Str, <<2#110:3, B1:5, 2#10:2, B2:6>>, Nexts, Buf, Opt)
  118. end;
  119. escape_string(<<2#1110:4, B1:4, 2#10:2, B2:6, 2#10:2, B3:6, Str/binary>>, Nexts, Buf, Opt) when not ?IS_REDUNDANT_UTF8(B1, B2, 4) ->
  120. case Opt?OPT.native_utf8 of
  121. false ->
  122. Unicode = (B1 bsl 12) + (B2 bsl 6) + B3,
  123. escape_unicode_char(Str, Unicode, Nexts, Buf, Opt);
  124. true ->
  125. unicode_char(Str, <<2#1110:4, B1:4, 2#10:2, B2:6, 2#10:2, B3:6>>, Nexts, Buf, Opt)
  126. end;
  127. escape_string(<<2#11110:5, B1:3, 2#10:2, B2:6, 2#10:2, B3:6, 2#10:2, B4:6, Str/binary>>, Nexts, Buf, Opt) when not ?IS_REDUNDANT_UTF8(B1, B2, 3) ->
  128. case Opt?OPT.native_utf8 of
  129. false ->
  130. Unicode = (B1 bsl 18) + (B2 bsl 12) + (B3 bsl 6) + B4,
  131. escape_unicode_char(Str, Unicode, Nexts, Buf, Opt);
  132. true ->
  133. unicode_char(Str, <<2#11000:5, B1:3, 2#10:2, B2:6, 2#10:2, B3:6, 2#10:2, B4:6>>, Nexts, Buf, Opt)
  134. end;
  135. escape_string(Str, Nexts, Buf, Opt) ->
  136. ?ERROR(escape_string, [Str, Nexts, Buf, Opt]).
  137. unicode_char(Str, Char, Nexts, Buf, Opt) ->
  138. escape_string(Str, Nexts, <<Buf/binary, Char/binary>>, Opt).
  139. -spec escape_unicode_char(binary(), char(), [next()], binary(), opt()) -> encode_result().
  140. escape_unicode_char(<<Str/binary>>, Unicode, Nexts, Buf, Opt) when Unicode =< 16#FFFF ->
  141. escape_string(Str, Nexts, <<Buf/binary, $\\, $u, ?UNICODE_TO_HEX(Unicode)>>, Opt);
  142. escape_unicode_char(<<Str/binary>>, Unicode, Nexts, Buf, Opt) ->
  143. %% Surrogate Pair
  144. <<High:10, Low:10>> = <<(Unicode - 16#10000):20>>, % XXX: inefficient
  145. escape_string(Str, Nexts, <<Buf/binary, $\\, $u, ?UNICODE_TO_HEX(High + 16#D800), $\\, $u, ?UNICODE_TO_HEX(Low + 16#DC00)>>, Opt).
  146. -spec array(jsone:json_array(), [next()], binary(), opt()) -> encode_result().
  147. array(List, Nexts, Buf, Opt) ->
  148. array_values(List, Nexts, pp_newline(<<Buf/binary, $[>>, Nexts, 1, Opt), Opt).
  149. -spec array_values(jsone:json_array(), [next()], binary(), opt()) -> encode_result().
  150. array_values([], Nexts, Buf, Opt) -> next(Nexts, <<(pp_newline(Buf, Nexts, Opt))/binary, $]>>, Opt);
  151. array_values([X | Xs], Nexts, Buf, Opt) -> value(X, [{array_values, Xs} | Nexts], Buf, Opt).
  152. -spec object(jsone:json_object_members(), [next()], binary(), opt()) -> encode_result().
  153. object(Members, Nexts, Buf, Opt) ->
  154. object_members(Members, Nexts, pp_newline(<<Buf/binary, ${>>, Nexts, 1, Opt), Opt).
  155. -spec object_members(jsone:json_object_members(), [next()], binary(), opt()) -> encode_result().
  156. object_members([], Nexts, Buf, Opt) -> next(Nexts, <<(pp_newline(Buf, Nexts, Opt))/binary, $}>>, Opt);
  157. object_members([{Key, Value} | Xs], Nexts, Buf, Opt) when ?IS_STR(Key) -> string(Key, [{object_value, Value, Xs} | Nexts], Buf, Opt);
  158. object_members(Arg, Nexts, Buf, Opt) -> ?ERROR(object_members, [Arg, Nexts, Buf, Opt]).
  159. -spec object_value(jsone:json_value(), jsone:json_object_members(), [next()], binary(), opt()) -> encode_result().
  160. object_value(Value, Members, Nexts, Buf, Opt) ->
  161. value(Value, [{object_members, Members} | Nexts], Buf, Opt).
  162. -spec pp_space(binary(), opt()) -> binary().
  163. pp_space(Buf, Opt) -> padding(Buf, Opt?OPT.space).
  164. -spec pp_newline(binary(), list(), opt()) -> binary().
  165. pp_newline(Buf, Level, Opt) -> pp_newline(Buf, Level, 0, Opt).
  166. -spec pp_newline(binary(), list(), non_neg_integer(), opt()) -> binary().
  167. pp_newline(Buf, _, _, ?OPT{indent = 0}) -> Buf;
  168. pp_newline(Buf, L, Extra, ?OPT{indent = N}) -> lists:foldl(fun (_, B) -> padding(B, N) end, padding(<<Buf/binary, $\n>>, Extra * N), L).
  169. -spec pp_newline_or_space(binary(), list(), opt()) -> binary().
  170. pp_newline_or_space(Buf, _, Opt = ?OPT{indent = 0}) -> pp_space(Buf, Opt);
  171. pp_newline_or_space(Buf, L, Opt) -> pp_newline(Buf, L, Opt).
  172. -spec padding(binary(), non_neg_integer()) -> binary().
  173. padding(Buf, 0) -> Buf;
  174. padding(Buf, N) -> padding(<<Buf/binary, $ >>, N - 1).
  175. -spec parse_options([jsone:encode_option()]) -> opt().
  176. parse_options(Options) ->
  177. parse_option(Options, ?OPT{}).
  178. -spec parse_option([jsone:encode_option()], opt()) -> opt().
  179. parse_option([], Opt) -> Opt;
  180. parse_option([native_utf8|T], Opt) ->
  181. parse_option(T, Opt?OPT{native_utf8=true});
  182. parse_option([{space, N}|T], Opt) when is_integer(N), N >= 0 ->
  183. parse_option(T, Opt?OPT{space = N});
  184. parse_option([{indent, N}|T], Opt) when is_integer(N), N >= 0 ->
  185. parse_option(T, Opt?OPT{indent = N});
  186. parse_option(List, Opt) ->
  187. error(badarg, [List, Opt]).