queue.odin 6.7 KB

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