Merge pull request #94 from aeternity/bytes-to-x

Add Bytes.to_int and Bytes.to_str
This commit is contained in:
Ulf Norell 2019-06-24 14:56:20 +02:00 committed by GitHub
commit c7a8a4af22
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 154 additions and 20 deletions

View File

@ -2,7 +2,7 @@
{erl_opts, [debug_info]}.
{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {ref,"59af12b"}}}
{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {ref,"bf05e14"}}}
, {getopt, "1.0.1"}
, {jsx, {git, "https://github.com/talentdeficit/jsx.git",
{tag, "2.8.0"}}}

View File

@ -1,7 +1,7 @@
{"1.1.0",
[{<<"aebytecode">>,
{git,"https://github.com/aeternity/aebytecode.git",
{ref,"59af12bf349edafcd470cd3ccefff09cacd2d010"}},
{ref,"bf05e14661ae25905bd020bfc2dcbc074f8ad66b"}},
0},
{<<"aeserialization">>,
{git,"https://github.com/aeternity/aeserialization.git",

View File

@ -486,6 +486,12 @@ global_env() ->
{"none", Bits},
{"all", Bits}]) },
%% Bytes
BytesScope = #scope
{ funs = MkDefs(
[{"to_int", Fun1(Bytes(any), Int)},
{"to_str", Fun1(Bytes(any), String)}]) },
%% Conversion
IntScope = #scope{ funs = MkDefs([{"to_str", Fun1(Int, String)}]) },
AddressScope = #scope{ funs = MkDefs([{"to_str", Fun1(Address, String)},
@ -504,6 +510,7 @@ global_env() ->
, ["Crypto"] => CryptoScope
, ["String"] => StringScope
, ["Bits"] => BitsScope
, ["Bytes"] => BytesScope
, ["Int"] => IntScope
, ["Address"] => AddressScope
} }.
@ -916,8 +923,8 @@ lookup_name(Env, As, Id, Options) ->
Freshen = proplists:get_value(freshen, Options, false),
check_stateful(Env, Id, Ty),
Ty1 = case Ty of
{type_sig, _, _, _, _} -> freshen_type(typesig_to_fun_t(Ty));
_ when Freshen -> freshen_type(Ty);
{type_sig, _, _, _, _} -> freshen_type(As, typesig_to_fun_t(Ty));
_ when Freshen -> freshen_type(As, Ty);
_ -> Ty
end,
{set_qname(QId, Id), Ty1}
@ -1218,7 +1225,7 @@ infer_block(Env, _, [E], BlockType) ->
[check_expr(Env, E, BlockType)];
infer_block(Env, Attrs, [Def={letfun, Ann, _, _, _, _}|Rest], BlockType) ->
{{Name, TypeSig}, LetFun} = infer_letfun(Env, Def),
FunT = freshen_type(typesig_to_fun_t(TypeSig)),
FunT = freshen_type(Ann, typesig_to_fun_t(TypeSig)),
NewE = bind_var({id, Ann, Name}, FunT, Env),
[LetFun|infer_block(NewE, Attrs, Rest, BlockType)];
infer_block(Env, _, [{letval, Attrs, Pattern, Type, E}|Rest], BlockType) ->
@ -1358,14 +1365,17 @@ when_option(Opt, Do) ->
create_constraints() ->
create_named_argument_constraints(),
create_bytes_constraints(),
create_field_constraints().
solve_constraints(Env) ->
solve_named_argument_constraints(Env),
solve_bytes_constraints(Env),
solve_field_constraints(Env).
destroy_and_report_unsolved_constraints(Env) ->
destroy_and_report_unsolved_field_constraints(Env),
destroy_and_report_unsolved_bytes_constraints(Env),
destroy_and_report_unsolved_named_argument_constraints(Env).
%% -- Named argument constraints --
@ -1414,6 +1424,37 @@ destroy_and_report_unsolved_named_argument_constraints(Env) ->
destroy_named_argument_constraints(),
ok.
%% -- Bytes constraints --
-type byte_constraint() :: {is_bytes, utype()}.
create_bytes_constraints() ->
ets_new(bytes_constraints, [bag]).
get_bytes_constraints() ->
ets_tab2list(bytes_constraints).
-spec add_bytes_constraint(byte_constraint()) -> true.
add_bytes_constraint(Constraint) ->
ets_insert(bytes_constraints, Constraint).
solve_bytes_constraints(_Env) ->
ok.
destroy_bytes_constraints() ->
ets_delete(bytes_constraints).
destroy_and_report_unsolved_bytes_constraints(Env) ->
[ check_bytes_constraint(Env, C) || C <- get_bytes_constraints() ],
destroy_bytes_constraints().
check_bytes_constraint(Env, {is_bytes, Type}) ->
Type1 = unfold_types_in_type(Env, instantiate(Type)),
case Type1 of
{bytes_t, _, _} -> ok;
_ -> type_error({cannot_unify, Type1, {bytes_t, [], any}, {at, Type}})
end.
%% -- Field constraints --
create_field_constraints() ->
@ -1840,26 +1881,31 @@ create_freshen_tvars() ->
destroy_freshen_tvars() ->
ets_delete(freshen_tvars).
freshen_type(Type) ->
freshen_type(Ann, Type) ->
create_freshen_tvars(),
Type1 = freshen(Type),
Type1 = freshen(Ann, Type),
destroy_freshen_tvars(),
Type1.
freshen({tvar, As, Name}) ->
freshen(Type) ->
freshen(aeso_syntax:get_ann(Type), Type).
freshen(Ann, {tvar, _, Name}) ->
NewT = case ets_lookup(freshen_tvars, Name) of
[] ->
fresh_uvar(As);
[{Name, T}] ->
T
[] -> fresh_uvar(Ann);
[{Name, T}] -> T
end,
ets_insert(freshen_tvars, {Name, NewT}),
NewT;
freshen(T) when is_tuple(T) ->
list_to_tuple(freshen(tuple_to_list(T)));
freshen([A|B]) ->
[freshen(A)|freshen(B)];
freshen(X) ->
freshen(Ann, {bytes_t, _, any}) ->
X = fresh_uvar(Ann),
add_bytes_constraint({is_bytes, X}),
X;
freshen(Ann, T) when is_tuple(T) ->
list_to_tuple(freshen(Ann, tuple_to_list(T)));
freshen(Ann, [A | B]) ->
[freshen(Ann, A) | freshen(Ann, B)];
freshen(_, X) ->
X.
%% Dereferences all uvars and replaces the uninstantiated ones with a
@ -2044,6 +2090,7 @@ pp_error(Err) ->
io_lib:format("Unknown error: ~p\n", [Err]).
pp_when({todo, What}) -> io_lib:format("[TODO] ~p\n", [What]);
pp_when({at, Ann}) -> io_lib:format("at ~s\n", [pp_loc(Ann)]);
pp_when({check_typesig, Name, Inferred, Given}) ->
io_lib:format("when checking the definition of ~s\n"
" inferred type: ~s\n"
@ -2196,6 +2243,7 @@ pp({tvar, _, Name}) ->
Name;
pp({tuple_t, _, Cpts}) ->
["(", pp(Cpts), ")"];
pp({bytes_t, _, any}) -> "bytes(_)";
pp({bytes_t, _, Len}) ->
["bytes(", integer_to_list(Len), ")"];
pp({app_t, _, T, []}) ->

View File

@ -190,6 +190,7 @@ builtins() ->
{["String"], [{"length", 1}, {"concat", 2}, {"sha3", 1}, {"sha256", 1}, {"blake2b", 1}]},
{["Bits"], [{"set", 2}, {"clear", 2}, {"test", 2}, {"sum", 1}, {"intersection", 2},
{"union", 2}, {"difference", 2}, {"none", none}, {"all", none}]},
{["Bytes"], [{"to_int", 1}, {"to_str", 1}]},
{["Int"], [{"to_str", 1}]},
{["Address"], [{"to_str", 1}]}
],
@ -1323,6 +1324,7 @@ pp_call(Fun, Args) ->
pp_ftype(T) when is_atom(T) -> pp_text(T);
pp_ftype(any) -> pp_text("_");
pp_ftype({tvar, X}) -> pp_text(X);
pp_ftype({bytes, N}) -> pp_text("bytes(" ++ integer_to_list(N) ++ ")");
pp_ftype({tuple, Ts}) ->
pp_parens(pp_par(pp_punctuate(pp_text(","), [pp_ftype(T) || T <- Ts])));
pp_ftype({list, T}) ->

