Skip to content

Commit

Permalink
Forward port changes from v0.6 release branch
Browse files Browse the repository at this point in the history
Merge `binary:split/2,3` fix from release-0.6 and few other fixes.
  • Loading branch information
bettio committed Jan 25, 2025
2 parents 087d892 + 0d6a00e commit ed48c7c
Show file tree
Hide file tree
Showing 9 changed files with 98 additions and 34 deletions.
15 changes: 11 additions & 4 deletions .github/workflows/build-and-test-on-freebsd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,17 @@ concurrency:

jobs:
build-and-test-on-freebsd:
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
name: Build and test AtomVM on FreeBSD
env:
ATOMVM_EXAMPLE: "atomvm-example"

strategy:
fail-fast: false

matrix:
os_release: ["13.4", "14.2"]

steps:

- uses: actions/checkout@v4
Expand All @@ -47,17 +54,17 @@ jobs:
uses: vmactions/freebsd-vm@v1
timeout-minutes: 20
with:
release: 13.2
release: ${{ matrix.os_release }}
envs: 'ATOMVM_EXAMPLE'
usesh: true
sync: rsync
copyback: false

prepare: |
pkg install -y curl cmake gperf erlang elixir mbedtls
pkg install -y curl cmake gperf erlang elixir rebar3 mbedtls
run: |
set -e
echo "%%"
echo "%% System Info"
echo "%%"
Expand Down
10 changes: 10 additions & 0 deletions .github/workflows/esp32-simtest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ jobs:
runs-on: ubuntu-24.04
if: needs.cli_token.outputs.token_check == 'true'
container: espressif/idf:${{ matrix.idf-version }}
services:
wokwi-ci-server:
image: wokwi/wokwi-ci-server
ports:
- 3000:3000

strategy:
fail-fast: false
# focus on device diversity.
Expand Down Expand Up @@ -120,6 +126,10 @@ jobs:
idf.py -DSDKCONFIG_DEFAULTS='sdkconfig.ci.wokwi' set-target ${{matrix.esp-idf-target}}
idf.py build
- name: Configure Wokwi environment
run: |
echo "WOKWI_CLI_SERVER=ws://wokwi-ci-server:3000" >> $GITHUB_ENV
- name: Run ESP32-sim tests using Wokwi CI
working-directory: ./src/platforms/esp32/test/
env:
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ certain VM instructions are used.
- Fixed compilation with latest debian gcc-arm-none-eabi
- Fix `network:stop/0` on ESP32 so the network can be started again
- Fix matching of binaries on unaligned boundaries for code compiled with older versions of OTP
- Fix a memory corruption caused by `binary:split/2,3`

## [0.6.5] - 2024-10-15

Expand Down
6 changes: 5 additions & 1 deletion src/libAtomVM/nifs.c
Original file line number Diff line number Diff line change
Expand Up @@ -3181,15 +3181,19 @@ static term nif_binary_split(Context *ctx, int argc, term argv[])
size_t num_segments = 1;
const char *temp_bin_data = bin_data;
int temp_bin_size = bin_size;
size_t heap_size = 0;
do {
const char *found = (const char *) memmem(temp_bin_data, temp_bin_size, pattern_data, pattern_size);
if (!found) break;
num_segments++;
heap_size += CONS_SIZE + term_sub_binary_heap_size(argv[0], found - temp_bin_data);
int next_search_offset = found - temp_bin_data + pattern_size;
temp_bin_data += next_search_offset;
temp_bin_size -= next_search_offset;
} while (global && temp_bin_size >= pattern_size);

heap_size += CONS_SIZE + term_sub_binary_heap_size(argv[0], temp_bin_size);

term result_list = term_nil();

if (num_segments == 1) {
Expand All @@ -3202,7 +3206,7 @@ static term nif_binary_split(Context *ctx, int argc, term argv[])
}

