/* * libwebsockets - small server side websockets and web server implementation * * Copyright (C) 2010 - 2021 Andy Green * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "private-lib-core.h" typedef struct lws_map_hashtable { struct lws_map *map_owner; /* so items can find map */ lws_dll2_owner_t ho; } lws_map_hashtable_t; typedef struct lws_map { lws_map_info_t info; /* array of info.modulo x lws_map_hashtable_t overallocated */ } lws_map_t; typedef struct lws_map_item { lws_dll2_t list; /* owned by hashtable */ size_t keylen; size_t valuelen; /* key then value is overallocated */ } lws_map_item_t; /* * lwsac-aware allocator */ void * lws_map_alloc_lwsac(struct lws_map *map, size_t x) { return lwsac_use((struct lwsac **)map->info.opaque, x, (size_t)map->info.aux); } void lws_map_free_lwsac(void *v) { } /* * Default allocation / free if none given in info */ void * lws_map_alloc_lws_malloc(struct lws_map *mo, size_t x) { return lws_malloc(x, __func__); } void lws_map_free_lws_free(void *v) { lws_free(v); } /* * This just needs to approximate a flat distribution, it's not related to * security at all. */ lws_map_hash_t lws_map_hash_from_key_default(const lws_map_key_t key, size_t kl) { lws_map_hash_t h = 0x12345678; const uint8_t *u = (const uint8_t *)key; while (kl--) h = ((((h << 7) | (h >> 25)) + 0xa1b2c3d4) ^ (*u++)) ^ h; return h; } int lws_map_compare_key_default(const lws_map_key_t key1, size_t kl1, const lws_map_value_t key2, size_t kl2) { if (kl1 != kl2) return 1; return memcmp(key1, key2, kl1); } lws_map_t * lws_map_create(const lws_map_info_t *info) { lws_map_t *map; lws_map_alloc_t a = info->_alloc; size_t modulo = info->modulo; lws_map_hashtable_t *ht; size_t size; if (!a) a = lws_map_alloc_lws_malloc; if (!modulo) modulo = 8; size = sizeof(*map) + (modulo * sizeof(lws_map_hashtable_t)); map = lws_malloc(size, __func__); if (!map) return NULL; memset(map, 0, size); map->info = *info; map->info._alloc = a; map->info.modulo = modulo; if (!info->_free) map->info._free = lws_map_free_lws_free; if (!info->_hash) map->info._hash = lws_map_hash_from_key_default; if (!info->_compare) map->info._compare = lws_map_compare_key_default; ht = (lws_map_hashtable_t *)&map[1]; while (modulo--) ht[modulo].map_owner = map; return map; } static int ho_free_item(struct lws_dll2 *d, void *user) { lws_map_item_t *i = lws_container_of(d, lws_map_item_t, list); lws_map_item_destroy(i); return 0; } void lws_map_destroy(lws_map_t **pmap) { lws_map_hashtable_t *ht; lws_map_t *map = *pmap; if (!map) return; /* empty out all the hashtables */ ht = (lws_map_hashtable_t *)&(map[1]); while (map->info.modulo--) { lws_dll2_foreach_safe(&ht->ho, ht, ho_free_item); ht++; } /* free the map itself */ lws_free_set_NULL(*pmap); } lws_map_item_t * lws_map_item_create(lws_map_t *map, const lws_map_key_t key, size_t keylen, const lws_map_value_t value, size_t valuelen) { lws_map_hashtable_t *ht; lws_map_item_t *item; lws_map_hash_t h; size_t hti; uint8_t *u; item = lws_map_item_lookup(map, key, keylen); if (item) lws_map_item_destroy(item); item = map->info._alloc(map, sizeof(*item) + keylen + valuelen); if (!item) return NULL; lws_dll2_clear(&item->list); item->keylen = keylen; item->valuelen = valuelen; u = (uint8_t *)&item[1]; memcpy(u, key, keylen); u += keylen; if (value) memcpy(u, value, valuelen); h = map->info._hash(key, keylen); hti = h % map->info.modulo; ht = (lws_map_hashtable_t *)&map[1]; lws_dll2_add_head(&item->list, &ht[hti].ho); return item; } void lws_map_item_destroy(lws_map_item_t *item) { lws_map_hashtable_t *ht = lws_container_of(item->list.owner, lws_map_hashtable_t, ho); lws_dll2_remove(&item->list); ht->map_owner->info._free(item); } lws_map_item_t * lws_map_item_lookup(lws_map_t *map, const lws_map_key_t key, size_t keylen) { lws_map_hash_t h = map->info._hash(key, keylen); lws_map_hashtable_t *ht = (lws_map_hashtable_t *)&map[1]; lws_start_foreach_dll(struct lws_dll2 *, p, ht[h % map->info.modulo].ho.head) { lws_map_item_t *i = lws_container_of(p, lws_map_item_t, list); if (!map->info._compare(key, keylen, &i[1], i->keylen)) return i; } lws_end_foreach_dll(p); return NULL; } const void * lws_map_item_key(lws_map_item_t *_item) { return ((void *)&_item[1]); } const void * lws_map_item_value(lws_map_item_t *_item) { return (void *)(((uint8_t *)&_item[1]) + _item->keylen); } size_t lws_map_item_key_len(lws_map_item_t *_item) { return _item->keylen; } size_t lws_map_item_value_len(lws_map_item_t *_item) { return _item->valuelen; }