# 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 = 32768 # slots; at most three quarters are used export property UiMemo { slots: words = null # index + 1 into keys, 0 empty @max(24576) keys: []string = null # the text a result was made from (or the joined text itself) @max(24576) tags: []int = null # what kind of result: a separator, or a kind below @max(24576) lists: [][]string = null @max(24576) grads: []UiGradient = null @max(24576) texts: []string = null # a text worked out from the key: tw, el_url @max(24576) vals: []Val = null # a Value made from it: cs_vars_in's @max(24576) vtpls: []UiVarTpl = null @max(24576) n1: []float = null # numbers worked out from the key: a colour and its alpha @max(24576) n2: []float = 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 const MM_TW: int = -4 # tpl_words const MM_URL: int = -5 # el_url const MM_VARS: int = -6 # cs_vars_in: keys[i] is the text with every var() put in const MM_VTPL: int = -7 # a value's var()s parsed (units_pieces.ludic) const MM_COLOR: int = -8 # ui_color: n1 the colour, n2 its alpha const MM_BIMG: int = -9 # border-image: texts the picture, lists what follows its url() const MM_TEXT: int = -10 # a text with holes, keys[i] the text itself (eval_text.ludic) @alloc_ok("once: the memo is made the first time it is asked") 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 m.texts = new []string m.vals = new []Val m.vtpls = new []UiVarTpl m.n1 = new []float m.n2 = new []float return m } function mm_of(ui_st: mut UiState) -> UiMemo { if ui_st.mm == null { ui_st.mm = mm_new() mm_warm_pct(ui_st, ui_st.mm) } return ui_st.mm } # every percentage a bar can be written as (0% .. 100%, to a tenth) made once at the start, so a bar # moving through them never makes its text in play - a text seen for the first time was still one string @alloc_ok("once: every percentage a bar can show, made with the memo") function mm_warm_pct(ui_st: mut UiState, m: UiMemo) -> void { for t in 0 .. 1001 { var s = mm_int(ui_st, t / 10) if t % 10 != 0 { s = s + tx_tenth(t % 10) } let text = s + "%" let j = mm_add(m, MM_TEXT, text) if j >= 0 { m.vals[j] = Value.str(text) } } } # 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 @alloc_ok("a memo entry: one per distinct text, bounded by MM_CAP") function mm_add(m: UiMemo, tag: int, s: string) -> int { if len(m.keys) * 4 >= MM_CAP * 3 { Mem.over("ludic.ui's memo, three quarters of MM_CAP") # 25.5a: past it every miss was made and lost 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) push(m.texts, null) push(m.vals, null) push(m.vtpls, null) push(m.n1, 0.0) push(m.n2, 0.0) 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) } # tpl_words, made once per text (a clean one is its own answer, and nothing is kept for it) function tw(ui_st: mut UiState, s: string) -> string { if tpl_clean(s) { return s } let m = mm_of(ui_st) let i = mm_find(m, MM_TW, s) if i >= 0 { return m.texts[i] } let t = tpl_words(s) let j = mm_add(m, MM_TW, s) if j >= 0 { m.texts[j] = t } return t } # el_url, made once per text function el_url_u(ui_st: mut UiState, s: string) -> string { let m = mm_of(ui_st) let i = mm_find(m, MM_URL, s) if i >= 0 { return m.texts[i] } let t = el_url(s) let j = mm_add(m, MM_URL, s) if j >= 0 { m.texts[j] = t } return t }