// binary:split/2,3 always return sub binaries, except when copied binaries are as small as sub-binaries.
if (UNLIKELY(memory_ensure_free_with_roots(ctx, LIST_SIZE(num_segments, TERM_BOXED_SUB_BINARY_SIZE), 2, argv, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) {
if (UNLIKELY(memory_ensure_free_with_roots(ctx, heap_size, 2, argv, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) {
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
}

Expand Down
2 changes: 2 additions & 0 deletions src/libAtomVM/otp_net.c
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,10 @@ static term eai_errno_to_term(int err, GlobalContext *glb)
#ifdef HAVE_EXTENDED_EAI_ERRNO
case EAI_BADHINTS:
return globalcontext_make_atom(glb, ATOM_STR("\xB", "eaibadhints"));
#ifdef HAVE_EAI_OVERFLOW
case EAI_OVERFLOW:
return globalcontext_make_atom(glb, ATOM_STR("\xB", "eaioverflow"));
#endif
case EAI_PROTOCOL:
return globalcontext_make_atom(glb, ATOM_STR("\xB", "eaiprotocol"));
case EAI_SYSTEM:
Expand Down
2 changes: 1 addition & 1 deletion src/libAtomVM/term.h
Original file line number Diff line number Diff line change
Expand Up @@ -1044,7 +1044,7 @@ static inline term term_from_literal_binary(const void *data, size_t size, Heap
*/
static inline size_t term_sub_binary_heap_size(term binary, size_t len)
{
if (term_is_refc_binary(binary) && len >= SUB_BINARY_MIN) {
if ((term_is_refc_binary(binary) || term_is_sub_binary(binary)) && len >= SUB_BINARY_MIN) {
return TERM_BOXED_SUB_BINARY_SIZE;
} else {
return term_binary_heap_size(len);
Expand Down
4 changes: 4 additions & 0 deletions src/platforms/generic_unix/lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ check_symbol_exists(EAI_BADHINTS "netdb.h" HAVE_EXTENDED_EAI_ERRNO)
if (HAVE_EXTENDED_EAI_ERRNO)
target_compile_definitions(libAtomVM${PLATFORM_LIB_SUFFIX} PUBLIC HAVE_EXTENDED_EAI_ERRNO)
endif()
check_symbol_exists(EAI_OVERFLOW "netdb.h" HAVE_EAI_OVERFLOW)
if (HAVE_EAI_OVERFLOW)
target_compile_definitions(libAtomVM${PLATFORM_LIB_SUFFIX} PUBLIC HAVE_EAI_OVERFLOW)
endif()

if (COVERAGE)
include(CodeCoverage)
Expand Down
90 changes: 63 additions & 27 deletions tests/erlang_tests/test_binary_split.erl
Original file line number Diff line number Diff line change
Expand Up @@ -20,63 +20,99 @@

-module(test_binary_split).

-export([start/0, split_compare/3, split_compare/2, compare_bin/2, fail_split/1]).
-export([start/0, split_compare/3, split_compare/2, compare_bin/2, fail_split/1, id/1]).

start() ->
split_compare(<<"Hello:World">>, <<"Hello">>, <<"World">>) +
split_compare(<<"Hello:::World:">>, <<"Hello">>, <<"::World:">>) +
split_compare(<<"Test:">>, <<"Test">>, <<>>) +
split_compare(<<":">>, <<>>, <<>>) +
split_compare(<<>>, <<>>) +
split_compare(<<"Test">>, <<>>) +
split_compare2(<<"Test">>, <<>>) +
split_compare2(<<"helloSEPARATORworld">>, <<"hello">>, <<"world">>) +
fail_split(<<>>) +
fail_split({1, 2}) +
fail_split2({1, 2}).
ok = split_compare(<<"Hello:World">>, <<"Hello">>, <<"World">>),
ok = split_compare(<<"Hello:::World:">>, <<"Hello">>, <<"::World:">>),
ok = split_compare(<<"Test:">>, <<"Test">>, <<>>),
ok = split_compare(<<":">>, <<>>, <<>>),
ok = split_compare(<<>>, <<>>),
ok = split_compare(<<"Test">>, <<>>),
ok = split_compare2(<<"Test">>, <<>>),
ok = split_compare2(<<"helloSEPARATORworld">>, <<"hello">>, <<"world">>),
ok = fail_split(<<>>),
ok = fail_split({1, 2}),
ok = fail_split2({1, 2}),
case erlang:system_info(machine) of
"BEAM" -> ok;
"ATOM" -> ok = memory_allocation_split()
end,
0.

split_compare(Bin, Part1) ->
[A] = binary:split(Bin, <<":">>),
compare_bin(Part1, A).

split_compare(Bin, Part1, Part2) ->
[A, B] = binary:split(Bin, <<":">>),
compare_bin(Part1, A) + compare_bin(B, Part2).
ok = compare_bin(Part1, A),
ok = compare_bin(B, Part2).

split_compare2(Bin, Part1) ->
[A] = binary:split(Bin, <<"SEPARATOR">>),
compare_bin(Part1, A).

split_compare2(Bin, Part1, Part2) ->
[A, B] = binary:split(Bin, <<"SEPARATOR">>),
compare_bin(Part1, A) + compare_bin(B, Part2).
ok = compare_bin(Part1, A),
ok = compare_bin(B, Part2).

compare_bin(Bin1, Bin2) ->
compare_bin(Bin1, Bin2, byte_size(Bin1) - 1).

compare_bin(_Bin1, _Bin2, -1) ->
1;
ok;
compare_bin(Bin1, Bin2, Index) ->
B1 = binary:at(Bin1, Index),
case binary:at(Bin2, Index) of
B1 ->
compare_bin(Bin1, Bin2, Index - 1);
_Any ->
0
end.
B1 = binary:at(Bin2, Index),
compare_bin(Bin1, Bin2, Index - 1).

fail_split(Separator) ->
try binary:split(<<"TESTBIN">>, Separator) of
_Any -> 2000
_Any -> {unexpected, _Any}
catch
error:badarg -> 1;
_:_ -> 4000
error:badarg -> ok;
T:V -> {unexpected, {T, V}}
end.

fail_split2(Bin) ->
try binary:split(Bin, <<"TESTSEPARATOR">>) of
_Any -> 2000
_Any -> {unxpected, _Any}
catch
error:badarg -> 1;
_:_ -> 4000
error:badarg -> ok;
T:V -> {unxpected, {T, V}}
end.

memory_allocation_split() ->
Parent = self(),
Hostname = <<"atomvm">>,
{Pid, MonitorRef} = spawn_opt(
fun() ->
% Carefully designed lists to generate a crash on unix 64 bits
% This binary is 63 bytes long, so it's stored on heap on 64 bits
% binary:split should allocate sufficient bytes as subbinaries
% have to be on heap as well
HeapBin = list_to_binary([
id(Hostname), <<"@atomvms3.object.stream.atomvms3.object.stream.atomvms3.o">>
]),
List1 = binary:split(HeapBin, <<"@">>, [global]),
Parent ! {self(), List1}
end,
[link, monitor, {atomvm_heap_growth, minimum}]
),
ok =
receive
{Pid, List1} ->
2 = length(List1),
ok
after 5000 -> timeout
end,
normal =
receive
{'DOWN', MonitorRef, process, Pid, Reason} -> Reason
after 5000 -> timeout
end,
ok.

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

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

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

Expand Down

0 comments on commit ed48c7c

Please sign in to comment.