# memo.ludic - what a frame would work out again from the same text is kept by that text: a value # split into its words, a gradient, a key joined from its parent's. Ludic frees nothing, so parsing # "2px 6px" per element per frame was a leak; the table is bounded, and past it a result is made # (and lost) as before rather than the table growing without end. const MM_CAP: int = 8192 # slots; at most three quarters are used export property UiMemo { slots: words = null # index + 1 into keys, 0 empty keys: []string = null # the text a result was made from (or the joined text itself) tags: []int = null # what kind of result: a separator, or a kind below lists: [][]string = null grads: []UiGradient = null } const MM_WORDS: int = -1 # el_words: spaces, a bracket's own kept const MM_GRAD: int = -2 const MM_JOIN: int = -3 # keys[i] is the joined text itself function mm_new() -> UiMemo { let m = new UiMemo m.slots = words(MM_CAP) m.keys = new []string m.tags = new []int m.lists = new [][]string m.grads = new []UiGradient return m } function mm_of(ui_st: mut UiState) -> UiMemo { if ui_st.mm == null { ui_st.mm = mm_new() } return ui_st.mm } # FNV-1a over the text, the tag mixed in first function mm_hash(tag: int, s: string) -> int { var h = 216613626 ^ (tag & 255) for i in 0 .. len(s) { h = ((h ^ s[i]) * 16777619) & 2147483647 } return h } # the entry for (tag, s), or -1 function mm_find(m: UiMemo, tag: int, s: string) -> int { var at = mm_hash(tag, s) & (MM_CAP - 1) while m.slots[at] != 0 { let i = m.slots[at] - 1 if m.tags[i] == tag and m.keys[i] == s { return i } at = (at + 1) & (MM_CAP - 1) } return -1 } # a new entry for (tag, s) with its results empty, or -1 when the table is full function mm_add(m: UiMemo, tag: int, s: string) -> int { if len(m.keys) * 4 >= MM_CAP * 3 { return -1 } var at = mm_hash(tag, s) & (MM_CAP - 1) while m.slots[at] != 0 { at = (at + 1) & (MM_CAP - 1) } push(m.keys, s) push(m.tags, tag) push(m.lists, null) push(m.grads, null) m.slots[at] = len(m.keys) return len(m.keys) - 1 } # lss_split's parts of `s` at `c`, made once per text; the list is shared - read it, never change it function mm_split(ui_st: mut UiState, s: string, c: int) -> []string { let m = mm_of(ui_st) let i = mm_find(m, c, s) if i >= 0 { return m.lists[i] } var parts: []string = null if c == MM_WORDS { parts = el_words(s) } else { parts = lss_split(s, c) } let j = mm_add(m, c, s) if j >= 0 { m.lists[j] = parts } return parts } function mm_words(ui_st: mut UiState, s: string) -> []string { return mm_split(ui_st, s, MM_WORDS) }