Skip to content

Commit

Permalink
Merge pull request #1389 from pguyot/w49/add-split_binary
Browse files Browse the repository at this point in the history
Add erlang:split_binary/2

These changes are made under both the "Apache 2.0" and the "GNU Lesser General
Public License 2.1 or later" license terms (dual license).

SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
  • Loading branch information
bettio committed Dec 12, 2024
2 parents 89c334c + 7e42a05 commit 652e677
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Added a limited implementation of the OTP `ets` interface
- Added `code:all_loaded/0` and `code:all_available/0`
- Added `erlang:split_binary/2`

## [0.6.6] - Unreleased

Expand Down
13 changes: 13 additions & 0 deletions libs/estdlib/src/erlang.erl
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@
garbage_collect/1,
binary_to_term/1,
term_to_binary/1,
split_binary/2,
timestamp/0,
universaltime/0,
localtime/0,
Expand Down Expand Up @@ -1223,6 +1224,18 @@ binary_to_term(_Binary) ->
term_to_binary(_Term) ->
erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @returns A tuple with two subbinaries
%% @param Bin binary to split
%% @param Pos position to split the binary, from 0 to `byte_size(Bin)'
%% @doc Split a binary into two sub-binaries. This operation is not destructive
%% and will create two new binaries.
%% @end
%%-----------------------------------------------------------------------------
-spec split_binary(Bin :: binary(), Pos :: non_neg_integer()) -> {binary(), binary()}.
split_binary(_Bin, _Pos) ->
erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @returns A tuple representing the current timestamp.
%% @see monotonic_time/1
Expand Down
36 changes: 36 additions & 0 deletions src/libAtomVM/nifs.c
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ static term nif_erlang_system_info(Context *ctx, int argc, term argv[]);
static term nif_erlang_system_flag(Context *ctx, int argc, term argv[]);
static term nif_erlang_binary_to_term(Context *ctx, int argc, term argv[]);
static term nif_erlang_term_to_binary(Context *ctx, int argc, term argv[]);
static term nif_erlang_split_binary(Context *ctx, int argc, term argv[]);
static term nif_erlang_throw(Context *ctx, int argc, term argv[]);
static term nif_erlang_raise(Context *ctx, int argc, term argv[]);
static term nif_ets_new(Context *ctx, int argc, term argv[]);
Expand Down Expand Up @@ -571,6 +572,12 @@ static const struct Nif term_to_binary_nif =
.nif_ptr = nif_erlang_term_to_binary
};

static const struct Nif split_binary_nif =
{
.base.type = NIFFunctionType,
.nif_ptr = nif_erlang_split_binary
};

static const struct Nif throw_nif =
{
.base.type = NIFFunctionType,
Expand Down Expand Up @@ -3022,6 +3029,35 @@ static term nif_erlang_term_to_binary(Context *ctx, int argc, term argv[])
return ret;
}

static term nif_erlang_split_binary(Context *ctx, int argc, term argv[])
{
UNUSED(argc);

term bin_term = argv[0];
term pos_term = argv[1];

VALIDATE_VALUE(bin_term, term_is_binary);
VALIDATE_VALUE(pos_term, term_is_integer);

int32_t size = term_binary_size(bin_term);
avm_int_t pos = term_to_int(pos_term);

if (UNLIKELY((pos < 0) || (pos > size))) {
RAISE_ERROR(BADARG_ATOM);
}

size_t alloc_heap_size = term_sub_binary_heap_size(bin_term, pos) + term_sub_binary_heap_size(bin_term, size - pos) + TUPLE_SIZE(2);
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 sub_binary_a = term_maybe_create_sub_binary(bin_term, 0, pos, &ctx->heap, ctx->global);
term sub_binary_b = term_maybe_create_sub_binary(bin_term, pos, size - pos, &ctx->heap, ctx->global);
term tuple = term_alloc_tuple(2, &ctx->heap);
term_put_tuple_element(tuple, 0, sub_binary_a);
term_put_tuple_element(tuple, 1, sub_binary_b);
return tuple;
}

static term nif_binary_at_2(Context *ctx, int argc, term argv[])
{
UNUSED(argc);
Expand Down
1 change: 1 addition & 0 deletions src/libAtomVM/nifs.gperf
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ erlang:put/2, &put_nif
erlang:binary_to_term/1, &binary_to_term_nif
erlang:binary_to_term/2, &binary_to_term_nif
erlang:term_to_binary/1, &term_to_binary_nif
erlang:split_binary/2, &split_binary_nif
erlang:throw/1, &throw_nif
erlang:raise/3, &raise_nif
erlang:unlink/1, &unlink_nif
Expand Down
2 changes: 2 additions & 0 deletions tests/erlang_tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ compile_erlang(test_unicode)

compile_erlang(test_binary_part)
compile_erlang(test_binary_split)
compile_erlang(test_split_binary)

compile_erlang(plusone)
compile_erlang(plusone2)
Expand Down Expand Up @@ -753,6 +754,7 @@ add_custom_target(erlang_test_modules DEPENDS

test_binary_part.beam
test_binary_split.beam
test_split_binary.beam

plusone.beam
plusone2.beam
Expand Down
60 changes: 60 additions & 0 deletions tests/erlang_tests/test_split_binary.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
%
% This file is part of AtomVM.
%
% Copyright 2024 Paul Guyot <[email protected]>
%
% 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_split_binary).

-export([
start/0,
id/1
]).

start() ->
ok = test_split_binary(),
0.

test_split_binary() ->
{<<"a">>, <<"bcde">>} = split_binary(?MODULE:id(<<"abcde">>), 1),
{<<>>, <<"abcde">>} = split_binary(?MODULE:id(<<"abcde">>), 0),
{<<>>, <<>>} = split_binary(?MODULE:id(<<>>), 0),
{<<"abcde">>, <<>>} = split_binary(?MODULE:id(<<"abcde">>), 5),
ok =
try
_ = split_binary(?MODULE:id(<<"abcde">>), 6),
unexpected
catch
error:badarg -> ok
end,
ok =
try
_ = split_binary(?MODULE:id(<<"abcde">>), -1),
unexpected
catch
error:badarg -> ok
end,
ok =
try
_ = split_binary(?MODULE:id(<<>>), 1),
unexpected
catch
error:badarg -> ok
end,
ok.

id(X) -> X.
1 change: 1 addition & 0 deletions tests/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ struct Test tests[] = {

TEST_CASE_EXPECTED(test_binary_part, 12),
TEST_CASE_EXPECTED(test_binary_split, 16),
TEST_CASE(test_split_binary),

TEST_CASE_COND(plusone, 134217728, LONG_MAX != 9223372036854775807),

Expand Down

0 comments on commit 652e677

Please sign in to comment.