mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-03-17 05:33:52 -04:00
xtgettcap: switch from a table based internal structure, to a single char array
Having the builtin terminfo DB as a table, with one entry per capability/value pair was ineffective memory-wise - we ended up adding ~20K to the final binary size. This patch changes the internal representation from a table, to a single NULL-separated (and NULL-terminated) char array: cap1-name \0 cap1-value \0 cap2-name \0 cap2-value \0 The capabilities are still sorted; to lookup a capability we need to scan from the beginning until we either find a match, or until the capabilities from the DB sort higher (lexicographically) than the searched-for capability. The terminfo char array is 3.3K - more better than before.
This commit is contained in:
parent
7258e0b005
commit
5a032c4c6f
2 changed files with 75 additions and 46 deletions
64
dcs.c
64
dcs.c
|
|
@ -66,19 +66,52 @@ err:
|
||||||
UNITTEST
|
UNITTEST
|
||||||
{
|
{
|
||||||
/* Verify table is sorted */
|
/* Verify table is sorted */
|
||||||
for (size_t i = 1; i < ALEN(terminfo_capabilities); i++) {
|
const char *p = terminfo_capabilities;
|
||||||
xassert(strcmp(terminfo_capabilities[i - 1].name,
|
size_t left = sizeof(terminfo_capabilities);
|
||||||
terminfo_capabilities[i].name ) <= 0);
|
|
||||||
|
const char *last_cap = NULL;
|
||||||
|
|
||||||
|
while (left > 0) {
|
||||||
|
const char *cap = p;
|
||||||
|
const char *val = cap + strlen(cap) + 1;
|
||||||
|
|
||||||
|
size_t size = strlen(cap) + 1 + strlen(val) + 1;;
|
||||||
|
xassert(size <= left);
|
||||||
|
p += size;
|
||||||
|
left -= size;
|
||||||
|
|
||||||
|
if (last_cap != NULL)
|
||||||
|
xassert(strcmp(last_cap, cap) < 0);
|
||||||
|
|
||||||
|
last_cap = cap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static bool
|
||||||
terminfo_entry_compar(const void *_key, const void *_entry)
|
lookup_capability(const char *name, const char **value)
|
||||||
{
|
{
|
||||||
const char *key = _key;
|
const char *p = terminfo_capabilities;
|
||||||
const struct foot_terminfo_entry *entry = _entry;
|
size_t left = sizeof(terminfo_capabilities);
|
||||||
|
|
||||||
return strcmp(key, entry->name);
|
while (left > 0) {
|
||||||
|
const char *cap = p;
|
||||||
|
const char *val = cap + strlen(cap) + 1;
|
||||||
|
|
||||||
|
size_t size = strlen(cap) + 1 + strlen(val) + 1;;
|
||||||
|
xassert(size <= left);
|
||||||
|
p += size;
|
||||||
|
left -= size;
|
||||||
|
|
||||||
|
int r = strcmp(cap, name);
|
||||||
|
if (r == 0) {
|
||||||
|
*value = val;
|
||||||
|
return true;
|
||||||
|
} else if (r > 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
*value = NULL;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
@ -88,18 +121,23 @@ xtgettcap_reply(struct terminal *term, const char *hex_cap_name, size_t len)
|
||||||
if (name == NULL)
|
if (name == NULL)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
|
#if 0
|
||||||
const struct foot_terminfo_entry *entry =
|
const struct foot_terminfo_entry *entry =
|
||||||
bsearch(name, terminfo_capabilities, ALEN(terminfo_capabilities),
|
bsearch(name, terminfo_capabilities, ALEN(terminfo_capabilities),
|
||||||
sizeof(*entry), &terminfo_entry_compar);
|
sizeof(*entry), &terminfo_entry_compar);
|
||||||
|
#endif
|
||||||
|
const char *value;
|
||||||
|
bool valid_capability = lookup_capability(name, &value);
|
||||||
|
xassert(!valid_capability || value != NULL);
|
||||||
|
|
||||||
LOG_DBG("XTGETTCAP: cap=%s (%.*s), value=%s",
|
LOG_DBG("XTGETTCAP: cap=%s (%.*s), value=%s",
|
||||||
name, (int)len, hex_cap_name,
|
name, (int)len, hex_cap_name,
|
||||||
entry != NULL ? entry->value : "<invalid>");
|
valid_capability ? value : "<invalid>");
|
||||||
|
|
||||||
if (entry == NULL)
|
if (!valid_capability)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
if (entry->value == NULL) {
|
if (value[0] == '\0') {
|
||||||
/* Boolean */
|
/* Boolean */
|
||||||
term_to_slave(term, "\033P1+r", 5);
|
term_to_slave(term, "\033P1+r", 5);
|
||||||
term_to_slave(term, hex_cap_name, len);
|
term_to_slave(term, hex_cap_name, len);
|
||||||
|
|
@ -116,13 +154,13 @@ xtgettcap_reply(struct terminal *term, const char *hex_cap_name, size_t len)
|
||||||
5 + /* DCS 1 + r (\EP1+r) */
|
5 + /* DCS 1 + r (\EP1+r) */
|
||||||
len + /* capability name, hex encoded */
|
len + /* capability name, hex encoded */
|
||||||
1 + /* ‘=’ */
|
1 + /* ‘=’ */
|
||||||
strlen(entry->value) * 2 + /* capability value, hex encoded */
|
strlen(value) * 2 + /* capability value, hex encoded */
|
||||||
2 + /* ST (\E\\) */
|
2 + /* ST (\E\\) */
|
||||||
1);
|
1);
|
||||||
|
|
||||||
int idx = sprintf(reply, "\033P1+r%.*s=", (int)len, hex_cap_name);
|
int idx = sprintf(reply, "\033P1+r%.*s=", (int)len, hex_cap_name);
|
||||||
|
|
||||||
for (const char *c = entry->value; *c != '\0'; c++) {
|
for (const char *c = value; *c != '\0'; c++) {
|
||||||
uint8_t nib1 = (uint8_t)*c >> 4;
|
uint8_t nib1 = (uint8_t)*c >> 4;
|
||||||
uint8_t nib2 = (uint8_t)*c & 0xf;
|
uint8_t nib2 = (uint8_t)*c & 0xf;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,19 @@ class IntCapability(Capability):
|
||||||
|
|
||||||
|
|
||||||
class StringCapability(Capability):
|
class StringCapability(Capability):
|
||||||
pass
|
def __init__(self, name: str, value: str):
|
||||||
|
# Expand \E to literal ESC in non-parameterized capabilities
|
||||||
|
if '%' not in value:
|
||||||
|
value = re.sub(r'\\E([0-7])', r'\\033" "\1', value)
|
||||||
|
value = re.sub(r'\\E', r'\\033', value)
|
||||||
|
else:
|
||||||
|
# Need to double-escape \E in C string literals
|
||||||
|
value = value.replace('\\E', '\\\\E')
|
||||||
|
|
||||||
|
# Don’t escape ‘:’
|
||||||
|
value = value.replace('\\:', ':')
|
||||||
|
|
||||||
|
super().__init__(name, value)
|
||||||
|
|
||||||
|
|
||||||
class Fragment:
|
class Fragment:
|
||||||
|
|
@ -156,48 +168,27 @@ def main():
|
||||||
entry.add_capability(StringCapability('TN', target_entry_name))
|
entry.add_capability(StringCapability('TN', target_entry_name))
|
||||||
entry.add_capability(IntCapability('RGB', 8)) # 8 bits per channel
|
entry.add_capability(IntCapability('RGB', 8)) # 8 bits per channel
|
||||||
|
|
||||||
target.write('#pragma once\n')
|
terminfo_parts = []
|
||||||
target.write('\n')
|
|
||||||
# target.write('enum terminfo_capability_type {\n')
|
|
||||||
# target.write(' TERMINFO_CAP_BOOL,\n')
|
|
||||||
# target.write(' TERMINFO_CAP_INT,\n')
|
|
||||||
# target.write(' TERMINFO_CAP_STRING,\n')
|
|
||||||
# target.write('};\n')
|
|
||||||
# target.write('\n')
|
|
||||||
target.write('struct foot_terminfo_entry {\n')
|
|
||||||
target.write(' const char *name;\n')
|
|
||||||
# target.write(' enum terminfo_capability_type type;\n')
|
|
||||||
target.write(' const char *value;\n')
|
|
||||||
target.write('};\n')
|
|
||||||
target.write('\n')
|
|
||||||
target.write(
|
|
||||||
'static const struct foot_terminfo_entry terminfo_capabilities[] = {\n')
|
|
||||||
|
|
||||||
for cap in sorted(entry.caps.values()):
|
for cap in sorted(entry.caps.values()):
|
||||||
name = cap.name
|
name = cap.name
|
||||||
value = str(cap.value)
|
value = str(cap.value)
|
||||||
|
|
||||||
# Expand \E to literal ESC in non-parameterized capabilities
|
# Escape ‘“‘
|
||||||
if '%' not in value:
|
|
||||||
value = re.sub(r'\\E([0-7])', r'\\033" "\1', value)
|
|
||||||
value = re.sub(r'\\E', r'\\033', value)
|
|
||||||
else:
|
|
||||||
# Need to double-escape \E in C string literals
|
|
||||||
value = value.replace('\\E', '\\\\E')
|
|
||||||
|
|
||||||
# Don’t escape ‘:’
|
|
||||||
value = value.replace('\\:', ':')
|
|
||||||
|
|
||||||
# Do escape ‘“‘
|
|
||||||
name = name.replace('"', '\"')
|
name = name.replace('"', '\"')
|
||||||
value = value.replace('"', '\"')
|
value = value.replace('"', '\"')
|
||||||
|
|
||||||
|
terminfo_parts.append(name)
|
||||||
if isinstance(cap, BoolCapability):
|
if isinstance(cap, BoolCapability):
|
||||||
target.write(f' {{"{name}", NULL}},\n')
|
terminfo_parts.append('')
|
||||||
else:
|
else:
|
||||||
target.write(f' {{"{name}", "{value}"}},\n')
|
terminfo_parts.append(value)
|
||||||
|
|
||||||
target.write('};\n')
|
terminfo = '\\0" "'.join(terminfo_parts)
|
||||||
|
|
||||||
|
target.write('#pragma once\n')
|
||||||
|
target.write('\n')
|
||||||
|
target.write(f'static const char terminfo_capabilities[] = "{terminfo}";')
|
||||||
|
target.write('\n')
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue