Skip to content

Instantly share code, notes, and snippets.

@hntrmrrs
Created August 2, 2011 16:43
Show Gist options
  • Save hntrmrrs/1120616 to your computer and use it in GitHub Desktop.
Save hntrmrrs/1120616 to your computer and use it in GitHub Desktop.
varint / ULEB128
-module(uleb128).
-export([encode/1, encode/2, decode/1, decode/2]).
% For property tests
-include_lib("proper/include/proper.hrl").
-export([prop_encode_decode/0]).
-spec encode(non_neg_integer()) -> binary().
encode(I) when I >= 0 -> encode_1(I, 0).
-spec encode(non_neg_integer(), binary()) -> binary().
encode(I, Acc) -> <<Acc/binary, (encode_1(I, 0))/binary>>.
-spec encode_1(non_neg_integer(),
non_neg_integer()) -> binary().
encode_1(I, Acc) when I > 16#7f ->
Val = I band 16#7f bor 16#80,
encode_1(I bsr 7, Acc bsl 8 bor Val);
encode_1(I, Acc) ->
binary:encode_unsigned(Acc bsl 8 bor I).
-type decode_cont() :: {cont,
non_neg_integer(),
non_neg_integer(),
non_neg_integer()}.
-type decode_ret() :: non_neg_integer()
| {non_neg_integer(), binary()}
| decode_cont().
-spec decode(binary()) -> decode_ret().
decode(<<0:1, A:7>>) -> A;
decode(<<0:1, A:7, Rest/binary>>) -> {A, Rest};
decode(<<1:1, A:7, 0:1, B:7>>) -> B bsl 7 bor A;
decode(<<1:1, A:7, 0:1, B:7, Rest/binary>>) ->
{B bsl 7 bor A, Rest};
decode(<<1:1, A:7, 1:1, B:7, 0:1, C:7>>) ->
(C bsl 14) bor (B bsl 7) bor A;
decode(<<1:1, A:7, 1:1, B:7, 0:1, C:7, Rest/binary>>) ->
{(C bsl 14) bor (B bsl 7) bor A, Rest};
decode(<<1:1, A:7, 1:1, B:7, 1:1, C:7, 0:1, D:7>>) ->
(D bsl 21) bor (C bsl 14) bor (B bsl 7) bor A;
decode(<<1:1, A:7, 1:1, B:7, 1:1, C:7, 0:1, D:7, Rest/binary>>) ->
{(D bsl 21) bor (C bsl 14) bor (B bsl 7) bor A, Rest};
decode(<<1:1, A:7, 1:1, B:7, 1:1, C:7, 1:1, D:7, _/binary>> = Bin) ->
Acc = (D bsl 21) bor (C bsl 14) bor (B bsl 7) bor A,
decode(Bin, {cont, Acc, 4, 4});
decode(Bin) ->
decode(Bin, {cont, 0, 0, 0}).
-spec decode(binary(), decode_cont()) -> decode_ret().
decode(Bin, {cont, Acc, X, Offset}) ->
case Bin of
<<_:Offset/bytes, 0:1, I:7>> ->
Acc bor (I bsl (X * 7));
<<_:Offset/bytes, 0:1, I:7, Rest/binary>> ->
Result = Acc bor (I bsl (X * 7)),
{Result, Rest};
<<_:Offset/bytes, 1:1, I:7>> ->
{cont, Acc bor (I bsl (X * 7)), X + 1, 0};
<<_:Offset/bytes, 1:1, I:7, _/binary>> ->
Acc1 = Acc bor (I bsl (X * 7)),
decode(Bin, {cont, Acc1, X + 1, Offset + 1})
end.
large_non_neg_integer() ->
union([integer(0, inf),
integer(16#7f, inf),
integer(16#3fff, inf),
integer(16#1fffff, inf),
integer(16#fffffff, inf)]).
% Properties for testing
prop_encode_decode() ->
?FORALL(I, large_non_neg_integer(), begin I =:= decode(encode(I)) end).
prop_encode_decode_long() ->
?FORALL(I, large_non_neg_integer(),
begin
Bin = encode(I),
Cont = decode(<<(binary:at(Bin, 0))>>),
I =:=
lists:foldl(
fun(X, Acc) ->
decode(<<(binary:at(Bin, X))>>, Acc)
end, Cont, lists:seq(1, byte_size(Bin) - 1))
end).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment