mirror of
https://github.com/arendst/Tasmota.git
synced 2026-07-27 20:05:46 +00:00
Berry bytes methods setbits/getbits transposed to native and support for big endian (#24857)
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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')
|
||||
|
||||
Reference in New Issue
Block a user