queue.odin 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. package container_queue
  2. import "core:builtin"
  3. import "core:runtime"
  4. // Dynamically resizable double-ended queue/ring-buffer
  5. Queue :: struct($T: typeid) {
  6. data: [dynamic]T,
  7. len: uint,
  8. offset: uint,
  9. }
  10. DEFAULT_CAPACITY :: 16
  11. // Procedure to initialize a queue
  12. init :: proc(q: ^$Q/Queue($T), capacity := DEFAULT_CAPACITY, allocator := context.allocator) -> bool {
  13. if q.data.allocator.procedure == nil {
  14. q.data.allocator = allocator
  15. }
  16. clear(q)
  17. return reserve(q, capacity)
  18. }
  19. // Procedure to initialize a queue from a fixed backing slice
  20. init_from_slice :: proc(q: ^$Q/Queue($T), backing: []T) -> bool {
  21. clear(q)
  22. q.data = transmute([dynamic]T)runtime.Raw_Dynamic_Array{
  23. data = raw_data(backing),
  24. len = builtin.len(backing),
  25. cap = builtin.len(backing),
  26. allocator = {procedure=runtime.nil_allocator_proc, data=nil},
  27. }
  28. return true
  29. }
  30. // Procedure to destroy a queue
  31. destroy :: proc(q: ^$Q/Queue($T)) {
  32. delete(q.data)
  33. }
  34. // The length of the queue
  35. len :: proc(q: $Q/Queue($T)) -> int {
  36. return int(q.len)
  37. }
  38. // The current capacity of the queue
  39. cap :: proc(q: $Q/Queue($T)) -> int {
  40. return builtin.len(q.data)
  41. }
  42. // Remaining space in the queue (cap-len)
  43. space :: proc(q: $Q/Queue($T)) -> int {
  44. return builtin.len(q.data) - int(q.len)
  45. }
  46. // Reserve enough space for at least the specified capacity
  47. reserve :: proc(q: ^$Q/Queue($T), capacity: int) -> bool {
  48. if uint(capacity) > q.len {
  49. return _grow(q, uint(capacity))
  50. }
  51. return true
  52. }
  53. get :: proc(q: ^$Q/Queue($T), #any_int i: int, loc := #caller_location) -> T {
  54. runtime.bounds_check_error_loc(loc, i, builtin.len(q.data))
  55. idx := (uint(i)+q.offset)%builtin.len(q.data)
  56. return q.data[idx]
  57. }
  58. set :: proc(q: ^$Q/Queue($T), #any_int i: int, val: T, loc := #caller_location) {
  59. runtime.bounds_check_error_loc(loc, i, builtin.len(q.data))
  60. idx := (uint(i)+q.offset)%builtin.len(q.data)
  61. q.data[idx] = val
  62. }
  63. get_ptr :: proc(q: ^$Q/Queue($T), #any_int i: int, loc := #caller_location) -> ^T {
  64. runtime.bounds_check_error_loc(loc, i, builtin.len(q.data))
  65. idx := (uint(i)+q.offset)%builtin.len(q.data)
  66. return &q.data[idx]
  67. }
  68. // Push an element to the back of the queue
  69. push_back :: proc(q: ^$Q/Queue($T), elem: T) -> bool {
  70. if space(q^) == 0 {
  71. _grow(q) or_return
  72. }
  73. q.data[q.len] = elem
  74. q.len += 1
  75. return true
  76. }
  77. // Push an element to the front of the queue
  78. push_front :: proc(q: ^$Q/Queue($T), elem: T) -> bool {
  79. if space(q^) == 0 {
  80. _grow(q) or_return
  81. }
  82. q.offset = uint(q.offset - 1 + builtin.len(q.data)) % builtin.len(q.data)
  83. q.len += 1
  84. q.data[q.offset] = elem
  85. return true
  86. }
  87. // Pop an element from the back of the queue
  88. pop_back :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> (elem: T) {
  89. assert(condition=q.len > 0, loc=loc)
  90. q.len -= 1
  91. idx := (q.offset+uint(q.len))%builtin.len(q.data)
  92. elem = q.data[idx]
  93. return
  94. }
  95. // Safely pop an element from the back of the queue
  96. pop_back_safe :: proc(q: ^$Q/Queue($T)) -> (elem: T, ok: bool) {
  97. if q.len > 0 {
  98. q.len -= 1
  99. idx := (q.offset+uint(q.len))%builtin.len(q.data)
  100. elem = q.data[idx]
  101. ok = true
  102. }
  103. return
  104. }
  105. // Pop an element from the front of the queue
  106. pop_front :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> (elem: T) {
  107. assert(condition=q.len > 0, loc=loc)
  108. elem = q.data[q.offset]
  109. q.len -= 1
  110. return
  111. }
  112. // Safely pop an element from the front of the queue
  113. pop_front_safe :: proc(q: ^$Q/Queue($T)) -> (elem: T, ok: bool) {
  114. if q.len > 0 {
  115. elem = q.data[q.offset]
  116. q.len -= 1
  117. ok = true
  118. }
  119. return
  120. }
  121. // Push multiple elements to the front of the queue
  122. push_back_elems :: proc(q: ^$Q/Queue($T), elems: ..T) -> bool {
  123. n := uint(builtin.len(elems))
  124. if space(q^) < int(n) {
  125. _grow(q, q.len + n) or_return
  126. }
  127. sz := uint(builtin.len(q.data))
  128. insert_from := (q.offset + q.len) % sz
  129. insert_to := n
  130. if insert_from + insert_to > sz {
  131. insert_to = sz - insert_from
  132. }
  133. copy(q.data[insert_from:], elems[:insert_to])
  134. copy(q.data[:insert_from], elems[insert_to:])
  135. q.len += n
  136. return true
  137. }
  138. // Consume `n` elements from the front of the queue
  139. consume_front :: proc(q: ^$Q/Queue($T), n: int, loc := #caller_location) {
  140. assert(condition=int(q.len) >= n, loc=loc)
  141. if n > 0 {
  142. nu := uint(n)
  143. q.offset = (q.offset + nu) % builtin.len(q.data)
  144. q.len -= nu
  145. }
  146. }
  147. // Consume `n` elements from the back of the queue
  148. consume_back :: proc(q: ^$Q/Queue($T), n: int, loc := #caller_location) {
  149. assert(condition=int(q.len) >= n, loc=loc)
  150. if n > 0 {
  151. q.len -= uint(n)
  152. }
  153. }
  154. append_elem :: push_back
  155. append_elems :: push_back_elems
  156. push :: proc{push_back, push_back_elems}
  157. append :: proc{push_back, push_back_elems}
  158. // Clear the contents of the queue
  159. clear :: proc(q: ^$Q/Queue($T)) {
  160. q.len = 0
  161. q.offset = 0
  162. }
  163. // Internal growinh procedure
  164. _grow :: proc(q: ^$Q/Queue($T), min_capacity: uint = 0) -> bool {
  165. new_capacity := max(min_capacity, uint(8), uint(builtin.len(q.data))*2)
  166. n := uint(builtin.len(q.data))
  167. builtin.resize(&q.data, int(new_capacity)) or_return
  168. if q.offset + q.len > n {
  169. diff := n - q.offset
  170. copy(q.data[new_capacity-diff:], q.data[q.offset:][:diff])
  171. q.offset += new_capacity - n
  172. }
  173. return true
  174. }