diff --git a/Makefile b/Makefile deleted file mode 100644 index e719fcc..0000000 --- a/Makefile +++ /dev/null @@ -1,14 +0,0 @@ - -all: - test -d ebin || mkdir ebin - erlc -b beam -o ebin src/*.erl - cp src/jsx.app.src ebin/jsx.app - -clean: - rm -Rf ebin - -test: - erlc -b beam -o ebin priv/b/*.erl - -run: - erl -pa ./ebin -pa ./*/ebin diff --git a/priv/b/jsx_b.beam b/priv/b/jsx_b.beam deleted file mode 100644 index cf970dd..0000000 Binary files a/priv/b/jsx_b.beam and /dev/null differ diff --git a/priv/b/jsx_b.erl b/priv/b/jsx_b.erl deleted file mode 100644 index 15865ab..0000000 --- a/priv/b/jsx_b.erl +++ /dev/null @@ -1,175 +0,0 @@ -%% The MIT License - -%% Copyright (c) 2012 Dmitry Kolesnikov - -%% Permission is hereby granted, free of charge, to any person obtaining a copy -%% of this software and associated documentation files (the "Software"), to deal -%% in the Software without restriction, including without limitation the rights -%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -%% copies of the Software, and to permit persons to whom the Software is -%% furnished to do so, subject to the following conditions: - -%% The above copyright notice and this permission notice shall be included in -%% all copies or substantial portions of the Software. - -%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -%% THE SOFTWARE. --module(jsx_b). - --export([run/2, hot/0]). - --define(LEN_KEY, 32). %% upper bound of object attribute --define(LEN_STR, 256). %% upper bound of string value --define(LEN_INT, 7). %% upper bound of digits --define(JSON, 20). %% number of attributes --define(ALPHA,"qwertyuiopasdfghjklzxcvbnm"). --define(TEXT,"qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890\"\\\b\f\n\r\t"). --define(DIGIT,"123456789"). - - -run(Set, Loop) -> - Json = lists:map(fun(_) -> gen_json(?JSON) end, lists:seq(1, Set)), - Term = lists:map(fun(_) -> gen_term(?JSON) end, lists:seq(1, Set)), - [ - { - b_jsx_json(Json, Loop)%, - %b_mochi_json(Json, Loop) - }, - { - b_jsx_term(Term, Loop)%, - %b_mochi_term(Term, Loop) - } - ]. - -hot() -> - b_jsx_term([gen_term(?JSON)], 100). - - -b_jsx_json(Set, Loop) -> - {T, _} = timer:tc( - fun() -> - lists:foreach( - fun(_) -> - lists:map(fun(X) -> jsx:to_term(X) end, Set) - end, - lists:seq(1, Loop) - ) - end, - [] - ), - {jsx, to_term, T / 1000, T / (Loop * length(Set) * 1000)}. - -b_jsx_term(Set, Loop) -> - erlang:garbage_collect(), - {T, _} = timer:tc( - fun() -> - lists:foreach( - fun(_) -> - %error_logger:info_report([{mem_jsx, erlang:memory(processes)}]), - lists:map(fun(X) -> jsx:to_json(X) end, Set) - end, - lists:seq(1, Loop) - ) - end, - [] - ), - {jsx, to_json, T / 1000, T / (Loop * length(Set) * 1000)}. - - -b_mochi_json(Set, Loop) -> - {T, _} = timer:tc( - fun() -> - lists:foreach( - fun(_) -> - lists:map(fun(X) -> mochijson2:decode(X) end, Set) - end, - lists:seq(1, Loop) - ) - end, - [] - ), - {mochi, to_term, T / 1000, T / (Loop * length(Set) * 1000)}. - -b_mochi_term(Set, Loop) -> - erlang:garbage_collect(), - {T, _} = timer:tc( - fun() -> - lists:foreach( - fun(_) -> - %error_logger:info_report([{mem_mochi, erlang:memory(processes)}]), - lists:map(fun(X) -> mochijson2:encode({struct, X})end, Set) - end, - lists:seq(1, Loop) - ) - end, - [] - ), - {mochi, to_json, T / 1000, T / (Loop * length(Set) * 1000)}. - - -%% -%% generates a json object -gen_json(Len) -> - list_to_binary( - io_lib:format("{~s}", [ - string:join( - lists:map( - fun(_) -> - case random:uniform(2) of - 1 -> - io_lib:format("\"~s\":\"~s\"", - [rstring(?LEN_KEY, ?ALPHA), rstring(?LEN_STR, ?ALPHA)] - ); - 2 -> - io_lib:format("\"~s\":~s", - [rstring(?LEN_KEY, ?ALPHA), rstring(?LEN_INT, ?DIGIT)] - ) - end - end, - lists:seq(1,Len) - ), - "," - ) - ]) - ). - -gen_term(Len) -> - lists:map( - fun(_) -> - case random:uniform(2) of - 1 -> { - list_to_binary(rstring(?LEN_KEY, ?ALPHA)), - list_to_binary(rstring(?LEN_STR, ?ALPHA)) - }; - 2 -> { - list_to_binary(rstring(?LEN_KEY, ?ALPHA)), - list_to_integer(rstring(?LEN_INT, ?DIGIT)) - } - end - end, - lists:seq(1,Len) - ). - -%% -%% -rstring(Length, Alphabet) -> - ustring(random:uniform(Length), Alphabet). - -%% -%% from http://blog.teemu.im/2009/11/07/generating-random-strings-in-erlang/ -ustring(Length, AllowedChars) -> - lists:foldl( - fun(_, Acc) -> - [lists:nth( - random:uniform(length(AllowedChars)), - AllowedChars - )] ++ Acc - end, - [], - lists:seq(1, Length) - ). \ No newline at end of file diff --git a/src/jsx_utils.erl b/src/jsx_utils.erl index 570d271..c131614 100644 --- a/src/jsx_utils.erl +++ b/src/jsx_utils.erl @@ -29,15 +29,6 @@ -include("jsx_opts.hrl"). --define(ESC(C), - <> -> - B = unicode:characters_to_binary(json_escape_sequence(C)), - json_escape2( - <>, - Opts, L + size(B), Len + size(B) - 1 - ); -). - %% parsing of jsx opts @@ -94,96 +85,63 @@ extract_parser_opts([K|Rest], Acc) -> %% everything else should be a legal json string component json_escape(String, Opts) when is_binary(String) -> - %<< <<(case X of $.->$,; _->X end)>> || <> <= String >>. - %json_escape(String, Opts, <<>>). - json_escape2(String, Opts, 0, size(String)). + json_escape(String, Opts, 0, size(String)). -json_escape2(Str, Opts, L, Len) when L < Len -> - case Str of - <> -> %" - json_escape2(<>, Opts, L + 2, Len + 1);%" - <> -> - json_escape2(<>, Opts, L + 2, Len + 1); - <> -> - json_escape2(<>, Opts, L + 2, Len + 1); - <> -> - json_escape2(<>, Opts, L + 2, Len + 1); - <> -> - json_escape2(<>, Opts, L + 2, Len + 1); - <> -> - json_escape2(<>, Opts, L + 2, Len + 1); - <> -> - json_escape2(<>, Opts, L + 2, Len + 1); - % jsonp - <> -> - B = unicode:characters_to_binary(json_escape_sequence(16#2028)), - json_escape2( - <>, - Opts, L + size(B), Len + size(B) - 1 - ); - <> -> - B = unicode:characters_to_binary(json_escape_sequence(16#2029)), - json_escape2( - <>, - Opts, L + size(B), Len + size(B) - 1 - ); - % C >= 0 and C < $\s - ?ESC(00) ?ESC(01) ?ESC(02) ?ESC(03) ?ESC(04) - ?ESC(05) ?ESC(06) ?ESC(07) - ?ESC(11) ?ESC(14) - ?ESC(15) ?ESC(16) ?ESC(17) ?ESC(18) ?ESC(19) - ?ESC(20) ?ESC(21) ?ESC(22) ?ESC(23) ?ESC(24) - ?ESC(25) ?ESC(26) ?ESC(27) ?ESC(28) ?ESC(29) - ?ESC(30) ?ESC(31) - _ -> - json_escape2(Str, Opts, L + 1, Len) - end; -json_escape2(Str, _, L, Len) when L =:= Len -> - Str. - -%% double quote -json_escape(<<$\", Rest/binary>>, Opts, Acc) -> %" - json_escape(Rest, Opts, <>); %" -%% backslash \ reverse solidus -json_escape(<<$\\, Rest/binary>>, Opts, Acc) -> - json_escape(Rest, Opts, <>); -%% backspace -json_escape(<<$\b, Rest/binary>>, Opts, Acc) -> - json_escape(Rest, Opts, <>); -%% form feed -json_escape(<<$\f, Rest/binary>>, Opts, Acc) -> - json_escape(Rest, Opts, <>); -%% newline -json_escape(<<$\n, Rest/binary>>, Opts, Acc) -> - json_escape(Rest, Opts, <>); -%% cr -json_escape(<<$\r, Rest/binary>>, Opts, Acc) -> - json_escape(Rest, Opts, <>); -%% tab -json_escape(<<$\t, Rest/binary>>, Opts, Acc) -> - json_escape(Rest, Opts, <>); -%% other control characters -json_escape(<>, Opts, Acc) when C >= 0, C < $\s -> - json_escape(Rest, Opts, <>); -%% escape forward slashes -- optionally -- to faciliate microsoft's retarded -%% date format -json_escape(<<$/, Rest/binary>>, Opts=#opts{escape_forward_slash=true}, Acc) -> - json_escape(Rest, Opts, <>); -%% skip escaping u+2028 and u+2029 -json_escape(<>, Opts=#opts{no_jsonp_escapes=true}, Acc) - when C == 16#2028; C == 16#2029 -> - json_escape(Rest, Opts, <>); -%% escape u+2028 and u+2029 to avoid problems with jsonp -json_escape(<>, Opts, Acc) - when C == 16#2028; C == 16#2029 -> - json_escape(Rest, Opts, <>); -%% any other legal codepoint -json_escape(<>, Opts, Acc) -> - json_escape(Rest, Opts, <>); -json_escape(<<>>, _Opts, Acc) -> - Acc; -json_escape(Bin, Opts, Acc) -> - erlang:error(badarg, [Bin, Opts, Acc]). +json_escape(Str, Opts, L, Len) when L < Len -> + case Str of + <> -> %" + json_escape(<>, Opts, L + 2, Len + 1); + <> -> + json_escape(<>, Opts, L + 2, Len + 1); + <> -> + json_escape(<>, Opts, L + 2, Len + 1); + <> -> + json_escape(<>, Opts, L + 2, Len + 1); + <> -> + json_escape(<>, Opts, L + 2, Len + 1); + <> -> + json_escape(<>, Opts, L + 2, Len + 1); + <> -> + json_escape(<>, Opts, L + 2, Len + 1); + <> -> + case Opts#opts.escape_forward_slash of + true -> + json_escape(<>, Opts, L + 2, Len + 1); + false -> + json_escape(<>, Opts, L + 1, Len) + end; + <> -> + case Opts#opts.no_jsonp_escapes of + true -> + json_escape(<>, Opts, L + 3, Len); + false -> + B = unicode:characters_to_binary(json_escape_sequence(16#2028)), + json_escape(<>, Opts, L + size(B), Len + size(B) - size(<<16#2028/utf8>>)) + end; + <> -> + case Opts#opts.no_jsonp_escapes of + true -> + json_escape(<>, Opts, L + 3, Len); + false -> + B = unicode:characters_to_binary(json_escape_sequence(16#2029)), + json_escape(<>, Opts, L + size(B), Len + size(B) - size(<<16#2029/utf8>>)) + end; + <> when X < 32 -> + B = unicode:characters_to_binary(json_escape_sequence(X)), + json_escape(<>, Opts, L + size(B), Len + size(B) - size(<>)); + <<_:L/binary, X/utf8, _/binary>> when X < 16#0080 -> + json_escape(Str, Opts, L + 1, Len); + <<_:L/binary, X/utf8, _/binary>> when X < 16#0800 -> + json_escape(Str, Opts, L + 2, Len); + <<_:L/binary, X/utf8, _/binary>> when X < 16#10000 -> + json_escape(Str, Opts, L + 3, Len); + <<_:L/binary, _/utf8, _/binary>> -> + json_escape(Str, Opts, L + 4, Len); + <> -> + erlang:error(badarg, [[<>, Opts]]) + end; +json_escape(Str, _, L, Len) when L =:= Len -> + Str. %% convert a codepoint to it's \uXXXX equiv. @@ -217,8 +175,8 @@ binary_escape_test_() -> }, {"json string hex escape", ?_assertEqual( - json_escape(<<1, 2, 3, 11, 26, 30, 31>>, #opts{}), - <<"\\u0001\\u0002\\u0003\\u000b\\u001a\\u001e\\u001f">> + json_escape(<<0, 1, 2, 3, 11, 26, 30, 31>>, #opts{}), + <<"\\u0000\\u0001\\u0002\\u0003\\u000b\\u001a\\u001e\\u001f">> ) }, {"jsonp protection", @@ -238,6 +196,15 @@ binary_escape_test_() -> json_escape(<<"/Date(1303502009425)/">>, #opts{escape_forward_slash=true}), <<"\\/Date(1303502009425)\\/">> ) + }, + {"bad utf8", + ?_assertError(badarg, json_escape(<<32, 64, 128, 256>>, #opts{})) + }, + {"all sizes of codepoints", + ?_assertEqual( + json_escape(unicode:characters_to_binary([0, 32, 16#80, 16#800, 16#10000]), #opts{}), + <<"\\u0000", 32/utf8, 16#80/utf8, 16#800/utf8, 16#10000/utf8>> + ) } ].