View File

@ -421,6 +421,13 @@ ast_body(?qid_app(["Address", "is_contract"], [Addr], _, _), Icode) ->
prim_call(?PRIM_CALL_ADDR_IS_CONTRACT, #integer{value = 0},
[ast_body(Addr, Icode)], [word], word);
ast_body(?qid_app(["Bytes", "to_int"], [Bytes], _, _), Icode) ->
{typed, _, _, {bytes_t, _, N}} = Bytes,
builtin_call({bytes_to_int, N}, [ast_body(Bytes, Icode)]);
ast_body(?qid_app(["Bytes", "to_str"], [Bytes], _, _), Icode) ->
{typed, _, _, {bytes_t, _, N}} = Bytes,
builtin_call({bytes_to_str, N}, [ast_body(Bytes, Icode)]);
%% Other terms
ast_body({id, _, Name}, _Icode) ->
#var_ref{name = Name};

View File

@ -44,6 +44,7 @@ builtin_deps1(addr_to_str) -> [{baseX_int, 58}];
builtin_deps1({baseX_int, X}) -> [{baseX_int_pad, X}];
builtin_deps1({baseX_int_pad, X}) -> [{baseX_int_encode, X}];
builtin_deps1({baseX_int_encode, X}) -> [{baseX_int_encode_, X}, {baseX_tab, X}, {baseX_digits, X}];
builtin_deps1({bytes_to_str, _}) -> [bytes_to_str_worker];
builtin_deps1(string_reverse) -> [string_reverse_];
builtin_deps1(require) -> [abort];
builtin_deps1(_) -> [].
@ -66,7 +67,7 @@ option_some(X) -> {tuple, [{integer, 1}, X]}.
-define(V(X), v(X)).
-define(A(Op), aeb_opcodes:mnemonic(Op)).
-define(LET(Var, Expr, Body), {switch, Expr, [{v(Var), Body}]}).
-define(DEREF(Var, Ptr, Body), {switch, v(Ptr), [{{tuple, [v(Var)]}, Body}]}).
-define(DEREF(Var, Ptr, Body), {switch, operand(Ptr), [{{tuple, [v(Var)]}, Body}]}).
-define(NXT(Ptr), op('+', Ptr, 32)).
-define(NEG(A), op('/', A, {unop, '-', {integer, 1}})).
-define(BYTE(Ix, Word), op('byte', Ix, Word)).
@ -160,6 +161,9 @@ builtin_function(BF) ->
{baseX_int_pad, X} -> bfun(BF, builtin_baseX_int_pad(X));
{baseX_int_encode, X} -> bfun(BF, builtin_baseX_int_encode(X));
{baseX_int_encode_, X} -> bfun(BF, builtin_baseX_int_encode_(X));
{bytes_to_int, N} -> bfun(BF, builtin_bytes_to_int(N));
{bytes_to_str, N} -> bfun(BF, builtin_bytes_to_str(N));
bytes_to_str_worker -> bfun(BF, builtin_bytes_to_str_worker());
string_reverse -> bfun(BF, builtin_string_reverse());
string_reverse_ -> bfun(BF, builtin_string_reverse_())
end.
@ -444,6 +448,10 @@ builtin_baseX_int_pad(X = 10) ->
?call({baseX_int_encode, X}, [?NEG(src), ?I(1), ?BSL($-, 31)]),
?call({baseX_int_encode, X}, [?V(src), ?V(ix), ?V(dst)])},
word};
builtin_baseX_int_pad(X = 16) ->
{[{"src", word}, {"ix", word}, {"dst", word}],
?call({baseX_int_encode, X}, [?V(src), ?V(ix), ?V(dst)]),
word};
builtin_baseX_int_pad(X = 58) ->
{[{"src", word}, {"ix", word}, {"dst", word}],
{ifte, ?GT(?ADD(?DIV(ix, 31), ?BYTE(ix, src)), 0),
@ -478,6 +486,57 @@ builtin_baseX_digits(X) ->
{ifte, ?EQ(x1, 0), ?V(dgts), ?call({baseX_digits, X}, [?V(x1), ?ADD(dgts, 1)])}),
word}.
builtin_bytes_to_int(32) ->
{[{"w", word}], ?V(w), word};
builtin_bytes_to_int(N) when N < 32 ->
{[{"w", word}], ?BSR(w, 32 - N), word};
builtin_bytes_to_int(N) when N > 32 ->
LastFullWord = N div 32 - 1,
Body = case N rem 32 of
0 -> ?DEREF(n, ?ADD(b, LastFullWord * 32), ?V(n));
R ->
?DEREF(hi, ?ADD(b, LastFullWord * 32),
?DEREF(lo, ?ADD(b, (LastFullWord + 1) * 32),
?ADD(?BSR(lo, 32 - R), ?BSL(hi, R))))
end,
{[{"b", pointer}], Body, word}.
builtin_bytes_to_str_worker() ->
<<Tab:256>> = <<"0123456789ABCDEF________________">>,
{[{"w", word}, {"offs", word}, {"acc", word}],
{seq, [{ifte, ?AND(?GT(offs, 0), ?EQ(0, ?MOD(offs, 16))),
{seq, [?V(acc), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]}]},
{inline_asm, []}},
{ifte, ?EQ(offs, 32), {inline_asm, [?A(?MSIZE)]},
?LET(b, ?BYTE(offs, w),
?LET(lo, ?BYTE(?MOD(b, 16), Tab),
?LET(hi, ?BYTE(op('bsr', 4 , b), Tab),
?call(bytes_to_str_worker,
[?V(w), ?ADD(offs, 1), ?ADD(?BSL(acc, 2), ?ADD(?BSL(hi, 1), lo))]))))
}
]},
word}.
builtin_bytes_to_str(N) when N =< 32 ->
{[{"w", word}],
?LET(ret, {inline_asm, [?A(?MSIZE)]},
{seq, [?I(N * 2), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]},
?call(bytes_to_str_worker, [?V(w), ?I(0), ?I(0)]),
{inline_asm, [?A(?POP)]},
?V(ret)]}),
string};
builtin_bytes_to_str(N) when N > 32 ->
Work = fun(I) ->
[?DEREF(w, ?ADD(p, 32 * I), ?call(bytes_to_str_worker, [?V(w), ?I(0), ?I(0)])),
{inline_asm, [?A(?POP)]}]
end,
{[{"p", pointer}],
?LET(ret, {inline_asm, [?A(?MSIZE)]},
{seq, [?I(N * 2), {inline_asm, [?A(?MSIZE), ?A(?MSTORE)]}] ++
lists:append([ Work(I) || I <- lists:seq(0, (N + 31) div 32 - 1) ]) ++
[?V(ret)]}),
string}.
builtin_string_reverse() ->
{[{"s", string}],
?DEREF(n, s,

View File

@ -102,6 +102,8 @@
Op =:= 'ECVERIFY_SECP256K1' orelse
Op =:= 'CONTRACT_TO_ADDRESS' orelse
Op =:= 'AUTH_TX_HASH' orelse
Op =:= 'BYTES_TO_INT' orelse
Op =:= 'BYTES_TO_STR' orelse
false)).
-record(env, { contract, vars = [], locals = [], tailpos = true }).
@ -470,6 +472,10 @@ builtin_to_scode(_Env, bits_none, []) ->
[aeb_fate_ops:bits_none(?a)];
builtin_to_scode(_Env, bits_all, []) ->
[aeb_fate_ops:bits_all(?a)];
builtin_to_scode(Env, bytes_to_int, [_] = Args) ->
call_to_scode(Env, aeb_fate_ops:bytes_to_int(?a, ?a), Args);
builtin_to_scode(Env, bytes_to_str, [_] = Args) ->
call_to_scode(Env, aeb_fate_ops:bytes_to_str(?a, ?a), Args);
builtin_to_scode(Env, abort, [_] = Args) ->
call_to_scode(Env, aeb_fate_ops:abort(?a), Args);
builtin_to_scode(Env, chain_spend, [_, _] = Args) ->
@ -802,6 +808,8 @@ attributes(I) ->
{'ECVERIFY_SECP256K1', A, B, C, D} -> Pure(A, [B, C, D]);
{'CONTRACT_TO_ADDRESS', A, B} -> Pure(A, [B]);
{'AUTH_TX_HASH', A} -> Pure(A, []);
{'BYTES_TO_INT', A, B} -> Pure(A, [B]);
{'BYTES_TO_STR', A, B} -> Pure(A, [B]);
{'ADDRESS', A} -> Pure(A, []);
{'BALANCE', A} -> Impure(A, []);
{'BALANCE_OTHER', A, B} -> Impure(A, [B]);

