mirror of
https://github.com/odin-lang/Odin.git
synced 2026-10-08 13:51:37 -04:00
Improve bit_array (#7665)
Fix `index: uint` -> `index: int`, examples. Add `create_from_enum` helper.
This commit is contained in:
1 parent
d7da8c0833
commit
da7371404e
2 files changed
+105
-70
No files matched your search
@@ -3,9 +3,7 @@ package container_dynamic_bit_array
|
||||
import "base:builtin"
|
||||
import "base:intrinsics"
|
||||
|
||||
/*
|
||||
Note that these constants are dependent on the backing being a u64.
|
||||
*/
|
||||
// Note that these constants are dependent on the backing being a u64.
|
||||
@(private="file")
|
||||
INDEX_SHIFT :: 6
|
||||
|
||||
@@ -27,27 +25,29 @@ Bit_Array_Iterator :: struct {
|
||||
word_idx: int,
|
||||
bit_idx: uint,
|
||||
}
|
||||
|
||||
/*
|
||||
Wraps a `Bit_Array` into an Iterator
|
||||
Wraps a `Bit_Array` into an `Bit_Array_Iterator`.
|
||||
|
||||
Inputs:
|
||||
- ba: Pointer to the Bit_Array
|
||||
- ba: Pointer to the `Bit_Array`
|
||||
|
||||
Returns:
|
||||
- it: Iterator struct
|
||||
- it: `Bit_Array_Iterator`
|
||||
*/
|
||||
make_iterator :: proc (ba: ^Bit_Array) -> (it: Bit_Array_Iterator) {
|
||||
return Bit_Array_Iterator { array = ba }
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the next bit, including its set-state. ok=false once exhausted
|
||||
Returns the next bit, including its set-state. ok=false once exhausted.
|
||||
|
||||
Inputs:
|
||||
- it: The iterator that holds the state.
|
||||
|
||||
Returns:
|
||||
- set: `true` if the bit at `index` is set.
|
||||
- index: The next bit of the Bit_Array referenced by `it`.
|
||||
- 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) {
|
||||
@@ -65,34 +65,37 @@ iterate_by_all :: proc (it: ^Bit_Array_Iterator) -> (set: bool, index: int, ok:
|
||||
|
||||
return set, index, true
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the next Set Bit, for example if `0b1010`, then the iterator will return index={1, 3} over two calls.
|
||||
Returns the next *set* bit, for example if `0b1010`, then the iterator will return index={1, 3} over two calls.
|
||||
|
||||
Inputs:
|
||||
- it: The iterator that holds the state.
|
||||
|
||||
Returns:
|
||||
- index: The next *set* bit of the Bit_Array referenced by `it`.
|
||||
- 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) {
|
||||
return iterate_internal_(it, true)
|
||||
return iterate_internal(it, true)
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the next Unset Bit, for example if `0b1010`, then the iterator will return index={0, 2} over two calls.
|
||||
Returns the next *unset* bit, for example if `0b1010`, then the iterator will return index={0, 2} over two calls.
|
||||
|
||||
Inputs:
|
||||
- it: The iterator that holds the state.
|
||||
|
||||
Returns:
|
||||
- index: The next *unset* bit of the Bit_Array referenced by `it`.
|
||||
- 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) {
|
||||
return iterate_internal_(it, false)
|
||||
return iterate_internal(it, false)
|
||||
}
|
||||
|
||||
/*
|
||||
Iterates through set/unset bits
|
||||
Iterates through set/unset bits.
|
||||
|
||||
*Private*
|
||||
|
||||
@@ -101,11 +104,11 @@ Inputs:
|
||||
- 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`.
|
||||
- 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")
|
||||
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 builtin.len(it.array.bits) > it.word_idx else 0
|
||||
when ! ITERATE_SET_BITS { word = ~word }
|
||||
|
||||
@@ -137,19 +140,20 @@ iterate_internal_ :: proc (it: ^Bit_Array_Iterator, $ITERATE_SET_BITS: bool) ->
|
||||
}
|
||||
return index, index < it.array.length + it.array.bias
|
||||
}
|
||||
|
||||
/*
|
||||
Gets the state of a bit in the bit-array
|
||||
Gets the state of a bit in the `Bit_Array`.
|
||||
|
||||
Inputs:
|
||||
- ba: Pointer to the Bit_Array
|
||||
- ba: Pointer to the `Bit_Array`
|
||||
- index: Which bit in the array
|
||||
|
||||
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) -> (res: bool, ok: bool) #optional_ok {
|
||||
idx := int(index) - ba.bias
|
||||
get :: proc(ba: ^Bit_Array, #any_int index: int) -> (res: bool, ok: bool) #optional_ok {
|
||||
idx := index - ba.bias
|
||||
|
||||
if ba == nil || int(index) < ba.bias { return false, false }
|
||||
|
||||
@@ -167,28 +171,30 @@ get :: proc(ba: ^Bit_Array, #any_int index: uint) -> (res: bool, ok: bool) #opti
|
||||
|
||||
return res, true
|
||||
}
|
||||
|
||||
/*
|
||||
Gets the state of a bit in the bit-array
|
||||
Gets the state of a bit in the `Bit_Array`.
|
||||
|
||||
*Bypasses all Checks*
|
||||
|
||||
Inputs:
|
||||
- ba: Pointer to the Bit_Array
|
||||
- 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)
|
||||
unsafe_get :: #force_inline proc(ba: ^Bit_Array, #any_int index: int) -> 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
|
||||
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
|
||||
- ba: Pointer to the `Bit_Array`
|
||||
- index: Which bit in the array
|
||||
- set_to: `true` sets the bit on, `false` to turn it off
|
||||
- allocator: (default is context.allocator)
|
||||
@@ -196,9 +202,9 @@ Inputs:
|
||||
Returns:
|
||||
- ok: Whether the set was successful, `false` on allocation failure or bad index
|
||||
*/
|
||||
set :: proc(ba: ^Bit_Array, #any_int index: uint, set_to: bool = true, allocator := context.allocator) -> (ok: bool) {
|
||||
set :: proc(ba: ^Bit_Array, #any_int index: int, set_to: bool = true, allocator := context.allocator) -> (ok: bool) {
|
||||
|
||||
idx := int(index) - ba.bias
|
||||
idx := index - ba.bias
|
||||
|
||||
if ba == nil || int(index) < ba.bias { return false }
|
||||
context.allocator = allocator
|
||||
@@ -218,41 +224,44 @@ set :: proc(ba: ^Bit_Array, #any_int index: uint, set_to: bool = true, allocator
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
Sets the state of a bit in the bit-array
|
||||
Sets the state of a bit in the `Bit_Array`.
|
||||
|
||||
*Bypasses all checks*
|
||||
|
||||
Inputs:
|
||||
- ba: Pointer to the Bit_Array
|
||||
- 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. (Convienence wrapper for `set`)
|
||||
Unsets the state of a bit in the `Bit_Array`. (Convienence wrapper for `set`)
|
||||
|
||||
*Conditionally Allocates (Resizes backing data when `index > len(ba.bits)`)*
|
||||
|
||||
Inputs:
|
||||
- ba: Pointer to the Bit_Array
|
||||
- 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 :: #force_inline proc(ba: ^Bit_Array, #any_int index: uint, allocator := context.allocator) -> (ok: bool) {
|
||||
unset :: #force_inline proc(ba: ^Bit_Array, #any_int index: int, allocator := context.allocator) -> (ok: bool) {
|
||||
return set(ba, index, false, allocator)
|
||||
}
|
||||
/*
|
||||
Unsets the state of a bit in the bit-array
|
||||
|
||||
*Bypasses all Checks*
|
||||
/*
|
||||
Unsets the state of a bit in the `Bit_Array`.
|
||||
|
||||
*Bypasses all checks*
|
||||
|
||||
Inputs:
|
||||
- ba: Pointer to the Bit_Array
|
||||
- ba: Pointer to the `Bit_Array`
|
||||
- index: Which bit in the array
|
||||
*/
|
||||
unsafe_unset :: proc(b: ^Bit_Array, bit: int) #no_bounds_check {
|
||||
@@ -260,7 +269,7 @@ unsafe_unset :: proc(b: ^Bit_Array, bit: int) #no_bounds_check {
|
||||
}
|
||||
|
||||
/*
|
||||
A helper function to create a Bit Array with optional bias, in case your smallest index is non-zero (including negative).
|
||||
A helper function to create a `Bit_Array` with optional bias, in case your smallest index is non-zero (including negative).
|
||||
|
||||
The range of bits created by this procedure is `min_index..<max_index`, and the
|
||||
array will be able to expand beyond `max_index` if needed.
|
||||
@@ -273,10 +282,11 @@ Inputs:
|
||||
- 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).
|
||||
- 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 {
|
||||
create :: proc(max_index: int, min_index: int = 0, allocator := context.allocator, loc := #caller_location) -> (res: ^Bit_Array, ok: bool) #optional_ok {
|
||||
size_in_bits := max_index - min_index
|
||||
assert(max_index >= min_index, loc=loc)
|
||||
|
||||
if size_in_bits < 0 { return {}, false }
|
||||
|
||||
@@ -290,7 +300,27 @@ create :: proc(max_index: int, min_index: int = 0, allocator := context.allocato
|
||||
}
|
||||
|
||||
/*
|
||||
A helper function to initialize a Bit Array with optional bias, in case your smallest index is non-zero (including negative).
|
||||
A helper function to create a `Bit_Array` from an enum `E`.
|
||||
|
||||
The range of bits created by this procedure is `min(E)..<max(E)`, and the
|
||||
array will be able to expand beyond `max(E)` if needed.
|
||||
|
||||
*Allocates (`new(Bit_Array) & make(ba.bits)`)*
|
||||
|
||||
Inputs:
|
||||
- e: an `enum`
|
||||
- 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_from_enum :: proc($T: typeid, allocator := context.allocator) -> (res: ^Bit_Array, ok: bool) where intrinsics.type_is_enum(T) #optional_ok {
|
||||
return create(int(max(T)), int(min(T)), allocator)
|
||||
}
|
||||
|
||||
/*
|
||||
A helper function to initialize a `Bit_Array` with optional bias, in case your smallest index is non-zero (including negative).
|
||||
|
||||
The range of bits created by this procedure is `min_index..<max_index`, and the
|
||||
array will be able to expand beyond `max_index` if needed.
|
||||
@@ -321,20 +351,21 @@ init :: proc(res: ^Bit_Array, max_index: int, min_index: int = 0, allocator := c
|
||||
}
|
||||
|
||||
/*
|
||||
Sets all values in the Bit_Array to zero.
|
||||
Sets all values in the `Bit_Array` to zero.
|
||||
|
||||
Inputs:
|
||||
- ba: The target Bit_Array
|
||||
- ba: The target `Bit_Array`
|
||||
*/
|
||||
clear :: proc(ba: ^Bit_Array) {
|
||||
if ba == nil { return }
|
||||
intrinsics.mem_zero(raw_data(ba.bits), builtin.len(ba.bits) * NUM_BITS / 8)
|
||||
}
|
||||
|
||||
/*
|
||||
Gets the length of set and unset valid bits in the Bit_Array.
|
||||
Gets the length of set and unset valid bits in the `Bit_Array`.
|
||||
|
||||
Inputs:
|
||||
- ba: The target Bit_Array
|
||||
- ba: The target `Bit_Array`
|
||||
|
||||
Returns:
|
||||
- length: The length of valid bits.
|
||||
@@ -343,11 +374,12 @@ len :: proc(ba: ^Bit_Array) -> (length: int) {
|
||||
if ba == nil { return }
|
||||
return ba.length
|
||||
}
|
||||
|
||||
/*
|
||||
Shrinks the Bit_Array's backing storage to the smallest possible size.
|
||||
Shrinks the `Bit_Array`'s backing storage to the smallest possible size.
|
||||
|
||||
Inputs:
|
||||
- ba: The target Bit_Array
|
||||
- ba: The target `Bit_Array`
|
||||
*/
|
||||
shrink :: proc(ba: ^Bit_Array) #no_bounds_check {
|
||||
if ba == nil { return }
|
||||
@@ -372,22 +404,24 @@ shrink :: proc(ba: ^Bit_Array) #no_bounds_check {
|
||||
resize(&ba.bits, legs_needed)
|
||||
builtin.shrink(&ba.bits)
|
||||
}
|
||||
|
||||
/*
|
||||
Deallocates the Bit_Array and its backing storage
|
||||
Deallocates the `Bit_Array` and its backing storage
|
||||
|
||||
Inputs:
|
||||
- ba: The target Bit_Array
|
||||
- ba: The target `Bit_Array`
|
||||
*/
|
||||
destroy :: proc(ba: ^Bit_Array) {
|
||||
if ba == nil { return }
|
||||
delete(ba.bits)
|
||||
if ba.free_pointer { // Only free if this Bit_Array was created using `create`, not when on the stack.
|
||||
if ba.free_pointer { // Only free if this `Bit_Array` was created using `create`, not when on the stack.
|
||||
free(ba)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Resizes the Bit Array. For internal use. Provisions needed capacity+1
|
||||
If you want to reserve the memory for a given-sized Bit Array up front, you can use `create`.
|
||||
Resizes the `Bit_Array`. For internal use. Provisions needed capacity+1
|
||||
If you want to reserve the memory for a given-sized `Bit_Array` up front, you can use `create`.
|
||||
*/
|
||||
@(private="file")
|
||||
resize_if_needed :: proc(ba: ^Bit_Array, legs: int, allocator := context.allocator) -> (ok: bool) {
|
||||
|
||||
@@ -8,20 +8,18 @@ Example:
|
||||
package test
|
||||
|
||||
import "core:fmt"
|
||||
import "core:container/bit_array"
|
||||
import ba "core:container/bit_array"
|
||||
|
||||
main :: proc() {
|
||||
using bit_array
|
||||
|
||||
bits: Bit_Array
|
||||
bits: ba.Bit_Array
|
||||
|
||||
// returns `true`
|
||||
fmt.println(set(&bits, 42))
|
||||
fmt.println(ba.set(&bits, 42))
|
||||
|
||||
// returns `false`, `false`, because this Bit Array wasn't created to allow negative indices.
|
||||
was_set, was_retrieved := get(&bits, -1)
|
||||
was_set, was_retrieved := ba.get(&bits, -1)
|
||||
fmt.println(was_set, was_retrieved)
|
||||
destroy(&bits)
|
||||
ba.destroy(&bits)
|
||||
}
|
||||
|
||||
A `Bit_Array` can optionally allow for negative indices, if the minimum value was given during creation.
|
||||
@@ -29,7 +27,7 @@ Example:
|
||||
package test
|
||||
|
||||
import "core:fmt"
|
||||
import "core:container/bit_array"
|
||||
import ba "core:container/bit_array"
|
||||
|
||||
main :: proc() {
|
||||
Foo :: enum int {
|
||||
@@ -38,17 +36,20 @@ Example:
|
||||
Leaves = 69105,
|
||||
}
|
||||
|
||||
using bit_array
|
||||
bits := ba.create_from_enum(Foo)
|
||||
defer ba.destroy(bits)
|
||||
|
||||
bits := create(int(max(Foo)), int(min(Foo)))
|
||||
defer destroy(bits)
|
||||
assert(bits.bias == int(Foo.Negative_Test))
|
||||
assert(bits.length == abs(int(min(Foo))) + int(max(Foo)))
|
||||
|
||||
fmt.printf("Set(Bar): %v\n", set(bits, Foo.Bar))
|
||||
fmt.printf("Get(Bar): %v, %v\n", get(bits, Foo.Bar))
|
||||
fmt.printf("Set(Negative_Test): %v\n", set(bits, Foo.Negative_Test))
|
||||
fmt.printf("Get(Leaves): %v, %v\n", get(bits, Foo.Leaves))
|
||||
fmt.printf("Get(Negative_Test): %v, %v\n", get(bits, Foo.Negative_Test))
|
||||
fmt.printf("Freed.\n")
|
||||
fmt.printfln("Set(Bar): %v", ba.set(bits, Foo.Bar))
|
||||
fmt.printfln("Get(Bar): %v", ba.get(bits, Foo.Bar))
|
||||
fmt.printfln("Set(Negative_Test): %v", ba.set(bits, Foo.Negative_Test))
|
||||
fmt.printfln("Get(Leaves): %v", ba.get(bits, Foo.Leaves))
|
||||
fmt.printfln("Get(Leaves): %v", ba.unsafe_get(bits, Foo.Leaves))
|
||||
fmt.printfln("Get(Negative_Test): %v", ba.get(bits, Foo.Negative_Test))
|
||||
fmt.printfln("Unset(Negative_Test): %v", ba.unset(bits, Foo.Negative_Test))
|
||||
assert(ba.get(bits, Foo.Negative_Test) == false)
|
||||
}
|
||||
*/
|
||||
package container_dynamic_bit_array
|
||||
Reference in new issue
Block a user