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,11 +12,10 @@ 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
//field *u32
// field *u32
field []u32
}
@ -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
}
@ -40,7 +63,7 @@ pub fn from_bytes(input []byte) BitField {
// array. Any character different from '0' is treated as '1'.
pub fn from_str(input string) BitField {
mut output := new(input.len)
for i in 0..input.len {
for i in 0 .. input.len {
if input[i] != `0` {
output.set_bit(i)
}
@ -52,11 +75,10 @@ pub fn from_str(input string) BitField {
// return the string
pub fn (input BitField) str() string {
mut output := ''
for i in 0..input.size {
for i in 0 .. input.size {
if input.get_bit(i) == 1 {
output = output + '1'
}
else {
} else {
output = output + '0'
}
}
@ -67,18 +89,18 @@ pub fn (input BitField) str() string {
pub fn new(size int) BitField {
output := BitField{
size: size
//field: *u32(calloc(zbitnslots(size) * slot_size / 8))
field: []u32{len:zbitnslots(size)}
// field: *u32(calloc(zbitnslots(size) * slot_size / 8))
field: []u32{len: zbitnslots(size)}
}
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 {
@ -105,7 +127,7 @@ pub fn (mut instance BitField) clear_bit(bitnr int) {
// set_all sets all bits in the array to 1.
pub fn (mut instance BitField) set_all() {
for i in 0..zbitnslots(instance.size) {
for i in 0 .. zbitnslots(instance.size) {
instance.field[i] = u32(-1)
}
instance.clear_tail()
@ -113,7 +135,7 @@ pub fn (mut instance BitField) set_all() {
// clear_all clears (sets to zero) all bits in the array.
pub fn (mut instance BitField) clear_all() {
for i in 0..zbitnslots(instance.size) {
for i in 0 .. zbitnslots(instance.size) {
instance.field[i] = u32(0)
}
}
@ -134,7 +156,7 @@ pub fn bf_and(input1 BitField, input2 BitField) BitField {
size := min(input1.size, input2.size)
bitnslots := zbitnslots(size)
mut output := new(size)
for i in 0..bitnslots {
for i in 0 .. bitnslots {
output.field[i] = input1.field[i] & input2.field[i]
}
output.clear_tail()
@ -146,7 +168,7 @@ pub fn bf_not(input BitField) BitField {
size := input.size
bitnslots := zbitnslots(size)
mut output := new(size)
for i in 0..bitnslots {
for i in 0 .. bitnslots {
output.field[i] = ~input.field[i]
}
output.clear_tail()
@ -160,7 +182,7 @@ pub fn bf_or(input1 BitField, input2 BitField) BitField {
size := min(input1.size, input2.size)
bitnslots := zbitnslots(size)
mut output := new(size)
for i in 0..bitnslots {
for i in 0 .. bitnslots {
output.field[i] = input1.field[i] | input2.field[i]
}
output.clear_tail()
@ -174,7 +196,7 @@ pub fn bf_xor(input1 BitField, input2 BitField) BitField {
size := min(input1.size, input2.size)
bitnslots := zbitnslots(size)
mut output := new(size)
for i in 0..bitnslots {
for i in 0 .. bitnslots {
output.field[i] = input1.field[i] ^ input2.field[i]
}
output.clear_tail()
@ -186,19 +208,15 @@ pub fn join(input1 BitField, input2 BitField) BitField {
output_size := input1.size + input2.size
mut output := new(output_size)
// copy the first input to output as is
for i in 0..zbitnslots(input1.size) {
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))
for i in 0 .. zbitnslots(input2.size) {
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
@ -213,14 +231,12 @@ pub fn join(input1 BitField, input2 BitField) BitField {
* If offset_bit is zero, no additional copies needed.
*/
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))
for i in 0 .. zbitnslots(input2.size) {
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))
for i in 0 .. zbitnslots(input2.size) - 1 {
output.field[i + offset_slot + 1] |= u32(input2.field[i] >> u32(slot_size - offset_bit))
}
}
return output
@ -235,7 +251,7 @@ pub fn (instance BitField) get_size() int {
pub fn (instance BitField) clone() BitField {
bitnslots := zbitnslots(instance.size)
mut output := new(instance.size)
for i in 0..bitnslots {
for i in 0 .. bitnslots {
output.field[i] = instance.field[i]
}
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}
for i in 0..zbitnslots(instance.size) {
if instance.field[i] != input.field[i] {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
}
}
return true
}
@ -257,14 +277,14 @@ pub fn (instance BitField) pop_count() int {
bitnslots := zbitnslots(size)
tail := size % slot_size
mut count := 0
for i in 0..bitnslots - 1 {
for j in 0..slot_size {
for i in 0 .. bitnslots - 1 {
for j in 0 .. slot_size {
if u32(instance.field[i] >> u32(j)) & u32(1) == u32(1) {
count++
}
}
}
for j in 0..tail {
for j in 0 .. tail {
if u32(instance.field[bitnslots - 1] >> u32(j)) & u32(1) == u32(1) {
count++
}
@ -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))
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))
}
}
else {
for i in 0..output_slots - 1 {
output.field[i] =
u32(input.field[start_slot + i])
} else {
for i in 0 .. output_slots - 1 {
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))
@ -373,15 +380,15 @@ pub fn (instance BitField) reverse() BitField {
size := instance.size
bitnslots := zbitnslots(size)
mut output := new(size)
for i:= 0; i < (bitnslots - 1); i++ {
for j in 0..slot_size {
for i := 0; i < (bitnslots - 1); i++ {
for j in 0 .. slot_size {
if u32(instance.field[i] >> u32(j)) & u32(1) == u32(1) {
output.set_bit(size - i * slot_size - j - 1)
}
}
}
bits_in_last_input_slot := (size - 1) % slot_size + 1
for j in 0..bits_in_last_input_slot {
for j in 0 .. bits_in_last_input_slot {
if u32(instance.field[bitnslots - 1] >> u32(j)) & u32(1) == u32(1) {
output.set_bit(bits_in_last_input_slot - j - 1)
}
@ -394,7 +401,7 @@ pub fn (mut instance BitField) resize(new_size int) {
new_bitnslots := zbitnslots(new_size)
old_size := instance.size
old_bitnslots := zbitnslots(old_size)
mut field := []u32{len:new_bitnslots}
mut field := []u32{len: new_bitnslots}
for i := 0; i < old_bitnslots && i < new_bitnslots; i++ {
field[i] = instance.field[i]
}
@ -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() {
@ -26,7 +25,7 @@ fn test_bf_and_not_or_xor() {
if rand.intn(2) == 1 {
input1.set_bit(i)
}
if rand.intn(2) == 1{
if rand.intn(2) == 1 {
input2.set_bit(i)
}
i++
@ -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
}
@ -46,7 +47,7 @@ fn test_bf_and_not_or_xor() {
fn test_clone_cmp() {
len := 80
mut input := bitfield.new(len)
for i in 0..len {
for i in 0 .. len {
if rand.intn(2) == 1 {
input.set_bit(i)
}
@ -59,7 +60,7 @@ fn test_clone_cmp() {
fn test_slice_join() {
len := 80
mut input := bitfield.new(len)
for i in 0..len {
for i in 0 .. len {
if rand.intn(2) == 1 {
input.set_bit(i)
}
@ -82,7 +83,7 @@ fn test_pop_count() {
len := 80
mut count0 := 0
mut input := bitfield.new(len)
for i in 0..len {
for i in 0 .. len {
if rand.intn(2) == 1 {
input.set_bit(i)
count0++
@ -97,7 +98,7 @@ fn test_hamming() {
mut count := 0
mut input1 := bitfield.new(len)
mut input2 := bitfield.new(len)
for i in 0..len {
for i in 0 .. len {
match rand.intn(4) {
0, 1 {
input1.set_bit(i)
@ -111,40 +112,33 @@ 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() {
len := 80
mut input := ''
for _ in 0..len {
for _ in 0 .. len {
if rand.intn(2) == 1 {
input = input + '1'
}
else {
} else {
input = input + '0'
}
}
output := bitfield.from_str(input)
mut result := 1
for i in 0..len {
for i in 0 .. len {
if input[i] != output.get_bit(i) + 48 {
result = 0
}
@ -155,23 +149,22 @@ fn test_bf_from_str() {
fn test_bf_bf2str() {
len := 80
mut input := bitfield.new(len)
for i in 0..len {
for i in 0 .. len {
if rand.intn(2) == 1 {
input.set_bit(i)
}
}
mut check := ''
for i in 0..len {
for i in 0 .. len {
if input.get_bit(i) == 1 {
check = check + '1'
}
else {
} else {
check = check + '0'
}
}
output := input.str()
mut result := 1
for i in 0..len {
for i in 0 .. len {
if check[i] != output[i] {
result = 0
}
@ -184,7 +177,7 @@ fn test_bf_set_all() {
mut input := bitfield.new(len)
input.set_all()
mut result := 1
for i in 0..len {
for i in 0 .. len {
if input.get_bit(i) != 1 {
result = 0
}
@ -195,14 +188,14 @@ fn test_bf_set_all() {
fn test_bf_clear_all() {
len := 80
mut input := bitfield.new(len)
for i in 0..len {
for i in 0 .. len {
if rand.intn(2) == 1 {
input.set_bit(i)
}
}
input.clear_all()
mut result := 1
for i in 0..len {
for i in 0 .. len {
if input.get_bit(i) != 0 {
result = 0
}
@ -213,7 +206,7 @@ fn test_bf_clear_all() {
fn test_bf_reverse() {
len := 80
mut input := bitfield.new(len)
for i in 0..len {
for i in 0 .. len {
if rand.intn(2) == 1 {
input.set_bit(i)
}
@ -221,7 +214,7 @@ fn test_bf_reverse() {
check := input.clone()
output := input.reverse()
mut result := 1
for i in 0..len {
for i in 0 .. len {
if output.get_bit(i) != check.get_bit(len - i - 1) {
result = 0
}
@ -232,7 +225,7 @@ fn test_bf_reverse() {
fn test_bf_resize() {
len := 80
mut input := bitfield.new(rand.intn(len) + 1)
for _ in 0..100 {
for _ in 0 .. 100 {
input.resize(rand.intn(len) + 1)
input.set_bit(input.get_size() - 1)
}
@ -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
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 {
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 {
@ -308,10 +297,10 @@ fn test_bf_rotate() {
assert result == 1
}
fn test_bf_printing(){
fn test_bf_printing() {
len := 80
mut input := bitfield.new(len)
for i in 0..len {
for i in 0 .. len {
if rand.intn(2) == 0 {
input.set_bit(i)
}