Merge pull request #13 from msgpack/refactor

Refactor, see #13
This commit is contained in:
UENISHI Kota 2013-07-08 20:23:56 -07:00
commit 06d8d98c78
10 changed files with 493 additions and 647 deletions

View File

@ -18,10 +18,10 @@ deps:
compile:
@$(REBAR) compile
xref:
xref: compile
@$(REBAR) xref
eunit: compile
eunit: xref
@$(REBAR) skip_deps=true eunit
test: eunit
@ -33,13 +33,37 @@ doc:
@$(REBAR) doc
bench: compile
@$(REBAR) euni skip_deps=true suites=bench_tests
@$(REBAR) eunit skip_deps=true suites=bench_tests
APPS = kernel stdlib sasl erts ssl tools os_mon runtime_tools crypto inets \
xmerl webtool snmp public_key mnesia eunit syntax_tools compiler
COMBO_PLT = $(HOME)/.msgpack_dialyzer_plt
check_plt: xref
dialyzer --check_plt --plt $(COMBO_PLT) --apps $(APPS) \
deps/*/ebin
build_plt: xref
dialyzer --build_plt --output_plt $(COMBO_PLT) --apps $(APPS) \
deps/*/ebin
dialyzer: xref
@echo
@echo Use "'make check_plt'" to check PLT prior to using this target.
@echo Use "'make build_plt'" to build PLT prior to using this target.
@echo
@sleep 1
dialyzer -Wno_return --plt $(COMBO_PLT) deps/*/ebin | \
fgrep -v -f ./dialyzer.ignore-warnings
check: compile xref
# @echo "you need $(REBAR) build-plt before make check"
# @$(REBAR) build-plt
@$(REBAR) check-plt
@$(REBAR) dialyze
dialyzer --check
# @$(REBAR) check-plt
# @$(REBAR) dialyze
crosslang:
@echo "do ERL_LIBS=../ before you make crosslang or fail"

27
include/msgpack.hrl Normal file
View File

@ -0,0 +1,27 @@
%%
%% MessagePack for Erlang
%%
%% Copyright (C) 2009-2013 UENISHI Kota
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
-type option() :: [jsx | jiffy | nif].
-record(options_v1, {
interface = jiffy :: jiffy | jsx,
map_unpack_fun = fun msgpack_unpacker:unpack_map_jiffy/4 :: fun(),
impl = erlang :: erlang | nif
}).
-define(OPTION, #options_v1).
-type msgpack_option() :: #options_v1{}.

View File

@ -1,6 +1,6 @@
{application, msgpack,
[{description, "MessagePack serializer/deserializer"},
{vsn, "0.1.2"},
{vsn, "0.1.3"},
{modules,
[msgpack]
},

View File

@ -50,68 +50,30 @@
-export_type([object/0, msgpack_map/0]).
-type object() :: msgpack_term().
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% external APIs
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-include("msgpack.hrl").
%% @doc Encode an erlang term into an msgpack binary.
%% Returns {error, {badarg, term()}} if the input is illegal.
-spec pack(Term::msgpack_term()) -> binary() | {error, {badarg, term()}}.
-spec pack(msgpack_term()) -> binary() | {error, {badarg, term()}}.
pack(Term) ->
pack(Term, [jiffy]).
pack(Term, Opt)->
try
pack_(Term, Opt)
msgpack_packer:pack(Term, ?OPTION{})
catch
throw:Exception ->
{error, Exception}
throw:Exception -> {error, Exception}
end.
pack_(Term, [jiffy])->
msgpack_jiffy:pack(Term);
pack_(Term, [jsx])->
msgpack_jsx:pack(Term);
pack_(Term, [nif])->
msgpack_nif:pack(Term).
%% @doc Decode an msgpack binary into an erlang term.
%% It only decodes the first msgpack packet contained in the binary; the rest is returned as is.
%% Returns {error, {badarg, term()}} if the input is corrupted.
%% Returns {error, incomplete} if the input is not a full msgpack packet (caller should gather more data and try again).
unpack_stream(D) ->
unpack_stream(D, [jiffy]).
unpack(D) ->
unpack(D, [jiffy]).
-spec unpack_stream(Bin::binary()) -> {msgpack_term(), binary()} | {error, incomplete} | {error, {badarg, term()}}.
unpack_stream(Bin, Opt) ->
pack(Term, [nif])->
msgpack_nif:pack(Term);
pack(Term, [Interface]) ->
try
unpack_stream_(Bin, Opt)
msgpack_packer:pack(Term, ?OPTION{interface=Interface,
map_unpack_fun=msgpack_unpacker:map_unpacker(Interface)})
catch
throw:Exception ->
{error, Exception}
throw:Exception -> {error, Exception}
end.
unpack_stream_(Bin, [jiffy]) when is_binary(Bin) ->
msgpack_jiffy:unpack(Bin);
unpack_stream_(Bin, [jsx]) when is_binary(Bin) ->
msgpack_jsx:unpack(Bin);
unpack_stream_(Bin, [nif]) when is_binary(Bin) ->
msgpack_nif:unpack(Bin);
unpack_stream_(Other, _Opts) ->
{error, {badarg, Other}}.
%%% @doc Decode an msgpack binary into an erlang terms.
%%% It only decodes ONLY ONE msgpack packets contained in the binary. No packets should not remain.
%%% Returns {error, {badarg, term()}} if the input is corrupted.
@ -120,15 +82,48 @@ unpack_stream_(Other, _Opts) ->
| {error, not_just_binary} % a term deserilized, but binary remains
| {error, incomplete} % too few binary to deserialize complete binary
| {error, {badarg, term()}}.
unpack(Data, Opts) when is_binary(Data) ->
case unpack_stream(Data, Opts) of
unpack(Bin) when is_binary(Bin) ->
case unpack_stream(Bin) of
{error, _} = E -> E;
{Term, <<>>} -> {ok, Term};
{_, Binary} when is_binary(Binary) andalso byte_size(Binary) > 0 -> {error, not_just_binary}
end;
unpack(Other) ->
{error, {badarg, Other}}.
unpack(Badarg, _Opts) ->
{error, {badarg, Badarg}}.
unpack(Bin, Opts) when is_binary(Bin) ->
case unpack_stream(Bin, Opts) of
{error, _} = E -> E;
{Term, <<>>} -> {ok, Term};
{_, Binary} when is_binary(Binary) andalso byte_size(Binary) > 0 -> {error, not_just_binary}
end;
unpack(Other, _) ->
{error, {badarg, Other}}.
-spec unpack_stream(binary()) -> {msgpack_term(), binary()}
| {error, incomplete}
| {error, {badarg, term()}}.
unpack_stream(Bin)->
try
msgpack_unpacker:unpack_stream(Bin, ?OPTION{})
catch
throw:Exception -> {error, Exception}
end.
-spec unpack_stream(binary(), list())-> {msgpack_term(), binary()}
| {error, incomplete}
| {error, {badarg, term()}}.
unpack_stream(Bin, [nif]) ->
msgpack_nif:unpack_stream(Bin);
unpack_stream(Bin, [Interface]) ->
try
msgpack_unpacker:unpack_stream(Bin,
?OPTION{interface=Interface,
map_unpack_fun=msgpack_unpacker:map_unpacker(Interface)})
catch
throw:Exception -> {error, Exception}
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% unit tests

View File

@ -1,59 +0,0 @@
-module(msgpack_gen).
-export([pack_uint/1, pack_int/1, pack_double/1, pack_raw/1]).
-spec pack_uint(non_neg_integer()) -> binary().
%% positive fixnum
pack_uint(N) when N < 128 ->
<< 2#0:1, N:7 >>;
%% uint 8
pack_uint(N) when (N band 16#FF) ->
<< 16#CC:8, N:8 >>;
%% uint 16
pack_uint(N) when (N band 16#FFFF) =:= N ->
<< 16#CD:8, N:16/big-unsigned-integer-unit:1 >>;
%% uint 32
pack_uint(N) when (N band 16#FFFFFFFF) =:= N->
<< 16#CE:8, N:32/big-unsigned-integer-unit:1 >>;
%% uint 64
pack_uint(N) ->
<< 16#CF:8, N:64/big-unsigned-integer-unit:1 >>.
-spec pack_int(integer()) -> binary().
%% negative fixnum
pack_int(N) when N >= -32->
<< 2#111:3, N:5 >>;
%% int 8
pack_int(N) when N > -128 ->
<< 16#D0:8, N:8/big-signed-integer-unit:1 >>;
%% int 16
pack_int(N) when N > -32768 ->
<< 16#D1:8, N:16/big-signed-integer-unit:1 >>;
%% int 32
pack_int(N) when (N band 16#FFFFFFFF) =:= N ->
<< 16#D2:8, N:32/big-signed-integer-unit:1 >>;
%% int 64
pack_int(N) ->
<< 16#D3:8, N:64/big-signed-integer-unit:1 >>.
-spec pack_double(float()) -> binary().
%% float : erlang's float is always IEEE 754 64bit format.
%% pack_float(F) when is_float(F)->
%% << 16#CA:8, F:32/big-float-unit:1 >>.
%% pack_double(F).
%% double
pack_double(F) ->
<< 16#CB:8, F:64/big-float-unit:1 >>.
-spec pack_raw(binary()) -> binary().
%% raw bytes
pack_raw(Bin) ->
case byte_size(Bin) of
Len when Len < 32->
<< 2#101:3, Len:5, Bin/binary >>;
Len when Len < 16#10000 -> % 65536
<< 16#DA:8, Len:16/big-unsigned-integer-unit:1, Bin/binary >>;
Len ->
<< 16#DB:8, Len:32/big-unsigned-integer-unit:1, Bin/binary >>
end.

View File

@ -1,259 +0,0 @@
-module(msgpack_jiffy).
-export([unpack/1, pack/1]).
%% pack them all
-spec pack(msgpack:object()) -> binary() | no_return().
pack(I) when is_integer(I), I < 0 ->
msgpack_gen:pack_int(I);
pack(I) when is_integer(I) ->
msgpack_gen:pack_uint(I);
pack(F) when is_float(F) ->
msgpack_gen:pack_double(F);
pack(nil) ->
<< 16#C0:8 >>;
pack(true) ->
<< 16#C3:8 >>;
pack(false) ->
<< 16#C2:8 >>;
pack(Bin) when is_binary(Bin) ->
msgpack_gen:pack_raw(Bin);
pack({Map}) ->
pack_map(Map);
pack(List) when is_list(List) ->
pack_array(List);
pack(Other) ->
throw({badarg, Other}).
%% unpack them all
-spec unpack(Bin::binary()) -> {msgpack:object(), binary()} | no_return().
%% ATOMS
unpack(<<16#C0, Rest/binary>>) ->
{nil, Rest};
unpack(<<16#C2, Rest/binary>>) ->
{false, Rest};
unpack(<<16#C3, Rest/binary>>) ->
{true, Rest};
%% Floats
unpack(<<16#CA, V:32/float-unit:1, Rest/binary>>) ->
{V, Rest};
unpack(<<16#CB, V:64/float-unit:1, Rest/binary>>) ->
{V, Rest};
%% Unsigned integers
unpack(<<16#CC, V:8/unsigned-integer, Rest/binary>>) ->
{V, Rest};
unpack(<<16#CD, V:16/big-unsigned-integer-unit:1, Rest/binary>>) ->
{V, Rest};
unpack(<<16#CE, V:32/big-unsigned-integer-unit:1, Rest/binary>>) ->
{V, Rest};
unpack(<<16#CF, V:64/big-unsigned-integer-unit:1, Rest/binary>>) ->
{V, Rest};
%% Signed integers
unpack(<<16#D0, V:8/signed-integer, Rest/binary>>) ->
{V, Rest};
unpack(<<16#D1, V:16/big-signed-integer-unit:1, Rest/binary>>) ->
{V, Rest};
unpack(<<16#D2, V:32/big-signed-integer-unit:1, Rest/binary>>) ->
{V, Rest};
unpack(<<16#D3, V:64/big-signed-integer-unit:1, Rest/binary>>) ->
{V, Rest};
%% Raw bytes
unpack(<<16#DA, L:16/unsigned-integer-unit:1, V:L/binary, Rest/binary>>) ->
{V, Rest};
unpack(<<16#DB, L:32/unsigned-integer-unit:1, V:L/binary, Rest/binary>>) ->
{V, Rest};
%% Arrays
unpack(<<16#DC, L:16/big-unsigned-integer-unit:1, Rest/binary>>) ->
unpack_array(Rest, L, []);
unpack(<<16#DD, L:32/big-unsigned-integer-unit:1, Rest/binary>>) ->
unpack_array(Rest, L, []);
%% Maps
unpack(<<16#DE, L:16/big-unsigned-integer-unit:1, Rest/binary>>) ->
unpack_map(Rest, L, []);
unpack(<<16#DF, L:32/big-unsigned-integer-unit:1, Rest/binary>>) ->
unpack_map(Rest, L, []);
%% Tag-encoded lengths (kept last, for speed)
unpack(<<0:1, V:7, Rest/binary>>) ->
{V, Rest}; % positive int
unpack(<<2#111:3, V:5, Rest/binary>>) ->
{V - 2#100000, Rest}; % negative int
unpack(<<2#101:3, L:5, V:L/binary, Rest/binary>>) ->
{V, Rest}; % raw bytes
unpack(<<2#1001:4, L:4, Rest/binary>>) ->
unpack_array(Rest, L, []); % array
unpack(<<2#1000:4, L:4, Rest/binary>>) ->
unpack_map(Rest, L, []); % map
%% Invalid data
unpack(<<F, R/binary>>) when F==16#C1;
F==16#C4; F==16#C5; F==16#C6; F==16#C7; F==16#C8; F==16#C9;
F==16#D4; F==16#D5; F==16#D6; F==16#D7; F==16#D8; F==16#D9 ->
throw({badarg, <<F, R/binary>>});
%% Incomplete data (we've covered every complete/invalid case; anything left is incomplete)
unpack(_) ->
throw(incomplete).
-spec pack_array([msgpack:object()]) -> binary() | no_return().
%% list
pack_array([]) ->
<< 2#1001:4, 0:4/integer-unit:1 >>;
pack_array([A]) ->
<< 2#1001:4, 1:4/integer-unit:1, (pack(A))/binary >>;
pack_array([A, B]) ->
<< 2#1001:4, 2:4/integer-unit:1, (pack(A))/binary, (pack(B))/binary >>;
pack_array([A, B, C]) ->
<< 2#1001:4, 3:4/integer-unit:1, (pack(A))/binary, (pack(B))/binary, (pack(C))/binary >>;
pack_array([A, B, C, D]) ->
<< 2#1001:4, 4:4/integer-unit:1,
(pack(A))/binary, (pack(B))/binary, (pack(C))/binary, (pack(D))/binary >>;
pack_array([A, B, C, D, E]) ->
<< 2#1001:4, 5:4/integer-unit:1,
(pack(A))/binary, (pack(B))/binary, (pack(C))/binary, (pack(D))/binary,
(pack(E))/binary >>;
pack_array([A, B, C, D, E, F]) ->
<< 2#1001:4, 6:4/integer-unit:1,
(pack(A))/binary, (pack(B))/binary, (pack(C))/binary, (pack(D))/binary,
(pack(E))/binary, (pack(F))/binary >>;
pack_array([A, B, C, D, E, F, G]) ->
<< 2#1001:4, 7:4/integer-unit:1,
(pack(A))/binary, (pack(B))/binary, (pack(C))/binary, (pack(D))/binary,
(pack(E))/binary, (pack(F))/binary, (pack(G))/binary >>;
pack_array([A, B, C, D, E, F, G, H]) ->
<< 2#1001:4, 8:4/integer-unit:1,
(pack(A))/binary, (pack(B))/binary, (pack(C))/binary, (pack(D))/binary,
(pack(E))/binary, (pack(F))/binary, (pack(G))/binary, (pack(H))/binary >>;
pack_array([A, B, C, D, E, F, G, H, I]) ->
<< 2#1001:4, 9:4/integer-unit:1,
(pack(A))/binary, (pack(B))/binary, (pack(C))/binary, (pack(D))/binary,
(pack(E))/binary, (pack(F))/binary, (pack(G))/binary, (pack(H))/binary,
(pack(I))/binary >>;
pack_array([A, B, C, D, E, F, G, H, I, J]) ->
<< 2#1001:4, 10:4/integer-unit:1,
(pack(A))/binary, (pack(B))/binary, (pack(C))/binary, (pack(D))/binary,
(pack(E))/binary, (pack(F))/binary, (pack(G))/binary, (pack(H))/binary,
(pack(I))/binary, (pack(J))/binary >>;
pack_array([A, B, C, D, E, F, G, H, I, J, K]) ->
<< 2#1001:4, 11:4/integer-unit:1,
(pack(A))/binary, (pack(B))/binary, (pack(C))/binary, (pack(D))/binary,
(pack(E))/binary, (pack(F))/binary, (pack(G))/binary, (pack(H))/binary,
(pack(I))/binary, (pack(J))/binary, (pack(K))/binary >>;
pack_array([A, B, C, D, E, F, G, H, I, J, K, L]) ->
<< 2#1001:4, 12:4/integer-unit:1,
(pack(A))/binary, (pack(B))/binary, (pack(C))/binary, (pack(D))/binary,
(pack(E))/binary, (pack(F))/binary, (pack(G))/binary, (pack(H))/binary,
(pack(I))/binary, (pack(J))/binary, (pack(K))/binary, (pack(L))/binary >>;
pack_array([A, B, C, D, E, F, G, H, I, J, K, L, M]) ->
<< 2#1001:4, 13:4/integer-unit:1,
(pack(A))/binary, (pack(B))/binary, (pack(C))/binary, (pack(D))/binary,
(pack(E))/binary, (pack(F))/binary, (pack(G))/binary, (pack(H))/binary,
(pack(I))/binary, (pack(J))/binary, (pack(K))/binary, (pack(L))/binary,
(pack(M))/binary >>;
pack_array([A, B, C, D, E, F, G, H, I, J, K, L, M, N]) ->
<< 2#1001:4, 14:4/integer-unit:1,
(pack(A))/binary, (pack(B))/binary, (pack(C))/binary, (pack(D))/binary,
(pack(E))/binary, (pack(F))/binary, (pack(G))/binary, (pack(H))/binary,
(pack(I))/binary, (pack(J))/binary, (pack(K))/binary, (pack(L))/binary,
(pack(M))/binary, (pack(N))/binary >>;
pack_array([A, B, C, D, E, F, G, H, I, J, K, L, M, N, O]) ->
<< 2#1001:4, 15:4/integer-unit:1,
(pack(A))/binary, (pack(B))/binary, (pack(C))/binary, (pack(D))/binary,
(pack(E))/binary, (pack(F))/binary, (pack(G))/binary, (pack(H))/binary,
(pack(I))/binary, (pack(J))/binary, (pack(K))/binary, (pack(L))/binary,
(pack(M))/binary, (pack(N))/binary, (pack(O))/binary >>;
pack_array(L) ->
case length(L) of
Len when Len < 16#10000 -> % 65536
<<16#DC:8, Len:16/big-unsigned-integer-unit:1, (<< <<(pack(E))/binary>> || E <- L >>)/binary>>;
Len ->
<<16#DD:8, Len:32/big-unsigned-integer-unit:1, (<< <<(pack(E))/binary>> || E <- L >>)/binary>>
end.
-spec unpack_array(binary(), non_neg_integer(), [msgpack:object()]) -> {[msgpack:object()], binary()} | no_return().
unpack_array(Bin, 0, Acc) ->
{lists:reverse(Acc), Bin};
unpack_array(Bin, Len, Acc) ->
{Term, Rest} = unpack(Bin),
unpack_array(Rest, Len-1, [Term|Acc]).
-spec pack_map(M::msgpack:msgpack_map()) -> binary() | no_return().
pack_map([{Ka, Va}])->
<< 2#1000:4, 1:4/integer-unit:1,
(pack(Ka))/binary, (pack(Va))/binary >>;
pack_map([{Ka, Va}, {Kb, Vb}])->
<< 2#1000:4, 2:4/integer-unit:1,
(pack(Ka))/binary, (pack(Va))/binary,
(pack(Kb))/binary, (pack(Vb))/binary >>;
pack_map([{Ka, Va}, {Kb, Vb}, {Kc, Vc}])->
<< 2#1000:4, 3:4/integer-unit:1,
(pack(Ka))/binary, (pack(Va))/binary,
(pack(Kb))/binary, (pack(Vb))/binary,
(pack(Kc))/binary, (pack(Vc))/binary >>;
pack_map([{Ka, Va}, {Kb, Vb}, {Kc, Vc}, {Kd, Vd}])->
<< 2#1000:4, 4:4/integer-unit:1,
(pack(Ka))/binary, (pack(Va))/binary,
(pack(Kb))/binary, (pack(Vb))/binary,
(pack(Kc))/binary, (pack(Vc))/binary,
(pack(Kd))/binary, (pack(Vd))/binary >>;
pack_map(M)->
case length(M) of
Len when Len < 16 ->
<<2#1000:4, Len:4/integer-unit:1,
(<< <<(pack(K))/binary, (pack(V))/binary>> || {K, V} <- M >>)/binary>>;
Len when Len < 16#10000 -> % 65536
<<16#DE:8, Len:16/big-unsigned-integer-unit:1,
(<< <<(pack(K))/binary, (pack(V))/binary>> || {K, V} <- M >>)/binary>>;
Len ->
<<16#DF:8, Len:32/big-unsigned-integer-unit:1,
(<< <<(pack(K))/binary, (pack(V))/binary>> || {K, V} <- M >>)/binary>>
end.
%% Users SHOULD NOT send too long list: this uses lists:reverse/1
-spec unpack_map(binary(), non_neg_integer(), msgpack:msgpack_map()) ->
{msgpack:msgpack_map(), binary()} | no_return().
unpack_map(Bin, 0, Acc) ->
{{lists:reverse(Acc)}, Bin};
unpack_map(Bin, Len, Acc) ->
{Key, Rest} = unpack(Bin),
{Value, Rest2} = unpack(Rest),
unpack_map(Rest2, Len-1, [{Key,Value}|Acc]).
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
map_test()->
Ints = lists:seq(0, 65),
Map = {[ {X, X*2} || X <- Ints ] ++ [{<<"hage">>, 324}, {43542, [nil, true, false]}]},
{ok, Map2} = msgpack:unpack(msgpack:pack(Map)),
?assertEqual(Map, Map2),
{ok, Empty} = msgpack:unpack(msgpack:pack({[]})),
?assertEqual({[]}, Empty),
ok.
-endif.

View File

@ -1,265 +0,0 @@
-module(msgpack_jsx).
-export([unpack/1, pack/1]).
%% pack them all
-spec pack(msgpack:object()) -> binary() | no_return().
pack(I) when is_integer(I), I < 0 ->
msgpack_gen:pack_int(I);
pack(I) when is_integer(I) ->
msgpack_gen:pack_uint(I);
pack(F) when is_float(F) ->
msgpack_gen:pack_double(F);
pack(nil) ->
<< 16#C0:8 >>;
pack(true) ->
<< 16#C3:8 >>;
pack(false) ->
<< 16#C2:8 >>;
pack(Bin) when is_binary(Bin) ->
msgpack_gen:pack_raw(Bin);
pack([{}] = Map) ->
pack_map(Map);
pack([{_,_} | _] = Map) ->
pack_map(Map);
pack(List) when is_list(List) ->
pack_array(List);
pack(Other) ->
throw({badarg, Other}).
%% unpack them all
-spec unpack(Bin::binary()) -> {msgpack:object(), binary()} | no_return().
%% ATOMS
unpack(<<16#C0, Rest/binary>>) ->
{nil, Rest};
unpack(<<16#C2, Rest/binary>>) ->
{false, Rest};
unpack(<<16#C3, Rest/binary>>) ->
{true, Rest};
%% Floats
unpack(<<16#CA, V:32/float-unit:1, Rest/binary>>) ->
{V, Rest};
unpack(<<16#CB, V:64/float-unit:1, Rest/binary>>) ->
{V, Rest};
%% Unsigned integers
unpack(<<16#CC, V:8/unsigned-integer, Rest/binary>>) ->
{V, Rest};
unpack(<<16#CD, V:16/big-unsigned-integer-unit:1, Rest/binary>>) ->
{V, Rest};
unpack(<<16#CE, V:32/big-unsigned-integer-unit:1, Rest/binary>>) ->
{V, Rest};
unpack(<<16#CF, V:64/big-unsigned-integer-unit:1, Rest/binary>>) ->
{V, Rest};
%% Signed integers
unpack(<<16#D0, V:8/signed-integer, Rest/binary>>) ->
{V, Rest};
unpack(<<16#D1, V:16/big-signed-integer-unit:1, Rest/binary>>) ->
{V, Rest};
unpack(<<16#D2, V:32/big-signed-integer-unit:1, Rest/binary>>) ->
{V, Rest};
unpack(<<16#D3, V:64/big-signed-integer-unit:1, Rest/binary>>) ->
{V, Rest};
%% Raw bytes
unpack(<<16#DA, L:16/unsigned-integer-unit:1, V:L/binary, Rest/binary>>) ->
{V, Rest};
unpack(<<16#DB, L:32/unsigned-integer-unit:1, V:L/binary, Rest/binary>>) ->
{V, Rest};
%% Arrays
unpack(<<16#DC, L:16/big-unsigned-integer-unit:1, Rest/binary>>) ->
unpack_array(Rest, L, []);
unpack(<<16#DD, L:32/big-unsigned-integer-unit:1, Rest/binary>>) ->
unpack_array(Rest, L, []);
%% Maps
unpack(<<16#DE, L:16/big-unsigned-integer-unit:1, Rest/binary>>) ->
unpack_map(Rest, L, []);
unpack(<<16#DF, L:32/big-unsigned-integer-unit:1, Rest/binary>>) ->
unpack_map(Rest, L, []);
%% Tag-encoded lengths (kept last, for speed)
unpack(<<0:1, V:7, Rest/binary>>) ->
{V, Rest}; % positive int
unpack(<<2#111:3, V:5, Rest/binary>>) ->
{V - 2#100000, Rest}; % negative int
unpack(<<2#101:3, L:5, V:L/binary, Rest/binary>>) ->
{V, Rest}; % raw bytes
unpack(<<2#1001:4, L:4, Rest/binary>>) ->
unpack_array(Rest, L, []); % array
unpack(<<2#1000:4, L:4, Rest/binary>>) ->
unpack_map(Rest, L, []); % map
% Invalid data
unpack(<<F, R/binary>>) when F==16#C1;
F==16#C4; F==16#C5; F==16#C6; F==16#C7; F==16#C8; F==16#C9;
F==16#D4; F==16#D5; F==16#D6; F==16#D7; F==16#D8; F==16#D9 ->
throw({badarg, <<F, R/binary>>});
% Incomplete data (we've covered every complete/invalid case; anything left is incomplete)
unpack(_) ->
throw(incomplete).
-spec pack_array([msgpack:object()]) -> binary() | no_return().
%% list
pack_array([]) ->
<< 2#1001:4, 0:4/integer-unit:1 >>;
pack_array([A]) ->
<< 2#1001:4, 1:4/integer-unit:1, (pack(A))/binary >>;
pack_array([A, B]) ->
<< 2#1001:4, 2:4/integer-unit:1, (pack(A))/binary, (pack(B))/binary >>;
pack_array([A, B, C]) ->
<< 2#1001:4, 3:4/integer-unit:1, (pack(A))/binary, (pack(B))/binary, (pack(C))/binary >>;
pack_array([A, B, C, D]) ->
<< 2#1001:4, 4:4/integer-unit:1,
(pack(A))/binary, (pack(B))/binary, (pack(C))/binary, (pack(D))/binary >>;
pack_array([A, B, C, D, E]) ->
<< 2#1001:4, 5:4/integer-unit:1,
(pack(A))/binary, (pack(B))/binary, (pack(C))/binary, (pack(D))/binary,
(pack(E))/binary >>;
pack_array([A, B, C, D, E, F]) ->
<< 2#1001:4, 6:4/integer-unit:1,
(pack(A))/binary, (pack(B))/binary, (pack(C))/binary, (pack(D))/binary,
(pack(E))/binary, (pack(F))/binary >>;
pack_array([A, B, C, D, E, F, G]) ->
<< 2#1001:4, 7:4/integer-unit:1,
(pack(A))/binary, (pack(B))/binary, (pack(C))/binary, (pack(D))/binary,
(pack(E))/binary, (pack(F))/binary, (pack(G))/binary >>;
pack_array([A, B, C, D, E, F, G, H]) ->
<< 2#1001:4, 8:4/integer-unit:1,
(pack(A))/binary, (pack(B))/binary, (pack(C))/binary, (pack(D))/binary,
(pack(E))/binary, (pack(F))/binary, (pack(G))/binary, (pack(H))/binary >>;
pack_array([A, B, C, D, E, F, G, H, I]) ->
<< 2#1001:4, 9:4/integer-unit:1,
(pack(A))/binary, (pack(B))/binary, (pack(C))/binary, (pack(D))/binary,
(pack(E))/binary, (pack(F))/binary, (pack(G))/binary, (pack(H))/binary,
(pack(I))/binary >>;
pack_array([A, B, C, D, E, F, G, H, I, J]) ->
<< 2#1001:4, 10:4/integer-unit:1,
(pack(A))/binary, (pack(B))/binary, (pack(C))/binary, (pack(D))/binary,
(pack(E))/binary, (pack(F))/binary, (pack(G))/binary, (pack(H))/binary,
(pack(I))/binary, (pack(J))/binary >>;
pack_array([A, B, C, D, E, F, G, H, I, J, K]) ->
<< 2#1001:4, 11:4/integer-unit:1,
(pack(A))/binary, (pack(B))/binary, (pack(C))/binary, (pack(D))/binary,
(pack(E))/binary, (pack(F))/binary, (pack(G))/binary, (pack(H))/binary,
(pack(I))/binary, (pack(J))/binary, (pack(K))/binary >>;
pack_array([A, B, C, D, E, F, G, H, I, J, K, L]) ->
<< 2#1001:4, 12:4/integer-unit:1,
(pack(A))/binary, (pack(B))/binary, (pack(C))/binary, (pack(D))/binary,
(pack(E))/binary, (pack(F))/binary, (pack(G))/binary, (pack(H))/binary,
(pack(I))/binary, (pack(J))/binary, (pack(K))/binary, (pack(L))/binary >>;
pack_array([A, B, C, D, E, F, G, H, I, J, K, L, M]) ->
<< 2#1001:4, 13:4/integer-unit:1,
(pack(A))/binary, (pack(B))/binary, (pack(C))/binary, (pack(D))/binary,
(pack(E))/binary, (pack(F))/binary, (pack(G))/binary, (pack(H))/binary,
(pack(I))/binary, (pack(J))/binary, (pack(K))/binary, (pack(L))/binary,
(pack(M))/binary >>;
pack_array([A, B, C, D, E, F, G, H, I, J, K, L, M, N]) ->
<< 2#1001:4, 14:4/integer-unit:1,
(pack(A))/binary, (pack(B))/binary, (pack(C))/binary, (pack(D))/binary,
(pack(E))/binary, (pack(F))/binary, (pack(G))/binary, (pack(H))/binary,
(pack(I))/binary, (pack(J))/binary, (pack(K))/binary, (pack(L))/binary,
(pack(M))/binary, (pack(N))/binary >>;
pack_array([A, B, C, D, E, F, G, H, I, J, K, L, M, N, O]) ->
<< 2#1001:4, 15:4/integer-unit:1,
(pack(A))/binary, (pack(B))/binary, (pack(C))/binary, (pack(D))/binary,
(pack(E))/binary, (pack(F))/binary, (pack(G))/binary, (pack(H))/binary,
(pack(I))/binary, (pack(J))/binary, (pack(K))/binary, (pack(L))/binary,
(pack(M))/binary, (pack(N))/binary, (pack(O))/binary >>;
pack_array(L) ->
case length(L) of
Len when Len < 16#10000 -> % 65536
<<16#DC:8, Len:16/big-unsigned-integer-unit:1, (<< <<(pack(E))/binary>> || E <- L >>)/binary>>;
Len ->
<<16#DD:8, Len:32/big-unsigned-integer-unit:1, (<< <<(pack(E))/binary>> || E <- L >>)/binary>>
end.
-spec unpack_array(binary(), non_neg_integer(), [msgpack:object()]) -> {[msgpack:object()], binary()} | no_return().
unpack_array(Bin, 0, Acc) ->
{lists:reverse(Acc), Bin};
unpack_array(Bin, Len, Acc) ->
{Term, Rest} = unpack(Bin),
unpack_array(Rest, Len-1, [Term|Acc]).
-spec pack_map(M::msgpack:msgpack_map()) -> binary() | no_return().
pack_map([{}])->
<< 2#1000:4, 0:4/integer-unit:1, <<>>/binary >>;
pack_map([{Ka, Va}])->
<< 2#1000:4, 1:4/integer-unit:1,
(pack(Ka))/binary, (pack(Va))/binary >>;
pack_map([{Ka, Va}, {Kb, Vb}])->
<< 2#1000:4, 2:4/integer-unit:1,
(pack(Ka))/binary, (pack(Va))/binary,
(pack(Kb))/binary, (pack(Vb))/binary >>;
pack_map([{Ka, Va}, {Kb, Vb}, {Kc, Vc}])->
<< 2#1000:4, 3:4/integer-unit:1,
(pack(Ka))/binary, (pack(Va))/binary,
(pack(Kb))/binary, (pack(Vb))/binary,
(pack(Kc))/binary, (pack(Vc))/binary >>;
pack_map([{Ka, Va}, {Kb, Vb}, {Kc, Vc}, {Kd, Vd}])->
<< 2#1000:4, 4:4/integer-unit:1,
(pack(Ka))/binary, (pack(Va))/binary,
(pack(Kb))/binary, (pack(Vb))/binary,
(pack(Kc))/binary, (pack(Vc))/binary,
(pack(Kd))/binary, (pack(Vd))/binary >>;
pack_map(M)->
case length(M) of
Len when Len < 16 ->
<<2#1000:4, Len:4/integer-unit:1,
(<< <<(pack(K))/binary, (pack(V))/binary>> || {K, V} <- M >>)/binary>>;
Len when Len < 16#10000 -> % 65536
<<16#DE:8, Len:16/big-unsigned-integer-unit:1,
(<< <<(pack(K))/binary, (pack(V))/binary>> || {K, V} <- M >>)/binary>>;
Len ->
<<16#DF:8, Len:32/big-unsigned-integer-unit:1,
(<< <<(pack(K))/binary, (pack(V))/binary>> || {K, V} <- M >>)/binary>>
end.
% Users SHOULD NOT send too long list: this uses lists:reverse/1
-spec unpack_map(binary(), non_neg_integer(), msgpack:msgpack_map()) ->
{msgpack:msgpack_map(), binary()} | no_return().
unpack_map(Bin, 0, []) ->
{[{}], Bin};
unpack_map(Bin, 0, Acc) ->
{lists:reverse(Acc), Bin};
unpack_map(Bin, Len, Acc) ->
{Key, Rest} = unpack(Bin),
{Value, Rest2} = unpack(Rest),
unpack_map(Rest2, Len-1, [{Key,Value}|Acc]).
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
map_test()->
Ints = lists:seq(0, 65),
Map = [ {X, X*2} || X <- Ints ] ++ [{<<"hage">>, 324}, {43542, [nil, true, false]}],
{ok, Map2} = msgpack:unpack(msgpack:pack(Map, [jsx]), [jsx]),
?assertEqual(Map, Map2),
{ok, Empty} = msgpack:unpack(msgpack:pack([{}], [jsx]), [jsx]),
?assertEqual([{}], Empty),
ok.
-endif.

237
src/msgpack_packer.erl Normal file
View File

@ -0,0 +1,237 @@
%%
%% MessagePack for Erlang
%%
%% Copyright (C) 2009-2013 UENISHI Kota
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
-module(msgpack_packer).
-export([pack/2]).
-include("msgpack.hrl").
%% pack them all
-spec pack(msgpack:object(), option()) -> binary().
pack(I, _) when is_integer(I) andalso I < 0 ->
pack_int(I);
pack(I, _) when is_integer(I) ->
pack_uint(I);
pack(F, _) when is_float(F) ->
pack_double(F);
pack(nil, _) ->
<< 16#C0:8 >>;
pack(true, _) ->
<< 16#C3:8 >>;
pack(false, _) ->
<< 16#C2:8 >>;
pack(Bin, _) when is_binary(Bin) ->
pack_raw(Bin);
%% jiffy interface
pack({Map}, Opt = ?OPTION{interface=jiffy}) ->
pack_map(Map, Opt);
%% jsx interface
pack(Map, Opt = ?OPTION{interface=jsx}) when Map =:= [{}]->
pack_map([], Opt);
pack([{_,_}|_] = Map, Opt = ?OPTION{interface=jsx}) ->
pack_map(Map, Opt);
pack(List, Opt) when is_list(List) ->
pack_array(List, Opt);
pack(Other, _) ->
throw({badarg, Other}).
-spec pack_int(integer()) -> binary().
%% negative fixnum
pack_int(N) when N >= -32->
<< 2#111:3, N:5 >>;
%% int 8
pack_int(N) when N > -128 ->
<< 16#D0:8, N:8/big-signed-integer-unit:1 >>;
%% int 16
pack_int(N) when N > -32768 ->
<< 16#D1:8, N:16/big-signed-integer-unit:1 >>;
%% int 32
pack_int(N) when (N band 16#FFFFFFFF) =:= N ->
<< 16#D2:8, N:32/big-signed-integer-unit:1 >>;
%% int 64
pack_int(N) ->
<< 16#D3:8, N:64/big-signed-integer-unit:1 >>.
-spec pack_uint(non_neg_integer()) -> binary().
%% positive fixnum
pack_uint(N) when N < 128 ->
<< 2#0:1, N:7 >>;
%% uint 8
pack_uint(N) when (N band 16#FF) =:= N ->
<< 16#CC:8, N:8 >>;
%% uint 16
pack_uint(N) when (N band 16#FFFF) =:= N ->
<< 16#CD:8, N:16/big-unsigned-integer-unit:1 >>;
%% uint 32
pack_uint(N) when (N band 16#FFFFFFFF) =:= N->
<< 16#CE:8, N:32/big-unsigned-integer-unit:1 >>;
%% uint 64
pack_uint(N) ->
<< 16#CF:8, N:64/big-unsigned-integer-unit:1 >>.
-spec pack_double(float()) -> binary().
%% float : erlang's float is always IEEE 754 64bit format.
%% pack_float(F) when is_float(F)->
%% << 16#CA:8, F:32/big-float-unit:1 >>.
%% pack_double(F).
%% double
pack_double(F) ->
<< 16#CB:8, F:64/big-float-unit:1 >>.
-spec pack_raw(binary()) -> binary().
%% raw bytes
pack_raw(Bin) ->
case byte_size(Bin) of
Len when Len < 32->
<< 2#101:3, Len:5, Bin/binary >>;
Len when Len < 16#10000 -> % 65536
<< 16#DA:8, Len:16/big-unsigned-integer-unit:1, Bin/binary >>;
Len ->
<< 16#DB:8, Len:32/big-unsigned-integer-unit:1, Bin/binary >>
end.
-spec pack_array([msgpack:object()], list()) -> binary() | no_return().
pack_array([], _) ->
<< 2#1001:4, 0:4/integer-unit:1 >>;
pack_array([A], Opt) ->
<< 2#1001:4, 1:4/integer-unit:1, (pack(A, Opt))/binary >>;
pack_array([A, B], Opt) ->
<< 2#1001:4, 2:4/integer-unit:1, (pack(A, Opt))/binary, (pack(B, Opt))/binary >>;
pack_array([A, B, C], Opt) ->
<< 2#1001:4, 3:4/integer-unit:1, (pack(A, Opt))/binary, (pack(B, Opt))/binary, (pack(C, Opt))/binary >>;
pack_array([A, B, C, D], Opt) ->
<< 2#1001:4, 4:4/integer-unit:1,
(pack(A, Opt))/binary, (pack(B, Opt))/binary, (pack(C, Opt))/binary, (pack(D, Opt))/binary >>;
pack_array([A, B, C, D, E], Opt) ->
<< 2#1001:4, 5:4/integer-unit:1,
(pack(A, Opt))/binary, (pack(B, Opt))/binary, (pack(C, Opt))/binary, (pack(D, Opt))/binary,
(pack(E, Opt))/binary >>;
pack_array([A, B, C, D, E, F], Opt) ->
<< 2#1001:4, 6:4/integer-unit:1,
(pack(A, Opt))/binary, (pack(B, Opt))/binary, (pack(C, Opt))/binary, (pack(D, Opt))/binary,
(pack(E, Opt))/binary, (pack(F, Opt))/binary >>;
pack_array([A, B, C, D, E, F, G], Opt) ->
<< 2#1001:4, 7:4/integer-unit:1,
(pack(A, Opt))/binary, (pack(B, Opt))/binary, (pack(C, Opt))/binary, (pack(D, Opt))/binary,
(pack(E, Opt))/binary, (pack(F, Opt))/binary, (pack(G, Opt))/binary >>;
pack_array([A, B, C, D, E, F, G, H], Opt) ->
<< 2#1001:4, 8:4/integer-unit:1,
(pack(A, Opt))/binary, (pack(B, Opt))/binary, (pack(C, Opt))/binary, (pack(D, Opt))/binary,
(pack(E, Opt))/binary, (pack(F, Opt))/binary, (pack(G, Opt))/binary, (pack(H, Opt))/binary >>;
pack_array([A, B, C, D, E, F, G, H, I], Opt) ->
<< 2#1001:4, 9:4/integer-unit:1,
(pack(A, Opt))/binary, (pack(B, Opt))/binary, (pack(C, Opt))/binary, (pack(D, Opt))/binary,
(pack(E, Opt))/binary, (pack(F, Opt))/binary, (pack(G, Opt))/binary, (pack(H, Opt))/binary,
(pack(I, Opt))/binary >>;
pack_array([A, B, C, D, E, F, G, H, I, J], Opt) ->
<< 2#1001:4, 10:4/integer-unit:1,
(pack(A, Opt))/binary, (pack(B, Opt))/binary, (pack(C, Opt))/binary, (pack(D, Opt))/binary,
(pack(E, Opt))/binary, (pack(F, Opt))/binary, (pack(G, Opt))/binary, (pack(H, Opt))/binary,
(pack(I, Opt))/binary, (pack(J, Opt))/binary >>;
pack_array([A, B, C, D, E, F, G, H, I, J, K], Opt) ->
<< 2#1001:4, 11:4/integer-unit:1,
(pack(A, Opt))/binary, (pack(B, Opt))/binary, (pack(C, Opt))/binary, (pack(D, Opt))/binary,
(pack(E, Opt))/binary, (pack(F, Opt))/binary, (pack(G, Opt))/binary, (pack(H, Opt))/binary,
(pack(I, Opt))/binary, (pack(J, Opt))/binary, (pack(K, Opt))/binary >>;
pack_array([A, B, C, D, E, F, G, H, I, J, K, L], Opt) ->
<< 2#1001:4, 12:4/integer-unit:1,
(pack(A, Opt))/binary, (pack(B, Opt))/binary, (pack(C, Opt))/binary, (pack(D, Opt))/binary,
(pack(E, Opt))/binary, (pack(F, Opt))/binary, (pack(G, Opt))/binary, (pack(H, Opt))/binary,
(pack(I, Opt))/binary, (pack(J, Opt))/binary, (pack(K, Opt))/binary, (pack(L, Opt))/binary >>;
pack_array([A, B, C, D, E, F, G, H, I, J, K, L, M], Opt) ->
<< 2#1001:4, 13:4/integer-unit:1,
(pack(A, Opt))/binary, (pack(B, Opt))/binary, (pack(C, Opt))/binary, (pack(D, Opt))/binary,
(pack(E, Opt))/binary, (pack(F, Opt))/binary, (pack(G, Opt))/binary, (pack(H, Opt))/binary,
(pack(I, Opt))/binary, (pack(J, Opt))/binary, (pack(K, Opt))/binary, (pack(L, Opt))/binary,
(pack(M, Opt))/binary >>;
pack_array([A, B, C, D, E, F, G, H, I, J, K, L, M, N], Opt) ->
<< 2#1001:4, 14:4/integer-unit:1,
(pack(A, Opt))/binary, (pack(B, Opt))/binary, (pack(C, Opt))/binary, (pack(D, Opt))/binary,
(pack(E, Opt))/binary, (pack(F, Opt))/binary, (pack(G, Opt))/binary, (pack(H, Opt))/binary,
(pack(I, Opt))/binary, (pack(J, Opt))/binary, (pack(K, Opt))/binary, (pack(L, Opt))/binary,
(pack(M, Opt))/binary, (pack(N, Opt))/binary >>;
pack_array([A, B, C, D, E, F, G, H, I, J, K, L, M, N, O], Opt) ->
<< 2#1001:4, 15:4/integer-unit:1,
(pack(A, Opt))/binary, (pack(B, Opt))/binary, (pack(C, Opt))/binary, (pack(D, Opt))/binary,
(pack(E, Opt))/binary, (pack(F, Opt))/binary, (pack(G, Opt))/binary, (pack(H, Opt))/binary,
(pack(I, Opt))/binary, (pack(J, Opt))/binary, (pack(K, Opt))/binary, (pack(L, Opt))/binary,
(pack(M, Opt))/binary, (pack(N, Opt))/binary, (pack(O, Opt))/binary >>;
pack_array(L, Opt) ->
case length(L) of
Len when Len < 16#10000 -> % 65536
<<16#DC:8, Len:16/big-unsigned-integer-unit:1, (<< <<(pack(E, Opt))/binary>> || E <- L >>)/binary>>;
Len ->
<<16#DD:8, Len:32/big-unsigned-integer-unit:1, (<< <<(pack(E, Opt))/binary>> || E <- L >>)/binary>>
end.
-spec pack_map(msgpack:msgpack_map(), list(option())) -> binary() | no_return().
pack_map([{Ka, Va}], Opt)->
<< 2#1000:4, 1:4/integer-unit:1,
(pack(Ka, Opt))/binary, (pack(Va, Opt))/binary >>;
pack_map([{Ka, Va}, {Kb, Vb}], Opt)->
<< 2#1000:4, 2:4/integer-unit:1,
(pack(Ka, Opt))/binary, (pack(Va, Opt))/binary,
(pack(Kb, Opt))/binary, (pack(Vb, Opt))/binary >>;
pack_map([{Ka, Va}, {Kb, Vb}, {Kc, Vc}], Opt)->
<< 2#1000:4, 3:4/integer-unit:1,
(pack(Ka, Opt))/binary, (pack(Va, Opt))/binary,
(pack(Kb, Opt))/binary, (pack(Vb, Opt))/binary,
(pack(Kc, Opt))/binary, (pack(Vc, Opt))/binary >>;
pack_map([{Ka, Va}, {Kb, Vb}, {Kc, Vc}, {Kd, Vd}], Opt)->
<< 2#1000:4, 4:4/integer-unit:1,
(pack(Ka, Opt))/binary, (pack(Va, Opt))/binary,
(pack(Kb, Opt))/binary, (pack(Vb, Opt))/binary,
(pack(Kc, Opt))/binary, (pack(Vc, Opt))/binary,
(pack(Kd, Opt))/binary, (pack(Vd, Opt))/binary >>;
pack_map(M, Opt)->
case length(M) of
Len when Len < 16 ->
<<2#1000:4, Len:4/integer-unit:1,
(<< <<(pack(K, Opt))/binary, (pack(V, Opt))/binary>> || {K, V} <- M >>)/binary>>;
Len when Len < 16#10000 -> % 65536
<<16#DE:8, Len:16/big-unsigned-integer-unit:1,
(<< <<(pack(K, Opt))/binary, (pack(V, Opt))/binary>> || {K, V} <- M >>)/binary>>;
Len ->
<<16#DF:8, Len:32/big-unsigned-integer-unit:1,
(<< <<(pack(K, Opt))/binary, (pack(V, Opt))/binary>> || {K, V} <- M >>)/binary>>
end.

146
src/msgpack_unpacker.erl Normal file
View File

@ -0,0 +1,146 @@
%%
%% MessagePack for Erlang
%%
%% Copyright (C) 2009-2013 UENISHI Kota
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
-module(msgpack_unpacker).
-export([unpack_stream/2, map_unpacker/1]).
-include("msgpack.hrl").
-export([unpack_map_jiffy/4, unpack_map_jsx/4]).
%% unpack them all
-spec unpack_stream(Bin::binary(), msgpack_option()) -> {msgpack:object(), binary()} | no_return().
%% ATOMS
unpack_stream(<<16#C0, Rest/binary>>, _) ->
{nil, Rest};
unpack_stream(<<16#C2, Rest/binary>>, _) ->
{false, Rest};
unpack_stream(<<16#C3, Rest/binary>>, _) ->
{true, Rest};
%% Floats
unpack_stream(<<16#CA, V:32/float-unit:1, Rest/binary>>, _) ->
{V, Rest};
unpack_stream(<<16#CB, V:64/float-unit:1, Rest/binary>>, _) ->
{V, Rest};
%% Unsigned integers
unpack_stream(<<16#CC, V:8/unsigned-integer, Rest/binary>>, _) ->
{V, Rest};
unpack_stream(<<16#CD, V:16/big-unsigned-integer-unit:1, Rest/binary>>, _) ->
{V, Rest};
unpack_stream(<<16#CE, V:32/big-unsigned-integer-unit:1, Rest/binary>>, _) ->
{V, Rest};
unpack_stream(<<16#CF, V:64/big-unsigned-integer-unit:1, Rest/binary>>, _) ->
{V, Rest};
%% Signed integers
unpack_stream(<<16#D0, V:8/signed-integer, Rest/binary>>, _) ->
{V, Rest};
unpack_stream(<<16#D1, V:16/big-signed-integer-unit:1, Rest/binary>>, _) ->
{V, Rest};
unpack_stream(<<16#D2, V:32/big-signed-integer-unit:1, Rest/binary>>, _) ->
{V, Rest};
unpack_stream(<<16#D3, V:64/big-signed-integer-unit:1, Rest/binary>>, _) ->
{V, Rest};
%% Raw bytes
unpack_stream(<<16#DA, L:16/unsigned-integer-unit:1, V:L/binary, Rest/binary>>, _) ->
{V, Rest};
unpack_stream(<<16#DB, L:32/unsigned-integer-unit:1, V:L/binary, Rest/binary>>, _) ->
{V, Rest};
%% Arrays
unpack_stream(<<16#DC, L:16/big-unsigned-integer-unit:1, Rest/binary>>, Opt) ->
unpack_array(Rest, L, [], Opt);
unpack_stream(<<16#DD, L:32/big-unsigned-integer-unit:1, Rest/binary>>, Opt) ->
unpack_array(Rest, L, [], Opt);
%% Maps
unpack_stream(<<16#DE, L:16/big-unsigned-integer-unit:1, Rest/binary>>, Opt) ->
Unpacker = Opt?OPTION.map_unpack_fun,
Unpacker(Rest, L, [], Opt);
unpack_stream(<<16#DF, L:32/big-unsigned-integer-unit:1, Rest/binary>>, Opt) ->
Unpacker = Opt?OPTION.map_unpack_fun,
Unpacker(Rest, L, [], Opt);
%% Tag-encoded lengths (kept last, for speed)
%% positive int
unpack_stream(<<0:1, V:7, Rest/binary>>, _) -> {V, Rest};
%% negative int
unpack_stream(<<2#111:3, V:5, Rest/binary>>, _) -> {V - 2#100000, Rest};
%% raw bytes
unpack_stream(<<2#101:3, L:5, V:L/binary, Rest/binary>>, _) -> {V, Rest};
%% array
unpack_stream(<<2#1001:4, L:4, Rest/binary>>, Opt) ->
unpack_array(Rest, L, [], Opt);
%% map
unpack_stream(<<2#1000:4, L:4, Rest/binary>>, Opt) ->
Unpacker = Opt?OPTION.map_unpack_fun,
Unpacker(Rest, L, [], Opt);
%% Invalid data
unpack_stream(<<F, R/binary>>, _) when F==16#C1;
F==16#C4; F==16#C5; F==16#C6; F==16#C7; F==16#C8; F==16#C9;
F==16#D4; F==16#D5; F==16#D6; F==16#D7; F==16#D8; F==16#D9 ->
throw({badarg, <<F, R/binary>>});
%% Incomplete data (we've covered every complete/invalid case; anything left is incomplete)
unpack_stream(_, _) ->
throw(incomplete).
-spec unpack_array(binary(), non_neg_integer(), [msgpack:object()], msgpack_option()) ->
{[msgpack:object()], binary()} | no_return().
unpack_array(Bin, 0, Acc, _) ->
{lists:reverse(Acc), Bin};
unpack_array(Bin, Len, Acc, Opt) ->
{Term, Rest} = unpack_stream(Bin, Opt),
unpack_array(Rest, Len-1, [Term|Acc], Opt).
map_unpacker(jiffy) ->
fun ?MODULE:unpack_map_jiffy/4;
map_unpacker(jsx) ->
fun ?MODULE:unpack_map_jsx/4.
%% Users SHOULD NOT send too long list: this uses lists:reverse/1
-spec unpack_map_jiffy(binary(), non_neg_integer(),
msgpack:msgpack_map(), msgpack_option()) ->
{msgpack:msgpack_map(), binary()} | no_return().
unpack_map_jiffy(Bin, 0, Acc, _) ->
{{lists:reverse(Acc)}, Bin};
unpack_map_jiffy(Bin, Len, Acc, Opt) ->
{Key, Rest} = unpack_stream(Bin, Opt),
{Value, Rest2} = unpack_stream(Rest, Opt),
unpack_map_jiffy(Rest2, Len-1, [{Key,Value}|Acc], Opt).
-spec unpack_map_jsx(binary(), non_neg_integer(),
msgpack:msgpack_map(), msgpack_option()) ->
{msgpack:msgpack_map(), binary()} | no_return().
unpack_map_jsx(Bin, 0, [], _) ->
{[{}], Bin};
unpack_map_jsx(Bin, 0, Acc, _) ->
{lists:reverse(Acc), Bin};
unpack_map_jsx(Bin, Len, Acc, Opt) ->
{Key, Rest} = unpack_stream(Bin, Opt),
{Value, Rest2} = unpack_stream(Rest, Opt),
unpack_map_jsx(Rest2, Len-1, [{Key,Value}|Acc], Opt).

View File

@ -35,19 +35,19 @@ benchmark0_test()->
Data=[test_data() || _ <- lists:seq(0, ?CNT)],
S=?debugTime(" serialize", msgpack:pack(Data, [jiffy])),
{ok, Data}=?debugTime("deserialize", msgpack:unpack(S, [jiffy])),
?debugFmt("for ~p KB test data(msgpack_jiffy).", [byte_size(S) div 1024]).
?debugFmt("for ~p KB test data(jiffy).", [byte_size(S) div 1024]).
benchmark1_test()->
Data=[test_data() || _ <- lists:seq(0, ?CNT)],
S=?debugTime(" serialize", msgpack:pack(Data, [jsx])),
{ok, Data}=?debugTime("deserialize", msgpack:unpack(S, [jsx])),
?debugFmt("for ~p KB test data(msgpack_jsx).", [byte_size(S) div 1024]).
?debugFmt("for ~p KB test data(jsx).", [byte_size(S) div 1024]).
benchmark2_test()->
Data=[test_data() || _ <- lists:seq(0, ?CNT)],
S=?debugTime(" serialize", msgpack_nif:pack(Data)),
{ok, Data}=?debugTime("deserialize", msgpack_nif:unpack(S)),
?debugFmt("for ~p KB test data(msgpack_nif).", [byte_size(S) div 1024]).
?debugFmt("for ~p KB test data(nif).", [byte_size(S) div 1024]).
benchmark3_test()->
Data=[test_data() || _ <- lists:seq(0, ?CNT)],
@ -93,7 +93,7 @@ multirunner(What, Pack, Unpack) ->
benchmark_p0_test_() ->
{timeout, 600,
?_assertEqual(ok,
multirunner("msgpack_jiffy",
multirunner("jiffy",
fun(Data) ->
msgpack:pack(Data, [jiffy])
end,
@ -104,7 +104,7 @@ benchmark_p0_test_() ->
benchmark_p1_test_() ->
{timeout, 600,
?_assertEqual(ok,
multirunner("msgpack_jsx",
multirunner("jsx",
fun(Data) ->
msgpack:pack(Data, [jsx])
end,
@ -115,7 +115,7 @@ benchmark_p1_test_() ->
benchmark_p2_test_() ->
{timeout, 600,
?_assertEqual(ok,
multirunner("msgpack_nif",
multirunner("nif",
fun(Data) ->
msgpack_nif:pack(Data)
end,