Created
August 2, 2011 16:43
-
-
Save hntrmrrs/1120616 to your computer and use it in GitHub Desktop.
varint / ULEB128
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-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