container/queue: Fix and add more bounds checking

This commit is contained in:
Feoramund
2025-06-11 11:54:47 -04:00
parent 5bf180d8f9
commit 27cd508571
+31 -11
View File
@@ -80,36 +80,48 @@ reserve :: proc(q: ^$Q/Queue($T), capacity: int) -> runtime.Allocator_Error {
get :: proc(q: ^$Q/Queue($T), #any_int i: int, loc := #caller_location) -> T { 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)) runtime.bounds_check_error_loc(loc, i, int(q.len))
idx := (uint(i)+q.offset)%builtin.len(q.data) idx := (uint(i)+q.offset)%builtin.len(q.data)
return q.data[idx] return q.data[idx]
} }
front :: proc(q: ^$Q/Queue($T)) -> T { front :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> T {
when !ODIN_NO_BOUNDS_CHECK {
ensure(q.len > 0, "Queue is empty.", loc)
}
return q.data[q.offset] return q.data[q.offset]
} }
front_ptr :: proc(q: ^$Q/Queue($T)) -> ^T { front_ptr :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> ^T {
when !ODIN_NO_BOUNDS_CHECK {
ensure(q.len > 0, "Queue is empty.", loc)
}
return &q.data[q.offset] return &q.data[q.offset]
} }
back :: proc(q: ^$Q/Queue($T)) -> T { back :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> T {
when !ODIN_NO_BOUNDS_CHECK {
ensure(q.len > 0, "Queue is empty.", loc)
}
idx := (q.offset+uint(q.len - 1))%builtin.len(q.data) idx := (q.offset+uint(q.len - 1))%builtin.len(q.data)
return q.data[idx] return q.data[idx]
} }
back_ptr :: proc(q: ^$Q/Queue($T)) -> ^T { back_ptr :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> ^T {
when !ODIN_NO_BOUNDS_CHECK {
ensure(q.len > 0, "Queue is empty.", loc)
}
idx := (q.offset+uint(q.len - 1))%builtin.len(q.data) idx := (q.offset+uint(q.len - 1))%builtin.len(q.data)
return &q.data[idx] return &q.data[idx]
} }
set :: proc(q: ^$Q/Queue($T), #any_int i: int, val: T, loc := #caller_location) { 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)) runtime.bounds_check_error_loc(loc, i, int(q.len))
idx := (uint(i)+q.offset)%builtin.len(q.data) idx := (uint(i)+q.offset)%builtin.len(q.data)
q.data[idx] = val q.data[idx] = val
} }
get_ptr :: proc(q: ^$Q/Queue($T), #any_int i: int, loc := #caller_location) -> ^T { 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)) runtime.bounds_check_error_loc(loc, i, int(q.len))
idx := (uint(i)+q.offset)%builtin.len(q.data) idx := (uint(i)+q.offset)%builtin.len(q.data)
return &q.data[idx] return &q.data[idx]
@@ -152,7 +164,9 @@ push_front :: proc(q: ^$Q/Queue($T), elem: T) -> (ok: bool, err: runtime.Allocat
// Pop an element from the back of the queue // Pop an element from the back of the queue
pop_back :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> (elem: T) { pop_back :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> (elem: T) {
assert(condition=q.len > 0, loc=loc) when !ODIN_NO_BOUNDS_CHECK {
ensure(q.len > 0, "Queue is empty.", loc)
}
q.len -= 1 q.len -= 1
idx := (q.offset+uint(q.len))%builtin.len(q.data) idx := (q.offset+uint(q.len))%builtin.len(q.data)
elem = q.data[idx] elem = q.data[idx]
@@ -171,7 +185,9 @@ pop_back_safe :: proc(q: ^$Q/Queue($T)) -> (elem: T, ok: bool) {
// Pop an element from the front of the queue // Pop an element from the front of the queue
pop_front :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> (elem: T) { pop_front :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> (elem: T) {
assert(condition=q.len > 0, loc=loc) when !ODIN_NO_BOUNDS_CHECK {
ensure(q.len > 0, "Queue is empty.", loc)
}
elem = q.data[q.offset] elem = q.data[q.offset]
q.offset = (q.offset+1)%builtin.len(q.data) q.offset = (q.offset+1)%builtin.len(q.data)
q.len -= 1 q.len -= 1
@@ -209,7 +225,9 @@ push_back_elems :: proc(q: ^$Q/Queue($T), elems: ..T) -> (ok: bool, err: runtime
// Consume `n` elements from the front of the queue // Consume `n` elements from the front of the queue
consume_front :: proc(q: ^$Q/Queue($T), n: int, loc := #caller_location) { consume_front :: proc(q: ^$Q/Queue($T), n: int, loc := #caller_location) {
assert(condition=int(q.len) >= n, loc=loc) when !ODIN_NO_BOUNDS_CHECK {
ensure(q.len >= uint(n), "Queue does not have enough elements to consume.", loc)
}
if n > 0 { if n > 0 {
nu := uint(n) nu := uint(n)
q.offset = (q.offset + nu) % builtin.len(q.data) q.offset = (q.offset + nu) % builtin.len(q.data)
@@ -219,7 +237,9 @@ consume_front :: proc(q: ^$Q/Queue($T), n: int, loc := #caller_location) {
// Consume `n` elements from the back of the queue // Consume `n` elements from the back of the queue
consume_back :: proc(q: ^$Q/Queue($T), n: int, loc := #caller_location) { consume_back :: proc(q: ^$Q/Queue($T), n: int, loc := #caller_location) {
assert(condition=int(q.len) >= n, loc=loc) when !ODIN_NO_BOUNDS_CHECK {
ensure(q.len >= uint(n), "Queue does not have enough elements to consume.", loc)
}
if n > 0 { if n > 0 {
q.len -= uint(n) q.len -= uint(n)
} }