Skip to content

Commit 103960a

Browse files
committed
Add support for non-byte-aligned bitstrings
Signed-off-by: Paul Guyot <pguyot@kallisys.net>
1 parent 1d9d50f commit 103960a

52 files changed

Lines changed: 2664 additions & 731 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/build-and-test-macos.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,12 @@ jobs:
259259
run: |
260260
./tests/test-term
261261
262+
- name: "Test: test-bitstring"
263+
timeout-minutes: 10
264+
working-directory: build
265+
run: |
266+
./tests/test-bitstring
267+
262268
- name: "Test: test_etest.avm"
263269
timeout-minutes: 5
264270
working-directory: build

.github/workflows/build-and-test-on-freebsd.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,13 @@ jobs:
164164
cd build
165165
./tests/test-term
166166
167+
- name: "Test: test-bitstring"
168+
shell: freebsd {0}
169+
run: |
170+
cd $GITHUB_WORKSPACE;
171+
cd build
172+
./tests/test-bitstring
173+
167174
- name: "Test: test_etest.avm"
168175
shell: freebsd {0}
169176
run: |

.github/workflows/build-and-test.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -808,6 +808,19 @@ jobs:
808808
ulimit -c unlimited
809809
./tests/test-erlang -s prime_smp
810810
811+
- name: "Test: test-bitstring with valgrind"
812+
if: matrix.library-arch == ''
813+
working-directory: build
814+
run: |
815+
ulimit -c unlimited
816+
valgrind --error-exitcode=1 ./tests/test-bitstring
817+
818+
- name: "Test: test-bitstring"
819+
working-directory: build
820+
run: |
821+
ulimit -c unlimited
822+
./tests/test-bitstring
823+
811824
- name: "Test: test-enif with valgrind"
812825
if: matrix.library-arch == ''
813826
working-directory: build

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3434
- Added `console:print_err/1` to write to standard error
3535
- Added `erlang:term_to_binary/2`, `erlang:is_builtin/3` and `erlang:bitstring_to_list/1`
3636
- Added `lists:mapfoldr/3`
37+
- Added support for non-byte-aligned bitstrings
3738

3839
### Changed
3940
- Updated network type db() to dbm() to reflect the actual representation of the type

code-queries/allocations-exceeding-ensure-free.ql

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -950,6 +950,14 @@ predicate consumesContextBudget(FunctionCall efCall, FunctionCall consumer) {
950950
ensureFreeContextVar(efCall)
951951
}
952952

