queue.odin 5.3 KB

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