update docs, add unsafe_get/set, add round up to create

This commit is contained in:
Jon Lipstate
2023-04-30 16:56:05 -07:00
parent f0ba5d3821
commit 075193af1d
+129 -63
View File
@@ -27,27 +27,28 @@ Bit_Array_Iterator :: struct {
word_idx: int, word_idx: int,
bit_idx: uint, bit_idx: uint,
} }
/* /*
In: Wraps a `Bit_Array` into an Iterator
- ba: ^Bit_Array - the array to iterate over
Out: Inputs:
- it: ^Bit_Array_Iterator - the iterator that holds iteration state - ba: Pointer to the Bit_Array
Returns:
- it: Iterator struct
*/ */
make_iterator :: proc (ba: ^Bit_Array) -> (it: Bit_Array_Iterator) { make_iterator :: proc (ba: ^Bit_Array) -> (it: Bit_Array_Iterator) {
return Bit_Array_Iterator { array = ba } return Bit_Array_Iterator { array = ba }
} }
/* /*
In: Returns the next bit,including its set-state. ok=false once exhausted
- it: ^Bit_Array_Iterator - the iterator struct that holds the state.
Out: Inputs:
- set: bool - the state of the bit at `index` - it: The iterator that holds the state.
- index: int - the next bit of the Bit_Array referenced by `it`.
- ok: bool - `true` if the iterator returned a valid index, Returns:
`false` if there were no more bits - set: `true` if the bit at `index` is set.
- index: The next bit of the Bit_Array referenced by `it`.
- ok: `true` if the iterator can continue, `false` if the iterator is done
*/ */
iterate_by_all :: proc (it: ^Bit_Array_Iterator) -> (set: bool, index: int, ok: bool) { iterate_by_all :: proc (it: ^Bit_Array_Iterator) -> (set: bool, index: int, ok: bool) {
index = it.word_idx * NUM_BITS + int(it.bit_idx) + it.array.bias index = it.word_idx * NUM_BITS + int(it.bit_idx) + it.array.bias
@@ -64,39 +65,51 @@ iterate_by_all :: proc (it: ^Bit_Array_Iterator) -> (set: bool, index: int, ok:
return set, index, true return set, index, true
} }
/* /*
In: Returns the next Set Bit, for example if `0b1010`, then the iterator will return index={1,3} over two calls.
- it: ^Bit_Array_Iterator - the iterator struct that holds the state.
Out: Inputs:
- index: int - the next set bit of the Bit_Array referenced by `it`. - it: The iterator that holds the state.
- ok: bool - `true` if the iterator returned a valid index,
`false` if there were no more bits set Returns:
- index: The next *set* bit of the Bit_Array referenced by `it`.
- ok: `true` if the iterator can continue, `false` if the iterator is done
*/ */
iterate_by_set :: proc (it: ^Bit_Array_Iterator) -> (index: int, ok: bool) { iterate_by_set :: proc (it: ^Bit_Array_Iterator) -> (index: int, ok: bool) {
return iterate_internal_(it, true) return iterate_internal_(it, true)
} }
/* /*
In: Returns the next Unset Bit, for example if `0b1010`, then the iterator will return index={0,2} over two calls.
- it: ^Bit_Array_Iterator - the iterator struct that holds the state.
Out: Inputs:
- index: int - the next unset bit of the Bit_Array referenced by `it`. - it: The iterator that holds the state.
- ok: bool - `true` if the iterator returned a valid index,
`false` if there were no more unset bits Returns:
- index: The next *unset* bit of the Bit_Array referenced by `it`.
- ok: `true` if the iterator can continue, `false` if the iterator is done
*/ */
iterate_by_unset:: proc (it: ^Bit_Array_Iterator) -> (index: int, ok: bool) { iterate_by_unset:: proc (it: ^Bit_Array_Iterator) -> (index: int, ok: bool) {
return iterate_internal_(it, false) return iterate_internal_(it, false)
} }
/*
Iterates through set/unset bits
*Private*
Inputs:
- it: The iterator that holds the state.
- ITERATE_SET_BITS: `true` for returning only set bits, false for returning only unset bits
Returns:
- index: The next *unset* bit of the Bit_Array referenced by `it`.
- ok: `true` if the iterator can continue, `false` if the iterator is done
*/
@(private="file") @(private="file")
iterate_internal_ :: proc (it: ^Bit_Array_Iterator, $ITERATE_SET_BITS: bool) -> (index: int, ok: bool) { iterate_internal_ :: proc (it: ^Bit_Array_Iterator, $ITERATE_SET_BITS: bool) -> (index: int, ok: bool) {
word := it.array.bits[it.word_idx] if len(it.array.bits) > it.word_idx else 0 word := it.array.bits[it.word_idx] if len(it.array.bits) > it.word_idx else 0
when ! ITERATE_SET_BITS { word = ~word } when ! ITERATE_SET_BITS { word = ~word }
// if the word is empty or we have already gone over all the bits in it, // If the word is empty or we have already gone over all the bits in it,
// b.bit_idx is greater than the index of any set bit in the word, // b.bit_idx is greater than the index of any set bit in the word,
// meaning that word >> b.bit_idx == 0. // meaning that word >> b.bit_idx == 0.
for it.word_idx < len(it.array.bits) && word >> it.bit_idx == 0 { for it.word_idx < len(it.array.bits) && word >> it.bit_idx == 0 {
@@ -106,14 +119,14 @@ iterate_internal_ :: proc (it: ^Bit_Array_Iterator, $ITERATE_SET_BITS: bool) ->
when ! ITERATE_SET_BITS { word = ~word } when ! ITERATE_SET_BITS { word = ~word }
} }
// if we are iterating the set bits, reaching the end of the array means we have no more bits to check // If we are iterating the set bits, reaching the end of the array means we have no more bits to check
when ITERATE_SET_BITS { when ITERATE_SET_BITS {
if it.word_idx >= len(it.array.bits) { if it.word_idx >= len(it.array.bits) {
return 0, false return 0, false
} }
} }
// reaching here means that the word has some set bits // Reaching here means that the word has some set bits
it.bit_idx += uint(intrinsics.count_trailing_zeros(word >> it.bit_idx)) it.bit_idx += uint(intrinsics.count_trailing_zeros(word >> it.bit_idx))
index = it.word_idx * NUM_BITS + int(it.bit_idx) + it.array.bias index = it.word_idx * NUM_BITS + int(it.bit_idx) + it.array.bias
@@ -124,24 +137,21 @@ iterate_internal_ :: proc (it: ^Bit_Array_Iterator, $ITERATE_SET_BITS: bool) ->
} }
return index, index <= it.array.max_index return index, index <= it.array.max_index
} }
/* /*
In: Gets the state of a bit in the bit-array
- ba: ^Bit_Array - a pointer to the Bit Array
- index: The bit index. Can be an enum member.
Out: Inputs:
- res: The bit you're interested in. - ba: Pointer to the Bit_Array
- ok: Whether the index was valid. Returns `false` if the index is smaller than the bias. - index: Which bit in the array
The `ok` return value may be ignored. Returns:
- res: `true` if the bit at `index` is set.
- ok: Whether the index was valid. Returns `false` if the index is smaller than the bias.
*/ */
get :: proc(ba: ^Bit_Array, #any_int index: uint, allocator := context.allocator) -> (res: bool, ok: bool) { get :: proc(ba: ^Bit_Array, #any_int index: uint) -> (res: bool, ok: bool) #optional_ok {
idx := int(index) - ba.bias idx := int(index) - ba.bias
if ba == nil || int(index) < ba.bias { return false, false } if ba == nil || int(index) < ba.bias { return false, false }
context.allocator = allocator
leg_index := idx >> INDEX_SHIFT leg_index := idx >> INDEX_SHIFT
bit_index := idx & INDEX_MASK bit_index := idx & INDEX_MASK
@@ -157,16 +167,33 @@ get :: proc(ba: ^Bit_Array, #any_int index: uint, allocator := context.allocator
return res, true return res, true
} }
/* /*
In: Gets the state of a bit in the bit-array
- ba: ^Bit_Array - a pointer to the Bit Array
- index: The bit index. Can be an enum member.
Out: *Bypasses all Checks*
- ok: Whether or not we managed to set requested bit.
`set` automatically resizes the Bit Array to accommodate the requested index if needed. Inputs:
- ba: Pointer to the Bit_Array
- index: Which bit in the array
Returns:
- `true` if bit is set
*/
unsafe_get :: #force_inline proc(ba: ^Bit_Array, #any_int index: uint) -> bool #no_bounds_check {
return bool((ba.bits[index >> INDEX_SHIFT] >> uint(index & INDEX_MASK)) & 1)
}
/*
Sets the state of a bit in the bit-array
*Conditionally Allocates (Resizes backing data when `index > len(ba.bits)`)*
Inputs:
- ba: Pointer to the Bit_Array
- index: Which bit in the array
- allocator: (default is context.allocator)
Returns:
- ok: Whether the set was successful, `false` on allocation failure or bad index
*/ */
set :: proc(ba: ^Bit_Array, #any_int index: uint, allocator := context.allocator) -> (ok: bool) { set :: proc(ba: ^Bit_Array, #any_int index: uint, allocator := context.allocator) -> (ok: bool) {
@@ -184,16 +211,30 @@ set :: proc(ba: ^Bit_Array, #any_int index: uint, allocator := context.allocator
ba.bits[leg_index] |= 1 << uint(bit_index) ba.bits[leg_index] |= 1 << uint(bit_index)
return true return true
} }
/* /*
In: Sets the state of a bit in the bit-array
- ba: ^Bit_Array - a pointer to the Bit Array
- index: The bit index. Can be an enum member.
Out: *Bypasses all checks*
- ok: Whether or not we managed to unset requested bit.
`unset` automatically resizes the Bit Array to accommodate the requested index if needed. Inputs:
- ba: Pointer to the Bit_Array
- index: Which bit in the array
*/
unsafe_set :: proc(ba: ^Bit_Array, bit: int) #no_bounds_check {
ba.bits[bit >> INDEX_SHIFT] |= 1 << uint(bit & INDEX_MASK)
}
/*
Unsets the state of a bit in the bit-array
*Conditionally Allocates (Resizes backing data when `index > len(ba.bits)`)*
Inputs:
- ba: Pointer to the Bit_Array
- index: Which bit in the array
- allocator: (default is context.allocator)
Returns:
- ok: Whether the unset was successful, `false` on allocation failure or bad index
*/ */
unset :: proc(ba: ^Bit_Array, #any_int index: uint, allocator := context.allocator) -> (ok: bool) { unset :: proc(ba: ^Bit_Array, #any_int index: uint, allocator := context.allocator) -> (ok: bool) {
@@ -211,17 +252,39 @@ unset :: proc(ba: ^Bit_Array, #any_int index: uint, allocator := context.allocat
ba.bits[leg_index] &= ~(1 << uint(bit_index)) ba.bits[leg_index] &= ~(1 << uint(bit_index))
return true return true
} }
/* /*
A helper function to create a Bit Array with optional bias, in case your smallest index is non-zero (including negative). Unsets the state of a bit in the bit-array
*Bypasses all Checks*
Inputs:
- ba: Pointer to the Bit_Array
- index: Which bit in the array
*/ */
create :: proc(max_index: int, min_index := 0, allocator := context.allocator) -> (res: ^Bit_Array, ok: bool) #optional_ok { unsafe_unset :: proc(b: ^Bit_Array, bit: int) #no_bounds_check {
b.bits[bit >> INDEX_SHIFT] &= ~(1 << uint(bit & INDEX_MASK))
}
/*
A helper function to create a Bit Array with optional bias, in case your smallest index is non-zero (including negative).
*Allocates (`new(Bit_Array) & make(ba.bits)`)*
Inputs:
- max_index: maximum starting index
- min_index: minimum starting index (used as a bias)
- allocator: (default is context.allocator)
Returns:
- ba: Allocates a bit_Array, backing data is set to `max-min / 64` indices, rounded up (eg 65 - 0 allocates for [2]u64).
*/
create :: proc(max_index: int, min_index: int = 0, allocator := context.allocator) -> (res: ^Bit_Array, ok: bool) #optional_ok {
context.allocator = allocator context.allocator = allocator
size_in_bits := max_index - min_index size_in_bits := max_index - min_index
if size_in_bits < 1 { return {}, false } if size_in_bits < 1 { return {}, false }
legs := size_in_bits >> INDEX_SHIFT legs := size_in_bits >> INDEX_SHIFT
if size_in_bits & INDEX_MASK > 0 {legs+=1}
res = new(Bit_Array) res = new(Bit_Array)
res.bias = min_index res.bias = min_index
@@ -229,17 +292,21 @@ create :: proc(max_index: int, min_index := 0, allocator := context.allocator) -
res.free_pointer = true res.free_pointer = true
return res, resize_if_needed(res, legs) return res, resize_if_needed(res, legs)
} }
/* /*
Sets all bits to `false`. Sets all values in the Bit_Array to zero.
Inputs:
- ba: The target Bit_Array
*/ */
clear :: proc(ba: ^Bit_Array) { clear :: proc(ba: ^Bit_Array) {
if ba == nil { return } if ba == nil { return }
mem.zero_slice(ba.bits[:]) mem.zero_slice(ba.bits[:])
} }
/* /*
Releases the memory used by the Bit Array. Deallocates the Bit_Array and its backing storage
Inputs:
- ba: The target Bit_Array
*/ */
destroy :: proc(ba: ^Bit_Array) { destroy :: proc(ba: ^Bit_Array) {
if ba == nil { return } if ba == nil { return }
@@ -248,7 +315,6 @@ destroy :: proc(ba: ^Bit_Array) {
free(ba) free(ba)
} }
} }
/* /*
Resizes the Bit Array. For internal use. Resizes the Bit Array. For internal use.
If you want to reserve the memory for a given-sized Bit Array up front, you can use `create`. If you want to reserve the memory for a given-sized Bit Array up front, you can use `create`.