953+
/**
954+
* Holds if `f` tears down a context (e.g. `context_destroy`): it frees the
955+
* context rather than re-establishing a usable heap budget on it.
956+
*/
957+
predicate isContextTeardown(Function f) {
958+
f.hasName("context_destroy")
959+
}
960+
953961
/**
954962
* Holds if `efCall` is a redundant reserving ensure_free: no allocating call
955963
* uses its budget, and `supersedingCall` is a subsequent call that resets
@@ -959,6 +967,8 @@ predicate consumesContextBudget(FunctionCall efCall, FunctionCall consumer) {
959967
pragma[nomagic]
960968
predicate isRedundantEnsureFree(FunctionCall efCall, FunctionCall supersedingCall) {
961969
isReservingEnsureFreeCall(efCall) and
970+
// Test code calls ensure_free for its GC side effects
971+
not efCall.getFile().getRelativePath().matches("tests/%") and
962972
// No allocating call uses this ensure_free's budget
963973
not exists(FunctionCall a | allocToBudget(a, efCall)) and
964974
// ...and no pointer-bumping consumer (memory_copy_term_tree) uses it either
@@ -986,8 +996,10 @@ predicate isRedundantEnsureFree(FunctionCall efCall, FunctionCall supersedingCal
986996
// A function that internally calls ensure_free on the caller's
987997
// context (e.g., enif_make_resource), passed that same context as an
988998
// argument. Uses the ensure_free-only notion: own-heap setup does not
989-
// reset this context's budget.
999+
// reset this context's budget. Context teardown (context_destroy) is
1000+
// excluded: it frees the context rather than re-establishing a budget.
9901001
not isEnsureFreeCall(supersedingCall) and
1002+
not isContextTeardown(supersedingCall.getTarget()) and
9911003
transitivelyCallsEnsureFreeOnly(supersedingCall.getTarget()) and
9921004
supersedingCall.getAnArgument().(VariableAccess).getTarget() = ctxVar
9931005
) and

doc/src/apidocs/libatomvm/functions.rst

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,6 @@ Functions
242242
.. doxygenfunction:: term_binary_heap_size
243243
.. doxygenfunction:: term_binary_size_is_heap_binary
244244
.. doxygenfunction:: term_boxed_size
245-
.. doxygenfunction:: term_bs_insert_binary
246245
.. doxygenfunction:: term_compare
247246
.. doxygenfunction:: term_create_empty_binary
248247
.. doxygenfunction:: term_create_uninitialized_binary

libs/estdlib/src/erlang.erl

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@
155155
hd/1,
156156
is_atom/1,
157157
is_binary/1,
158+
is_bitstring/1,
158159
is_boolean/1,
159160
is_float/1,
160161
is_function/1,
@@ -933,15 +934,16 @@ binary_to_list(_Binary) ->
933934

934935
%%-----------------------------------------------------------------------------
935936
%% @param Bitstring Bitstring to convert to list
936-
%% @returns a list of bytes from the bitstring
937+
%% @returns a list of bytes, with a final element holding the trailing bits if
938+
%% `Bitstring' is not a whole number of bytes
937939
%% @doc Convert a bitstring to a list of bytes.
938940
%%
939-
%% Unlike Erlang/OTP, AtomVM only supports byte-aligned bitstrings (binaries),
940-
%% so the returned list never has a trailing bitstring and this function
941-
%% behaves like `binary_to_list/1'.
941+
%% If the number of bits in `Bitstring' is not divisible by `8', the
942+
%% last element of the list is a bitstring containing the trailing
943+
%% `1..7' bits, e.g. `bitstring_to_list(<<1:1>>)' returns `[<<1:1>>]'.
942944
%% @end
943945
%%-----------------------------------------------------------------------------
944-
-spec bitstring_to_list(Bitstring :: bitstring()) -> [byte()].
946+
-spec bitstring_to_list(Bitstring :: bitstring()) -> [byte() | bitstring()].
945947
bitstring_to_list(_Bitstring) ->
946948
erlang:nif_error(undefined).
947949

@@ -1973,6 +1975,19 @@ is_atom(_Term) ->
19731975
is_binary(_Term) ->
19741976
erlang:nif_error(undefined).
19751977

1978+
%%-----------------------------------------------------------------------------
1979+
%% @param Term the term to test
1980+
%% @returns `true' if `Term' is a bitstring; `false', otherwise.
1981+
%% @doc Return `true' if `Term' is a bitstring (including a binary);
1982+
%% `false', otherwise.
1983+
%%
1984+
%% This function may be used in a guard expression.
1985+
%% @end
1986+
%%-----------------------------------------------------------------------------
1987+
-spec is_bitstring(Term :: term()) -> boolean().
1988+
is_bitstring(_Term) ->
1989+
erlang:nif_error(undefined).
1990+
19761991
%%-----------------------------------------------------------------------------
19771992
%% @param Term the term to test
19781993
%% @returns `true' if `Term' is a boolean (`true' or `false'); `false', otherwise.

libs/jit/src/default_atoms.hrl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
-define(ALL_ATOM_INDEX, 15).
3838
-define(LOWERCASE_EXIT_ATOM_INDEX, 16).
3939
-define(BADRECORD_ATOM_INDEX, 17).
40+
-define(SYSTEM_LIMIT_ATOM_INDEX, 20).
4041

4142
-define(FALSE_ATOM, ((?FALSE_ATOM_INDEX bsl ?TERM_IMMED2_TAG_SIZE) bor ?TERM_IMMED2_ATOM)).
4243
-define(TRUE_ATOM, ((?TRUE_ATOM_INDEX bsl ?TERM_IMMED2_TAG_SIZE) bor ?TERM_IMMED2_ATOM)).
@@ -68,6 +69,9 @@
6869
((?LOWERCASE_EXIT_ATOM_INDEX bsl ?TERM_IMMED2_TAG_SIZE) bor ?TERM_IMMED2_ATOM)
6970
).
7071
-define(BADRECORD_ATOM, ((?BADRECORD_ATOM_INDEX bsl ?TERM_IMMED2_TAG_SIZE) bor ?TERM_IMMED2_ATOM)).
72+
-define(SYSTEM_LIMIT_ATOM,
73+
((?SYSTEM_LIMIT_ATOM_INDEX bsl ?TERM_IMMED2_TAG_SIZE) bor ?TERM_IMMED2_ATOM)
74+
).
7175

7276
-define(DEFAULT_ATOMS, #{
7377
false => ?FALSE_ATOM,

0 commit comments

Comments
 (0)