bit_array.odin 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  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. Bit_Array :: struct {
  11. bits: [dynamic]u64,
  12. bias: int,
  13. }
  14. /*
  15. In:
  16. - ba: ^Bit_Array - a pointer to the Bit Array
  17. - index: The bit index. Can be an enum member.
  18. Out:
  19. - res: The bit you're interested in.
  20. - ok: Whether the index was valid. Returns `false` if the index is smaller than the bias.
  21. The `ok` return value may be ignored.
  22. */
  23. get :: proc(ba: ^Bit_Array, #any_int index: uint, allocator := context.allocator) -> (res: bool, ok: bool) {
  24. idx := int(index) - ba.bias
  25. if ba == nil || int(index) < ba.bias { return false, false }
  26. context.allocator = allocator
  27. leg_index := idx >> INDEX_SHIFT
  28. bit_index := idx & INDEX_MASK
  29. /*
  30. If we `get` a bit that doesn't fit in the Bit Array, it's naturally `false`.
  31. This early-out prevents unnecessary resizing.
  32. */
  33. if leg_index + 1 > len(ba.bits) { return false, true }
  34. val := u64(1 << uint(bit_index))
  35. res = ba.bits[leg_index] & val == val
  36. return res, true
  37. }
  38. /*
  39. In:
  40. - ba: ^Bit_Array - a pointer to the Bit Array
  41. - index: The bit index. Can be an enum member.
  42. Out:
  43. - ok: Whether or not we managed to set requested bit.
  44. `set` automatically resizes the Bit Array to accommodate the requested index if needed.
  45. */
  46. set :: proc(ba: ^Bit_Array, #any_int index: uint, allocator := context.allocator) -> (ok: bool) {
  47. idx := int(index) - ba.bias
  48. if ba == nil || int(index) < ba.bias { return false }
  49. context.allocator = allocator
  50. leg_index := idx >> INDEX_SHIFT
  51. bit_index := idx & INDEX_MASK
  52. resize_if_needed(ba, leg_index) or_return
  53. ba.bits[leg_index] |= 1 << uint(bit_index)
  54. return true
  55. }
  56. /*
  57. A helper function to create a Bit Array with optional bias, in case your smallest index is non-zero (including negative).
  58. */
  59. create :: proc(max_index: int, min_index := 0, allocator := context.allocator) -> (res: Bit_Array, ok: bool) #optional_ok {
  60. context.allocator = allocator
  61. size_in_bits := max_index - min_index
  62. if size_in_bits < 1 { return {}, false }
  63. legs := size_in_bits >> INDEX_SHIFT
  64. res = Bit_Array{
  65. bias = min_index,
  66. }
  67. return res, resize_if_needed(&res, legs)
  68. }
  69. /*
  70. Sets all bits to `false`.
  71. */
  72. clear :: proc(ba: ^Bit_Array) {
  73. if ba == nil { return }
  74. ba.bits = {}
  75. }
  76. /*
  77. Releases the memory used by the Bit Array.
  78. */
  79. destroy :: proc(ba: ^Bit_Array) {
  80. if ba == nil { return }
  81. delete(ba.bits)
  82. }
  83. /*
  84. Resizes the Bit Array. For internal use.
  85. If you want to reserve the memory for a given-sized Bit Array up front, you can use `create`.
  86. */
  87. @(private="file")
  88. resize_if_needed :: proc(ba: ^Bit_Array, legs: int, allocator := context.allocator) -> (ok: bool) {
  89. if ba == nil { return false }
  90. context.allocator = allocator
  91. if legs + 1 > len(ba.bits) {
  92. resize(&ba.bits, legs + 1)
  93. }
  94. return len(ba.bits) > legs
  95. }