bit_array.odin 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. package dynamic_bit_array
  2. import "core:intrinsics"
  3. /*
  4. Note that these constants are dependent on the backing being a u64.
  5. */
  6. @(private="file")
  7. INDEX_SHIFT :: 6
  8. @(private="file")
  9. INDEX_MASK :: 63
  10. @(private="file")
  11. NUM_BITS :: 64
  12. Bit_Array :: struct {
  13. bits: [dynamic]u64,
  14. bias: int,
  15. }
  16. Bit_Array_Iterator :: struct {
  17. array: ^Bit_Array,
  18. current_word: uint,
  19. current_bit: uint,
  20. }
  21. /*
  22. In:
  23. - it: ^Bit_Array_Iterator - the iterator struct that holds the state.
  24. Out:
  25. - index: int - the next set bit of the Bit_Array referenced by `it`.
  26. - ok: bool - `true` if the iterator returned a valid index,
  27. `false` if there were no more bits set
  28. */
  29. next :: proc (it: ^Bit_Array_Iterator) -> (index: int, ok: bool) {
  30. words := it.array.bits
  31. // if the word is empty or we have already gone over all the bits in it,
  32. // b.current_bit is greater than the index of any set bit in the word,
  33. // meaning that word >> b.current_bit == 0.
  34. for it.current_word < len(words) && (words[it.current_word] >> it.current_bit == 0) {
  35. it.current_word += 1
  36. it.current_bit = 0
  37. }
  38. if it.current_word >= len(words) { return 0, false }
  39. // since we exited the loop and didn't return, this word has some bits higher than
  40. // or equal to `it.current_bit` set.
  41. offset := intrinsics.count_trailing_zeros(words[it.current_word] >> it.current_bit)
  42. // skip over the bit, if the resulting it.current_bit is over 63,
  43. // it is handled by the initial for loop in the next iteration.
  44. it.current_bit += uint(offset)
  45. defer it.current_bit += 1
  46. return int(it.current_word * NUM_BITS + it.current_bit) + it.array.bias, true
  47. }
  48. /*
  49. In:
  50. - ba: ^Bit_Array - a pointer to the Bit Array
  51. - index: The bit index. Can be an enum member.
  52. Out:
  53. - res: The bit you're interested in.
  54. - ok: Whether the index was valid. Returns `false` if the index is smaller than the bias.
  55. The `ok` return value may be ignored.
  56. */
  57. get :: proc(ba: ^Bit_Array, #any_int index: uint, allocator := context.allocator) -> (res: bool, ok: bool) {
  58. idx := int(index) - ba.bias
  59. if ba == nil || int(index) < ba.bias { return false, false }
  60. context.allocator = allocator
  61. leg_index := idx >> INDEX_SHIFT
  62. bit_index := idx & INDEX_MASK
  63. /*
  64. If we `get` a bit that doesn't fit in the Bit Array, it's naturally `false`.
  65. This early-out prevents unnecessary resizing.
  66. */
  67. if leg_index + 1 > len(ba.bits) { return false, true }
  68. val := u64(1 << uint(bit_index))
  69. res = ba.bits[leg_index] & val == val
  70. return res, true
  71. }
  72. /*
  73. In:
  74. - ba: ^Bit_Array - a pointer to the Bit Array
  75. - index: The bit index. Can be an enum member.
  76. Out:
  77. - ok: Whether or not we managed to set requested bit.
  78. `set` automatically resizes the Bit Array to accommodate the requested index if needed.
  79. */
  80. set :: proc(ba: ^Bit_Array, #any_int index: uint, allocator := context.allocator) -> (ok: bool) {
  81. idx := int(index) - ba.bias
  82. if ba == nil || int(index) < ba.bias { return false }
  83. context.allocator = allocator
  84. leg_index := idx >> INDEX_SHIFT
  85. bit_index := idx & INDEX_MASK
  86. resize_if_needed(ba, leg_index) or_return
  87. ba.bits[leg_index] |= 1 << uint(bit_index)
  88. return true
  89. }
  90. /*
  91. A helper function to create a Bit Array with optional bias, in case your smallest index is non-zero (including negative).
  92. */
  93. create :: proc(max_index: int, min_index := 0, allocator := context.allocator) -> (res: Bit_Array, ok: bool) #optional_ok {
  94. context.allocator = allocator
  95. size_in_bits := max_index - min_index
  96. if size_in_bits < 1 { return {}, false }
  97. legs := size_in_bits >> INDEX_SHIFT
  98. res = Bit_Array{
  99. bias = min_index,
  100. }
  101. return res, resize_if_needed(&res, legs)
  102. }
  103. /*
  104. Sets all bits to `false`.
  105. */
  106. clear :: proc(ba: ^Bit_Array) {
  107. if ba == nil { return }
  108. ba.bits = {}
  109. }
  110. /*
  111. Releases the memory used by the Bit Array.
  112. */
  113. destroy :: proc(ba: ^Bit_Array) {
  114. if ba == nil { return }
  115. delete(ba.bits)
  116. }
  117. /*
  118. Resizes the Bit Array. For internal use.
  119. If you want to reserve the memory for a given-sized Bit Array up front, you can use `create`.
  120. */
  121. @(private="file")
  122. resize_if_needed :: proc(ba: ^Bit_Array, legs: int, allocator := context.allocator) -> (ok: bool) {
  123. if ba == nil { return false }
  124. context.allocator = allocator
  125. if legs + 1 > len(ba.bits) {
  126. resize(&ba.bits, legs + 1)
  127. }
  128. return len(ba.bits) > legs
  129. }