Berry bytes methods setbits/getbits transposed to native and support for big endian (#24857)

This commit is contained in:
s-hadinger
2026-06-26 09:23:33 +02:00
committed by GitHub
parent 8b4828807b
commit 39ad0f57a4
4 changed files with 633 additions and 301 deletions
+1
View File
@@ -5,6 +5,7 @@ All notable changes to this project will be documented in this file.
## [15.5.0.1]
### Added
- Berry `bytes` methods `setbits`/`getbits` transposed to native and support for big endian
### Breaking Changed
@@ -1098,6 +1098,171 @@ def m_addfloat(vm):
be_api.be_raise(vm, "type_error", "operands must be int or float")
return be_api.be_returnnilvalue(vm)
# static int m_getbits(bvm *vm)
# {
# int argc = be_top(vm);
# buf_impl attr = bytes_check_data(vm, 0);
# check_ptr(vm, &attr);
# if (argc >= 3 && be_isint(vm, 2) && be_isint(vm, 3)) {
# int32_t offset_bits = be_toint(vm, 2);
# int32_t len_bits = be_toint(vm, 3);
# if (len_bits < -32 || len_bits > 32) {
# be_raise(vm, "value_error", "length in bits must be between 0 and 32");
# }
# if (len_bits == 0) { be_return_nil(vm); }
# bbool big_endian = bfalse;
# if (len_bits < 0) { big_endian = btrue; len_bits = -len_bits; }
# uint32_t ret = 0;
# int32_t offset_bytes = offset_bits >> 3;
# offset_bits = offset_bits & 7;
# int bit_shift = 0;
# while (len_bits > 0) {
# int block_bits = 8 - offset_bits;
# if (block_bits > len_bits) { block_bits = len_bits; }
# uint8_t byte_val = buf_get1(&attr, offset_bytes);
# uint32_t mask_val = (1u << block_bits) - 1;
# if (big_endian) {
# bit_shift = 8 - offset_bits - block_bits;
# ret = (ret << block_bits) | ((byte_val >> bit_shift) & mask_val);
# } else {
# ret |= (uint32_t)(((byte_val >> offset_bits) & mask_val) << bit_shift);
# bit_shift += block_bits;
# }
# len_bits -= block_bits;
# offset_bits = 0;
# offset_bytes += 1;
# }
# be_pop(vm, argc - 1);
# be_pushint(vm, (bint) ret);
# be_return(vm);
# }
# be_raise(vm, "type_error", "operands must be int");
# be_return_nil(vm);
# }
def m_getbits(vm):
be_api = _lazy_be_api()
argc = be_api.be_top(vm)
attr = bytes_check_data(vm, 0)
check_ptr(vm, attr)
if argc >= 3 and be_api.be_isint(vm, 2) and be_api.be_isint(vm, 3):
offset_bits = be_api.be_toint(vm, 2)
len_bits = be_api.be_toint(vm, 3)
if len_bits < -32 or len_bits > 32:
be_api.be_raise(vm, "value_error", "length in bits must be between 0 and 32")
if len_bits == 0:
return be_api.be_returnnilvalue(vm)
big_endian = len_bits < 0
if big_endian:
len_bits = -len_bits
ret = 0
offset_bytes = offset_bits >> 3
offset_bits = offset_bits & 7
bit_shift = 0 # bit number to write to
while len_bits > 0:
block_bits = 8 - offset_bits # how many bits to read in the current block (block = byte)
if block_bits > len_bits:
block_bits = len_bits
byte_val = buf_get1(attr, offset_bytes)
mask_val = (1 << block_bits) - 1
if big_endian:
bit_shift = 8 - offset_bits - block_bits # non-zero only on the last partial byte
ret = ((ret << block_bits) | ((byte_val >> bit_shift) & mask_val)) & 0xFFFFFFFF # shift ret left to make space
else:
ret |= ((byte_val >> offset_bits) & mask_val) << bit_shift # append to the left of ret
bit_shift += block_bits
# move the input window
len_bits -= block_bits
offset_bits = 0 # start at full next byte
offset_bytes += 1
be_api.be_pop(vm, argc - 1)
be_api.be_pushint(vm, ret & 0xFFFFFFFF)
return be_api.be_returnvalue(vm)
be_api.be_raise(vm, "type_error", "operands must be int")
return be_api.be_returnnilvalue(vm)
# static int m_setbits(bvm *vm)
# {
# int argc = be_top(vm);
# buf_impl attr = bytes_check_data(vm, 0);
# check_ptr_modifiable(vm, &attr);
# if (argc >= 4 && be_isint(vm, 2) && be_isint(vm, 3) && (be_isint(vm, 4) || be_isbool(vm, 4))) {
# int32_t offset_bits = be_toint(vm, 2);
# int32_t len_bits = be_toint(vm, 3);
# uint32_t val = be_isint(vm, 4) ? (uint32_t)be_toint(vm, 4) : (be_tobool(vm, 4) ? 1u : 0u);
# if (len_bits < -32 || len_bits > 32) {
# be_raise(vm, "value_error", "length in bits must be between -32 and 32");
# }
# bbool big_endian = bfalse;
# if (len_bits < 0) { big_endian = btrue; len_bits = -len_bits; }
# int32_t offset_bytes = offset_bits >> 3;
# offset_bits = offset_bits & 7;
# while (len_bits > 0) {
# int block_bits = 8 - offset_bits;
# if (block_bits > len_bits) { block_bits = len_bits; }
# uint32_t mask_val = (1u << block_bits) - 1;
# len_bits -= block_bits;
# uint32_t extracted;
# if (big_endian) {
# extracted = (val >> len_bits) & mask_val;
# offset_bits = 8 - offset_bits - block_bits;
# } else {
# extracted = val & mask_val;
# val >>= block_bits;
# }
# uint8_t cur = buf_get1(&attr, offset_bytes);
# buf_set1(&attr, offset_bytes,
# (uint8_t)((cur & ((mask_val << offset_bits) ^ 0xFF)) | (extracted << offset_bits)));
# offset_bits = 0;
# offset_bytes += 1;
# }
# be_pop(vm, argc - 1);
# be_return(vm);
# }
# be_raise(vm, "type_error", "operands must be int");
# be_return_nil(vm);
# }
def m_setbits(vm):
be_api = _lazy_be_api()
argc = be_api.be_top(vm)
attr = bytes_check_data(vm, 0)
check_ptr_modifiable(vm, attr)
if (argc >= 4 and be_api.be_isint(vm, 2) and be_api.be_isint(vm, 3)
and (be_api.be_isint(vm, 4) or be_api.be_isbool(vm, 4))):
offset_bits = be_api.be_toint(vm, 2)
len_bits = be_api.be_toint(vm, 3)
if be_api.be_isint(vm, 4):
val = be_api.be_toint(vm, 4) & 0xFFFFFFFF
else:
val = 1 if be_api.be_tobool(vm, 4) else 0
if len_bits < -32 or len_bits > 32:
be_api.be_raise(vm, "value_error", "length in bits must be between -32 and 32")
big_endian = len_bits < 0
if big_endian:
len_bits = -len_bits
offset_bytes = offset_bits >> 3
offset_bits = offset_bits & 7
while len_bits > 0:
block_bits = 8 - offset_bits # how many bits to write in the current block (block = byte)
if block_bits > len_bits:
block_bits = len_bits
mask_val = (1 << block_bits) - 1
len_bits -= block_bits
if big_endian:
extracted = (val >> len_bits) & mask_val
offset_bits = 8 - offset_bits - block_bits # non-zero only on the last partial byte
else:
extracted = val & mask_val
val >>= block_bits
cur = buf_get1(attr, offset_bytes)
buf_set1(attr, offset_bytes,
(cur & (((mask_val << offset_bits) ^ 0xFF) & 0xFF)) | (extracted << offset_bits))
offset_bits = 0 # start at full next byte
offset_bytes += 1
be_api.be_pop(vm, argc - 1) # leave self on top of stack
return be_api.be_returnvalue(vm) # return self
be_api.be_raise(vm, "type_error", "operands must be int")
return be_api.be_returnnilvalue(vm)
# static int m_setbytes(bvm *vm) { ... }
def m_setbytes(vm):
be_api = _lazy_be_api()
@@ -1639,5 +1804,7 @@ def be_load_byteslib(vm):
("..", m_connect),
("==", m_equal),
("!=", m_nequal),
("getbits", m_getbits),
("setbits", m_setbits),
]
be_api.be_regclass(vm, "bytes", members)
+161 -301
View File
@@ -1725,7 +1725,113 @@ BERRY_API bbool be_isbytes(bvm *vm, int rel_index)
return ret;
}
/* Helper code to compile bytecode
/*
* Read a bit-field in a `bytes()` object
* `getbits(offset_bits:int, len_bits:int) -> int`
*
* offset_bits: bit number to start from (0 = LSB of byte 0).
* Big-endian reverses per-byte bit order (0 = MSB for each byte).
* len_bits : how many bits to read. Positive assumes Little-Endian, negative Big-Endian.
* Range is -32..32; a length of 0 returns nil.
*/
static int m_getbits(bvm *vm)
{
int argc = be_top(vm);
buf_impl attr = bytes_check_data(vm, 0);
check_ptr(vm, &attr);
if (argc >= 3 && be_isint(vm, 2) && be_isint(vm, 3)) {
int32_t offset_bits = be_toint(vm, 2);
int32_t len_bits = be_toint(vm, 3);
if (len_bits < -32 || len_bits > 32) {
be_raise(vm, "value_error", "length in bits must be between 0 and 32");
}
if (len_bits == 0) {
be_return_nil(vm);
}
bbool big_endian = bfalse;
if (len_bits < 0) { big_endian = btrue; len_bits = -len_bits; }
uint32_t ret = 0;
int32_t offset_bytes = offset_bits >> 3;
offset_bits = offset_bits & 7;
int bit_shift = 0; /* bit number to write to */
while (len_bits > 0) {
int block_bits = 8 - offset_bits; /* how many bits to read in the current block (block = byte) */
if (block_bits > len_bits) { block_bits = len_bits; }
uint8_t byte_val = buf_get1(&attr, offset_bytes);
uint32_t mask_val = (1u << block_bits) - 1;
if (big_endian) {
bit_shift = 8 - offset_bits - block_bits; /* non-zero only on the last partial byte */
ret = (ret << block_bits) | ((byte_val >> bit_shift) & mask_val); /* shift ret left to make space for new bits */
} else {
ret |= (uint32_t)(((byte_val >> offset_bits) & mask_val) << bit_shift); /* append to the left of ret */
bit_shift += block_bits;
}
/* move the input window */
len_bits -= block_bits;
offset_bits = 0; /* start at full next byte */
offset_bytes += 1;
}
be_pop(vm, argc - 1);
be_pushint(vm, (bint) ret);
be_return(vm);
}
be_raise(vm, "type_error", "operands must be int");
be_return_nil(vm);
}
/*
* Write a bit-field in a `bytes()` object
* `setbits(offset_bits:int, len_bits:int, val:int) -> instance`
*
* offset_bits: bit number to start from (0 = LSB of byte 0).
* Big-endian reverses per-byte bit order (0 = MSB for each byte).
* len_bits : how many bits to write. Positive assumes Little-Endian, negative Big-Endian.
* Range is -32..32.
* val : value to set
*/
static int m_setbits(bvm *vm)
{
int argc = be_top(vm);
buf_impl attr = bytes_check_data(vm, 0);
check_ptr_modifiable(vm, &attr);
if (argc >= 4 && be_isint(vm, 2) && be_isint(vm, 3) && (be_isint(vm, 4) || be_isbool(vm, 4))) {
int32_t offset_bits = be_toint(vm, 2);
int32_t len_bits = be_toint(vm, 3);
uint32_t val = be_isint(vm, 4) ? (uint32_t)be_toint(vm, 4) : (be_tobool(vm, 4) ? 1u : 0u);
if (len_bits < -32 || len_bits > 32) {
be_raise(vm, "value_error", "length in bits must be between -32 and 32");
}
bbool big_endian = bfalse;
if (len_bits < 0) { big_endian = btrue; len_bits = -len_bits; }
int32_t offset_bytes = offset_bits >> 3;
offset_bits = offset_bits & 7;
while (len_bits > 0) {
int block_bits = 8 - offset_bits; /* how many bits to write in the current block (block = byte) */
if (block_bits > len_bits) { block_bits = len_bits; }
uint32_t mask_val = (1u << block_bits) - 1;
len_bits -= block_bits;
uint32_t extracted;
if (big_endian) {
extracted = (val >> len_bits) & mask_val;
offset_bits = 8 - offset_bits - block_bits; /* non-zero only on the last partial byte */
} else {
extracted = val & mask_val;
val >>= block_bits;
}
uint8_t cur = buf_get1(&attr, offset_bytes);
buf_set1(&attr, offset_bytes,
(uint8_t)((cur & ((mask_val << offset_bits) ^ 0xFF)) | (extracted << offset_bits)));
offset_bits = 0; /* start at full next byte */
offset_bytes += 1;
}
be_pop(vm, argc - 1); /* leave self on top of stack */
be_return(vm); /* return self */
}
be_raise(vm, "type_error", "operands must be int");
be_return_nil(vm);
}
/* Reference Berry implementation (kept for documentation)
class Bytes : bytes
@@ -1735,29 +1841,37 @@ class Bytes : bytes
#- Reads a bit-field in a `bytes()` object
#-
#- Input:
#- offset_bits (int): bit number to start reading from (0 = LSB)
#- len_bits (int): how many bits to read
#- offset_bits (int): bit number to start from (0 = LSB of byte 0).
#- Big-endian reverses per-byte bit order (0 = MSB for each byte).
#- len_bits (int): how many bits to read. Positive assumes Little-Endian, negative Big-Endian
#- Output:
#- valuer (int)
#-------------------------------------------------------------#
def getbits(offset_bits, len_bits)
if len_bits <= 0 || len_bits > 32 raise "value_error", "length in bits must be between 0 and 32" end
if len_bits < -32 || len_bits > 32 raise "value_error", "length in bits must be between 0 and 32" end
if len_bits == 0 return nil end
var big_endian = len_bits < 0
if big_endian len_bits = -len_bits end
var ret = 0
var offset_bytes = offset_bits >> 3
offset_bits = offset_bits % 8
offset_bits = offset_bits & 7
var bit_shift = 0 #- bit number to write to -#
while (len_bits > 0)
var block_bits = 8 - offset_bits # how many bits to read in the current block (block = byte) -#
if block_bits > len_bits block_bits = len_bits end
var mask = ( (1<<block_bits) - 1) << offset_bits
ret = ret | ( ((self[offset_bytes] & mask) >> offset_bits) << bit_shift)
#- move the input window -#
bit_shift += block_bits
var byte_val = self[offset_bytes]
var mask_val = (1 << block_bits) - 1
if big_endian
bit_shift = 8 - offset_bits - block_bits # non-zero only on the last partial byte
ret = (ret << block_bits) | ((byte_val >> bit_shift) & mask_val) # shift ret left to make space for new bits
else
ret = ret | (((byte_val >> offset_bits) & mask_val) << bit_shift) # append to the left of ret
bit_shift += block_bits
end
len_bits -= block_bits
offset_bits = 0 #- start at full next byte -#
offset_bytes += 1
@@ -1765,35 +1879,43 @@ class Bytes : bytes
return ret
end
#-------------------------------------------------------------
#- 'setbits' function
#-
#- Writes a bit-field in a `bytes()` object
#-
#- Input:
#- offset_bits (int): bit number to start writing to (0 = LSB)
#- len_bits (int): how many bits to write
#- val (int): value to set
#-------------------------------------------------------------#
#-------------------------------------------------------------
#- 'setbits' function
#-
#- Writes a bit-field in a `bytes()` object
#-
#- Input:
#- offset_bits (int): bit number to start from (0 = LSB of byte 0).
#- Big-endian reverses per-byte bit order (0 = MSB for each byte).
#- len_bits (int): how many bits to write. Positive assumes Little-Endian, negative Big-Endian
#- val (int): value to set
#-------------------------------------------------------------#
def setbits(offset_bits, len_bits, val)
if len_bits < 0 || len_bits > 32 raise "value_error", "length in bits must be between 0 and 32" end
if len_bits < -32 || len_bits > 32 raise "value_error", "length in bits must be between -32 and 32" end
val = int(val) #- convert bool or others to int -#
var big_endian = len_bits < 0
if big_endian len_bits = -len_bits end
var offset_bytes = offset_bits >> 3
offset_bits = offset_bits % 8
offset_bits = offset_bits & 7
while (len_bits > 0)
var block_bits = 8 - offset_bits #- how many bits to write in the current block (block = byte) -#
if block_bits > len_bits block_bits = len_bits end
var mask_val = (1<<block_bits) - 1 #- mask to the n bits to get for this block -#
var mask_b_inv = 0xFF - (mask_val << offset_bits)
self[offset_bytes] = (self[offset_bytes] & mask_b_inv) | ((val & mask_val) << offset_bits)
#- move the input window -#
val >>= block_bits
var mask_val = (1 << block_bits) - 1
len_bits -= block_bits
var extracted
if big_endian
extracted = (val >> len_bits) & mask_val
offset_bits = 8 - offset_bits - block_bits # non-zero only on the last partial byte
else
extracted = val & mask_val
val >>= block_bits
end
self[offset_bytes] = (self[offset_bytes] & ((mask_val << offset_bits) ^ 0xFF)) | (extracted << offset_bits)
offset_bits = 0 #- start at full next byte -#
offset_bytes += 1
end
@@ -1803,266 +1925,6 @@ end
*/
/********************************************************************
** Solidified function: getbits
********************************************************************/
#if BE_USE_COMPACT_KTAB
be_local_closure(getbits, /* name */
be_nested_proto(
9, /* nstack */
3, /* argc */
0, /* varg */
0, /* has upvals */
NULL, /* no upvals */
0, /* has sup protos */
NULL, /* no sub protos */
1, /* has constants */
( &(const union bvaldata[ 5]) { /* constants */
/* K0 */ be_kv_int(0),
/* K1 */ be_kv_str(value_error),
/* K2 */ be_kv_str(length_X20in_X20bits_X20must_X20be_X20between_X200_X20and_X2032),
/* K3 */ be_kv_int(3),
/* K4 */ be_kv_int(1),
}),
( &(const bbyte[ 5]) { /* constant types */
/* K0 */ BE_INT,
/* K1 */ BE_STRING,
/* K2 */ BE_STRING,
/* K3 */ BE_INT,
/* K4 */ BE_INT,
}),
&be_const_str_getbits,
&be_const_str_solidified,
( &(const binstruction[32]) { /* code */
0x180C0500, // 0000 LE R3 R2 K0
0x740E0002, // 0001 JMPT R3 #0005
0x540E001F, // 0002 LDINT R3 32
0x240C0403, // 0003 GT R3 R2 R3
0x780E0000, // 0004 JMPF R3 #0006
0xB0060302, // 0005 RAISE 1 K1 K2
0x580C0000, // 0006 LDCONST R3 K0
0x3C100303, // 0007 SHR R4 R1 K3
0x54160007, // 0008 LDINT R5 8
0x10040205, // 0009 MOD R1 R1 R5
0x58140000, // 000A LDCONST R5 K0
0x24180500, // 000B GT R6 R2 K0
0x781A0011, // 000C JMPF R6 #001F
0x541A0007, // 000D LDINT R6 8
0x04180C01, // 000E SUB R6 R6 R1
0x241C0C02, // 000F GT R7 R6 R2
0x781E0000, // 0010 JMPF R7 #0012
0x5C180400, // 0011 MOVE R6 R2
0x381E0806, // 0012 SHL R7 K4 R6
0x041C0F04, // 0013 SUB R7 R7 K4
0x381C0E01, // 0014 SHL R7 R7 R1
0x94200004, // 0015 GETIDX R8 R0 R4
0x2C201007, // 0016 AND R8 R8 R7
0x3C201001, // 0017 SHR R8 R8 R1
0x38201005, // 0018 SHL R8 R8 R5
0x300C0608, // 0019 OR R3 R3 R8
0x00140A06, // 001A ADD R5 R5 R6
0x04080406, // 001B SUB R2 R2 R6
0x58040000, // 001C LDCONST R1 K0
0x00100904, // 001D ADD R4 R4 K4
0x7001FFEB, // 001E JMP #000B
0x80040600, // 001F RET 1 R3
})
)
);
#else
be_local_closure(getbits, /* name */
be_nested_proto(
9, /* nstack */
3, /* argc */
0, /* varg */
0, /* has upvals */
NULL, /* no upvals */
0, /* has sup protos */
NULL, /* no sub protos */
1, /* has constants */
( &(const bvalue[ 5]) { /* constants */
/* K0 */ be_const_int(0),
/* K1 */ be_nested_str(value_error),
/* K2 */ be_nested_str(length_X20in_X20bits_X20must_X20be_X20between_X200_X20and_X2032),
/* K3 */ be_const_int(3),
/* K4 */ be_const_int(1),
}),
&be_const_str_getbits,
&be_const_str_solidified,
( &(const binstruction[32]) { /* code */
0x180C0500, // 0000 LE R3 R2 K0
0x740E0002, // 0001 JMPT R3 #0005
0x540E001F, // 0002 LDINT R3 32
0x240C0403, // 0003 GT R3 R2 R3
0x780E0000, // 0004 JMPF R3 #0006
0xB0060302, // 0005 RAISE 1 K1 K2
0x580C0000, // 0006 LDCONST R3 K0
0x3C100303, // 0007 SHR R4 R1 K3
0x54160007, // 0008 LDINT R5 8
0x10040205, // 0009 MOD R1 R1 R5
0x58140000, // 000A LDCONST R5 K0
0x24180500, // 000B GT R6 R2 K0
0x781A0011, // 000C JMPF R6 #001F
0x541A0007, // 000D LDINT R6 8
0x04180C01, // 000E SUB R6 R6 R1
0x241C0C02, // 000F GT R7 R6 R2
0x781E0000, // 0010 JMPF R7 #0012
0x5C180400, // 0011 MOVE R6 R2
0x381E0806, // 0012 SHL R7 K4 R6
0x041C0F04, // 0013 SUB R7 R7 K4
0x381C0E01, // 0014 SHL R7 R7 R1
0x94200004, // 0015 GETIDX R8 R0 R4
0x2C201007, // 0016 AND R8 R8 R7
0x3C201001, // 0017 SHR R8 R8 R1
0x38201005, // 0018 SHL R8 R8 R5
0x300C0608, // 0019 OR R3 R3 R8
0x00140A06, // 001A ADD R5 R5 R6
0x04080406, // 001B SUB R2 R2 R6
0x58040000, // 001C LDCONST R1 K0
0x00100904, // 001D ADD R4 R4 K4
0x7001FFEB, // 001E JMP #000B
0x80040600, // 001F RET 1 R3
})
)
);
#endif
/*******************************************************************/
/********************************************************************
** Solidified function: setbits
********************************************************************/
#if BE_USE_COMPACT_KTAB
be_local_closure(setbits, /* name */
be_nested_proto(
10, /* nstack */
4, /* argc */
0, /* varg */
0, /* has upvals */
NULL, /* no upvals */
0, /* has sup protos */
NULL, /* no sub protos */
1, /* has constants */
( &(const union bvaldata[ 5]) { /* constants */
/* K0 */ be_kv_int(0),
/* K1 */ be_kv_str(value_error),
/* K2 */ be_kv_str(length_X20in_X20bits_X20must_X20be_X20between_X200_X20and_X2032),
/* K3 */ be_kv_int(3),
/* K4 */ be_kv_int(1),
}),
( &(const bbyte[ 5]) { /* constant types */
/* K0 */ BE_INT,
/* K1 */ BE_STRING,
/* K2 */ BE_STRING,
/* K3 */ BE_INT,
/* K4 */ BE_INT,
}),
&be_const_str_setbits,
&be_const_str_solidified,
( &(const binstruction[37]) { /* code */
0x14100500, // 0000 LT R4 R2 K0
0x74120002, // 0001 JMPT R4 #0005
0x5412001F, // 0002 LDINT R4 32
0x24100404, // 0003 GT R4 R2 R4
0x78120000, // 0004 JMPF R4 #0006
0xB0060302, // 0005 RAISE 1 K1 K2
0x60100009, // 0006 GETGBL R4 G9
0x5C140600, // 0007 MOVE R5 R3
0x7C100200, // 0008 CALL R4 1
0x5C0C0800, // 0009 MOVE R3 R4
0x3C100303, // 000A SHR R4 R1 K3
0x54160007, // 000B LDINT R5 8
0x10040205, // 000C MOD R1 R1 R5
0x24140500, // 000D GT R5 R2 K0
0x78160014, // 000E JMPF R5 #0024
0x54160007, // 000F LDINT R5 8
0x04140A01, // 0010 SUB R5 R5 R1
0x24180A02, // 0011 GT R6 R5 R2
0x781A0000, // 0012 JMPF R6 #0014
0x5C140400, // 0013 MOVE R5 R2
0x381A0805, // 0014 SHL R6 K4 R5
0x04180D04, // 0015 SUB R6 R6 K4
0x541E00FE, // 0016 LDINT R7 255
0x38200C01, // 0017 SHL R8 R6 R1
0x041C0E08, // 0018 SUB R7 R7 R8
0x94200004, // 0019 GETIDX R8 R0 R4
0x2C201007, // 001A AND R8 R8 R7
0x2C240606, // 001B AND R9 R3 R6
0x38241201, // 001C SHL R9 R9 R1
0x30201009, // 001D OR R8 R8 R9
0x98000808, // 001E SETIDX R0 R4 R8
0x3C0C0605, // 001F SHR R3 R3 R5
0x04080405, // 0020 SUB R2 R2 R5
0x58040000, // 0021 LDCONST R1 K0
0x00100904, // 0022 ADD R4 R4 K4
0x7001FFE8, // 0023 JMP #000D
0x80040000, // 0024 RET 1 R0
})
)
);
#else
be_local_closure(setbits, /* name */
be_nested_proto(
10, /* nstack */
4, /* argc */
0, /* varg */
0, /* has upvals */
NULL, /* no upvals */
0, /* has sup protos */
NULL, /* no sub protos */
1, /* has constants */
( &(const bvalue[ 5]) { /* constants */
/* K0 */ be_const_int(0),
/* K1 */ be_nested_str(value_error),
/* K2 */ be_nested_str(length_X20in_X20bits_X20must_X20be_X20between_X200_X20and_X2032),
/* K3 */ be_const_int(3),
/* K4 */ be_const_int(1),
}),
&be_const_str_setbits,
&be_const_str_solidified,
( &(const binstruction[37]) { /* code */
0x14100500, // 0000 LT R4 R2 K0
0x74120002, // 0001 JMPT R4 #0005
0x5412001F, // 0002 LDINT R4 32
0x24100404, // 0003 GT R4 R2 R4
0x78120000, // 0004 JMPF R4 #0006
0xB0060302, // 0005 RAISE 1 K1 K2
0x60100009, // 0006 GETGBL R4 G9
0x5C140600, // 0007 MOVE R5 R3
0x7C100200, // 0008 CALL R4 1
0x5C0C0800, // 0009 MOVE R3 R4
0x3C100303, // 000A SHR R4 R1 K3
0x54160007, // 000B LDINT R5 8
0x10040205, // 000C MOD R1 R1 R5
0x24140500, // 000D GT R5 R2 K0
0x78160014, // 000E JMPF R5 #0024
0x54160007, // 000F LDINT R5 8
0x04140A01, // 0010 SUB R5 R5 R1
0x24180A02, // 0011 GT R6 R5 R2
0x781A0000, // 0012 JMPF R6 #0014
0x5C140400, // 0013 MOVE R5 R2
0x381A0805, // 0014 SHL R6 K4 R5
0x04180D04, // 0015 SUB R6 R6 K4
0x541E00FE, // 0016 LDINT R7 255
0x38200C01, // 0017 SHL R8 R6 R1
0x041C0E08, // 0018 SUB R7 R7 R8
0x94200004, // 0019 GETIDX R8 R0 R4
0x2C201007, // 001A AND R8 R8 R7
0x2C240606, // 001B AND R9 R3 R6
0x38241201, // 001C SHL R9 R9 R1
0x30201009, // 001D OR R8 R8 R9
0x98000808, // 001E SETIDX R0 R4 R8
0x3C0C0605, // 001F SHR R3 R3 R5
0x04080405, // 0020 SUB R2 R2 R5
0x58040000, // 0021 LDCONST R1 K0
0x00100904, // 0022 ADD R4 R4 K4
0x7001FFE8, // 0023 JMP #000D
0x80040000, // 0024 RET 1 R0
})
)
);
#endif
/*******************************************************************/
#if !BE_USE_PRECOMPILED_OBJECT
void be_load_byteslib(bvm *vm)
{
@@ -2107,10 +1969,8 @@ void be_load_byteslib(bvm *vm)
{ "..", m_connect },
{ "==", m_equal },
{ "!=", m_nequal },
{ NULL, (bntvfunc) BE_CLOSURE }, /* mark section for berry closures */
{ "getbits", (bntvfunc) &getbits_closure },
{ "setbits", (bntvfunc) &setbits_closure },
{ "getbits", m_getbits },
{ "setbits", m_setbits },
{ NULL, NULL }
};
@@ -2163,8 +2023,8 @@ class be_class_bytes (scope: global, name: bytes) {
==, func(m_equal)
!=, func(m_nequal)
getbits, closure(getbits_closure)
setbits, closure(setbits_closure)
getbits, func(m_getbits)
setbits, func(m_setbits)
}
@const_object_info_end */
#include "../generate/be_fixed_be_class_bytes.h"
+304
View File
@@ -381,3 +381,307 @@ assert_error(def () b.addfloat(true) end, 'type_error')
assert_error(def () b.addfloat(nil) end, 'type_error')
assert_error(def () b.addfloat('foo') end, 'type_error')
assert_error(def () b.addfloat() end, 'type_error')
#- ------------------------------------------------------------ -#
#- getbits / setbits -#
#- ------------------------------------------------------------ -#
#- getbits: basic reads within a single byte (LSB = bit 0) -#
b = bytes("AF000000")
assert(b.getbits(0, 4) == 0x0F)
assert(b.getbits(4, 4) == 0x0A)
assert(b.getbits(0, 8) == 0xAF)
assert(b.getbits(0, 1) == 1)
assert(b.getbits(7, 1) == 1)
assert(b.getbits(4, 1) == 0) #- 0xAF = 1010_1111, bit4 = 0 -#
assert(b.getbits(5, 1) == 1)
#- getbits: reading zero bytes returns 0 -#
b = bytes("00000000")
assert(b.getbits(0, 8) == 0)
assert(b.getbits(0, 32) == 0)
#- getbits: spanning byte boundaries (byte0 is least significant) -#
b = bytes("3412") #- bytes[0]=0x34, bytes[1]=0x12 -#
assert(b.getbits(0, 16) == 0x1234)
assert(b.getbits(0, 4) == 0x4) #- low nibble of byte0 -#
assert(b.getbits(4, 8) == 0x23) #- high nibble of byte0 + low nibble of byte1 -#
assert(b.getbits(8, 8) == 0x12) #- byte1 -#
assert(b.getbits(12, 4) == 0x1) #- high nibble of byte1 -#
#- getbits: full 32-bit read -#
b = bytes("78563412")
assert(b.getbits(0, 32) == 0x12345678)
#- getbits: reading past the end of the buffer yields zero bits -#
b = bytes("FF")
assert(b.getbits(0, 8) == 0xFF)
assert(b.getbits(0, 16) == 0x00FF) #- second byte is out of range -> 0 -#
assert(b.getbits(8, 8) == 0)
#- getbits: length validation -#
assert(bytes("00").getbits(0, 0) == nil) #- zero length returns nil (like get()) -#
assert_error(def () bytes("00").getbits(0, 33) end, 'value_error')
assert_error(def () bytes("00").getbits(0, -33) end, 'value_error')
#- getbits: argument type validation -#
assert_error(def () bytes("00").getbits() end, 'type_error')
assert_error(def () bytes("00").getbits(0) end, 'type_error')
assert_error(def () bytes("00").getbits('a', 4) end, 'type_error')
assert_error(def () bytes("00").getbits(0, 'a') end, 'type_error')
assert_error(def () bytes("00").getbits(0.5, 4) end, 'type_error')
#- setbits: basic writes within a single byte -#
b = bytes("00000000")
assert(b.setbits(0, 4, 0x0F) == bytes("0F000000"))
assert(b.setbits(4, 4, 0x0A) == bytes("AF000000"))
#- setbits: overwriting existing bits clears the old value -#
b = bytes("FF000000")
b.setbits(0, 4, 0x0)
assert(b == bytes("F0000000"))
b.setbits(4, 4, 0x0)
assert(b == bytes("00000000"))
#- setbits: spanning byte boundaries (little-endian byte order) -#
b = bytes("00000000")
b.setbits(4, 8, 0xAB)
assert(b == bytes("B00A0000")) #- low nibble in byte0, high nibble in byte1 -#
assert(b.getbits(4, 8) == 0xAB)
b = bytes("00000000")
b.setbits(0, 16, 0x1234)
assert(b == bytes("34120000"))
#- setbits: full 32-bit write -#
b = bytes("00000000")
b.setbits(0, 32, 0x12345678)
assert(b == bytes("78563412"))
assert(b.getbits(0, 32) == 0x12345678)
#- setbits: only the targeted bits are modified, neighbours are preserved -#
b = bytes("FFFFFFFF")
b.setbits(4, 8, 0x00)
assert(b == bytes("0FF0FFFF"))
assert(b.getbits(0, 4) == 0x0F) #- low nibble preserved -#
assert(b.getbits(4, 8) == 0x00) #- cleared field -#
#- setbits: value larger than len_bits is masked to len_bits -#
b = bytes("00")
b.setbits(0, 4, 0xFF) #- only low 4 bits should be written -#
assert(b == bytes("0F"))
#- setbits: writing zero bits leaves the buffer unchanged and returns self -#
b = bytes("1234")
assert(b.setbits(0, 0, 0xFF) == bytes("1234"))
#- setbits: accepts bool values (converted to int) -#
b = bytes("00")
b.setbits(0, 1, true)
assert(b.getbits(0, 1) == 1)
b.setbits(1, 1, false)
assert(b.getbits(1, 1) == 0)
#- setbits: returns self so calls can be chained -#
b = bytes("00000000")
assert(b.setbits(0, 4, 1).setbits(4, 4, 2) == bytes("21000000"))
#- setbits / getbits round-trip across a range of offsets and lengths -#
b = bytes("0000000000")
b.setbits(3, 12, 0xABC)
assert(b.getbits(3, 12) == 0xABC)
b.setbits(15, 17, 0x1FFFF)
assert(b.getbits(15, 17) == 0x1FFFF)
assert(b.getbits(3, 12) == 0xABC) #- previous field untouched -#
#- setbits: length validation -#
assert(bytes("FF").setbits(0, 0, 0) == bytes("FF")) #- zero length is a no-op -#
assert_error(def () bytes("00").setbits(0, 33, 0) end, 'value_error')
assert_error(def () bytes("00").setbits(0, -33, 0) end, 'value_error')
#- setbits: argument type validation -#
assert_error(def () bytes("00").setbits() end, 'type_error')
assert_error(def () bytes("00").setbits(0) end, 'type_error')
assert_error(def () bytes("00").setbits(0, 4) end, 'type_error')
assert_error(def () bytes("00").setbits('a', 4, 0) end, 'type_error')
assert_error(def () bytes("00").setbits(0, 'a', 0) end, 'type_error')
assert_error(def () bytes("00").setbits(0, 4, 'a') end, 'type_error')
assert_error(def () bytes("00").setbits(0, 4, nil) end, 'type_error')
#- ------------------------------------------------------------ -#
#- getbits / setbits — Big Endian support (PR berry-lang#537) -#
#- ------------------------------------------------------------ -#
#- Single byte - endiannes does't matter -#
def getbit_and_setbit_roundtrip(byte, offset, length, val)
# check for expected value within a predefined byte
assert(byte.getbits(offset, length) == val)
# set the value at a position, roundtrip and should return same value
assert(bytes(-2).setbits(offset, length, val).getbits(offset, length) == val)
end
b = bytes("ABCD") # bytes constructor uses big endian, btw
assert(b.get(0) == 0xAB)
assert(b.get(1) == 0xCD)
assert(b.get(0,-2) == 0xABCD)
# little endian
v=0x3; o=0; l= 2; getbit_and_setbit_roundtrip(b, o, l, v)
v=0xB; o=0; l= 4; getbit_and_setbit_roundtrip(b, o, l, v)
v=0xA; o=4; l= 4; getbit_and_setbit_roundtrip(b, o, l, v)
v=0xAB; o=0; l= 8; getbit_and_setbit_roundtrip(b, o, l, v)
v=0xDA; o=4; l= 8; getbit_and_setbit_roundtrip(b, o, l, v)
v=0xDAB; o=0; l= 12; getbit_and_setbit_roundtrip(b, o, l, v)
v=0xC; o=12; l= 4; getbit_and_setbit_roundtrip(b, o, l, v)
v=0xCDAB; o=0; l= 16; getbit_and_setbit_roundtrip(b, o, l, v)
# big endian
v=0xB; o=4; l= -4; getbit_and_setbit_roundtrip(b, o, l, v)
v=0xA; o=0; l= -4; getbit_and_setbit_roundtrip(b, o, l, v)
v=0xAB; o=0; l= -8; getbit_and_setbit_roundtrip(b, o, l, v)
v=0xBC; o=4; l= -8; getbit_and_setbit_roundtrip(b, o, l, v)
v=0xABC; o=0; l= -12; getbit_and_setbit_roundtrip(b, o, l, v)
v=0xD; o=12; l= -4; getbit_and_setbit_roundtrip(b, o, l, v)
v=0xABCD; o=0; l= -16; getbit_and_setbit_roundtrip(b, o, l, v)
#- LE: read from known pattern -#
b = bytes("A55A")
assert(b.getbits(0, 16) == 0x5AA5)
#- BE: read from known pattern -#
b = bytes("A55A")
assert(b.getbits(0, -16) == 0xA55A)
#- setbit - input value read/write -#
# we always truncate input value according to big endian - so in both cases we will see alteration of A and B nibbles in the output
b = bytes(-3)
b.setbits(0, 8, 0xDEADAB)
assert(b == bytes("AB0000"))
# endiannes doesn't matter for a single contiguous byte:
b = bytes(-3)
b.setbits(0, -8, 0xBEEFAB)
assert(b == bytes("AB0000"))
# endiannes and offset only influences the output
b = bytes(-3)
b.setbits(4, 8, 0xBEEFAB)
assert(b == bytes("B00A00")) # write down bytes vertically if it doesn't make sense ;)
b = bytes(-3)
b.setbits(4, -8, 0xBEEFAB)
assert(b == bytes("0AB000"))
#- setbit - input longer than length - partial byte-#
b = bytes("0000")
b.setbits(0, 4, 0xFFFF)
assert(b == bytes("0F00"))
assert(b.getbits(0, 4) == 0xF)
#- LE: single-bit set -#
b = bytes("5A")
b.setbits(0, 1, 1)
assert(b == bytes("5B")) #- LSB 0→1 -#
b = bytes("5A")
b.setbits(7, 1, 1)
assert(b == bytes("DA")) #- MSB 0→1 -#
b.setbits(7, 1, 0)
assert(b == bytes("5A")) #- MSB 1→0 -#
#- BE: single-bit -#
b = bytes("5A")
b.setbits(0, -1, 1)
assert(b == bytes("DA")) # MSB-0 bit 0 (phys 7) 0→1
b = bytes("5A")
b.setbits(7, -1, 1)
assert(b == bytes("5B")) # MSB-0 bit 7 (phys 0) 0→1
b.setbits(7, -1, 0)
assert(b == bytes("5A"))
#- LE: partial middle of byte -#
b = bytes("00")
b.setbits(2, 3, 5) #- 5 = 101b at bits 2,3,4 -#
assert(b == bytes("14")) #- 0001_0100 -#
assert(b.getbits(2, 3) == 5)
b = bytes("FF")
b.setbits(1, 2, 0)
assert(b == bytes("F9")) #- clear bits 1,2 → 1111_1001 -#
assert(b.getbits(1, 2) == 0)
#- BE: partial middle of byte -#
b = bytes("00")
b.setbits(1, -3, 5)
assert(b == bytes("50")) # 5 = 101b at bits 1,2,3 → phys 6,5,4 = 0101_0000
assert(b.getbits(1, -3) == 5)
b = bytes("FF")
b.setbits(1, -2, 0)
assert(b == bytes("9F")) # clear bits 1,2 (phys 6,5) → 1001_1111
assert(b.getbits(1, -2) == 0)
#- LE: full byte -#
b = bytes("00")
b.setbits(0, 8, 0x5A)
assert(b == bytes("5A"))
b.setbits(0, 8, 0xA5)
assert(b == bytes("A5"))
#- BE: full byte -#
b = bytes("00")
b.setbits(0, -8, 0x5A)
assert(b == bytes("5A"))
b.setbits(0, -8, 0xA5)
assert(b == bytes("A5"))
#- LE: multi-byte -#
b = bytes("0000")
b.setbits(0, 16, 0xA55A)
assert(b == bytes("5AA5")) #- LE wire order: LSB first -#
b = bytes("0000")
b.setbits(8, 8, 0x5A)
assert(b == bytes("005A"))
#- BE multi-byte = MSB-first byte order (opposite of LE) -#
b = bytes("0000")
b.setbits(0, -16, 0xA55A)
assert(b == bytes("A55A")) # BE wire order: MSB first
b = bytes("0000")
b.setbits(8, -8, 0x5A)
assert(b == bytes("005A"))
#- LE: cross-byte partial -#
b = bytes("0000")
b.setbits(4, 10, 0xFFF)
assert(b == bytes("F03F")) #- byte0 bits 4-7 = 0xF, byte1 bits 0-5 = 0x3F -#
assert(b.getbits(4, 10) == 0x3FF)
#- BE: cross-byte partial -#
b = bytes("0000")
b.setbits(4, -10, 0xFFF)
assert(b == bytes("0FFC")) # byte0 bits 4-7 = 0x0F, byte1 bits 0-5 = 0xFC
assert(b.getbits(4, -10) == 0x3FF)
#- mixing LE/BE -#
b = bytes(-3)
b.setbits(0, -12, 0xABC)
b.setbits(12, 12, 0xDEF) # F corrupts C nibble, one nibble empty
assert(b == bytes("ABF0DE"))
b = bytes(-4) # let's try again
b.setbits(0, -12, 0xABC)
b.setbits(16, 12, 0xDEF) # we need to start in byte 2
assert(b == bytes("ABC0EF0D"))
# length check
assert(bytes("FF").getbits(0, 0) == nil)
assert(bytes("FF").setbits(0, 0, 0) == bytes("FF"))
assert_error(def () bytes("00").getbits(0, 33) end, 'value_error')
assert_error(def () bytes("00").setbits(0, 33, 0) end, 'value_error')
assert_error(def () bytes("00").getbits(0, -33) end, 'value_error')
assert_error(def () bytes("00").setbits(0, -33, 0) end, 'value_error')