From dfe2003ad1e42ea2d23adf944e55e0c926e72685 Mon Sep 17 00:00:00 2001 From: Paul Guyot Date: Sun, 29 Sep 2024 16:21:33 +0200 Subject: [PATCH] Add support for `binary:copy/1,2` Signed-off-by: Paul Guyot --- CHANGELOG.md | 1 + src/libAtomVM/nifs.c | 38 +++++++++++++++++++++++ src/libAtomVM/nifs.gperf | 2 ++ tests/erlang_tests/CMakeLists.txt | 2 ++ tests/erlang_tests/test_binary_copy.erl | 40 +++++++++++++++++++++++++ tests/test.c | 1 + 6 files changed, 84 insertions(+) create mode 100644 tests/erlang_tests/test_binary_copy.erl diff --git a/CHANGELOG.md b/CHANGELOG.md index b85320761..a7cfaa861 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ also non string parameters (e.g. `Enum.join([1, 2], ",")` - Add support to Elixir for `Keyword.split/2` - Support for `binary:split/3` and `string:find/2,3` - Support for large tuples (more than 255 elements) in external terms. +- Support for `binary:copy/1,2` ### Changed diff --git a/src/libAtomVM/nifs.c b/src/libAtomVM/nifs.c index ea202c77f..03daba1a7 100644 --- a/src/libAtomVM/nifs.c +++ b/src/libAtomVM/nifs.c @@ -88,6 +88,7 @@ static term binary_to_atom(Context *ctx, int argc, term argv[], int create_new); static term list_to_atom(Context *ctx, int argc, term argv[], int create_new); static term nif_binary_at_2(Context *ctx, int argc, term argv[]); +static term nif_binary_copy(Context *ctx, int argc, term argv[]); static term nif_binary_first_1(Context *ctx, int argc, term argv[]); static term nif_binary_last_1(Context *ctx, int argc, term argv[]); static term nif_binary_part_3(Context *ctx, int argc, term argv[]); @@ -211,6 +212,12 @@ static const struct Nif binary_at_nif = .nif_ptr = nif_binary_at_2 }; +static const struct Nif binary_copy_nif = +{ + .base.type = NIFFunctionType, + .nif_ptr = nif_binary_copy +}; + static const struct Nif binary_first_nif = { .base.type = NIFFunctionType, @@ -2943,6 +2950,37 @@ static term nif_binary_at_2(Context *ctx, int argc, term argv[]) return term_from_int11(term_binary_data(bin_term)[pos]); } +static term nif_binary_copy(Context *ctx, int argc, term argv[]) +{ + term bin_term = argv[0]; + VALIDATE_VALUE(bin_term, term_is_binary); + + size_t count = 1; + + if (argc == 2) { + term count_term = argv[1]; + VALIDATE_VALUE(count_term, term_is_integer); + count = term_to_int(count_term); + } + + size_t size = term_binary_size(bin_term); + size_t dest_size = size * count; + + size_t alloc_heap_size = term_binary_heap_size(dest_size); + if (UNLIKELY(memory_ensure_free_with_roots(ctx, alloc_heap_size, 1, &bin_term, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { + RAISE_ERROR(OUT_OF_MEMORY_ATOM); + } + + term result = term_create_uninitialized_binary(dest_size, &ctx->heap, ctx->global); + uint8_t *dest = (uint8_t *) term_binary_data(result); + const void *src = (const void *) term_binary_data(bin_term); + for (size_t i = 0; i < count; i++) { + memcpy((void *) dest, src, size); + dest += size; + } + return result; +} + static term nif_binary_first_1(Context *ctx, int argc, term argv[]) { UNUSED(argc); diff --git a/src/libAtomVM/nifs.gperf b/src/libAtomVM/nifs.gperf index 5852e86bf..f23644630 100644 --- a/src/libAtomVM/nifs.gperf +++ b/src/libAtomVM/nifs.gperf @@ -32,6 +32,8 @@ struct NifNameAndNifPtr }; %% binary:at/2, &binary_at_nif +binary:copy/1, &binary_copy_nif +binary:copy/2, &binary_copy_nif binary:first/1, &binary_first_nif binary:last/1, &binary_last_nif binary:part/3, &binary_part_nif diff --git a/tests/erlang_tests/CMakeLists.txt b/tests/erlang_tests/CMakeLists.txt index fce4e7166..979d9bcf6 100644 --- a/tests/erlang_tests/CMakeLists.txt +++ b/tests/erlang_tests/CMakeLists.txt @@ -267,6 +267,7 @@ compile_erlang(spawn_fun3) compile_erlang(binary_at_test) compile_erlang(binary_first_test) compile_erlang(binary_last_test) +compile_erlang(test_binary_copy) compile_erlang(test_integer_to_binary) compile_erlang(test_list_to_binary) @@ -736,6 +737,7 @@ add_custom_target(erlang_test_modules DEPENDS binary_at_test.beam binary_first_test.beam binary_last_test.beam + test_binary_copy.beam test_integer_to_binary.beam test_list_to_binary.beam diff --git a/tests/erlang_tests/test_binary_copy.erl b/tests/erlang_tests/test_binary_copy.erl new file mode 100644 index 000000000..18552fd40 --- /dev/null +++ b/tests/erlang_tests/test_binary_copy.erl @@ -0,0 +1,40 @@ +% +% This file is part of AtomVM. +% +% Copyright 2024 Paul Guyot +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. +% +% SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later +% + +-module(test_binary_copy). + +-export([start/0]). + +start() -> + ok = test_copy1(), + ok = test_copy2(), + 0. + +test_copy1() -> + <<>> = binary:copy(<<>>), + <<"foo">> = binary:copy(<<"foo">>), + ok. + +test_copy2() -> + <<>> = binary:copy(<<>>, 1), + <<"foo">> = binary:copy(<<"foo">>, 1), + <<>> = binary:copy(<<"foo">>, 0), + <<"foofoo">> = binary:copy(<<"foo">>, 2), + ok. diff --git a/tests/test.c b/tests/test.c index 37701611e..637343448 100644 --- a/tests/test.c +++ b/tests/test.c @@ -309,6 +309,7 @@ struct Test tests[] = { TEST_CASE_EXPECTED(binary_at_test, 121), TEST_CASE_EXPECTED(binary_first_test, 82), TEST_CASE_EXPECTED(binary_last_test, 110), + TEST_CASE(test_binary_copy), TEST_CASE(test_integer_to_binary), TEST_CASE(test_list_to_binary),