68 lines
2.6 KiB
Text
68 lines
2.6 KiB
Text
# 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) }
|