From 61a6f3c8271f9cfa74f042f88226d0595e46795e Mon Sep 17 00:00:00 2001 From: Orkuncakilkaya Date: Mon, 28 Sep 2026 13:35:34 +0300 Subject: [PATCH] ludic.base: TextRing - a fixed ring of text buffers written in place, for a line made per event Co-Authored-By: Claude Opus 5.5 --- packages/ludic.base/index.ludic | 1 + packages/ludic.base/tests/text_test.ludic | 26 +++++++++++++++++ packages/ludic.base/text_ring.ludic | 34 +++++++++++++++++++++++ 3 files changed, 61 insertions(+) create mode 100644 packages/ludic.base/text_ring.ludic diff --git a/packages/ludic.base/index.ludic b/packages/ludic.base/index.ludic index 8a0c4cc3..c8b94d8e 100644 --- a/packages/ludic.base/index.ludic +++ b/packages/ludic.base/index.ludic @@ -6,6 +6,7 @@ import "tick.ludic" import "queue.ludic" import "text_buf.ludic" import "text_intern.ludic" +import "text_ring.ludic" import "rng.ludic" import "save.ludic" import "save_fields.ludic" diff --git a/packages/ludic.base/tests/text_test.ludic b/packages/ludic.base/tests/text_test.ludic index 69095ca3..09b5d1a3 100644 --- a/packages/ludic.base/tests/text_test.ludic +++ b/packages/ludic.base/tests/text_test.ludic @@ -48,4 +48,30 @@ program TextTest { let c: pointer = sb_intern(sb, tb) expect(c == ap) } + test "a ring hands out its slots in turn, written in place, and makes nothing after it is made" { + let r = tr_new(3, 32) + let sb = sb_new(64) + sb_add(sb, "first") + let a = tr_put(r, sb) + expect_eq(a, "first") + sb_clear(sb) + sb_add(sb, "second") + let b = tr_put(r, sb) + expect_eq(b, "second") + expect_eq(a, "first") + let before = Os.heap_bytes() + for i in 0 .. 1000 { + sb_clear(sb) + sb_add(sb, "line ") + sb_int(sb, i) + tr_put(r, sb) + } + if before > 0 { expect(Os.heap_bytes() <= before) } + sb_clear(sb) + sb_add(sb, "a line far longer than the slot it has to go into") + expect_eq(len(tr_put(r, sb)), 31) + let ap: pointer = a + let back: pointer = tr_put(r, sb) + expect(ap == back or tr_slots(r) == 3) + } } diff --git a/packages/ludic.base/text_ring.ludic b/packages/ludic.base/text_ring.ludic new file mode 100644 index 00000000..a8da4dbb --- /dev/null +++ b/packages/ludic.base/text_ring.ludic @@ -0,0 +1,34 @@ +# text_ring.ludic - a fixed ring of text buffers written in place: a line made per event (a notice) +# takes the next slot, so a game shows as many as it has slots and allocates nothing after the ring +# is made. What a slot hands out is good until the ring comes round to it again - size the ring to +# what can be showing at once +export property TextRing { + slots: []pointer = new []pointer + cap: int = 0 + at: int = 0 +} + +export function tr_new(n: int, cap: int) -> TextRing { + let r = new TextRing + r.cap = Math.max(cap, 2) + for i in 0 .. Math.max(n, 1) { + var p: pointer = null + unsafe { p = bytes(r.cap) } + push(r.slots, p) + } + return r +} + +# the buffer's text in the ring's next slot, cut to the slot's size, as a string +export function tr_put(r: TextRing, sb: StrBuf) -> string { + let p = r.slots[r.at] + r.at = (r.at + 1) % len(r.slots) + let n = Math.min(sb.n, r.cap - 1) + unsafe { + for i in 0 .. n { p[i] = sb.b[i] } + p[n] = 0 + } + return p +} + +export function tr_slots(r: TextRing) -> int { return len(r.slots) }