bit_array.odin 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. package dynamic_bit_array
  2. import "core:intrinsics"
  3. import "core:mem"
  4. /*
  5. Note that these constants are dependent on the backing being a u64.
  6. */
  7. @(private="file")
  8. INDEX_SHIFT :: 6
  9. @(private="file")
  10. INDEX_MASK :: 63
  11. @(private="file")
  12. NUM_BITS :: 64
  13. Bit_Array :: struct {
  14. bits: [dynamic]u64,
  15. bias: int,
  16. max_index: int,
  17. free_pointer: bool,
  18. }
  19. Bit_Array_Iterator :: struct {
  20. array: ^Bit_Array,
  21. word_idx: int,
  22. bit_idx: uint,
  23. }
  24. /*
  25. Wraps a `Bit_Array` into an Iterator
  26. Inputs:
  27. - ba: Pointer to the Bit_Array
  28. Returns:
  29. - it: Iterator struct
  30. */
  31. make_iterator :: proc (ba: ^Bit_Array) -> (it: Bit_Array_Iterator) {
  32. return Bit_Array_Iterator { array = ba }
  33. }
  34. /*
  35. Returns the next bit, including its set-state. ok=false once exhausted
  36. Inputs:
  37. - it: The iterator that holds the state.
  38. Returns:
  39. - set: `true` if the bit at `index` is set.
  40. - index: The next bit of the Bit_Array referenced by `it`.
  41. - ok: `true` if the iterator can continue, `false` if the iterator is done
  42. */
  43. iterate_by_all :: proc (it: ^Bit_Array_Iterator) -> (set: bool, index: int, ok: bool) {
  44. index = it.word_idx * NUM_BITS + int(it.bit_idx) + it.array.bias
  45. if index > it.array.max_index { return false, 0, false }
  46. word := it.array.bits[it.word_idx] if len(it.array.bits) > it.word_idx else 0
  47. set = (word >> it.bit_idx & 1) == 1
  48. it.bit_idx += 1
  49. if it.bit_idx >= NUM_BITS {
  50. it.bit_idx = 0
  51. it.word_idx += 1
  52. }
  53. return set, index, true
  54. }
  55. /*
  56. Returns the next Set Bit, for example if `0b1010`, then the iterator will return index={1, 3} over two calls.
  57. Inputs:
  58. - it: The iterator that holds the state.
  59. Returns:
  60. - index: The next *set* bit of the Bit_Array referenced by `it`.
  61. - ok: `true` if the iterator can continue, `false` if the iterator is done
  62. */
  63. iterate_by_set :: proc (it: ^Bit_Array_Iterator) -> (index: int, ok: bool) {
  64. return iterate_internal_(it, true)
  65. }
  66. /*
  67. Returns the next Unset Bit, for example if `0b1010`, then the iterator will return index={0, 2} over two calls.
  68. Inputs:
  69. - it: The iterator that holds the state.
  70. Returns:
  71. - index: The next *unset* bit of the Bit_Array referenced by `it`.
  72. - ok: `true` if the iterator can continue, `false` if the iterator is done
  73. */
  74. iterate_by_unset:: proc (it: ^Bit_Array_Iterator) -> (index: int, ok: bool) {
  75. return iterate_internal_(it, false)
  76. }
  77. /*
  78. Iterates through set/unset bits
  79. *Private*
  80. Inputs:
  81. - it: The iterator that holds the state.
  82. - ITERATE_SET_BITS: `true` for returning only set bits, false for returning only unset bits
  83. Returns:
  84. - index: The next *unset* bit of the Bit_Array referenced by `it`.
  85. - ok: `true` if the iterator can continue, `false` if the iterator is done
  86. */
  87. @(private="file")
  88. iterate_internal_ :: proc (it: ^Bit_Array_Iterator, $ITERATE_SET_BITS: bool) -> (index: int, ok: bool) {
  89. word := it.array.bits[it.word_idx] if len(it.array.bits) > it.word_idx else 0
  90. when ! ITERATE_SET_BITS { word = ~word }
  91. // If the word is empty or we have already gone over all the bits in it,
  92. // b.bit_idx is greater than the index of any set bit in the word,
  93. // meaning that word >> b.bit_idx == 0.
  94. for it.word_idx < len(it.array.bits) && word >> it.bit_idx == 0 {
  95. it.word_idx += 1
  96. it.bit_idx = 0
  97. word = it.array.bits[it.word_idx] if len(it.array.bits) > it.word_idx else 0
  98. when ! ITERATE_SET_BITS { word = ~word }
  99. }
  100. // If we are iterating the set bits, reaching the end of the array means we have no more bits to check
  101. when ITERATE_SET_BITS {
  102. if it.word_idx >= len(it.array.bits) {
  103. return 0, false
  104. }
  105. }
  106. // Reaching here means that the word has some set bits
  107. it.bit_idx += uint(intrinsics.count_trailing_zeros(word >> it.bit_idx))
  108. index = it.word_idx * NUM_BITS + int(it.bit_idx) + it.array.bias
  109. it.bit_idx += 1
  110. if it.bit_idx >= NUM_BITS {
  111. it.bit_idx = 0
  112. it.word_idx += 1
  113. }
  114. return index, index <= it.array.max_index
  115. }
  116. /*
  117. Gets the state of a bit in the bit-array
  118. Inputs:
  119. - ba: Pointer to the Bit_Array
  120. - index: Which bit in the array
  121. Returns:
  122. - res: `true` if the bit at `index` is set.
  123. - ok: Whether the index was valid. Returns `false` if the index is smaller than the bias.
  124. */
  125. get :: proc(ba: ^Bit_Array, #any_int index: uint) -> (res: bool, ok: bool) #optional_ok {
  126. idx := int(index) - ba.bias
  127. if ba == nil || int(index) < ba.bias { return false, false }
  128. leg_index := idx >> INDEX_SHIFT
  129. bit_index := idx & INDEX_MASK
  130. /*
  131. If we `get` a bit that doesn't fit in the Bit Array, it's naturally `false`.
  132. This early-out prevents unnecessary resizing.
  133. */
  134. if leg_index + 1 > len(ba.bits) { return false, true }
  135. val := u64(1 << uint(bit_index))
  136. res = ba.bits[leg_index] & val == val
  137. return res, true
  138. }
  139. /*
  140. Gets the state of a bit in the bit-array
  141. *Bypasses all Checks*
  142. Inputs:
  143. - ba: Pointer to the Bit_Array
  144. - index: Which bit in the array
  145. Returns:
  146. - `true` if bit is set
  147. */
  148. unsafe_get :: #force_inline proc(ba: ^Bit_Array, #any_int index: uint) -> bool #no_bounds_check {
  149. return bool((ba.bits[index >> INDEX_SHIFT] >> uint(index & INDEX_MASK)) & 1)
  150. }
  151. /*
  152. Sets the state of a bit in the bit-array
  153. *Conditionally Allocates (Resizes backing data when `index > len(ba.bits)`)*
  154. Inputs:
  155. - ba: Pointer to the Bit_Array
  156. - index: Which bit in the array
  157. - set_to: `true` sets the bit on, `false` to turn it off
  158. - allocator: (default is context.allocator)
  159. Returns:
  160. - ok: Whether the set was successful, `false` on allocation failure or bad index
  161. */
  162. set :: proc(ba: ^Bit_Array, #any_int index: uint, set_to: bool = true, allocator := context.allocator) -> (ok: bool) {
  163. idx := int(index) - ba.bias
  164. if ba == nil || int(index) < ba.bias { return false }
  165. context.allocator = allocator
  166. leg_index := idx >> INDEX_SHIFT
  167. bit_index := idx & INDEX_MASK
  168. resize_if_needed(ba, leg_index) or_return
  169. ba.max_index = max(idx, ba.max_index)
  170. if set_to{ ba.bits[leg_index] |= 1 << uint(bit_index) }
  171. else { ba.bits[leg_index] &= ~(1 << uint(bit_index)) }
  172. return true
  173. }
  174. /*
  175. Sets the state of a bit in the bit-array
  176. *Bypasses all checks*
  177. Inputs:
  178. - ba: Pointer to the Bit_Array
  179. - index: Which bit in the array
  180. */
  181. unsafe_set :: proc(ba: ^Bit_Array, bit: int) #no_bounds_check {
  182. ba.bits[bit >> INDEX_SHIFT] |= 1 << uint(bit & INDEX_MASK)
  183. }
  184. /*
  185. Unsets the state of a bit in the bit-array. (Convienence wrapper for `set`)
  186. *Conditionally Allocates (Resizes backing data when `index > len(ba.bits)`)*
  187. Inputs:
  188. - ba: Pointer to the Bit_Array
  189. - index: Which bit in the array
  190. - allocator: (default is context.allocator)
  191. Returns:
  192. - ok: Whether the unset was successful, `false` on allocation failure or bad index
  193. */
  194. unset :: #force_inline proc(ba: ^Bit_Array, #any_int index: uint, allocator := context.allocator) -> (ok: bool) {
  195. return set(ba, index, false, allocator)
  196. }
  197. /*
  198. Unsets the state of a bit in the bit-array
  199. *Bypasses all Checks*
  200. Inputs:
  201. - ba: Pointer to the Bit_Array
  202. - index: Which bit in the array
  203. */
  204. unsafe_unset :: proc(b: ^Bit_Array, bit: int) #no_bounds_check {
  205. b.bits[bit >> INDEX_SHIFT] &= ~(1 << uint(bit & INDEX_MASK))
  206. }
  207. /*
  208. A helper function to create a Bit Array with optional bias, in case your smallest index is non-zero (including negative).
  209. *Allocates (`new(Bit_Array) & make(ba.bits)`)*
  210. Inputs:
  211. - max_index: maximum starting index
  212. - min_index: minimum starting index (used as a bias)
  213. - allocator: (default is context.allocator)
  214. Returns:
  215. - ba: Allocates a bit_Array, backing data is set to `max-min / 64` indices, rounded up (eg 65 - 0 allocates for [2]u64).
  216. */
  217. create :: proc(max_index: int, min_index: int = 0, allocator := context.allocator) -> (res: ^Bit_Array, ok: bool) #optional_ok {
  218. context.allocator = allocator
  219. size_in_bits := max_index - min_index
  220. if size_in_bits < 1 { return {}, false }
  221. legs := size_in_bits >> INDEX_SHIFT
  222. if size_in_bits & INDEX_MASK > 0 {legs+=1}
  223. bits, err := make([dynamic]u64, legs)
  224. ok = err == mem.Allocator_Error.None
  225. res = new(Bit_Array)
  226. res.bits = bits
  227. res.bias = min_index
  228. res.max_index = max_index
  229. res.free_pointer = true
  230. return
  231. }
  232. /*
  233. Sets all values in the Bit_Array to zero.
  234. Inputs:
  235. - ba: The target Bit_Array
  236. */
  237. clear :: proc(ba: ^Bit_Array) {
  238. if ba == nil { return }
  239. mem.zero_slice(ba.bits[:])
  240. }
  241. /*
  242. Deallocates the Bit_Array and its backing storage
  243. Inputs:
  244. - ba: The target Bit_Array
  245. */
  246. destroy :: proc(ba: ^Bit_Array) {
  247. if ba == nil { return }
  248. delete(ba.bits)
  249. if ba.free_pointer { // Only free if this Bit_Array was created using `create`, not when on the stack.
  250. free(ba)
  251. }
  252. }
  253. /*
  254. Resizes the Bit Array. For internal use. Provisions needed capacity+1
  255. If you want to reserve the memory for a given-sized Bit Array up front, you can use `create`.
  256. */
  257. @(private="file")
  258. resize_if_needed :: proc(ba: ^Bit_Array, legs: int, allocator := context.allocator) -> (ok: bool) {
  259. if ba == nil { return false }
  260. context.allocator = allocator
  261. if legs + 1 > len(ba.bits) {
  262. resize(&ba.bits, legs + 1)
  263. }
  264. return len(ba.bits) > legs
  265. }