Skip to content

Instantly share code, notes, and snippets.

@g-andrade
Last active November 1, 2020 03:28
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save g-andrade/724d850809913a35b23453f90a9e5ed1 to your computer and use it in GitHub Desktop.
Save g-andrade/724d850809913a35b23453f90a9e5ed1 to your computer and use it in GitHub Desktop.
Decoding Erlang's external term format (incomplete)
-module(decode_erlang_external_term_format).
-export([binary_to_term/1]).
binary_to_term(<<131, 80, _UncompressedSize:32, CompressedData/binary>>) ->
Data = zlib:uncompress(CompressedData),
decode(Data);
binary_to_term(<<131, Data/binary>>) ->
decode(Data).
decode(<<82, _AtomCacheReferenceIndex, _Rest/binary>>) ->
% ATOM_CACHE_REF
exit(atom_cache_reference_unsupported);
decode(<<97, Uint8, Rest/binary>>) ->
% SMALL_INTEGER_EXT
{Uint8, Rest};
decode(<<98, Int32:4/signed-unit:8, Rest/binary>>) ->
% INTEGER_EXT
{Int32, Rest};
decode(<<99, FloatBin:31/binary, Rest1/binary>>) ->
% FLOAT_EXT
% old float format - used in minor version 0
Float = binary_to_float(FloatBin),
{Float, Rest1};
decode(<<101, _BodyAndRest/binary>>) ->
% REFERENCE_EXT
%NodeSize = byte_size(Body) - 4 - 1,
%<<_Node:NodeSize/binary, _Id:4, _Creation:1>> = Body,
exit(reference_ext_unsupported);
decode(<<102, _BodyAndRest/binary>>) ->
% PORT_EXT
exit(port_ext_unsupported);
decode(<<103, _BodyAndRest/binary>>) ->
% PID_EXT
exit(pid_ext_unsupported);
decode(<<104, Arity, Rest1/binary>>) ->
% SMALL_TUPLE_EXT
{List, Rest2} =
lists:mapfoldl(
fun (_, RestAcc) -> decode(RestAcc) end,
Rest1,
lists:seq(1, Arity)),
{list_to_tuple(List), Rest2};
decode(<<105, Arity:32, Rest1/binary>>) ->
% LARGE_TUPLE_EXT
{List, Rest2} =
lists:mapfoldl(
fun (_, RestAcc) -> decode(RestAcc) end,
Rest1,
lists:seq(1, Arity)),
{list_to_tuple(List), Rest2};
decode(<<116, Arity:32, Rest1/binary>>) ->
% MAP_EXT (Erlang 17 and up)
{KVList, Rest2} =
lists:mapfoldl(
fun (_, RestAcc1) ->
{Key, RestAcc2} = decode(RestAcc1),
{Value, RestAcc3} = decode(RestAcc2),
{{Key, Value}, RestAcc3}
end,
Rest1,
lists:seq(1, Arity)),
{maps:from_list(KVList), Rest2};
decode(<<106, Rest/binary>>) ->
% NIL_EXT
{[], Rest};
decode(<<107, Length:16, Binary:Length/binary, Rest/binary>>) ->
% STRING_EXT
List = binary_to_list(Binary),
{List, Rest};
decode(<<108, Length:32, Rest1/binary>>) ->
% LIST_EXT
{Elements, Rest2} =
lists:mapfoldl(
fun (_, RestAcc) ->
decode(RestAcc)
end,
Rest1,
lists:seq(1, Length)),
{Tail, Rest3} = decode(Rest2),
{Elements ++ Tail, Rest3};
decode(<<109, Length:32, Binary:Length/binary, Rest/binary>>) ->
% BINARY_EXT
{Binary, Rest};
decode(<<110, N, Sign, BigNum:N/little-signed-unit:8, Rest/binary>>) when Sign =:= 0 ->
% SMALL_BIG_EXT (positive)
{BigNum, Rest};
decode(<<110, N, Sign, BigNum:N/little-signed-unit:8, Rest/binary>>) when Sign =:= 1 ->
% SMALL_BIG_EXT (negative)
{-BigNum, Rest};
decode(<<111, N:32, Sign, BigNum:N/little-signed-unit:8, Rest/binary>>) when Sign =:= 0 ->
% LARGE_BIG_EXT (positive)
{BigNum, Rest};
decode(<<111, N:32, Sign, BigNum:N/little-signed-unit:8, Rest/binary>>) when Sign =:= 1 ->
% LARGE_BIG_EXT (negative)
{-BigNum, Rest};
decode(<<114, _BodyAndRest/binary>>) ->
% NEW_REFERENCE_EXT
exit(new_reference_ext_unsupported);
decode(<<117, _BodyAndRest/binary>>) ->
% FUN_EXT
exit(fun_ext_unsupported);
decode(<<112, _BodyAndRest/binary>>) ->
% NEW_FUN_EXT
exit(new_fun_ext_unsupported);
decode(<<113, Rest1/binary>>) ->
% EXPORT_EXT
{Module, Rest2} = decode(Rest1),
{Function, Rest3} = decode(Rest2),
{Arity, Rest4} = decode(Rest3),
{fun Module:Function/Arity, Rest4};
decode(<<77, Len:32, Bits, Binary:Len/binary, Rest/binary>>) ->
% BIT_BINARY_EXT
ExcessBits = 8 - Bits,
BitstringLength = (Len * 8) - ExcessBits,
<<Bitstring:BitstringLength/bitstring, 0:ExcessBits>> = Binary,
{Bitstring, Rest};
decode(<<70, Float:8/big-float-unit:8, Rest/binary>>) ->
% NEW_FLOAT_EXT
% new float format - used in minor version 1
{Float, Rest};
decode(<<118, Len:16, AtomName:Len/binary, Rest/binary>>) ->
% ATOM_UTF8_EXT
{binary_to_atom(AtomName, utf8), Rest};
decode(<<119, Len, AtomName:Len/binary, Rest/binary>>) ->
% SMALL_ATOM_UTF8_EXT
{binary_to_atom(AtomName, utf8), Rest};
decode(<<100, Len:16, AtomName:Len/binary, Rest/binary>>) ->
% ATOM_EXT (deprecated)
{binary_to_atom(AtomName, latin1), Rest};
decode(<<100, Len, AtomName:Len/binary, Rest/binary>>) ->
% SMALL_ATOM_EXT (deprecated)
{binary_to_atom(AtomName, latin1), Rest}.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment