bit_array.odin 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  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. max_index: int,
  16. }
  17. Bit_Array_Iterator :: struct {
  18. array: ^Bit_Array,
  19. word_idx: int,
  20. bit_idx: uint,
  21. }
  22. /*
  23. In:
  24. - ba: ^Bit_Array - the array to iterate over
  25. Out:
  26. - it: ^Bit_Array_Iterator - the iterator that holds iteration state
  27. */
  28. make_iterator :: proc (ba: ^Bit_Array) -> (it: Bit_Array_Iterator) {
  29. return Bit_Array_Iterator { array = ba }
  30. }
  31. /*
  32. In:
  33. - it: ^Bit_Array_Iterator - the iterator struct that holds the state.
  34. Out:
  35. - set: bool - the state of the bit at `index`
  36. - index: int - the next bit of the Bit_Array referenced by `it`.
  37. - ok: bool - `true` if the iterator returned a valid index,
  38. `false` if there were no more bits
  39. */
  40. iterate_by_all :: proc (it: ^Bit_Array_Iterator) -> (set: bool, index: int, ok: bool) {
  41. index = it.word_idx * NUM_BITS + int(it.bit_idx) + it.array.bias
  42. if index > it.array.max_index { return false, 0, false }
  43. word := it.array.bits[it.word_idx] if len(it.array.bits) > it.word_idx else 0
  44. set = (word >> it.bit_idx & 1) == 1
  45. it.bit_idx += 1
  46. if it.bit_idx >= NUM_BITS {
  47. it.bit_idx = 0
  48. it.word_idx += 1
  49. }
  50. return set, index, true
  51. }
  52. /*
  53. In:
  54. - it: ^Bit_Array_Iterator - the iterator struct that holds the state.
  55. Out:
  56. - index: int - the next set bit of the Bit_Array referenced by `it`.
  57. - ok: bool - `true` if the iterator returned a valid index,
  58. `false` if there were no more bits set
  59. */
  60. iterate_by_set :: proc (it: ^Bit_Array_Iterator) -> (index: int, ok: bool) {
  61. return iterate_internal_(it, true)
  62. }
  63. /*
  64. In:
  65. - it: ^Bit_Array_Iterator - the iterator struct that holds the state.
  66. Out:
  67. - index: int - the next unset bit of the Bit_Array referenced by `it`.
  68. - ok: bool - `true` if the iterator returned a valid index,
  69. `false` if there were no more unset bits
  70. */
  71. iterate_by_unset:: proc (it: ^Bit_Array_Iterator) -> (index: int, ok: bool) {
  72. return iterate_internal_(it, false)
  73. }
  74. @(private="file")
  75. iterate_internal_ :: proc (it: ^Bit_Array_Iterator, $ITERATE_SET_BITS: bool) -> (index: int, ok: bool) {
  76. word := it.array.bits[it.word_idx] if len(it.array.bits) > it.word_idx else 0
  77. when ! ITERATE_SET_BITS { word = ~word }
  78. // if the word is empty or we have already gone over all the bits in it,
  79. // b.bit_idx is greater than the index of any set bit in the word,
  80. // meaning that word >> b.bit_idx == 0.
  81. for it.word_idx < len(it.array.bits) && word >> it.bit_idx == 0 {
  82. it.word_idx += 1
  83. it.bit_idx = 0
  84. word = it.array.bits[it.word_idx] if len(it.array.bits) > it.word_idx else 0
  85. when ! ITERATE_SET_BITS { word = ~word }
  86. }
  87. // if we are iterating the set bits, reaching the end of the array means we have no more bits to check
  88. when ITERATE_SET_BITS {
  89. if it.word_idx >= len(it.array.bits) {
  90. return 0, false
  91. }
  92. }
  93. // reaching here means that the word has some set bits
  94. it.bit_idx += uint(intrinsics.count_trailing_zeros(word >> it.bit_idx))
  95. index = it.word_idx * NUM_BITS + int(it.bit_idx) + it.array.bias
  96. it.bit_idx += 1
  97. if it.bit_idx >= NUM_BITS {
  98. it.bit_idx = 0
  99. it.word_idx += 1
  100. }
  101. return index, index <= it.array.max_index
  102. }
  103. /*
  104. In:
  105. - ba: ^Bit_Array - a pointer to the Bit Array
  106. - index: The bit index. Can be an enum member.
  107. Out:
  108. - res: The bit you're interested in.
  109. - ok: Whether the index was valid. Returns `false` if the index is smaller than the bias.
  110. The `ok` return value may be ignored.
  111. */
  112. get :: proc(ba: ^Bit_Array, #any_int index: uint, allocator := context.allocator) -> (res: bool, ok: bool) {
  113. idx := int(index) - ba.bias
  114. if ba == nil || int(index) < ba.bias { return false, false }
  115. context.allocator = allocator
  116. leg_index := idx >> INDEX_SHIFT
  117. bit_index := idx & INDEX_MASK
  118. /*
  119. If we `get` a bit that doesn't fit in the Bit Array, it's naturally `false`.
  120. This early-out prevents unnecessary resizing.
  121. */
  122. if leg_index + 1 > len(ba.bits) { return false, true }
  123. val := u64(1 << uint(bit_index))
  124. res = ba.bits[leg_index] & val == val
  125. return res, true
  126. }
  127. /*
  128. In:
  129. - ba: ^Bit_Array - a pointer to the Bit Array
  130. - index: The bit index. Can be an enum member.
  131. Out:
  132. - ok: Whether or not we managed to set requested bit.
  133. `set` automatically resizes the Bit Array to accommodate the requested index if needed.
  134. */
  135. set :: proc(ba: ^Bit_Array, #any_int index: uint, allocator := context.allocator) -> (ok: bool) {
  136. idx := int(index) - ba.bias
  137. if ba == nil || int(index) < ba.bias { return false }
  138. context.allocator = allocator
  139. leg_index := idx >> INDEX_SHIFT
  140. bit_index := idx & INDEX_MASK
  141. resize_if_needed(ba, leg_index) or_return
  142. ba.max_index = max(idx, ba.max_index)
  143. ba.bits[leg_index] |= 1 << uint(bit_index)
  144. return true
  145. }
  146. /*
  147. A helper function to create a Bit Array with optional bias, in case your smallest index is non-zero (including negative).
  148. */
  149. create :: proc(max_index: int, min_index := 0, allocator := context.allocator) -> (res: Bit_Array, ok: bool) #optional_ok {
  150. context.allocator = allocator
  151. size_in_bits := max_index - min_index
  152. if size_in_bits < 1 { return {}, false }
  153. legs := size_in_bits >> INDEX_SHIFT
  154. res = Bit_Array{
  155. bias = min_index,
  156. max_index = max_index,
  157. }
  158. return res, resize_if_needed(&res, legs)
  159. }
  160. /*
  161. Sets all bits to `false`.
  162. */
  163. clear :: proc(ba: ^Bit_Array) {
  164. if ba == nil { return }
  165. ba.bits = {}
  166. }
  167. /*
  168. Releases the memory used by the Bit Array.
  169. */
  170. destroy :: proc(ba: ^Bit_Array) {
  171. if ba == nil { return }
  172. delete(ba.bits)
  173. }
  174. /*
  175. Resizes the Bit Array. For internal use.
  176. If you want to reserve the memory for a given-sized Bit Array up front, you can use `create`.
  177. */
  178. @(private="file")
  179. resize_if_needed :: proc(ba: ^Bit_Array, legs: int, allocator := context.allocator) -> (ok: bool) {
  180. if ba == nil { return false }
  181. context.allocator = allocator
  182. if legs + 1 > len(ba.bits) {
  183. resize(&ba.bits, legs + 1)
  184. }
  185. return len(ba.bits) > legs
  186. }