diff --git a/packages/ludic.base/queue.ludic b/packages/ludic.base/queue.ludic index ff3f2a2c..55e5205a 100644 --- a/packages/ludic.base/queue.ludic +++ b/packages/ludic.base/queue.ludic @@ -3,6 +3,7 @@ export property Queue { items: []T = null tag: QueueTag = null + none: []T = null # what draining an empty queue hands back, never pushed to } # what a queue says about itself, whatever it holds: its name and how many facts wait in it export property QueueTag { @@ -22,6 +23,7 @@ export function queue_new(name: string) -> Queue { q.items = new []T q.tag = new QueueTag q.tag.name = name + q.none = new []T return q } @@ -30,8 +32,11 @@ export function q_push(q: Queue, v: T) -> void { q.tag.pending = len(q.items) } -# the facts in the order they were pushed; the queue is empty afterwards +# the facts in the order they were pushed; the queue is empty afterwards. Most queues are empty +# most frames, and Ludic frees nothing: an empty drain hands back one shared empty list rather +# than a new one a frame. Read what a drain returns; never push to it. export function q_drain(q: Queue) -> []T { + if len(q.items) == 0 { return q.none } let out = q.items q.items = new []T q.tag.pending = 0 @@ -40,8 +45,9 @@ export function q_drain(q: Queue) -> []T { export function q_len(q: Queue) -> int { return len(q.items) } +# in place: the live list was never handed out (a drain gives its list away and starts another) export function q_clear(q: Queue) -> void { - q.items = new []T + List.clear(q.items) q.tag.pending = 0 } diff --git a/packages/ludic.base/tests/queue_test.ludic b/packages/ludic.base/tests/queue_test.ludic index 0d2c9a81..15631343 100644 --- a/packages/ludic.base/tests/queue_test.ludic +++ b/packages/ludic.base/tests/queue_test.ludic @@ -67,7 +67,27 @@ program QueueTest { expect_eq(len(core_undrained(tags)), 0) } + # an empty drain allocates nothing and hands back the same empty list; a drained list is the + # caller's, untouched by what is pushed or cleared after it + function empty_case() -> void { + let q: Queue = queue_new("quiet") + let a = q_drain(q) + let b = q_drain(q) + expect_eq(len(a), 0) + expect(a == b) + q_push(q, 5) + expect_eq(len(a), 0) + let got = q_drain(q) + q_push(q, 6) + q_clear(q) + expect_eq(len(got), 1) + expect_eq(got[0], 5) + expect_eq(q_len(q), 0) + expect_eq(len(q_drain(q)), 0) + } + test "a queue drains in the order it was pushed" () { fifo_case() } + test "an empty drain allocates nothing, and a drained list is the caller's" () { empty_case() } test "a queue holds records" () { record_case() } test "clear drops what is waiting" () { clear_case() } test "a queue's element type is told by the queue alone" () { infer_case() }