File: text-cache.c

package info (click to toggle)
kitty 0.45.0-2
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 27,476 kB
  • sloc: ansic: 84,285; python: 57,992; objc: 5,432; sh: 1,333; xml: 364; makefile: 144; javascript: 78
file content (149 lines) | stat: -rw-r--r-- 4,657 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
/*
 * 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 {
    const char_type *chars;
    size_t count;
} Chars;
static_assert(sizeof(Chars) == sizeof(void*) + sizeof(size_t), "reorder 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"

#define MA_NAME Chars
#define MA_BLOCK_SIZE 16u
#define MA_ARENA_NUM_BLOCKS 128u
#include "arena.h"

typedef struct TextCache {
    struct { Chars *items; size_t capacity; char_type count; } array;
    chars_map map;
    unsigned refcnt;
    CharsMonotonicArena arena;
} 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;
}

static void
free_text_cache(TextCache *self) {
    vt_cleanup(&self->map);
    Chars_free_all(&self->arena);
    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 = Chars_get(&self->arena, 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;
}