aboutsummaryrefslogtreecommitdiff
path: root/core/container/queue
diff options
context:
space:
mode:
Diffstat (limited to 'core/container/queue')
-rw-r--r--core/container/queue/queue.odin209
1 files changed, 209 insertions, 0 deletions
diff --git a/core/container/queue/queue.odin b/core/container/queue/queue.odin
new file mode 100644
index 000000000..8ca3a85ac
--- /dev/null
+++ b/core/container/queue/queue.odin
@@ -0,0 +1,209 @@
+package container_queue
+
+import "core:builtin"
+import "core:runtime"
+_ :: runtime
+
+// Dynamically resizable double-ended queue/ring-buffer
+Queue :: struct($T: typeid) {
+ data: [dynamic]T,
+ len: uint,
+ offset: uint,
+}
+
+DEFAULT_CAPACITY :: 16
+
+// Procedure to initialize a queue
+init :: proc(q: ^$Q/Queue($T), capacity := DEFAULT_CAPACITY, allocator := context.allocator) -> bool {
+ if q.data.allocator.procedure == nil {
+ q.data.allocator = allocator
+ }
+ clear(q)
+ return reserve(q, capacity)
+}
+
+// Procedure to initialize a queue from a fixed backing slice
+init_from_slice :: proc(q: ^$Q/Queue($T), backing: []T) -> bool {
+ clear(q)
+ q.data = transmute([dynamic]T)runtime.Raw_Dynamic_Array{
+ data = raw_data(backing),
+ len = builtin.len(backing),
+ cap = builtin.len(backing),
+ allocator = {procedure=runtime.nil_allocator_proc, data=nil},
+ }
+ return true
+}
+
+// Procedure to destroy a queue
+destroy :: proc(q: ^$Q/Queue($T)) {
+ delete(q.data)
+}
+
+// The length of the queue
+len :: proc(q: $Q/Queue($T)) -> int {
+ return int(q.len)
+}
+
+// The current capacity of the queue
+cap :: proc(q: $Q/Queue($T)) -> int {
+ return builtin.len(q.data)
+}
+
+// Remaining space in the queue (cap-len)
+space :: proc(q: $Q/Queue($T)) -> int {
+ return builtin.len(q.data) - int(q.len)
+}
+
+// Reserve enough space for at least the specified capacity
+reserve :: proc(q: ^$Q/Queue($T), capacity: int) -> bool {
+ if uint(capacity) > q.len {
+ return _grow(q, uint(capacity))
+ }
+ return true
+}
+
+
+get :: proc(q: ^$Q/Queue($T), #any_int i: int, loc := #caller_location) -> T {
+ runtime.bounds_check_error_loc(loc, i, builtin.len(q.data))
+
+ idx := (uint(i)+q.offset)%builtin.len(q.data)
+ return q.data[idx]
+}
+set :: proc(q: ^$Q/Queue($T), #any_int i: int, val: T, loc := #caller_location) {
+ runtime.bounds_check_error_loc(loc, i, builtin.len(q.data))
+
+ idx := (uint(i)+q.offset)%builtin.len(q.data)
+ q.data[idx] = val
+}
+get_ptr :: proc(q: ^$Q/Queue($T), #any_int i: int, loc := #caller_location) -> ^T {
+ runtime.bounds_check_error_loc(loc, i, builtin.len(q.data))
+
+ idx := (uint(i)+q.offset)%builtin.len(q.data)
+ return &q.data[idx]
+}
+
+// Push an element to the back of the queue
+push_back :: proc(q: ^$Q/Queue($T), elem: T) -> bool {
+ if space(q^) == 0 {
+ _grow(q) or_return
+ }
+ idx := (q.offset+uint(q.len))%builtin.len(q.data)
+ q.data[idx] = elem
+ q.len += 1
+ return true
+}
+
+// Push an element to the front of the queue
+push_front :: proc(q: ^$Q/Queue($T), elem: T) -> bool {
+ if space(q^) == 0 {
+ _grow(q) or_return
+ }
+ q.offset = uint(q.offset - 1 + builtin.len(q.data)) % builtin.len(q.data)
+ q.len += 1
+ q.data[q.offset] = elem
+ return true
+}
+
+
+// Pop an element from the back of the queue
+pop_back :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> (elem: T) {
+ assert(condition=q.len > 0, loc=loc)
+ q.len -= 1
+ idx := (q.offset+uint(q.len))%builtin.len(q.data)
+ elem = q.data[idx]
+ return
+}
+// Safely pop an element from the back of the queue
+pop_back_safe :: proc(q: ^$Q/Queue($T)) -> (elem: T, ok: bool) {
+ if q.len > 0 {
+ q.len -= 1
+ idx := (q.offset+uint(q.len))%builtin.len(q.data)
+ elem = q.data[idx]
+ ok = true
+ }
+ return
+}
+
+// Pop an element from the front of the queue
+pop_front :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> (elem: T) {
+ assert(condition=q.len > 0, loc=loc)
+ elem = q.data[q.offset]
+ q.offset = (q.offset+1)%builtin.len(q.data)
+ q.len -= 1
+ return
+}
+// Safely pop an element from the front of the queue
+pop_front_safe :: proc(q: ^$Q/Queue($T)) -> (elem: T, ok: bool) {
+ if q.len > 0 {
+ elem = q.data[q.offset]
+ q.offset = (q.offset+1)%builtin.len(q.data)
+ q.len -= 1
+ ok = true
+ }
+ return
+}
+
+// Push multiple elements to the front of the queue
+push_back_elems :: proc(q: ^$Q/Queue($T), elems: ..T) -> bool {
+ n := uint(builtin.len(elems))
+ if space(q^) < int(n) {
+ _grow(q, q.len + n) or_return
+ }
+
+ sz := uint(builtin.len(q.data))
+ insert_from := (q.offset + q.len) % sz
+ insert_to := n
+ if insert_from + insert_to > sz {
+ insert_to = sz - insert_from
+ }
+ copy(q.data[insert_from:], elems[:insert_to])
+ copy(q.data[:insert_from], elems[insert_to:])
+ q.len += n
+ return true
+}
+
+// Consume `n` elements from the front of the queue
+consume_front :: proc(q: ^$Q/Queue($T), n: int, loc := #caller_location) {
+ assert(condition=int(q.len) >= n, loc=loc)
+ if n > 0 {
+ nu := uint(n)
+ q.offset = (q.offset + nu) % builtin.len(q.data)
+ q.len -= nu
+ }
+}
+
+// Consume `n` elements from the back of the queue
+consume_back :: proc(q: ^$Q/Queue($T), n: int, loc := #caller_location) {
+ assert(condition=int(q.len) >= n, loc=loc)
+ if n > 0 {
+ q.len -= uint(n)
+ }
+}
+
+
+
+append_elem :: push_back
+append_elems :: push_back_elems
+push :: proc{push_back, push_back_elems}
+append :: proc{push_back, push_back_elems}
+
+
+// Clear the contents of the queue
+clear :: proc(q: ^$Q/Queue($T)) {
+ q.len = 0
+ q.offset = 0
+}
+
+
+// Internal growinh procedure
+_grow :: proc(q: ^$Q/Queue($T), min_capacity: uint = 0) -> bool {
+ new_capacity := max(min_capacity, uint(8), uint(builtin.len(q.data))*2)
+ n := uint(builtin.len(q.data))
+ builtin.resize(&q.data, int(new_capacity)) or_return
+ if q.offset + q.len > n {
+ diff := n - q.offset
+ copy(q.data[new_capacity-diff:], q.data[q.offset:][:diff])
+ q.offset += new_capacity - n
+ }
+ return true
+}