View File

@ -233,6 +233,7 @@ type({app_t, _, Type, Args}) ->
beside(type(Type), tuple_type(Args));
type({tuple_t, _, Args}) ->
tuple_type(Args);
type({bytes_t, _, any}) -> text("bytes(_)");
type({bytes_t, _, Len}) ->
text(lists:concat(["bytes(", Len, ")"]));
type({named_arg_t, _, Name, Type, _Default}) ->

View File

@ -59,7 +59,7 @@
-type type() :: {fun_t, ann(), [named_arg_t()], [type()], type()}
| {app_t, ann(), type(), [type()]}
| {tuple_t, ann(), [type()]}
| {bytes_t, ann(), integer()}
| {bytes_t, ann(), integer() | any}
| id() | qid()
| con() | qcon() %% contracts
| tvar().

View File

@ -115,7 +115,8 @@ compilable_contracts() ->
"address_literals",
"bytes_equality",
"address_chain",
"namespace_bug"
"namespace_bug",
"bytes_to_x"
].
not_yet_compilable(fate) ->

View File

@ -0,0 +1,8 @@
contract BytesToX =
function to_int(b : bytes(42)) : int = Bytes.to_int(b)
function to_str(b : bytes(12)) : string =
String.concat(Bytes.to_str(b), Bytes.to_str(#ffff))
function to_str_big(b : bytes(65)) : string =
Bytes.to_str(b)