queue.odin 12 KB

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