bitfield: fix bf.from_bytes/1 ( now, bf.from_bytes(b) == bf.from_str(bf.from_bytes(b).str()) )

pull/7402/head
Delyan Angelov 2020-12-18 21:47:24 +02:00
parent 05f6e8b5aa
commit c5c310280f
No known key found for this signature in database
GPG Key ID: 66886C0F12D595ED
2 changed files with 123 additions and 128 deletions

View File

@ -12,7 +12,6 @@ Bit arrays are stored in data structures called 'BitField'. The structure is
'opaque', i.e. its internals are not available to the end user. This module
provides API (functions and methods) for accessing and modifying bit arrays.
*/
pub struct BitField {
mut:
size int
@ -26,12 +25,36 @@ const (
)
// public functions
// from_bytes() converts a byte array into a bitfield.
pub fn from_bytes(input []byte) BitField {
mut output := new(input.len * 8)
for i, b in input {
output.field[i / 4] |= u32(b) << ((i % 4) * 8)
mut ob := byte(0)
if b & 0b10000000 > 0 {
ob |= 0b00000001
}
if b & 0b01000000 > 0 {
ob |= 0b00000010
}
if b & 0b00100000 > 0 {
ob |= 0b00000100
}
if b & 0b00010000 > 0 {
ob |= 0b00001000
}
if b & 0b00001000 > 0 {
ob |= 0b00010000
}
if b & 0b00000100 > 0 {
ob |= 0b00100000
}
if b & 0b00000010 > 0 {
ob |= 0b01000000
}
if b & 0b00000001 > 0 {
ob |= 0b10000000
}
output.field[i / 4] |= u32(ob) << ((i % 4) * 8)
}
return output
}
@ -55,8 +78,7 @@ pub fn (input BitField) str() string {
for i in 0 .. input.size {
if input.get_bit(i) == 1 {
output = output + '1'
}
else {
} else {
output = output + '0'
}
}
@ -72,13 +94,13 @@ pub fn new(size int) BitField {
}
return output
}
/*
pub fn del(instance *BitField) {
free(instance.field)
free(instance)
}
*/
// get_bit returns the value (0 or 1) of bit number 'bit_nr' (count from 0).
pub fn (instance BitField) get_bit(bitnr int) int {
if bitnr >= instance.size {
@ -189,16 +211,12 @@ pub fn join(input1 BitField, input2 BitField) BitField {
for i in 0 .. zbitnslots(input1.size) {
output.field[i] = input1.field[i]
}
// find offset bit and offset slot
offset_bit := input1.size % slot_size
offset_slot := input1.size / slot_size
for i in 0 .. zbitnslots(input2.size) {
output.field[i + offset_slot] |=
u32(input2.field[i] << u32(offset_bit))
output.field[i + offset_slot] |= u32(input2.field[i] << u32(offset_bit))
}
/*
* If offset_bit is not zero, additional operations are needed.
* Number of iterations depends on the nr of slots in output. Two
@ -214,13 +232,11 @@ pub fn join(input1 BitField, input2 BitField) BitField {
*/
if (output_size - 1) % slot_size < (input2.size - 1) % slot_size {
for i in 0 .. zbitnslots(input2.size) {
output.field[i + offset_slot + 1] |=
u32(input2.field[i] >> u32(slot_size - offset_bit))
output.field[i + offset_slot + 1] |= u32(input2.field[i] >> u32(slot_size - offset_bit))
}
} else if (output_size - 1) % slot_size > (input2.size - 1) % slot_size {
for i in 0 .. zbitnslots(input2.size) - 1 {
output.field[i + offset_slot + 1] |=
u32(input2.field[i] >> u32(slot_size - offset_bit))
output.field[i + offset_slot + 1] |= u32(input2.field[i] >> u32(slot_size - offset_bit))
}
}
return output
@ -244,9 +260,13 @@ pub fn (instance BitField) clone() BitField {
// cmp compares two bit arrays bit by bit and returns 'true' if they are
// identical by length and contents and 'false' otherwise.
pub fn (instance BitField) cmp(input BitField) bool {
if instance.size != input.size {return false}
if instance.size != input.size {
return false
}
for i in 0 .. zbitnslots(instance.size) {
if instance.field[i] != input.field[i] {return false}
if instance.field[i] != input.field[i] {
return false
}
}
return true
}
@ -284,7 +304,6 @@ pub fn (haystack BitField) pos(needle BitField) int {
heystack_size := haystack.size
needle_size := needle.size
diff := heystack_size - needle_size
// needle longer than haystack; return error code -2
if diff < 0 {
return -2
@ -312,53 +331,41 @@ pub fn (input BitField) slice(_start int, _end int) BitField {
if start > end {
start = end // or panic?
}
mut output := new(end - start)
start_offset := start % slot_size
end_offset := (end - 1) % slot_size
start_slot := start / slot_size
end_slot := (end - 1) / slot_size
output_slots := zbitnslots(end - start)
if output_slots > 1 {
if start_offset != 0 {
for i in 0 .. output_slots - 1 {
output.field[i] =
u32(input.field[start_slot + i] >> u32(start_offset))
output.field[i] = output.field[i] |
u32(input.field[start_slot + i + 1] <<
u32(slot_size - start_offset))
output.field[i] = u32(input.field[start_slot + i] >> u32(start_offset))
output.field[i] = output.field[i] | u32(input.field[start_slot + i + 1] << u32(slot_size -
start_offset))
}
}
else {
} else {
for i in 0 .. output_slots - 1 {
output.field[i] =
u32(input.field[start_slot + i])
output.field[i] = u32(input.field[start_slot + i])
}
}
}
if start_offset > end_offset {
output.field[(end - start - 1) / slot_size] =
u32(input.field[end_slot - 1] >> u32(start_offset))
output.field[(end - start - 1) / slot_size] = u32(input.field[end_slot - 1] >> u32(start_offset))
mut mask := u32((1 << (end_offset + 1)) - 1)
mask = input.field[end_slot] & mask
mask = u32(mask << u32(slot_size - start_offset))
output.field[(end - start - 1) / slot_size] |= mask
}
else if start_offset == 0 {
} else if start_offset == 0 {
mut mask := u32(0)
if end_offset == slot_size - 1 {
mask = u32(-1)
}
else {
} else {
mask = u32(u32(1) << u32(end_offset + 1))
mask = mask - u32(1)
}
output.field[(end - start - 1) / slot_size] =
(input.field[end_slot] & mask)
}
else {
output.field[(end - start - 1) / slot_size] = (input.field[end_slot] & mask)
} else {
mut mask := u32(((1 << (end_offset - start_offset + 1)) - 1) << start_offset)
mask = input.field[end_slot] & mask
mask = u32(mask >> u32(start_offset))
@ -408,14 +415,15 @@ pub fn (mut instance BitField) resize(new_size int) {
// rotate circular-shifts the bits by 'offset' positions (move
// 'offset' bit to 0, 'offset+1' bit to 1, and so on).
pub fn (instance BitField) rotate(offset int) BitField {
/**
/*
*
* This function "cuts" the bitfield into two and swaps them.
* If the offset is positive, the cutting point is counted from the
* beginning of the bit array, otherwise from the end.
**/
*
*/
size := instance.size
// removing extra rotations
mut offset_internal := offset % size
if offset_internal == 0 {
// nothing to shift
@ -424,7 +432,6 @@ pub fn (instance BitField) rotate(offset int) BitField {
if offset_internal < 0 {
offset_internal = offset_internal + size
}
first_chunk := instance.slice(0, offset_internal)
second_chunk := instance.slice(offset_internal, size)
output := join(second_chunk, first_chunk)
@ -432,14 +439,14 @@ pub fn (instance BitField) rotate(offset int) BitField {
}
// Internal functions
fn (mut instance BitField) clear_tail() {
tail := instance.size % slot_size
if tail != 0 {
// create a mask for the tail
mask := u32((1 << tail) - 1)
// clear the extra bits
instance.field[zbitnslots(instance.size) - 1] = instance.field[zbitnslots(instance.size) - 1] & mask
instance.field[zbitnslots(instance.size) - 1] = instance.field[zbitnslots(instance.size) -
1] & mask
}
}
@ -454,8 +461,7 @@ fn bitslot(size int) int {
fn min(input1 int, input2 int) int {
if input1 < input2 {
return input1
}
else {
} else {
return input2
}
}

View File

@ -1,5 +1,4 @@
import bitfield
import rand
fn test_bf_new_size() {
@ -38,7 +37,9 @@ fn test_bf_and_not_or_xor() {
output2 := bitfield.bf_and(bf_or, bf_not)
mut result := 1
for i < len {
if output1.get_bit(i) != output2.get_bit(i) {result = 0}
if output1.get_bit(i) != output2.get_bit(i) {
result = 0
}
}
assert result == 1
}
@ -111,24 +112,18 @@ fn test_hamming() {
input1.set_bit(i)
input2.set_bit(i)
}
else {
}
else {}
}
}
assert count == bitfield.hamming(input1, input2)
}
fn test_bf_from_bytes() {
input := [byte(0xF0), byte(0x0F), byte(0xF0), byte(0xFF)]
output := bitfield.from_bytes(input)
mut result := 1
for i in 0..input.len * 8 {
if (input[i / 8] >> (i % 8)) & 1 != output.get_bit(i) {
result = 0
}
}
assert result == 1
input := [byte(0x01), 0xF0, 0x0F, 0xF0, 0xFF]
output := bitfield.from_bytes(input).str()
assert output == '00000001' + '11110000' + '00001111' + '11110000' + '11111111'
newoutput := bitfield.from_str(output).str()
assert newoutput == output
}
fn test_bf_from_str() {
@ -137,8 +132,7 @@ fn test_bf_from_str() {
for _ in 0 .. len {
if rand.intn(2) == 1 {
input = input + '1'
}
else {
} else {
input = input + '0'
}
}
@ -164,8 +158,7 @@ fn test_bf_bf2str() {
for i in 0 .. len {
if input.get_bit(i) == 1 {
check = check + '1'
}
else {
} else {
check = check + '0'
}
}
@ -240,48 +233,44 @@ fn test_bf_resize() {
}
fn test_bf_pos() {
/**
/*
*
* set haystack size to 80
* test different sizes of needle, from 1 to 80
* test different positions of needle, from 0 to where it fits
* all haystacks here contain exactly one instanse of needle,
* so search should return non-negative-values
**/
*
*/
len := 80
mut result := 1
for i := 1; i < len; i++ { // needle size
for j in 0 .. len - i { // needle position in the haystack
// create the needle
mut needle := bitfield.new(i)
// fill the needle with random values
for k in 0 .. i {
if rand.intn(2) == 1 {
needle.set_bit(k)
}
}
// make sure the needle contains at least one set bit, selected randomly
r := rand.intn(i)
needle.set_bit(r)
// create the haystack, make sure it contains the needle
mut haystack := needle.clone()
// if there is space between the start of the haystack and the sought needle, fill it with zeroes
if j > 0 {
start := bitfield.new(j)
tmp := bitfield.join(start, haystack)
haystack = tmp
}
// if there is space between the sought needle and the end of haystack, fill it with zeroes
if j + i < len {
end := bitfield.new(len - j - i)
tmp2 := bitfield.join(haystack, end)
haystack = tmp2
}
// now let's test
// the result should be equal to j
if haystack.pos(needle) != j {