Merge pull request #732 from tarruda/map-improvements

This commit is contained in:
Thiago de Arruda
2014-05-19 11:01:55 -03:00
7 changed files with 133 additions and 165 deletions

View File

@@ -2,6 +2,9 @@ src/nvim/indent.c
src/nvim/indent.h src/nvim/indent.h
src/nvim/log.c src/nvim/log.c
src/nvim/log.h src/nvim/log.h
src/nvim/map.c
src/nvim/map.h
src/nvim/map_defs.h
src/nvim/os/env.c src/nvim/os/env.c
src/nvim/os/event.c src/nvim/os/event.c
src/nvim/os/event_defs.h src/nvim/os/event_defs.h

View File

@@ -48,6 +48,7 @@ set(CONV_SRCS
arabic.c arabic.c
garray.c garray.c
memory.c memory.c
map.c
os/env.c os/env.c
os/event.c os/event.c
os/job.c os/job.c

View File

@@ -9,27 +9,18 @@
#include "nvim/window.h" #include "nvim/window.h"
#include "nvim/memory.h" #include "nvim/memory.h"
#include "nvim/eval.h" #include "nvim/eval.h"
#include "nvim/map_defs.h"
#include "nvim/map.h"
#include "nvim/option.h" #include "nvim/option.h"
#include "nvim/option_defs.h" #include "nvim/option_defs.h"
#include "nvim/lib/khash.h"
#if defined(ARCH_64)
#define ptr_hash_func(key) kh_int64_hash_func(key)
#elif defined(ARCH_32)
#define ptr_hash_func(key) kh_int_hash_func(key)
#endif
KHASH_INIT(Lookup, uintptr_t, char, 0, ptr_hash_func, kh_int_hash_equal)
/// Recursion helper for the `vim_to_object`. This uses a pointer table /// Recursion helper for the `vim_to_object`. This uses a pointer table
/// to avoid infinite recursion due to cyclic references /// to avoid infinite recursion due to cyclic references
/// ///
/// @param obj The source object /// @param obj The source object
/// @param lookup Lookup table containing pointers to all processed objects /// @param lookup Lookup table containing pointers to all processed objects
/// @return The converted value /// @return The converted value
static Object vim_to_object_rec(typval_T *obj, khash_t(Lookup) *lookup); static Object vim_to_object_rec(typval_T *obj, Map(ptr_t) *lookup);
static bool object_to_vim(Object obj, typval_T *tv, Error *err); static bool object_to_vim(Object obj, typval_T *tv, Error *err);
@@ -285,10 +276,10 @@ Object vim_to_object(typval_T *obj)
{ {
Object rv; Object rv;
// We use a lookup table to break out of cyclic references // We use a lookup table to break out of cyclic references
khash_t(Lookup) *lookup = kh_init(Lookup); Map(ptr_t) *lookup = map_new(ptr_t)();
rv = vim_to_object_rec(obj, lookup); rv = vim_to_object_rec(obj, lookup);
// Free the table // Free the table
kh_destroy(Lookup, lookup); map_free(ptr_t)(lookup);
return rv; return rv;
} }
@@ -443,19 +434,18 @@ static bool object_to_vim(Object obj, typval_T *tv, Error *err)
return true; return true;
} }
static Object vim_to_object_rec(typval_T *obj, khash_t(Lookup) *lookup) static Object vim_to_object_rec(typval_T *obj, Map(ptr_t) *lookup)
{ {
Object rv = {.type = kObjectTypeNil}; Object rv = {.type = kObjectTypeNil};
if (obj->v_type == VAR_LIST || obj->v_type == VAR_DICT) { if (obj->v_type == VAR_LIST || obj->v_type == VAR_DICT) {
int ret;
// Container object, add it to the lookup table // Container object, add it to the lookup table
kh_put(Lookup, lookup, (uintptr_t)obj, &ret); if (map_has(ptr_t)(lookup, obj)) {
if (!ret) {
// It's already present, meaning we alredy processed it so just return // It's already present, meaning we alredy processed it so just return
// nil instead. // nil instead.
return rv; return rv;
} }
map_put(ptr_t)(lookup, obj, NULL);
} }
switch (obj->v_type) { switch (obj->v_type) {

View File

@@ -8,84 +8,80 @@
#include "nvim/lib/khash.h" #include "nvim/lib/khash.h"
typedef struct { #define cstr_t_hash kh_str_hash_func
void *ptr; #define cstr_t_eq kh_str_hash_equal
} Value; #define uint64_t_hash kh_int64_hash_func
#define uint64_t_eq kh_int64_hash_equal
#define uint32_t_hash kh_int_hash_func
#define uint32_t_eq kh_int_hash_equal
KHASH_MAP_INIT_STR(Map, Value) #if defined(ARCH_64)
#define ptr_t_hash(key) uint64_t_hash((uint64_t)key)
#define ptr_t_eq(a, b) uint64_t_eq((uint64_t)a, (uint64_t)b)
#elif defined(ARCH_32)
#define ptr_t_hash(key) uint32_t_hash((uint32_t)key)
#define ptr_t_eq(a, b) uint32_t_eq((uint32_t)a, (uint32_t)b)
#endif
struct map {
khash_t(Map) *table;
};
Map *map_new() #define MAP_IMPL(T) \
{ __KHASH_IMPL(T##_map,, T, void *, 1, T##_hash, T##_eq) \
Map *rv = xmalloc(sizeof(Map)); \
rv->table = kh_init(Map); Map(T) *map_##T##_new() \
return rv; { \
} Map(T) *rv = xmalloc(sizeof(Map(T))); \
rv->table = kh_init(T##_map); \
void map_free(Map *map) return rv; \
{ } \
kh_clear(Map, map->table); \
kh_destroy(Map, map->table); void map_##T##_free(Map(T) *map) \
free(map); { \
} kh_destroy(T##_map, map->table); \
free(map); \
void *map_get(Map *map, const char *key) } \
{ \
khiter_t k; void *map_##T##_get(Map(T) *map, T key) \
{ \
if ((k = kh_get(Map, map->table, key)) == kh_end(map->table)) { khiter_t k; \
return NULL; \
} if ((k = kh_get(T##_map, map->table, key)) == kh_end(map->table)) { \
return NULL; \
return kh_val(map->table, k).ptr; } \
} \
return kh_val(map->table, k); \
bool map_has(Map *map, const char *key) } \
{ \
return map_get(map, key) != NULL; bool map_##T##_has(Map(T) *map, T key) \
} { \
return kh_get(T##_map, map->table, key) != kh_end(map->table); \
void *map_put(Map *map, const char *key, void *value) } \
{ \
int ret; void *map_##T##_put(Map(T) *map, T key, void *value) \
void *rv = NULL; { \
khiter_t k = kh_put(Map, map->table, key, &ret); int ret; \
Value val = {.ptr = value}; void *rv = NULL; \
khiter_t k = kh_put(T##_map, map->table, key, &ret); \
if (!ret) { \
// key present, return the current value if (!ret) { \
rv = kh_val(map->table, k).ptr; rv = kh_val(map->table, k); \
kh_del(Map, map->table, k); } \
} \
kh_val(map->table, k) = value; \
kh_val(map->table, k) = val; return rv; \
} \
return rv; \
} void *map_##T##_del(Map(T) *map, T key) \
{ \
void *map_del(Map *map, const char *key) void *rv = NULL; \
{ khiter_t k; \
void *rv = NULL; \
khiter_t k; if ((k = kh_get(T##_map, map->table, key)) != kh_end(map->table)) { \
rv = kh_val(map->table, k); \
if ((k = kh_get(Map, map->table, key)) != kh_end(map->table)) { kh_del(T##_map, map->table, k); \
rv = kh_val(map->table, k).ptr; } \
kh_del(Map, map->table, k); \
} return rv; \
return rv;
}
void map_foreach(Map *map, key_value_cb cb)
{
const char *key;
Value value;
kh_foreach(map->table, key, value, {
cb(map, (const char *)key, value.ptr);
});
} }
MAP_IMPL(cstr_t)
MAP_IMPL(ptr_t)

View File

@@ -1,4 +1,3 @@
// General-purpose string->pointer associative array with a simple API
#ifndef NVIM_MAP_H #ifndef NVIM_MAP_H
#define NVIM_MAP_H #define NVIM_MAP_H
@@ -6,52 +5,35 @@
#include "nvim/map_defs.h" #include "nvim/map_defs.h"
/// Creates a new `Map` instance #define MAP_DECLS(T) \
/// KHASH_DECLARE(T##_map, T, void *) \
/// @return a pointer to the new instance \
Map *map_new(void); typedef struct { \
khash_t(T##_map) *table; \
} Map(T); \
\
Map(T) *map_##T##_new(void); \
void map_##T##_free(Map(T) *map); \
void *map_##T##_get(Map(T) *map, T key); \
bool map_##T##_has(Map(T) *map, T key); \
void* map_##T##_put(Map(T) *map, T key, void *value); \
void* map_##T##_del(Map(T) *map, T key);
/// Frees memory for a `Map` instance MAP_DECLS(cstr_t)
/// MAP_DECLS(ptr_t)
/// @param map The `Map` instance
void map_free(Map *map);
/// Gets the value corresponding to a key in a `Map` instance #define map_new(T) map_##T##_new
/// #define map_free(T) map_##T##_free
/// @param map The `Map` instance #define map_get(T) map_##T##_get
/// @param key A key string #define map_has(T) map_##T##_has
/// @return The value if the key exists in the map, or NULL if it doesn't #define map_put(T) map_##T##_put
void *map_get(Map *map, const char *key); #define map_del(T) map_##T##_del
/// Checks if a key exists in the map #define map_foreach(map, key, value, block) \
/// kh_foreach(map->table, key, value, block)
/// @param map The `Map` instance
/// @param key A key string
/// @return true if the key exists, false otherwise
bool map_has(Map *map, const char *key);
/// Set the value corresponding to a key in a `Map` instance and returns #define map_foreach_value(map, value, block) \
/// the old value. kh_foreach_value(map->table, value, block)
///
/// @param map The `Map` instance
/// @param key A key string
/// @param value A value
/// @return The current value if exists or NULL otherwise
void *map_put(Map *map, const char *key, void *value);
/// Deletes the value corresponding to a key in a `Map` instance and returns #endif // NVIM_MAP_H
/// the old value.
///
/// @param map The `Map` instance
/// @param key A key string
/// @return The current value if exists or NULL otherwise
void *map_del(Map *map, const char *key);
/// Iterates through each key/value pair in the map
///
/// @param map The `Map` instance
/// @param cb A function that will be called for each key/value
void map_foreach(Map *map, key_value_cb cb);
#endif /* NVIM_MAP_H */

View File

@@ -1,14 +1,14 @@
#ifndef NVIM_MAP_DEFS_H #ifndef NVIM_MAP_DEFS_H
#define NVIM_MAP_DEFS_H #define NVIM_MAP_DEFS_H
typedef struct map Map;
/// Callback for iterating through each key/value pair in a map #include "nvim/lib/khash.h"
///
/// @param map The `Map` instance
/// @param key A key string
/// @param value A value
typedef void (*key_value_cb)(Map *map, const char *key, void *value);
#endif /* NVIM_MAP_DEFS_H */ typedef const char * cstr_t;
typedef void * ptr_t;
#define Map(T) Map_##T
#endif // NVIM_MAP_DEFS_H

View File

@@ -43,16 +43,15 @@ typedef struct {
} socket; } socket;
} Server; } Server;
static Map *servers = NULL; static Map(cstr_t) *servers = NULL;
static void close_server(Map *map, const char *endpoint, void *server);
static void connection_cb(uv_stream_t *server, int status); static void connection_cb(uv_stream_t *server, int status);
static void free_client(uv_handle_t *handle); static void free_client(uv_handle_t *handle);
static void free_server(uv_handle_t *handle); static void free_server(uv_handle_t *handle);
void server_init() void server_init()
{ {
servers = map_new(); servers = map_new(cstr_t)();
if (!os_getenv("NEOVIM_LISTEN_ADDRESS")) { if (!os_getenv("NEOVIM_LISTEN_ADDRESS")) {
char *listen_address = (char *)vim_tempname('s'); char *listen_address = (char *)vim_tempname('s');
@@ -70,7 +69,15 @@ void server_teardown()
return; return;
} }
map_foreach(servers, close_server); Server *server;
map_foreach_value(servers, server, {
if (server->type == kServerTypeTcp) {
uv_close((uv_handle_t *)&server->socket.tcp.handle, free_server);
} else {
uv_close((uv_handle_t *)&server->socket.pipe.handle, free_server);
}
});
} }
void server_start(char *endpoint, ChannelProtocol prot) void server_start(char *endpoint, ChannelProtocol prot)
@@ -81,7 +88,7 @@ void server_start(char *endpoint, ChannelProtocol prot)
strncpy(addr, endpoint, sizeof(addr)); strncpy(addr, endpoint, sizeof(addr));
// Check if the server already exists // Check if the server already exists
if (map_has(servers, addr)) { if (map_has(cstr_t)(servers, addr)) {
EMSG2("Already listening on %s", addr); EMSG2("Already listening on %s", addr);
return; return;
} }
@@ -167,7 +174,7 @@ void server_start(char *endpoint, ChannelProtocol prot)
server->type = server_type; server->type = server_type;
// Add the server to the hash table // Add the server to the hash table
map_put(servers, addr, server); map_put(cstr_t)(servers, addr, server);
} }
void server_stop(char *endpoint) void server_stop(char *endpoint)
@@ -178,7 +185,7 @@ void server_stop(char *endpoint)
// Trim to `ADDRESS_MAX_SIZE` // Trim to `ADDRESS_MAX_SIZE`
strncpy(addr, endpoint, sizeof(addr)); strncpy(addr, endpoint, sizeof(addr));
if ((server = map_get(servers, addr)) == NULL) { if ((server = map_get(cstr_t)(servers, addr)) == NULL) {
EMSG2("Not listening on %s", addr); EMSG2("Not listening on %s", addr);
return; return;
} }
@@ -189,7 +196,7 @@ void server_stop(char *endpoint)
uv_close((uv_handle_t *)&server->socket.pipe.handle, free_server); uv_close((uv_handle_t *)&server->socket.pipe.handle, free_server);
} }
map_del(servers, addr); map_del(cstr_t)(servers, addr);
} }
static void connection_cb(uv_stream_t *server, int status) static void connection_cb(uv_stream_t *server, int status)
@@ -221,17 +228,6 @@ static void connection_cb(uv_stream_t *server, int status)
channel_from_stream(client, srv->protocol); channel_from_stream(client, srv->protocol);
} }
static void close_server(Map *map, const char *endpoint, void *srv)
{
Server *server = srv;
if (server->type == kServerTypeTcp) {
uv_close((uv_handle_t *)&server->socket.tcp.handle, free_server);
} else {
uv_close((uv_handle_t *)&server->socket.pipe.handle, free_server);
}
}
static void free_client(uv_handle_t *handle) static void free_client(uv_handle_t *handle)
{ {
free(handle); free(handle);