Skip to content

Commit 8538c84

Browse files
committed
Reject negative dynamic segment sizes in bit syntax matching
A dynamic segment size comes from a register and can be negative. It was scaled by the segment unit before being validated, so the scaled value could wrap to a small size that passed the capacity check and matched, or move the match state offset before the start of the binary. The JIT also untagged small integers with a logical shift, which turned any negative integer into a large positive one. Signed-off-by: Paul Guyot <pguyot@kallisys.net>
1 parent f7594af commit 8538c84

7 files changed

Lines changed: 189 additions & 46 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8282
- Fixed ESP32 socket driver holding the global socket-list lock across blocking TCP connects, leaking the port on connect failure, losing concurrent `accept` waiters, leaking `netbuf` on receive error paths, and a recycled-`netconn` race between socket close and the event handler
8383
- Fixed generic_unix TCP server sockets performing an abortive close that could truncate replies awaiting ack
8484
- Fixed a JIT crash (`EXC_BAD_ACCESS`/SIGBUS) on Apple Silicon
85+
- Fixed a bug where negative or oversized segment sizes were not rejected in binary matching
8586

8687
## [0.7.0-alpha.1] - 2026-04-06
8788

libs/jit/src/jit.erl

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1307,7 +1307,7 @@ first_pass(<<?OP_BS_GET_INTEGER2, Rest0/binary>>, MMod, MSt0, State0) ->
13071307
is_integer(SizeReg) ->
13081308
{MSt4, SizeReg * Unit};
13091309
true ->
1310-
MSt5 = MMod:mul(MSt4, SizeReg, Unit),
1310+
MSt5 = scale_size_by_unit(SizeReg, Unit, Fail, MMod, MSt4),
13111311
{MSt5, SizeReg}
13121312
end,
13131313
{MSt7, BSBinaryReg} = MMod:get_array_element(MSt6, MatchStateRegPtr, 1),
@@ -1345,7 +1345,7 @@ first_pass(<<?OP_BS_GET_FLOAT2, Rest0/binary>>, MMod, MSt0, State0) ->
13451345
is_integer(SizeReg) ->
13461346
{MSt4, SizeReg * Unit};
13471347
true ->
1348-
MSt5 = MMod:mul(MSt4, SizeReg, Unit),
1348+
MSt5 = scale_size_by_unit(SizeReg, Unit, Fail, MMod, MSt4),
13491349
{MSt5, SizeReg}
13501350
end,
13511351
{MSt7, BSBinaryReg} = MMod:get_array_element(MSt6, MatchStateRegPtr, 1),
@@ -1420,11 +1420,12 @@ first_pass(<<?OP_BS_GET_BINARY2, Rest0/binary>>, MMod, MSt0, State0) ->
14201420
MMod:free_native_registers(BSt1, [SizeValReg])
14211421
end,
14221422
fun(BSt0) ->
1423-
{BSt1, SizeValReg} = term_to_int(SizeValReg, 0, MMod, BSt0),
1424-
BSt2 = MMod:sub(BSt1, SizeReg, SizeValReg),
1425-
BSt3 = cond_jump_to_label({SizeReg, '<', BSOffsetReg1}, Fail, MMod, BSt2),
1426-
BSt4 = MMod:move_to_native_register(BSt3, SizeValReg, SizeReg),
1427-
MMod:free_native_registers(BSt4, [SizeValReg])
1423+
{BSt1, SizeValReg} = term_to_int(SizeValReg, Fail, MMod, BSt0),
1424+
BSt2 = cond_jump_to_label({SizeValReg, '<', 0}, Fail, MMod, BSt1),
1425+
BSt3 = MMod:sub(BSt2, SizeReg, SizeValReg),
1426+
BSt4 = cond_jump_to_label({SizeReg, '<', BSOffsetReg1}, Fail, MMod, BSt3),
1427+
BSt5 = MMod:move_to_native_register(BSt4, SizeValReg, SizeReg),
1428+
MMod:free_native_registers(BSt5, [SizeValReg])
14281429
end
14291430
),
14301431
{MSt12, SizeReg}
@@ -1472,7 +1473,7 @@ first_pass(<<?OP_BS_SKIP_BITS2, Rest0/binary>>, MMod, MSt0, State0) ->
14721473
is_integer(SizeReg) ->
14731474
{MSt4, SizeReg * Unit};
14741475
true ->
1475-
MSt5 = MMod:mul(MSt4, SizeReg, Unit),
1476+
MSt5 = scale_size_by_unit(SizeReg, Unit, Fail, MMod, MSt4),
14761477
{MSt5, SizeReg}
14771478
end,
14781479
{MSt7, BSBinaryReg} = MMod:get_array_element(MSt6, MatchStateRegPtr, 1),
@@ -4496,15 +4497,36 @@ cond_raise_badarg_or_jump_to_fail_label(Cond, 0, MMod, MSt0) ->
44964497
cond_raise_badarg_or_jump_to_fail_label(Cond, FailLabel, MMod, MSt0) when FailLabel > 0 ->
44974498
cond_jump_to_label(Cond, FailLabel, MMod, MSt0).
44984499

