mirror of
https://github.com/kovidgoyal/kitty
synced 2026-06-09 15:08:13 +02:00
Store multi cell data in the CPUCell rather than in TextCache. This sends the CPUCell size back to 12 but in benchmarks ASCII performance is untouched and Unicode performace goes back to what it was before multicell
149 lines
4.5 KiB
C
149 lines
4.5 KiB
C
/*
|
|
* text-cache.c
|
|
* Copyright (C) 2024 Kovid Goyal <kovid at kovidgoyal.net>
|
|
*
|
|
* Distributed under terms of the GPL3 license.
|
|
*/
|
|
|
|
#include "data-types.h"
|
|
typedef struct Chars {
|
|
size_t count;
|
|
const char_type *chars;
|
|
} Chars;
|
|
|
|
#define NAME chars_map
|
|
#define KEY_TY Chars
|
|
#define VAL_TY char_type
|
|
static uint64_t hash_chars(Chars k);
|
|
static bool cmpr_chars(Chars a, Chars b);
|
|
#define HASH_FN hash_chars
|
|
#define CMPR_FN cmpr_chars
|
|
#include "kitty-verstable.h"
|
|
|
|
typedef struct TextCache {
|
|
struct { Chars *items; size_t capacity; char_type count; } array;
|
|
chars_map map;
|
|
unsigned refcnt;
|
|
} TextCache;
|
|
static uint64_t hash_chars(Chars k) { return vt_hash_bytes(k.chars, sizeof(k.chars[0]) * k.count); }
|
|
static bool cmpr_chars(Chars a, Chars b) { return a.count == b.count && memcmp(a.chars, b.chars, sizeof(a.chars[0]) * a.count) == 0; }
|
|
|
|
#define TEXT_CACHE_IMPLEMENTATION
|
|
#include "text-cache.h"
|
|
|
|
TextCache*
|
|
tc_alloc(void) {
|
|
TextCache *ans = calloc(1, sizeof(TextCache));
|
|
if (!ans) return NULL;
|
|
ans->array.capacity = 256;
|
|
ans->array.items = malloc(ans->array.capacity * sizeof(ans->array.items[0]));
|
|
if (!ans->array.items) { free(ans); ans = NULL; return ans; }
|
|
vt_init(&ans->map);
|
|
ans->refcnt = 1;
|
|
return ans;
|
|
}
|
|
|
|
void
|
|
tc_clear(TextCache *ans) {
|
|
ans->array.count = 0;
|
|
vt_cleanup(&ans->map);
|
|
}
|
|
|
|
static void
|
|
free_text_cache(TextCache *self) {
|
|
vt_cleanup(&self->map);
|
|
for (char_type i = 0; i < self->array.count; i++) free((char_type*)self->array.items[i].chars);
|
|
free(self->array.items);
|
|
free(self);
|
|
}
|
|
|
|
TextCache*
|
|
tc_incref(TextCache *self) { if (self) { self->refcnt++; } return self; }
|
|
|
|
TextCache*
|
|
tc_decref(TextCache *self) {
|
|
if (self) {
|
|
if (self->refcnt < 2) free_text_cache(self);
|
|
else self->refcnt--;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
char_type
|
|
tc_first_char_at_index(const TextCache *self, char_type idx) {
|
|
if (self->array.count > idx) return self->array.items[idx].chars[0];
|
|
return 0;
|
|
}
|
|
|
|
char_type
|
|
tc_last_char_at_index(const TextCache *self, char_type idx) {
|
|
if (self->array.count > idx) return self->array.items[idx].chars[self->array.items[idx].count-1];
|
|
return 0;
|
|
}
|
|
|
|
|
|
void
|
|
tc_chars_at_index(const TextCache *self, char_type idx, ListOfChars *ans) {
|
|
if (self->array.count > idx) {
|
|
ensure_space_for_chars(ans, self->array.items[idx].count);
|
|
ans->count = self->array.items[idx].count;
|
|
memcpy(ans->chars, self->array.items[idx].chars, sizeof(ans->chars[0]) * ans->count);
|
|
} else {
|
|
ans->count = 0;
|
|
}
|
|
}
|
|
|
|
bool
|
|
tc_chars_at_index_without_alloc(const TextCache *self, char_type idx, ListOfChars *ans) {
|
|
if (self->array.count > idx) {
|
|
ans->count = self->array.items[idx].count;
|
|
if (ans->capacity < ans->count) return false;
|
|
memcpy(ans->chars, self->array.items[idx].chars, sizeof(ans->chars[0]) * ans->count);
|
|
} else {
|
|
ans->count = 0;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
unsigned
|
|
tc_num_codepoints(const TextCache *self, char_type idx) {
|
|
return self->array.count > idx ? self->array.items[idx].count : 0;
|
|
}
|
|
|
|
unsigned
|
|
tc_chars_at_index_ansi(const TextCache *self, char_type idx, ANSIBuf *output) {
|
|
unsigned count = 0;
|
|
if (self->array.count > idx) {
|
|
count = self->array.items[idx].count;
|
|
// we ensure space for one extra byte for ANSI escape code trailer if multicell
|
|
ensure_space_for(output, buf, output->buf[0], output->len + count + 1, capacity, 2048, false);
|
|
memcpy(output->buf + output->len, self->array.items[idx].chars, sizeof(output->buf[0]) * count);
|
|
output->len += count;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
static char_type
|
|
copy_and_insert(TextCache *self, const Chars key) {
|
|
if (self->array.count > MAX_CHAR_TYPE_VALUE) fatal("Too many items in TextCache");
|
|
ensure_space_for(&(self->array), items, Chars, self->array.count + 1, capacity, 256, false);
|
|
char_type *copy = malloc(key.count * sizeof(key.chars[0]));
|
|
if (!copy) fatal("Out of memory");
|
|
memcpy(copy, key.chars, key.count * sizeof(key.chars[0]));
|
|
char_type ans = self->array.count;
|
|
Chars *k = self->array.items + self->array.count++;
|
|
k->count = key.count; k->chars = copy;
|
|
chars_map_itr i = vt_insert(&self->map, *k, ans);
|
|
if (vt_is_end(i)) fatal("Out of memory");
|
|
return ans;
|
|
}
|
|
|
|
char_type
|
|
tc_get_or_insert_chars(TextCache *self, const ListOfChars *chars) {
|
|
Chars key = {.count=chars->count, .chars=chars->chars};
|
|
chars_map_itr i = vt_get(&self->map, key);
|
|
if (vt_is_end(i)) return copy_and_insert(self, key);
|
|
return i.data->val;
|
|
}
|