mirror of
https://github.com/neovim/neovim.git
synced 2026-01-30 17:11:11 +10:00
Previously, a screen cell would occupy 28+4=32 bytes per cell as we always made space for up to MAX_MCO+1 codepoints in a cell. As an example, even a pretty modest 50*80 screen would consume 50*80*2*32 = 256000, i e a quarter megabyte With the factor of two due to the TUI side buffer, and even more when using msg_grid and/or ext_multigrid. This instead stores a 4-byte union of either: - a valid UTF-8 sequence up to 4 bytes - an escape char which is invalid UTF-8 (0xFF) plus a 24-bit index to a glyph cache This avoids allocating space for huge composed glyphs _upfront_, while still keeping rendering such glyphs reasonably fast (1 hash table lookup + one plain index lookup). If the same large glyphs are using repeatedly on the screen, this is still a net reduction of memory/cache consumption. The only case which really gets worse is if you blast the screen full with crazy emojis and zalgo text and even this case only leads to 4 extra bytes per char. When only <= 4-byte glyphs are used, plus the 4-byte attribute code, i e 8 bytes in total there is a factor of four reduction of memory use. Memory which will be quite hot in cache as the screen buffer is scanned over in win_line() buffer text drawing A slight complication is that the representation depends on host byte order. I've tested this manually by compling and running this in qemu-s390x and it works fine. We might add a qemu based solution to CI at some point.
230 lines
6.1 KiB
C
230 lines
6.1 KiB
C
#ifndef NVIM_MAP_H
|
|
#define NVIM_MAP_H
|
|
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
|
|
#include "nvim/api/private/defs.h"
|
|
#include "nvim/assert.h"
|
|
#include "nvim/highlight_defs.h"
|
|
#include "nvim/types.h"
|
|
|
|
#if defined(__NetBSD__)
|
|
# undef uint64_t
|
|
# define uint64_t uint64_t
|
|
#endif
|
|
|
|
typedef const char *cstr_t;
|
|
typedef void *ptr_t;
|
|
|
|
// when used as a key, String doesn't need to be NUL terminated,
|
|
// and can also contain embedded NUL:s as part of the data.
|
|
static inline uint32_t hash_String(String s)
|
|
{
|
|
uint32_t h = 0;
|
|
for (size_t i = 0; i < s.size; i++) {
|
|
h = (h << 5) - h + (uint8_t)s.data[i];
|
|
}
|
|
return h;
|
|
}
|
|
|
|
static inline bool equal_String(String a, String b)
|
|
{
|
|
if (a.size != b.size) {
|
|
return false;
|
|
}
|
|
return memcmp(a.data, b.data, a.size) == 0;
|
|
}
|
|
|
|
#define Set(type) Set_##type
|
|
#define Map(T, U) Map_##T##U
|
|
#define PMap(T) Map(T, ptr_t)
|
|
|
|
static const int value_init_int = 0;
|
|
static const ptr_t value_init_ptr_t = NULL;
|
|
static const ssize_t value_init_ssize_t = -1;
|
|
static const uint32_t value_init_uint32_t = 0;
|
|
static const uint64_t value_init_uint64_t = 0;
|
|
static const int64_t value_init_int64_t = 0;
|
|
static const String value_init_String = STRING_INIT;
|
|
static const ColorItem value_init_ColorItem = COLOR_ITEM_INITIALIZER;
|
|
|
|
// layer 0: type non-specific code
|
|
|
|
typedef struct {
|
|
uint32_t n_buckets;
|
|
uint32_t size;
|
|
uint32_t n_occupied;
|
|
uint32_t upper_bound;
|
|
uint32_t n_keys; // this is almost always "size", but keys[] could contain ded items..
|
|
uint32_t keys_capacity;
|
|
uint32_t *hash;
|
|
} MapHash;
|
|
|
|
#define MAPHASH_INIT { 0, 0, 0, 0, 0, 0, NULL }
|
|
#define SET_INIT { MAPHASH_INIT, NULL }
|
|
#define MAP_INIT { SET_INIT, NULL }
|
|
|
|
#define MH_TOMBSTONE UINT32_MAX
|
|
|
|
#define mh_is_empty(h, i) ((h)->hash[i] == 0)
|
|
#define mh_is_del(h, i) ((h)->hash[i] == MH_TOMBSTONE)
|
|
#define mh_is_either(h, i) ((uint32_t)((h)->hash[i] + 1U) <= 1U)
|
|
|
|
typedef enum {
|
|
kMHExisting = 0,
|
|
kMHNewKeyDidFit,
|
|
kMHNewKeyRealloc,
|
|
} MHPutStatus;
|
|
|
|
void mh_clear(MapHash *h);
|
|
void mh_realloc(MapHash *h, uint32_t n_min_buckets);
|
|
|
|
// layer 1: key type specific defs
|
|
// This is all need for sets.
|
|
|
|
#define MH_DECLS(T, K, K_query) \
|
|
typedef struct { \
|
|
MapHash h; \
|
|
K *keys; \
|
|
} Set(T); \
|
|
\
|
|
uint32_t mh_find_bucket_##T(Set(T) *set, K_query key, bool put); \
|
|
uint32_t mh_get_##T(Set(T) *set, K_query key); \
|
|
void mh_rehash_##T(Set(T) *set); \
|
|
uint32_t mh_put_##T(Set(T) *set, K_query key, MHPutStatus *new); \
|
|
|
|
#define KEY_DECLS(T) \
|
|
MH_DECLS(T, T, T) \
|
|
uint32_t mh_delete_##T(Set(T) *set, T *key); \
|
|
static inline bool set_put_##T(Set(T) *set, T key, T **key_alloc) { \
|
|
MHPutStatus status; \
|
|
uint32_t k = mh_put_##T(set, key, &status); \
|
|
if (key_alloc) { \
|
|
*key_alloc = &set->keys[k]; \
|
|
} \
|
|
return status != kMHExisting; \
|
|
} \
|
|
static inline T set_del_##T(Set(T) *set, T key) \
|
|
{ \
|
|
mh_delete_##T(set, &key); \
|
|
return key; \
|
|
} \
|
|
static inline bool set_has_##T(Set(T) *set, T key) { \
|
|
return mh_get_##T(set, key) != MH_TOMBSTONE; \
|
|
} \
|
|
|
|
// layer 2: key+value specific defs
|
|
// now we finally get Maps
|
|
|
|
#define MAP_DECLS(T, U) \
|
|
typedef struct { \
|
|
Set(T) set; \
|
|
U *values; \
|
|
} Map(T, U); \
|
|
static inline U map_get_##T##U(Map(T, U) *map, T key) \
|
|
{ \
|
|
uint32_t k = mh_get_##T(&map->set, key); \
|
|
return k == MH_TOMBSTONE ? value_init_##U : map->values[k]; \
|
|
} \
|
|
U *map_ref_##T##U(Map(T, U) *map, T key, T **key_alloc); \
|
|
U *map_put_ref_##T##U(Map(T, U) *map, T key, T **key_alloc, bool *new_item); \
|
|
static inline void map_put_##T##U(Map(T, U) *map, T key, U value) \
|
|
{ \
|
|
U *val = map_put_ref_##T##U(map, key, NULL, NULL); \
|
|
*val = value; \
|
|
} \
|
|
U map_del_##T##U(Map(T, U) *map, T key, T *key_alloc); \
|
|
|
|
// NOTE: Keys AND values must be allocated! Map and Set does not make a copy.
|
|
|
|
#define quasiquote(x, y) x##y
|
|
|
|
MH_DECLS(glyph, char, String)
|
|
KEY_DECLS(int)
|
|
KEY_DECLS(cstr_t)
|
|
KEY_DECLS(ptr_t)
|
|
KEY_DECLS(uint64_t)
|
|
KEY_DECLS(int64_t)
|
|
KEY_DECLS(uint32_t)
|
|
KEY_DECLS(String)
|
|
KEY_DECLS(HlEntry)
|
|
KEY_DECLS(ColorKey)
|
|
|
|
MAP_DECLS(int, int)
|
|
MAP_DECLS(int, ptr_t)
|
|
MAP_DECLS(cstr_t, ptr_t)
|
|
MAP_DECLS(cstr_t, int)
|
|
MAP_DECLS(ptr_t, ptr_t)
|
|
MAP_DECLS(uint32_t, ptr_t)
|
|
MAP_DECLS(uint64_t, ptr_t)
|
|
MAP_DECLS(uint64_t, ssize_t)
|
|
MAP_DECLS(uint64_t, uint64_t)
|
|
MAP_DECLS(int64_t, int64_t)
|
|
MAP_DECLS(int64_t, ptr_t)
|
|
MAP_DECLS(uint32_t, uint32_t)
|
|
MAP_DECLS(String, int)
|
|
MAP_DECLS(int, String)
|
|
MAP_DECLS(ColorKey, ColorItem)
|
|
|
|
#define set_has(T, set, key) set_has_##T(set, key)
|
|
#define set_put(T, set, key) set_put_##T(set, key, NULL)
|
|
#define set_put_ref(T, set, key, key_alloc) set_put_##T(set, key, key_alloc)
|
|
#define set_put_idx(T, set, key, status) mh_put_##T(set, key, status)
|
|
#define set_del(T, set, key) set_del_##T(set, key)
|
|
#define set_destroy(T, set) (xfree((set)->keys), xfree((set)->h.hash))
|
|
#define set_clear(T, set) mh_clear(&(set)->h)
|
|
#define set_size(set) ((set)->h.size)
|
|
|
|
#define map_get(T, U) map_get_##T##U
|
|
#define map_has(T, map, key) set_has(T, &(map)->set, key)
|
|
#define map_put(T, U) map_put_##T##U
|
|
#define map_ref(T, U) map_ref_##T##U
|
|
#define map_put_ref(T, U) map_put_ref_##T##U
|
|
#define map_del(T, U) map_del_##T##U
|
|
#define map_destroy(T, map) (set_destroy(T, &(map)->set), xfree((map)->values))
|
|
#define map_clear(T, map) set_clear(T, &(map)->set)
|
|
#define map_size(map) set_size(&(map)->set)
|
|
|
|
#define pmap_get(T) map_get(T, ptr_t)
|
|
#define pmap_put(T) map_put(T, ptr_t)
|
|
#define pmap_ref(T) map_ref(T, ptr_t)
|
|
#define pmap_put_ref(T) map_put_ref(T, ptr_t)
|
|
/// @see pmap_del2
|
|
#define pmap_del(T) map_del(T, ptr_t)
|
|
|
|
#define set_foreach(set, key, block) \
|
|
{ \
|
|
uint32_t __i; \
|
|
for (__i = 0; __i < (set)->h.n_keys; __i++) { \
|
|
(key) = (set)->keys[__i]; \
|
|
block; \
|
|
} \
|
|
}
|
|
|
|
#define map_foreach_key(map, key, block) set_foreach(&(map)->set, key, block)
|
|
|
|
#define map_foreach(map, key, value, code) \
|
|
{ \
|
|
uint32_t __i; \
|
|
for (__i = 0; __i < (map)->set.h.n_keys; __i++) { \
|
|
(key) = (map)->set.keys[__i]; \
|
|
(value) = (map)->values[__i]; \
|
|
code; \
|
|
} \
|
|
}
|
|
|
|
#define map_foreach_value(map, value, code) \
|
|
{ \
|
|
uint32_t __i; \
|
|
for (__i = 0; __i < (map)->set.h.n_keys; __i++) { \
|
|
(value) = (map)->values[__i]; \
|
|
code; \
|
|
} \
|
|
}
|
|
|
|
void pmap_del2(PMap(cstr_t) *map, const char *key);
|
|
|
|
#endif // NVIM_MAP_H
|