4500+
scale_size_by_unit(SizeReg, Unit, Fail, MMod, MSt0) ->
4501+
MSt1 = cond_jump_to_label({SizeReg, '<', 0}, Fail, MMod, MSt0),
4502+
{_MinSmall, MaxSmall} = small_integer_bounds(MMod),
4503+
% leave two bits of headroom so that adding the match offset cannot overflow
4504+
MaxSize = (1 bsl (MMod:word_size() * 8 - 2)) div max(Unit, 1),
4505+
MSt2 =
4506+
if
4507+
MaxSize >= MaxSmall ->
4508+
% the size is a small integer, so it can never exceed MaxSize
4509+
MSt1;
4510+
true ->
4511+
{MSt1a, MaxReg} = MMod:move_to_native_register(MSt1, MaxSize),
4512+
cond_jump_to_label({{free, MaxReg}, '<', SizeReg}, Fail, MMod, MSt1a)
4513+
end,
4514+
MMod:mul(MSt2, SizeReg, Unit).
4515+
44994516
term_to_int(Term, _FailLabel, _MMod, MSt0) when is_integer(Term) ->
45004517
{MSt0, Term bsr 4};
45014518
term_to_int({literal, Val}, _FailLabel, _MMod, MSt0) when is_integer(Val) ->
45024519
{MSt0, Val};
4503-
% Optimized case: when we have type information showing this is an integer, skip the type check
4504-
term_to_int({typed, Term, {t_integer, _Range}}, _FailLabel, MMod, MSt0) ->
4505-
{MSt1, Reg} = MMod:move_to_native_register(MSt0, Term),
4506-
{MSt2, IntReg} = MMod:shift_right(MSt1, {free, Reg}, 4),
4507-
{MSt2, IntReg};
4520+
% Optimized case: when we have type information showing this is an integer small enough, skip the type check
4521+
term_to_int({typed, Term, {t_integer, Range}}, FailLabel, MMod, MSt0) ->
4522+
case is_small_integer_range(Range, Range, MMod) of
4523+
true ->
4524+
{MSt1, Reg} = MMod:move_to_native_register(MSt0, Term),
4525+
{MSt2, IntReg} = MMod:shift_right_arith(MSt1, {free, Reg}, 4),
4526+
{MSt2, IntReg};
4527+
false ->
4528+
term_to_int(Term, FailLabel, MMod, MSt0)
4529+
end;
45084530
term_to_int({typed, Term, _NonIntegerType}, FailLabel, MMod, MSt0) ->
45094531
% Type information shows it's not an integer, fall back to generic path
45104532
term_to_int(Term, FailLabel, MMod, MSt0);
@@ -4513,7 +4535,7 @@ term_to_int(Term, FailLabel, MMod, MSt0) ->
45134535
MSt2 = cond_raise_badarg_or_jump_to_fail_label(
45144536
{Reg, '&', ?TERM_IMMED_TAG_MASK, '!=', ?TERM_INTEGER_TAG}, FailLabel, MMod, MSt1
45154537
),
4516-
{MSt3, IntReg} = MMod:shift_right(MSt2, {free, Reg}, 4),
4538+
{MSt3, IntReg} = MMod:shift_right_arith(MSt2, {free, Reg}, 4),
45174539
{MSt3, IntReg}.
45184540

45194541
first_pass_float3(Primitive, Rest0, MMod, MSt0, State0) ->

src/libAtomVM/jit.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1479,10 +1479,10 @@ static term extract_bigint(Context *ctx, JITState *jit_state, const uint8_t *byt
14791479
}
14801480

