mirror of
https://github.com/neovim/neovim.git
synced 2026-04-20 06:20:53 +00:00
This makes little difference in practice except for extremely unlikely ambiguities. But it looks nicer in compiler errors and stacktraces where you see the fully expanded names
238 lines
6.3 KiB
C
238 lines
6.3 KiB
C
#pragma once
|
|
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
|
|
#include "nvim/api/private/defs.h"
|
|
#include "nvim/assert_defs.h"
|
|
#include "nvim/highlight_defs.h"
|
|
#include "nvim/types_defs.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 (a.size == 0) || (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(uint64_t, int)
|
|
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_size(set) ((set)->h.size)
|
|
#define set_clear(T, set) mh_clear(&(set)->h)
|
|
#define set_destroy(T, set) \
|
|
do { \
|
|
xfree((set)->keys); \
|
|
xfree((set)->h.hash); \
|
|
*(set) = (Set(T)) SET_INIT; \
|
|
} while (0)
|
|
|
|
#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_size(map) set_size(&(map)->set)
|
|
#define map_clear(T, map) set_clear(T, &(map)->set)
|
|
#define map_destroy(T, map) \
|
|
do { \
|
|
set_destroy(T, &(map)->set); \
|
|
XFREE_CLEAR((map)->values); \
|
|
} while (0)
|
|
|
|
#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);
|