queue.odin 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479
  1. // A dynamically resizable double-ended queue/ring-buffer.
  2. package container_queue
  3. import "base:builtin"
  4. import "base:runtime"
  5. _ :: runtime
  6. /*
  7. `Queue` is a dynamically resizable double-ended queue/ring-buffer.
  8. Being double-ended means that either end may be pushed onto or popped from
  9. across the same block of memory, in any order, thus providing both stack and
  10. queue-like behaviors in the same data structure.
  11. */
  12. Queue :: struct($T: typeid) {
  13. data: [dynamic]T,
  14. len: uint,
  15. offset: uint,
  16. }
  17. DEFAULT_CAPACITY :: 16
  18. /*
  19. Initialize a `Queue` with a starting `capacity` and an `allocator`.
  20. */
  21. init :: proc(q: ^$Q/Queue($T), capacity := DEFAULT_CAPACITY, allocator := context.allocator, loc := #caller_location) -> runtime.Allocator_Error {
  22. clear(q)
  23. q.data = transmute([dynamic]T)runtime.Raw_Dynamic_Array{
  24. data = nil,
  25. len = 0,
  26. cap = 0,
  27. allocator = allocator,
  28. }
  29. return reserve(q, capacity, loc)
  30. }
  31. /*
  32. Initialize a `Queue` from a fixed `backing` slice into which modifications are
  33. made directly.
  34. The contents of the `backing` will be overwritten as items are pushed onto the
  35. `Queue`. Any previous contents will not be available through the API but are
  36. not explicitly zeroed either.
  37. Note that procedures which need space to work (`push_back`, ...) will fail if
  38. the backing slice runs out of space.
  39. */
  40. init_from_slice :: proc(q: ^$Q/Queue($T), backing: []T) -> bool {
  41. clear(q)
  42. q.data = transmute([dynamic]T)runtime.Raw_Dynamic_Array{
  43. data = raw_data(backing),
  44. len = builtin.len(backing),
  45. cap = builtin.len(backing),
  46. allocator = {procedure=runtime.nil_allocator_proc, data=nil},
  47. }
  48. return true
  49. }
  50. /*
  51. Initialize a `Queue` from a fixed `backing` slice into which modifications are
  52. made directly.
  53. The contents of the queue will start out with all of the elements in `backing`,
  54. effectively creating a full queue from the slice. As such, no procedures will
  55. be able to add more elements to the queue until some are taken off.
  56. */
  57. init_with_contents :: proc(q: ^$Q/Queue($T), backing: []T) -> bool {
  58. clear(q)
  59. q.data = transmute([dynamic]T)runtime.Raw_Dynamic_Array{
  60. data = raw_data(backing),
  61. len = builtin.len(backing),
  62. cap = builtin.len(backing),
  63. allocator = {procedure=runtime.nil_allocator_proc, data=nil},
  64. }
  65. q.len = builtin.len(backing)
  66. return true
  67. }
  68. /*
  69. Delete memory that has been dynamically allocated from a `Queue` that was setup with `init`.
  70. Note that this procedure should not be used on queues setup with
  71. `init_from_slice` or `init_with_contents`, as neither of those procedures keep
  72. track of the allocator state of the underlying `backing` slice.
  73. */
  74. destroy :: proc(q: ^$Q/Queue($T)) {
  75. delete(q.data)
  76. }
  77. /*
  78. Return the length of the queue.
  79. */
  80. len :: proc(q: $Q/Queue($T)) -> int {
  81. return int(q.len)
  82. }
  83. /*
  84. Return the capacity of the queue.
  85. */
  86. cap :: proc(q: $Q/Queue($T)) -> int {
  87. return builtin.len(q.data)
  88. }
  89. /*
  90. Return the remaining space in the queue.
  91. This will be `cap() - len()`.
  92. */
  93. space :: proc(q: $Q/Queue($T)) -> int {
  94. return builtin.len(q.data) - int(q.len)
  95. }
  96. /*
  97. Reserve enough space in the queue for at least the specified capacity.
  98. This may return an error if allocation failed.
  99. */
  100. reserve :: proc(q: ^$Q/Queue($T), capacity: int, loc := #caller_location) -> runtime.Allocator_Error {
  101. if capacity > space(q^) {
  102. return _grow(q, uint(capacity), loc)
  103. }
  104. return nil
  105. }
  106. /*
  107. Shrink a queue's dynamically allocated array.
  108. This has no effect if the queue was initialized with a backing slice.
  109. */
  110. shrink :: proc(q: ^$Q/Queue($T), temp_allocator := context.temp_allocator, loc := #caller_location) {
  111. if q.data.allocator.procedure == runtime.nil_allocator_proc {
  112. return
  113. }
  114. if q.len > 0 && q.offset > 0 {
  115. // Make the array contiguous again.
  116. buffer := make([]T, q.len, temp_allocator)
  117. defer delete(buffer, temp_allocator)
  118. right := uint(builtin.len(q.data)) - q.offset
  119. copy(buffer[:], q.data[q.offset:])
  120. copy(buffer[right:], q.data[:q.offset])
  121. copy(q.data[:], buffer[:])
  122. q.offset = 0
  123. }
  124. builtin.shrink(&q.data, q.len, loc)
  125. }
  126. /*
  127. Get the element at index `i`.
  128. This will raise a bounds checking error if `i` is an invalid index.
  129. */
  130. get :: proc(q: ^$Q/Queue($T), #any_int i: int, loc := #caller_location) -> T {
  131. runtime.bounds_check_error_loc(loc, i, int(q.len))
  132. idx := (uint(i)+q.offset)%builtin.len(q.data)
  133. return q.data[idx]
  134. }
  135. /*
  136. Get a pointer to the element at index `i`.
  137. This will raise a bounds checking error if `i` is an invalid index.
  138. */
  139. get_ptr :: proc(q: ^$Q/Queue($T), #any_int i: int, loc := #caller_location) -> ^T {
  140. runtime.bounds_check_error_loc(loc, i, int(q.len))
  141. idx := (uint(i)+q.offset)%builtin.len(q.data)
  142. return &q.data[idx]
  143. }
  144. /*
  145. Set the element at index `i` to `val`.
  146. This will raise a bounds checking error if `i` is an invalid index.
  147. */
  148. set :: proc(q: ^$Q/Queue($T), #any_int i: int, val: T, loc := #caller_location) {
  149. runtime.bounds_check_error_loc(loc, i, int(q.len))
  150. idx := (uint(i)+q.offset)%builtin.len(q.data)
  151. q.data[idx] = val
  152. }
  153. /*
  154. Get the element at the front of the queue.
  155. This will raise a bounds checking error if the queue is empty.
  156. */
  157. front :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> T {
  158. when !ODIN_NO_BOUNDS_CHECK {
  159. ensure(q.len > 0, "Queue is empty.", loc)
  160. }
  161. return q.data[q.offset]
  162. }
  163. /*
  164. Get a pointer to the element at the front of the queue.
  165. This will raise a bounds checking error if the queue is empty.
  166. */
  167. front_ptr :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> ^T {
  168. when !ODIN_NO_BOUNDS_CHECK {
  169. ensure(q.len > 0, "Queue is empty.", loc)
  170. }
  171. return &q.data[q.offset]
  172. }
  173. /*
  174. Get the element at the back of the queue.
  175. This will raise a bounds checking error if the queue is empty.
  176. */
  177. back :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> T {
  178. when !ODIN_NO_BOUNDS_CHECK {
  179. ensure(q.len > 0, "Queue is empty.", loc)
  180. }
  181. idx := (q.offset+uint(q.len - 1))%builtin.len(q.data)
  182. return q.data[idx]
  183. }
  184. /*
  185. Get a pointer to the element at the back of the queue.
  186. This will raise a bounds checking error if the queue is empty.
  187. */
  188. back_ptr :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> ^T {
  189. when !ODIN_NO_BOUNDS_CHECK {
  190. ensure(q.len > 0, "Queue is empty.", loc)
  191. }
  192. idx := (q.offset+uint(q.len - 1))%builtin.len(q.data)
  193. return &q.data[idx]
  194. }
  195. @(deprecated="Use `front_ptr` instead")
  196. peek_front :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> ^T {
  197. return front_ptr(q, loc)
  198. }
  199. @(deprecated="Use `back_ptr` instead")
  200. peek_back :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> ^T {
  201. return back_ptr(q, loc)
  202. }
  203. /*
  204. Push an element to the back of the queue.
  205. If there is no more space left and allocation fails to get more, this will
  206. return false with an `Allocator_Error`.
  207. Example:
  208. import "base:runtime"
  209. import "core:container/queue"
  210. // This demonstrates typical queue behavior (First-In First-Out).
  211. main :: proc() {
  212. q: queue.Queue(int)
  213. queue.init(&q)
  214. queue.push_back(&q, 1)
  215. queue.push_back(&q, 2)
  216. queue.push_back(&q, 3)
  217. // q.data is now [1, 2, 3, ...]
  218. assert(queue.pop_front(&q) == 1)
  219. assert(queue.pop_front(&q) == 2)
  220. assert(queue.pop_front(&q) == 3)
  221. }
  222. */
  223. push_back :: proc(q: ^$Q/Queue($T), elem: T, loc := #caller_location) -> (ok: bool, err: runtime.Allocator_Error) {
  224. if space(q^) == 0 {
  225. _grow(q, loc = loc) or_return
  226. }
  227. idx := (q.offset+uint(q.len))%builtin.len(q.data)
  228. q.data[idx] = elem
  229. q.len += 1
  230. return true, nil
  231. }
  232. /*
  233. Push an element to the front of the queue.
  234. If there is no more space left and allocation fails to get more, this will
  235. return false with an `Allocator_Error`.
  236. Example:
  237. import "base:runtime"
  238. import "core:container/queue"
  239. // This demonstrates stack behavior (First-In Last-Out).
  240. main :: proc() {
  241. q: queue.Queue(int)
  242. queue.init(&q)
  243. queue.push_back(&q, 1)
  244. queue.push_back(&q, 2)
  245. queue.push_back(&q, 3)
  246. // q.data is now [1, 2, 3, ...]
  247. assert(queue.pop_back(&q) == 3)
  248. assert(queue.pop_back(&q) == 2)
  249. assert(queue.pop_back(&q) == 1)
  250. }
  251. */
  252. push_front :: proc(q: ^$Q/Queue($T), elem: T, loc := #caller_location) -> (ok: bool, err: runtime.Allocator_Error) {
  253. if space(q^) == 0 {
  254. _grow(q, loc = loc) or_return
  255. }
  256. q.offset = uint(q.offset - 1 + builtin.len(q.data)) % builtin.len(q.data)
  257. q.len += 1
  258. q.data[q.offset] = elem
  259. return true, nil
  260. }
  261. /*
  262. Pop an element from the back of the queue.
  263. This will raise a bounds checking error if the queue is empty.
  264. Example:
  265. import "base:runtime"
  266. import "core:container/queue"
  267. // This demonstrates stack behavior (First-In Last-Out) at the far end of the data array.
  268. main :: proc() {
  269. q: queue.Queue(int)
  270. queue.init(&q)
  271. queue.push_front(&q, 1)
  272. queue.push_front(&q, 2)
  273. queue.push_front(&q, 3)
  274. // q.data is now [..., 3, 2, 1]
  275. log.infof("%#v", q)
  276. assert(queue.pop_front(&q) == 3)
  277. assert(queue.pop_front(&q) == 2)
  278. assert(queue.pop_front(&q) == 1)
  279. }
  280. */
  281. pop_back :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> (elem: T) {
  282. when !ODIN_NO_BOUNDS_CHECK {
  283. ensure(q.len > 0, "Queue is empty.", loc)
  284. }
  285. q.len -= 1
  286. idx := (q.offset+uint(q.len))%builtin.len(q.data)
  287. elem = q.data[idx]
  288. return
  289. }
  290. /*
  291. Pop an element from the back of the queue if one exists and return true.
  292. Otherwise, return a nil element and false.
  293. */
  294. pop_back_safe :: proc(q: ^$Q/Queue($T)) -> (elem: T, ok: bool) {
  295. if q.len > 0 {
  296. q.len -= 1
  297. idx := (q.offset+uint(q.len))%builtin.len(q.data)
  298. elem = q.data[idx]
  299. ok = true
  300. }
  301. return
  302. }
  303. /*
  304. Pop an element from the front of the queue
  305. This will raise a bounds checking error if the queue is empty.
  306. */
  307. pop_front :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> (elem: T) {
  308. when !ODIN_NO_BOUNDS_CHECK {
  309. ensure(q.len > 0, "Queue is empty.", loc)
  310. }
  311. elem = q.data[q.offset]
  312. q.offset = (q.offset+1)%builtin.len(q.data)
  313. q.len -= 1
  314. return
  315. }
  316. /*
  317. Pop an element from the front of the queue if one exists and return true.
  318. Otherwise, return a nil element and false.
  319. */
  320. pop_front_safe :: proc(q: ^$Q/Queue($T)) -> (elem: T, ok: bool) {
  321. if q.len > 0 {
  322. elem = q.data[q.offset]
  323. q.offset = (q.offset+1)%builtin.len(q.data)
  324. q.len -= 1
  325. ok = true
  326. }
  327. return
  328. }
  329. /*
  330. Push many elements at once to the back of the queue.
  331. If there is not enough space left and allocation fails to get more, this will
  332. return false with an `Allocator_Error`.
  333. */
  334. push_back_elems :: proc(q: ^$Q/Queue($T), elems: ..T, loc := #caller_location) -> (ok: bool, err: runtime.Allocator_Error) {
  335. n := uint(builtin.len(elems))
  336. if space(q^) < int(n) {
  337. _grow(q, q.len + n, loc) or_return
  338. }
  339. sz := uint(builtin.len(q.data))
  340. insert_from := (q.offset + q.len) % sz
  341. insert_to := n
  342. if insert_from + insert_to > sz {
  343. insert_to = sz - insert_from
  344. }
  345. copy(q.data[insert_from:], elems[:insert_to])
  346. copy(q.data[:insert_from], elems[insert_to:])
  347. q.len += n
  348. return true, nil
  349. }
  350. /*
  351. Consume `n` elements from the back of the queue.
  352. This will raise a bounds checking error if the queue does not have enough elements.
  353. */
  354. consume_front :: proc(q: ^$Q/Queue($T), n: int, loc := #caller_location) {
  355. when !ODIN_NO_BOUNDS_CHECK {
  356. ensure(q.len >= uint(n), "Queue does not have enough elements to consume.", loc)
  357. }
  358. if n > 0 {
  359. nu := uint(n)
  360. q.offset = (q.offset + nu) % builtin.len(q.data)
  361. q.len -= nu
  362. }
  363. }
  364. /*
  365. Consume `n` elements from the back of the queue.
  366. This will raise a bounds checking error if the queue does not have enough elements.
  367. */
  368. consume_back :: proc(q: ^$Q/Queue($T), n: int, loc := #caller_location) {
  369. when !ODIN_NO_BOUNDS_CHECK {
  370. ensure(q.len >= uint(n), "Queue does not have enough elements to consume.", loc)
  371. }
  372. if n > 0 {
  373. q.len -= uint(n)
  374. }
  375. }
  376. append_elem :: push_back
  377. append_elems :: push_back_elems
  378. push :: proc{push_back, push_back_elems}
  379. append :: proc{push_back, push_back_elems}
  380. enqueue :: push_back
  381. dequeue :: pop_front
  382. /*
  383. Reset the queue's length and offset to zero, letting it write new elements over
  384. old memory, in effect clearing the accessible contents.
  385. */
  386. clear :: proc(q: ^$Q/Queue($T)) {
  387. q.len = 0
  388. q.offset = 0
  389. }
  390. // Internal growing procedure
  391. _grow :: proc(q: ^$Q/Queue($T), min_capacity: uint = 0, loc := #caller_location) -> runtime.Allocator_Error {
  392. new_capacity := max(min_capacity, uint(8), uint(builtin.len(q.data))*2)
  393. n := uint(builtin.len(q.data))
  394. builtin.resize(&q.data, int(new_capacity), loc) or_return
  395. if q.offset + q.len > n {
  396. diff := n - q.offset
  397. copy(q.data[new_capacity-diff:], q.data[q.offset:][:diff])
  398. q.offset += new_capacity - n
  399. }
  400. return nil
  401. }