diff --git a/.travis.yml b/.travis.yml index a7265c2..25a09e8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,4 @@ language: erlang script: rebar compile && rebar skip_deps=true eunit otp_release: - - R15B - - R14B02 - - R14B03 - - R14B04 \ No newline at end of file + - R15B \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e719fcc --- /dev/null +++ b/Makefile @@ -0,0 +1,14 @@ + +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 new file mode 100644 index 0000000..cf970dd Binary files /dev/null and b/priv/b/jsx_b.beam differ diff --git a/priv/b/jsx_b.erl b/priv/b/jsx_b.erl new file mode 100644 index 0000000..15865ab --- /dev/null +++ b/priv/b/jsx_b.erl @@ -0,0 +1,175 @@ +%% 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 0ca6e8c..570d271 100644 --- a/src/jsx_utils.erl +++ b/src/jsx_utils.erl @@ -29,6 +29,15 @@ -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 @@ -85,11 +94,56 @@ extract_parser_opts([K|Rest], Acc) -> %% everything else should be a legal json string component json_escape(String, Opts) when is_binary(String) -> - json_escape(String, Opts, <<>>). + %<< <<(case X of $.->$,; _->X end)>> || <> <= String >>. + %json_escape(String, Opts, <<>>). + json_escape2(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, <>); +json_escape(<<$\", Rest/binary>>, Opts, Acc) -> %" + json_escape(Rest, Opts, <>); %" %% backslash \ reverse solidus json_escape(<<$\\, Rest/binary>>, Opts, Acc) -> json_escape(Rest, Opts, <>);