File 1201-Add-a-array-from-function.patch of Package erlang
From 8f7ccf58330d538714f71b926ff5c1334e9e976b Mon Sep 17 00:00:00 2001
From: Dan Gudmundsson <dgud@erlang.org>
Date: Wed, 15 Oct 2025 12:55:18 +0200
Subject: [PATCH] Add a array from function
To allow array creation from any type of input, for example from
binaries without the need to first create a redundant list.
---
lib/stdlib/src/array.erl | 79 ++++++++++++++++++++++++++++++++-
lib/stdlib/test/array_SUITE.erl | 79 ++++++++++++++++++++++++++++++---
2 files changed, 150 insertions(+), 8 deletions(-)
diff --git a/lib/stdlib/src/array.erl b/lib/stdlib/src/array.erl
index dfb96545d0..8cf247732b 100644
--- a/lib/stdlib/src/array.erl
+++ b/lib/stdlib/src/array.erl
@@ -101,6 +101,7 @@ beyond the last set entry:
-export([new/0, new/1, new/2, is_array/1, set/3, get/2, size/1,
sparse_size/1, default/1, reset/2, to_list/1, sparse_to_list/1,
from_list/1, from_list/2, to_orddict/1, sparse_to_orddict/1,
+ from/2, from/3,
from_orddict/1, from_orddict/2, map/2, sparse_map/2, foldl/3,
foldr/3, sparse_foldl/3, sparse_foldr/3, fix/1, relax/1, is_fix/1,
resize/1, resize/2]).
@@ -1064,6 +1065,81 @@ from_list_test_() ->
-endif.
+-doc "Equivalent to [`from(Fun, State, undefined)`](`from/3`).".
+-doc(#{since => <<"OTP 29.0">>}).
+-spec from(Function, State :: term()) -> array(Type) when
+ Function :: fun((State0 :: term()) -> {Type, State1 :: term()} | done).
+
+from(Fun, State) ->
+ from(Fun, State, undefined).
+
+
+-doc """
+Creates an extendible array with values obtained with `Function(State)`.
+
+The 'Function(State)' shall return `{Value, NewState}` or `done`, and is invoked
+until `done` is returned, otherwise the call fails with reason `badarg`.
+
+`Default` is used as the value for uninitialized entries of the array.
+
+## Examples
+
+```erlang
+1> Floats = << <<N:32/float-native>> || N <- lists:seq(0, 2047)>>.
+2> BinToVal = fun(I) ->
+ case Floats of
+ <<_:I/binary, N:32/float-native, _/binary>> ->
+ {N, I+4};
+ _ ->
+ done
+ end
+ end.
+3> A = array:from(BinToVal, 0).
+4> array:get(10, A).
+10.0
+5> array:size(A).
+2048
+6> ValToBin = fun(_K, V, Acc) -> <<Acc/binary, V:32/float-native>> end.
+7> Floats == array:foldl(ValToBin, <<>>, A).
+true
+```
+
+See also `new/2`, `from_list/1`, `foldl/3`.
+""".
+
+-doc(#{since => <<"OTP 29.0">>}).
+-spec from(Function, State :: term(), Default :: term()) -> array(Type) when
+ Function :: fun((State0 :: term()) -> {Type, State1 :: term()} | done).
+
+from(Fun, S0, Default) when is_function(Fun, 1) ->
+ VS = Fun(S0),
+ {E, N, M} = from_fun_1(?LEAFSIZE, Default, Fun, VS, 0, [], []),
+ #array{size = N, max = M, default = Default, elements = E};
+from(_, _, _) ->
+ error(badarg).
+
+
+from_fun_1(0, D, Fun, VS, N, As, Es) ->
+ E = list_to_tuple(lists:reverse(As)),
+ case VS of
+ done ->
+ case Es of
+ [] ->
+ {E, N, ?LEAFSIZE};
+ _ ->
+ from_list_2_0(N, [E | Es], ?LEAFSIZE)
+ end;
+ _ ->
+ from_fun_1(?LEAFSIZE, D, Fun, VS, N, [], [E | Es])
+ end;
+from_fun_1(I, D, Fun, done, N, As, Es) ->
+ from_fun_1(I-1, D, Fun, done, N, [D | As], Es);
+from_fun_1(I, D, Fun, {X, S}, N, As, Es) ->
+ from_fun_1(I-1, D, Fun, Fun(S), N+1, [X | As], Es);
+from_fun_1(_I, _D, _Fun, _VS, _N, _As, _Es) ->
+ erlang:error(badarg).
+
+
-doc """
Converts the array to an ordered list of pairs `{Index, Value}`.
@@ -1241,8 +1317,6 @@ sparse_to_orddict_test_() ->
-endif.
-%% @equiv from_orddict(Orddict, undefined)
-
-doc "Equivalent to [`from_orddict(Orddict, undefined)`](`from_orddict/2`).".
-spec from_orddict(Orddict :: indx_pairs(Value :: Type)) -> array(Type).
@@ -1943,6 +2017,7 @@ sparse_foldr_test_() ->
Vals = fun(_K,undefined,{C,L}) -> {C+1,L};
(K,X,{C,L}) -> {C,[K+X|L]}
end,
+
[?_assertError(badarg, sparse_foldr([], 0, new())),
?_assertError(badarg, sparse_foldr([], 0, new(10))),
?_assert(sparse_foldr(Count, 0, new()) =:= 0),
diff --git a/lib/stdlib/test/array_SUITE.erl b/lib/stdlib/test/array_SUITE.erl
index a60f69f619..2a5c930176 100644
--- a/lib/stdlib/test/array_SUITE.erl
+++ b/lib/stdlib/test/array_SUITE.erl
@@ -38,6 +38,7 @@
to_list_test/1,
sparse_to_list_test/1,
from_list_test/1,
+ from_test/1,
to_orddict_test/1,
sparse_to_orddict_test/1,
from_orddict_test/1,
@@ -46,15 +47,18 @@
foldl_test/1,
sparse_foldl_test/1,
foldr_test/1,
- sparse_foldr_test/1
+ sparse_foldr_test/1,
+ import_export/1,
+ doctests/1
]).
-export([t/0,t/1,extract_tests/0]).
--import(array,
+-import(array,
[new/0, new/1, new/2, is_array/1, set/3, get/2, %size/1,
sparse_size/1, default/1, reset/2, to_list/1, sparse_to_list/1,
+ from/2, from/3,
from_list/1, from_list/2, to_orddict/1, sparse_to_orddict/1,
from_orddict/1, from_orddict/2, map/2, sparse_map/2, foldl/3,
foldr/3, sparse_foldl/3, sparse_foldr/3, fix/1, relax/1, is_fix/1,
@@ -67,15 +71,16 @@ suite() ->
[{ct_hooks,[ts_install_cth]},
{timetrap,{minutes,1}}].
-all() ->
+all() ->
[new_test, fix_test, relax_test, resize_test,
set_get_test, to_list_test, sparse_to_list_test,
+ from_test,
from_list_test, to_orddict_test, sparse_to_orddict_test,
from_orddict_test, map_test, sparse_map_test,
- foldl_test, sparse_foldl_test, foldr_test,
- sparse_foldr_test].
+ foldl_test, sparse_foldl_test, foldr_test, sparse_foldr_test,
+ import_export, doctests].
-groups() ->
+groups() ->
[].
init_per_suite(Config) ->
@@ -450,6 +455,37 @@ from_list_test_() ->
?_assertError(badarg, from_list(no_array))
].
+from_test_() ->
+ Seq = fun({N,Max}) ->
+ if N =< Max -> {N, {N+1, Max}};
+ true -> done
+ end
+ end,
+ N0 = ?LEAFSIZE,
+ N1 = ?NODESIZE*N0,
+ N2 = ?NODESIZE*N1,
+ N3 = ?NODESIZE*N2,
+ N4 = ?NODESIZE*N3,
+ [?_assert(array:size(from(Seq, {1,0})) =:= 0),
+ ?_assert(array:is_fix(from(Seq, {1,0})) =:= false),
+ ?_assert(array:size(from(Seq, {1,1})) =:= 1),
+ ?_assert(array:is_fix(from(Seq, {1,1})) =:= false),
+ ?_assert(to_list(from(Seq, {1,N0-1})) =:= lists:seq(1,N0-1)),
+ ?_assert(to_list(from(Seq, {1,N0})) =:= lists:seq(1,N0)),
+ ?_assert(to_list(from(Seq, {1,N0+1})) =:= lists:seq(1,N0+1)),
+ ?_assert(to_list(from(Seq, {1,N0+2})) =:= lists:seq(1,N0+2)),
+ ?_assert(to_list(from(Seq, {1,N2-1})) =:= lists:seq(1,N2-1)),
+ ?_assert(to_list(from(Seq, {1,N2})) =:= lists:seq(1,N2)),
+ ?_assert(to_list(from(Seq, {1,N2+1})) =:= lists:seq(1,N2+1)),
+ ?_assert(to_list(from(Seq, {0,N3})) =:= lists:seq(0,N3)),
+ ?_assert(to_list(from(Seq, {0,N4})) =:= lists:seq(0,N4)),
+ ?_assert(array:size(from(Seq, {1,N1})) =:= N1),
+ ?_assertError(badarg, from(fun(A) -> A end, foo)),
+ ?_assertError(badarg, from(no_fun, foo))
+ ].
+
+
+
to_orddict_test_() ->
N0 = ?LEAFSIZE,
[?_assert([] =:= to_orddict(new())),
@@ -758,6 +794,36 @@ sparse_foldr_test_() ->
set(0,0,new())))))
].
+import_export(_Config) ->
+ %% Some examples of usages
+ FloatBin = << <<N:32/float-native>> || N <- lists:seq(1, 20000)>>,
+ ToFloat32 = fun(_K, V, Acc) when is_binary(Acc) ->
+ <<Acc/binary, V:32/float-native>>
+ end,
+ FromFloat32 = fun(<<N:32/float-native, Rest/binary>>) ->
+ {N, Rest};
+ (<<>>) ->
+ done
+ end,
+
+ ?_assert(FloatBin =:= array:foldl(ToFloat32, <<>>, array:from(FromFloat32, FloatBin))),
+ RGBBin = << <<N:8, N:8, N:8>> || N <- lists:seq(1, 256)>>,
+ RGB2Bin = fun(_K, {R,G,B}, Acc) ->
+ <<Acc/binary, R:8, G:8, B:8>>
+ end,
+ Bin2RGB = fun(<<R:8,G:8,B:8, Rest/binary>>) ->
+ {{R,G,B}, Rest};
+ (<<>>) ->
+ done
+ end,
+ ?_assert(RGBBin =:= array:foldl(RGB2Bin, <<>>, array:from(Bin2RGB, RGBBin))),
+
+ ok.
+
+doctests(Config) when is_list(Config) ->
+ shell_docs:test(array, []).
+
+
new_test(Config) when is_list(Config) -> new_test_(), ok.
fix_test(Config) when is_list(Config) -> fix_test_(), ok.
relax_test(Config) when is_list(Config) -> relax_test_(), ok.
@@ -775,3 +841,4 @@ foldl_test(Config) when is_list(Config) -> foldl_test_(), ok.
sparse_foldl_test(Config) when is_list(Config) -> sparse_foldl_test_(), ok.
foldr_test(Config) when is_list(Config) -> foldr_test_(), ok.
sparse_foldr_test(Config) when is_list(Config) -> sparse_foldr_test_(), ok.
+from_test(Config) when is_list(Config) -> from_test_(), ok.
--
2.51.0