From 7b9130096245f6c5f6d5d9a9b49b61584bc1aaf6 Mon Sep 17 00:00:00 2001 From: Orkuncakilkaya Date: Tue, 29 Sep 2026 13:29:00 +0300 Subject: [PATCH] photo: a frame's tags written into a kept buffer and interned, one string per distinct text - a viewfinder composes every frame, and each tag concatenated a new text the frame dropped Co-Authored-By: Claude Opus 5.5 --- packages/ludic.photo/frame.ludic | 25 ++++++++++++++++++++++--- packages/ludic.photo/roll.ludic | 2 ++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/packages/ludic.photo/frame.ludic b/packages/ludic.photo/frame.ludic index be4d4136..7b33d6a0 100644 --- a/packages/ludic.photo/frame.ludic +++ b/packages/ludic.photo/frame.ludic @@ -2,6 +2,7 @@ # takes the best subject's grade - which is what a photographer would say about it export function photo_frame_begin(photo_st: mut PhotoState) -> void { photo_st.pht_pending_tags = "" + sb_clear(photo_st.pht_tag_sb) photo_st.pht_pending_mask = 0 photo_st.pht_pending_legend = 0 photo_st.pht_pending_grade = 0 @@ -27,11 +28,29 @@ export function photo_frame_subject(photo_st: mut PhotoState, sp: int, name: str } # anything else in the picture worth naming: a landmark, the water, a sign -@alloc_ok("a shot's tags: once per photograph taken") +# (a viewfinder composes every frame: the text is written into a kept buffer and interned) export function photo_frame_tag(photo_st: mut PhotoState, text: string) -> void { if len(text) == 0 { return } - if len(photo_st.pht_pending_tags) > 0 { photo_st.pht_pending_tags = photo_st.pht_pending_tags + ", " } - photo_st.pht_pending_tags = photo_st.pht_pending_tags + text + let sb = photo_st.pht_tag_sb + if sb_len(sb) > 0 { sb_add(sb, ", ") } + sb_add(sb, text) + photo_st.pht_pending_tags = pht_tags_text(photo_st) +} + +# past the table's most, a copy only when the text changed - never one a frame +@alloc_ok("a tag text past 4096 distinct ones: one copy per change") +function pht_tags_text(photo_st: PhotoState) -> string { + let sb = photo_st.pht_tag_sb + let tb = photo_st.pht_tag_tb + if strs_count(tb) < tb.most { return sb_intern(sb, tb) } + if pht_same(sb, photo_st.pht_pending_tags) { return photo_st.pht_pending_tags } + return keep(text_of(sb.b, sb_len(sb))) +} + +function pht_same(sb: StrBuf, s: string) -> bool { + if len(s) != sb_len(sb) { return false } + for i in 0 .. sb_len(sb) { if s[i] != sb.b[i] { return false } } + return true } export function photo_frame_sign(photo_st: mut PhotoState, sign: int) -> void { photo_st.pht_pending_sign = sign } diff --git a/packages/ludic.photo/roll.ludic b/packages/ludic.photo/roll.ludic index 263c0952..5e245ad9 100644 --- a/packages/ludic.photo/roll.ludic +++ b/packages/ludic.photo/roll.ludic @@ -47,6 +47,8 @@ export state PhotoState { pht_legend_shot: int = 0 pht_cover: int = -1 # the one frame a game shows for the whole roll pht_pending_tags: string = "" + pht_tag_sb: StrBuf = sb_new(512) # the tags written in place, then one string per distinct text + pht_tag_tb: StrTable = strs_new(4096) pht_pending_mask: int = 0 pht_pending_legend: int = 0 pht_pending_grade: int = 0