Current section
Files
Jump to
Current section
Files
src/msgpack_unpacker.erl
%%
%% 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").
-include_lib("eunit/include/eunit.hrl").
-ifndef(without_map).
-export([unpack_map/3]).
-endif.
-export([unpack_map_jiffy/3, unpack_map_jsx/3]).
%% unpack them all
-spec unpack_stream(Bin::binary(), msgpack_option()) -> {msgpack:object(), binary()} | no_return().
%% ATOMS
unpack_stream(<<16#C0, Rest/binary>>, _Opt = ?OPTION{interface=jsx}) ->
{null, Rest};
unpack_stream(<<16#C0, Rest/binary>>, _Opt = ?OPTION{interface=jiffy}) ->
{null, Rest};
unpack_stream(<<16#C0, Rest/binary>>, _) ->
{nil, Rest};
unpack_stream(<<16#C2, Rest/binary>>, _) ->
{false, Rest};
unpack_stream(<<16#C3, Rest/binary>>, _) ->
{true, Rest};
%% Raw bytes
unpack_stream(<<16#C4, L:8/big-unsigned-integer-unit:1, V:L/binary, Rest/binary>>, _) ->
{V, Rest};
unpack_stream(<<16#C5, L:16/big-unsigned-integer-unit:1, V:L/binary, Rest/binary>>, _) ->
{V, Rest};
unpack_stream(<<16#C6, L:32/big-unsigned-integer-unit:1, V:L/binary, Rest/binary>>, _) ->
{V, 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};
%% Strings as new spec, or Raw bytes as old spec
unpack_stream(<<2#101:3, L:5, V:L/binary, Rest/binary>>, Opt) ->
unpack_string_or_raw(V, Opt, Rest);
unpack_stream(<<16#D9, L:8/big-unsigned-integer-unit:1, V:L/binary, Rest/binary>>,
?OPTION{enable_str=true} = _Opt) ->
{unpack_string(V), Rest};
unpack_stream(<<16#DA, L:16/big-unsigned-integer-unit:1, V:L/binary, Rest/binary>>, Opt) ->
unpack_string_or_raw(V, Opt, Rest);
unpack_stream(<<16#DB, L:32/big-unsigned-integer-unit:1, V:L/binary, Rest/binary>>, Opt) ->
unpack_string_or_raw(V, Opt, Rest);
%% Arrays
unpack_stream(<<2#1001:4, L:4, Rest/binary>>, Opt) ->
unpack_array(Rest, L, [], Opt);
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(<<2#1000:4, L:4, Rest/binary>>, Opt) ->
Unpacker = Opt?OPTION.map_unpack_fun,
Unpacker(Rest, L, Opt);
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};
%% Invalid data
unpack_stream(<<16#C1, _R/binary>>, _) -> throw({badarg, 16#C1});
%% for extention types
%% fixext 1 stores an integer and a byte array whose length is 1 byte
unpack_stream(<<16#D4, T:1/signed-integer-unit:8, Data:1/binary, Rest/binary>>,
?OPTION{ext_unpacker=Unpack, original_list=Orig} = _Opt) ->
maybe_unpack_ext(16#D4, Unpack, T, Data, Rest, Orig);
%% fixext 2 stores an integer and a byte array whose length is 2 bytes
unpack_stream(<<16#D5, T:1/signed-integer-unit:8, Data:2/binary, Rest/binary>>,
?OPTION{ext_unpacker=Unpack, original_list=Orig} = _Opt) ->
maybe_unpack_ext(16#D5, Unpack, T, Data, Rest, Orig);
%% fixext 4 stores an integer and a byte array whose length is 4 bytes
unpack_stream(<<16#D6, T:1/signed-integer-unit:8, Data:4/binary, Rest/binary>>,
?OPTION{ext_unpacker=Unpack, original_list=Orig} = _Opt) ->
maybe_unpack_ext(16#D6, Unpack, T, Data, Rest, Orig);
%% fixext 8 stores an integer and a byte array whose length is 8 bytes
unpack_stream(<<16#D7, T:1/signed-integer-unit:8, Data:8/binary, Rest/binary>>,
?OPTION{ext_unpacker=Unpack, original_list=Orig} = _Opt) ->
maybe_unpack_ext(16#D7, Unpack, T, Data, Rest, Orig);
%% fixext 16 stores an integer and a byte array whose length is 16 bytes
unpack_stream(<<16#D8, T:1/signed-integer-unit:8, Data:16/binary, Rest/binary>>,
?OPTION{ext_unpacker=Unpack, original_list=Orig} = _Opt) ->
maybe_unpack_ext(16#D8, Unpack, T, Data, Rest, Orig);
%% ext 8 stores an integer and a byte array whose length is upto (2^8)-1 bytes:
unpack_stream(<<16#C7, Len:8, Type:1/signed-integer-unit:8, Data:Len/binary, Rest/binary>>,
?OPTION{ext_unpacker=Unpack, original_list=Orig} = _Opt) ->
maybe_unpack_ext(16#C7, Unpack, Type, Data, Rest, Orig);
%% ext 16 stores an integer and a byte array whose length is upto (2^16)-1 bytes:
unpack_stream(<<16#C8, Len:16, Type:1/signed-integer-unit:8, Data:Len/binary, Rest/binary>>,
?OPTION{ext_unpacker=Unpack, original_list=Orig} = _Opt) ->
maybe_unpack_ext(16#C8, Unpack, Type, Data, Rest, Orig);
%% ext 32 stores an integer and a byte array whose length is upto (2^32)-1 bytes:
unpack_stream(<<16#C9, Len:32, Type:1/signed-integer-unit:8, Data:Len/binary, Rest/binary>>,
?OPTION{ext_unpacker=Unpack, original_list=Orig} = _Opt) ->
maybe_unpack_ext(16#C9, Unpack, Type, Data, Rest, Orig);
unpack_stream(_Bin, _) -> 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).
-ifdef(without_map).
map_unpacker(jiffy) ->
fun ?MODULE:unpack_map_jiffy/3;
map_unpacker(jsx) ->
fun ?MODULE:unpack_map_jsx/3.
-else.
map_unpacker(map) ->
fun ?MODULE:unpack_map/3;
map_unpacker(jiffy) ->
fun ?MODULE:unpack_map_jiffy/3;
map_unpacker(jsx) ->
fun ?MODULE:unpack_map_jsx/3.
-endif.
-ifndef(without_map).
-spec unpack_map(binary(), non_neg_integer(), msgpack_option()) ->
{map(), binary()} | no_return().
unpack_map(Bin, Len, Opt) ->
{Map, Rest} = unpack_map_as_proplist(Bin, Len, [], Opt),
{maps:from_list(Map), Rest}.
%% unpack_map(Bin, Len, #{}, Opt).
%% unpack_map(Bin, Len, Acc, _) -> {Acc, Bin};
%% unpack_map(Bin, Len, Acc, Opt) ->
%% {Key, Rest} = unpack_stream(Bin, Opt),
%% {Value, Rest2} = unpack_stream(Rest, Opt),
%% unpack_map(Rest2, Len-1, maps:put(Key, Value, Acc), Opt).
-endif.
%% Users SHOULD NOT send too long list: this uses lists:reverse/1
-spec unpack_map_jiffy(binary(), non_neg_integer(), msgpack_option()) ->
{msgpack:msgpack_map_jiffy(), binary()} | no_return().
unpack_map_jiffy(Bin, Len, Opt) ->
{Map, Rest} = unpack_map_as_proplist(Bin, Len, [], Opt),
{{Map}, Rest}.
-spec unpack_map_jsx(binary(), non_neg_integer(), msgpack_option()) ->
{msgpack:msgpack_map_jsx(), binary()} | no_return().
unpack_map_jsx(Bin, Len, Opt) ->
case unpack_map_as_proplist(Bin, Len, [], Opt) of
{[], Rest} -> {[{}], Rest};
{Map, Rest} -> {Map, Rest}
end.
-spec unpack_map_as_proplist(binary(), non_neg_integer(), proplists:proplist(), msgpack_option()) ->
{proplists:proplist(), binary()} | no_return().
unpack_map_as_proplist(Bin, 0, Acc, _) ->
{lists:reverse(Acc), Bin};
unpack_map_as_proplist(Bin, Len, Acc, Opt) ->
{Key, Rest} = unpack_stream(Bin, Opt),
{Value, Rest2} = unpack_stream(Rest, Opt),
unpack_map_as_proplist(Rest2, Len-1, [{Key,Value}|Acc], Opt).
unpack_string_or_raw(V, ?OPTION{enable_str=true} = _Opt, Rest) ->
{unpack_string(V), Rest};
unpack_string_or_raw(V, ?OPTION{enable_str=false} = _Opt, Rest) ->
{V, Rest}.
%% NOTE: msgpack DOES validate the binary as valid unicode string.
unpack_string(Binary) ->
case unicode:characters_to_list(Binary) of
{error, _S, _Rest} -> throw({error, {invalid_string, Binary}});
{incomplete, _S, _Rest} -> throw({error, {invalid_string, Binary}});
String -> String
end.
maybe_unpack_ext(F, undefined, _, _, _Rest, _) -> throw({badarg, {bad_ext, F}});
maybe_unpack_ext(_, Unpack, Type, Data, Rest, Orig) when is_function(Unpack, 3) ->
case Unpack(Type, Data, Orig) of
{ok, Term} -> {Term, Rest};
{error, E} -> {error, E}
end;
maybe_unpack_ext(_, Unpack, Type, Data, Rest, _) when is_function(Unpack, 2) ->
case Unpack(Type, Data) of
{ok, Term} -> {Term, Rest};
{error, E} -> {error, E}
end.