14811481
static term jit_bitstring_extract_integer(
1482-
Context *ctx, JITState *jit_state, term *bin_ptr, size_t offset, int n, int bs_flags)
1482+
Context *ctx, JITState *jit_state, term *bin_ptr, size_t offset, size_t n, int bs_flags)
14831483
{
14841484
TRACE("jit_bitstring_extract_integer: bin_ptr=%p offset=%d n=%d bs_flags=%d\n",
1485-
(void *) bin_ptr, (int) offset, n, bs_flags);
1485+
(void *) bin_ptr, (int) offset, (int) n, bs_flags);
14861486
if (n <= 64) {
14871487
union maybe_unsigned_int64 value;
14881488
bool status = bitstring_extract_integer(
@@ -1522,9 +1522,9 @@ static term jit_bitstring_extract_integer(
15221522
}
15231523
}
15241524

1525-
static term jit_bitstring_extract_float(Context *ctx, term *bin_ptr, size_t offset, int n, int bs_flags)
1525+
static term jit_bitstring_extract_float(Context *ctx, term *bin_ptr, size_t offset, size_t n, int bs_flags)
15261526
{
1527-
TRACE("jit_bitstring_extract_float: bin_ptr=%p offset=%d n=%d bs_flags=%d\n", (void *) bin_ptr, (int) offset, n, bs_flags);
1527+
TRACE("jit_bitstring_extract_float: bin_ptr=%p offset=%d n=%d bs_flags=%d\n", (void *) bin_ptr, (int) offset, (int) n, bs_flags);
15281528
avm_float_t value;
15291529
bool status;
15301530
switch (n) {

src/libAtomVM/jit.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ struct ModuleNativeInterface
216216
bool (*catch_end)(Context *ctx, JITState *jit_state);
217217
bool (*memory_ensure_free_with_roots)(Context *ctx, JITState *jit_state, int sz, int live, int flags);
218218
term (*term_alloc_bin_match_state)(Context *ctx, term src, int slots);
219-
term (*bitstring_extract_integer)(Context *ctx, JITState *jit_state, term *bin_ptr, size_t offset, int n, int bs_flags);
219+
term (*bitstring_extract_integer)(Context *ctx, JITState *jit_state, term *bin_ptr, size_t offset, size_t n, int bs_flags);
220220
size_t (*term_sub_binary_heap_size)(term *bin_ptr, size_t size);
221221
term (*term_maybe_create_sub_binary)(Context *ctx, term bin, size_t offset, size_t len);
222222
int (*term_find_map_pos)(Context *ctx, term map, term key);
@@ -234,7 +234,7 @@ struct ModuleNativeInterface
234234
void *(*malloc)(Context *ctx, JITState *jit_state, size_t sz);
235235
void (*free)(void *ptr);
236236
term (*put_map_assoc)(Context *ctx, JITState *jit_state, term src, size_t new_entries, size_t num_elements, term *kv);
237-
term (*bitstring_extract_float)(Context *ctx, term *bin_ptr, size_t offset, int n, int bs_flags);
237+
term (*bitstring_extract_float)(Context *ctx, term *bin_ptr, size_t offset, size_t n, int bs_flags);
238238
int (*module_get_fun_arity)(Module *fun_module, uint32_t fun_index);
239239
bool (*bitstring_match_module_str)(Context *ctx, JITState *jit_state, term bin, size_t offset, int str_id, size_t len);
240240
term (*bitstring_get_utf8)(term src);

src/libAtomVM/opcodesswitch.h

Lines changed: 80 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1240,6 +1240,40 @@ static bool sort_kv_pairs(struct kv_pair *kv, int size, GlobalContext *global)
12401240
}
12411241
#endif
12421242

1243+
/**
1244+
* @brief Scale a dynamic segment size by its unit.
1245+
*
1246+
* @details Matching opcodes take a segment size from a register and scale it by
1247+
* the segment unit. The size can be negative or large enough for the product to
1248+
* overflow, so it cannot be multiplied blindly: the scaled size is compared
1249+
* against the remaining capacity and added to the match offset, and scaling
1250+
* first lets a negative size wrap to a small one that passes the capacity check,
1251+
* or move the match offset before the start of the binary.
1252+
*
1253+
* @param size the segment size, as a term (must be any integer)
1254+
* @param unit the segment unit
1255+
* @param max_value the largest acceptable scaled size
1256+
* @param scaled_size on success, the scaled size
1257+
* @returns \c true if the scaled size is representable and at most
1258+
* \c max_value, \c false if the match should fail
1259+
*/
1260+
static inline bool bs_scaled_size(term size, uint32_t unit, size_t max_value, size_t *scaled_size)
1261+
{
1262+
if (!term_is_integer(size)) {
1263+
// a size that doesn't fit in a small integer cannot fit in max_value
1264+
return false;
1265+
}
1266+
avm_int_t size_val = term_to_int(size);
1267+
if (size_val < 0) {
1268+
return false;
1269+
}
1270+
if (unit != 0 && (size_t) size_val > max_value / unit) {
1271+
return false;
1272+
}
1273+
*scaled_size = (size_t) size_val * unit;
1274+
return true;
1275+
}
1276+
12431277
static term maybe_alloc_boxed_integer_fragment(Context *ctx, avm_int64_t value)
12441278
{
12451279
#if BOXED_TERMS_REQUIRED_FOR_INT64 > 1
@@ -4254,17 +4288,17 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
42544288
DECODE_LITERAL(flags_value, pc)
42554289

42564290
VERIFY_IS_MATCH_STATE(src, "bs_skip_bits2", 0);
4257-
VERIFY_IS_INTEGER(size, "bs_skip_bits2", 0);
4291+
VERIFY_IS_ANY_INTEGER(size, "bs_skip_bits2", 0);
42584292
// Ignore flags value as skipping bits is the same whatever the endianness
4259-
avm_int_t size_val = term_to_int(size);
4260-
4261-
TRACE("bs_skip_bits2/5, fail=%u src=%p size=0x%lx unit=%u flags=%x\n", (unsigned) fail, (void *) src, (unsigned long) size_val, (unsigned) unit, (int) flags_value);
4293+
TRACE("bs_skip_bits2/5, fail=%u src=%p unit=%u flags=%x\n", (unsigned) fail, (void *) src, (unsigned) unit, (int) flags_value);
42624294

4263-
size_t increment = size_val * unit;
42644295
avm_int_t bs_offset = term_get_match_state_offset(src);
42654296
term bs_bin = term_get_match_state_binary(src);
4266-
if ((bs_offset + increment) > term_binary_size(bs_bin) * 8) {
4267-
TRACE("bs_skip_bits2: Insufficient capacity to skip bits: %lu, inc: %zu\n", (unsigned long) bs_offset, increment);
4297+
size_t bs_capacity = term_binary_size(bs_bin) * 8;
4298+
size_t increment;
4299+
if ((size_t) bs_offset > bs_capacity
4300+
|| !bs_scaled_size(size, unit, bs_capacity - bs_offset, &increment)) {
4301+
TRACE("bs_skip_bits2: Insufficient capacity to skip bits: %lu\n", (unsigned long) bs_offset);
42684302
JUMP_TO_ADDRESS(mod->labels[fail]);
42694303
} else {
42704304
term_set_match_state_offset(src, bs_offset + increment);
@@ -4336,16 +4370,22 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
43364370
DECODE_LITERAL(flags_value, pc)
43374371

43384372
VERIFY_IS_MATCH_STATE(src, "bs_get_integer", 0);
4339-
VERIFY_IS_INTEGER(size, "bs_get_integer", 0);
4340-
4341-
avm_int_t size_val = term_to_int(size);
4373+
VERIFY_IS_ANY_INTEGER(size, "bs_get_integer", 0);
43424374

4343-
TRACE("bs_get_integer2/7, fail=%u src=%p live=%u size=%u unit=%u flags=%x\n", (unsigned) fail, (void *) src, (unsigned) size_val, (unsigned) live, (unsigned) unit, (int) flags_value);
4375+
TRACE("bs_get_integer2/7, fail=%u src=%p live=%u unit=%u flags=%x\n", (unsigned) fail, (void *) src, (unsigned) live, (unsigned) unit, (int) flags_value);
43444376

4345-
avm_int_t increment = size_val * unit;
43464377
union maybe_unsigned_int64 value;
43474378
term bs_bin = term_get_match_state_binary(src);
43484379
avm_int_t bs_offset = term_get_match_state_offset(src);
4380+
size_t bs_capacity = term_binary_size(bs_bin) * 8;
4381+
size_t increment_bits;
4382+
if ((size_t) bs_offset > bs_capacity
4383+
|| !bs_scaled_size(size, unit, bs_capacity - bs_offset, &increment_bits)) {
4384+
TRACE("bs_get_integer2: size is negative or exceeds the remaining capacity\n");
4385+
JUMP_TO_ADDRESS(mod->labels[fail]);
4386+
}
4387+
// bounded by the remaining capacity, so it fits in an avm_int_t
4388+
avm_int_t increment = (avm_int_t) increment_bits;
43494389
term t;
43504390
if (increment <= 64) {
43514391
bool status = bitstring_extract_integer(bs_bin, bs_offset, increment, flags_value, &value);
@@ -4409,16 +4449,23 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
44094449
DECODE_LITERAL(flags_value, pc);
44104450

44114451
VERIFY_IS_MATCH_STATE(src, "bs_get_float", 0);
4412-
VERIFY_IS_INTEGER(size, "bs_get_float", 0);
4452+
VERIFY_IS_ANY_INTEGER(size, "bs_get_float", 0);
44134453

4414-
avm_int_t size_val = term_to_int(size);
4454+
TRACE("bs_get_float2/7, fail=%u src=%p unit=%u flags=%x\n", (unsigned) fail, (void *) src, (unsigned) unit, (int) flags_value);
44154455

4416-
TRACE("bs_get_float2/7, fail=%u src=%p size=%u unit=%u flags=%x\n", (unsigned) fail, (void *) src, (unsigned) size_val, (unsigned) unit, (int) flags_value);
4417-
4418-
avm_int_t increment = size_val * unit;
44194456
avm_float_t value;
44204457
term bs_bin = term_get_match_state_binary(src);
44214458
avm_int_t bs_offset = term_get_match_state_offset(src);
4459+
size_t bs_capacity = term_binary_size(bs_bin) * 8;
4460+
size_t increment_bits;
4461+
if ((size_t) bs_offset > bs_capacity
4462+
|| !bs_scaled_size(size, unit, bs_capacity - bs_offset, &increment_bits)) {
4463+
TRACE("bs_get_float2: size is negative or exceeds the remaining capacity\n");
4464+
JUMP_TO_ADDRESS(mod->labels[fail]);
4465+
}
4466+
// both bounded by the remaining capacity, so they fit in an avm_int_t
4467+
avm_int_t size_val = term_to_int(size);
4468+
avm_int_t increment = (avm_int_t) increment_bits;
44224469
bool status;
44234470
switch (size_val) {
44244471
case 16:
@@ -4478,11 +4525,21 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
44784525
TRACE("bs_get_binary2: Unsupported: unit must be 8.\n");
44794526
RAISE_ERROR(UNSUPPORTED_ATOM);
44804527
}
4481-
avm_int_t size_val = 0;
4482-
if (term_is_integer(size)) {
4483-
size_val = term_to_int(size);
4528+
size_t bs_capacity = term_binary_size(bs_bin);
4529+
if ((size_t) bs_offset / 8 > bs_capacity) {
4530+
TRACE("bs_get_binary2: match state offset is past the end of the binary\n");
4531+
JUMP_TO_ADDRESS(mod->labels[fail]);
4532+
}
4533+
size_t remaining_bytes = bs_capacity - bs_offset / 8;
4534+
size_t size_val = 0;
4535+
if (term_is_any_integer(size)) {
4536+
// A negative or oversized size fails the match, as on BEAM
4537+
if (!bs_scaled_size(size, 1, remaining_bytes, &size_val)) {
4538+
TRACE("bs_get_binary2: size is negative or exceeds the remaining capacity\n");
4539+
JUMP_TO_ADDRESS(mod->labels[fail]);
4540+
}
44844541
} else if (size == ALL_ATOM) {
4485-
size_val = term_binary_size(bs_bin) - bs_offset / 8;
4542+
size_val = remaining_bytes;
44864543
} else {
44874544
TRACE("bs_get_binary2: size is neither an integer nor the atom `all`\n");
44884545
RAISE_ERROR(BADARG_ATOM);
@@ -4498,8 +4555,8 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
44984555

44994556
TRACE("bs_get_binary2/7, fail=%u src=%p live=%u unit=%u\n", (unsigned) fail, (void *) bs_bin, (unsigned) live, (unsigned) unit);
45004557

4501-
if ((unsigned int) (bs_offset / unit + size_val) > term_binary_size(bs_bin)) {
4502-
TRACE("bs_get_binary2: insufficient capacity -- bs_offset = %d, size_val = %d\n", (int) bs_offset, (int) size_val);
4558+
if (size_val > remaining_bytes) {
4559+
TRACE("bs_get_binary2: insufficient capacity -- bs_offset = %d, size_val = %zu\n", (int) bs_offset, size_val);
45034560
JUMP_TO_ADDRESS(mod->labels[fail]);
45044561
} else {
45054562
term_set_match_state_offset(src, bs_offset + size_val * unit);

tests/erlang_tests/test_bs.erl

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ start() ->
104104
ok = test_bs_skip_bits2_little(),
105105

106106
ok = test_bs_variable_size_bitstring(),
107+
ok = test_negative_dynamic_size(),
108+
ok = test_oversized_dynamic_size(),
107109
ok = test_float(),
108110

109111
0.
@@ -689,6 +691,67 @@ check_x86_64_jt(<<>>) -> ok;
689691
check_x86_64_jt(<<16#e9, _Offset:32/little, Tail/binary>>) -> check_x86_64_jt(Tail);
690692
check_x86_64_jt(Bin) -> {unexpected, Bin}.
691693

694+
test_negative_dynamic_size() ->
695+
B = id(<<1>>),
696+
nope = skip_unit64(id(-1), B),
697+
nope = skip_unit64(id(-(1 bsl 58)), B),
698+
nope = skip_unit64(id(-(1 bsl 58) - 1), B),
699+
nope = skip_unit8(id(-1), B),
700+
nope = skip_unit8(id(-(1 bsl 61)), B),
701+
nope = int_unit64(id(-1), B),
702+
nope = int_unit64(id(-(1 bsl 58)), B),
703+
nope = float_unit64(id(-1), id(<<1, 2, 3, 4, 5, 6, 7, 8>>)),
704+
nope = bin_unit8(id(-1), B),
705+
nope = bin_unit8(id(-(1 bsl 61)), B),
706+
% the same segments still match when the size is valid
707+
<<>> = skip_unit8(id(1), B),
708+
{<<1>>, <<>>} = bin_unit8(id(1), B),
709+
ok.
710+
711+
test_oversized_dynamic_size() ->
712+
B = id(<<1>>),
713+
nope = skip_unit64(id(1 bsl 26), B),
714+
nope = skip_unit64(id(1 bsl 58), B),
715+
nope = skip_unit8(id(1 bsl 26), B),
716+
nope = int_unit64(id(1 bsl 26), B),
717+
nope = int_unit64(id(1 bsl 58), B),
718+
nope = float_unit64(id(1 bsl 26), id(<<1, 2, 3, 4, 5, 6, 7, 8>>)),
719+
nope = bin_unit8(id(1 bsl 26), B),
720+
nope = skip_unit64(id(1 bsl 61), B),
721+
nope = int_unit64(id(1 bsl 61), B),
722+
nope = bin_unit8(id(1 bsl 61), B),
723+
ok.
724+
725+
skip_unit64(N, B) ->
726+
case B of
727+
<<_:N/binary-unit:64, R/binary>> -> R;
728+
_ -> nope
729+
end.
730+
731+
skip_unit8(N, B) ->
732+
case B of
733+
<<_:N/binary-unit:8, R/binary>> -> R;
734+
_ -> nope
735+
end.
736+
737+
bin_unit8(N, B) ->
738+
case B of
739+
<<X:N/binary-unit:8, R/binary>> -> {X, R};
740+
_ -> nope
741+
end.
742+
743+
int_unit64(N, B) ->
744+
case B of
745+
<<X:N/integer-unit:64, _/binary>> -> X;
746+
_ -> nope
747+
end.
748+
749+
float_unit64(N, B) ->
750+
case B of
751+
<<X:N/float-unit:64, _/binary>> -> X;
752+
_ -> nope
753+
end.
754+
692755
id(X) -> ?MODULE:ext_id(X).
693756

694757
ext_id(X) -> X.

0 commit comments

Comments
 (0)