mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-09 00:00:04 +01:00
lws_map
This commit is contained in:
parent
6ab149b5d3
commit
b67d192100
7 changed files with 855 additions and 0 deletions
117
READMEs/README.lws_map.md
Normal file
117
READMEs/README.lws_map.md
Normal file
|
@ -0,0 +1,117 @@
|
|||
# lws_map generic map abstraction
|
||||
|
||||
|||
|
||||
|---|---|---|
|
||||
|cmake|core feature|
|
||||
|Header| ./include/libwebsockets/lws-map.h|
|
||||
|api-test| ./minimal-examples/api-tests/api-test-lws_map/|
|
||||
|
||||
lws_map provides a robust abstraction for containing a collection of items that
|
||||
map key objects to value objects, where both the key and value objects may
|
||||
differ in size each time and have any type.
|
||||
|
||||
Its one-level linked-list hashtables are useful up to hundreds or low thousands
|
||||
of items in the map on may platforms.
|
||||
|
||||
The map itself and the items inside it are opaque.
|
||||
|
||||
## Creating and destroying the map
|
||||
|
||||
The user should prepare a `lws_map_info_t` object, it's legal for it to be
|
||||
all zeros to select defaults, an 8-way hashtable with item allocation from heap,
|
||||
simple bytewise key comparison, and xor / shift key hashing. These are often
|
||||
what you want simplifying construction.
|
||||
|
||||
The info object allows user override of item allocator, freeing, key comparison
|
||||
and object hashing, allowing custom objects to be keys if desired.
|
||||
|
||||
Custom allocator / free implementations for using lwsac for item allocation are
|
||||
provided to simplify that case.
|
||||
|
||||
Just call `lws_map_create()` with the info struct to create the map, later it
|
||||
and all its contents can be destroyed with `lws_map_destroy()`. The info struct
|
||||
can go out of scope immediately after the create call.
|
||||
|
||||
```
|
||||
lws_map_t *
|
||||
lws_map_create(const lws_map_info_t *info);
|
||||
void
|
||||
lws_map_destroy(lws_map_t **pmap);
|
||||
```
|
||||
|
||||
## Keys in lws_map
|
||||
|
||||
Items are managed in the map by a key, this may be, eg, a string, but it also
|
||||
can be an arbitrary object itself. If comparing keys takes more than a simple
|
||||
bytewise comparison, the map creation info struct ._compare() operation should
|
||||
be overridden with a user-supplied one that knows how to use the user's
|
||||
custom key objects.
|
||||
|
||||
Keys are not required to be the same length, so objects with variable size
|
||||
overallocation can be used as keys.
|
||||
|
||||
Keys and values are copied into allocations inside the map, the original objects
|
||||
they are copied from may go out of scope after item creation assuming there are
|
||||
no pointers to them copied in the objects themselves.
|
||||
|
||||
## Adding items to a map
|
||||
|
||||
The map's info._alloc allocator is used for creating items. By default that
|
||||
just creates into the heap.
|
||||
|
||||
If you create a new item with the same key as an existing one, the existing one
|
||||
is destroyed before the new one is created. So there is only one item allowed
|
||||
at a given key at a time.
|
||||
|
||||
To allocate and create a new item containing the key and value, use
|
||||
`lws_map_item_create()`
|
||||
|
||||
```
|
||||
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);
|
||||
```
|
||||
|
||||
Eg,
|
||||
|
||||
```
|
||||
if (!lws_map_item_create(map, (lws_map_key_t)&my_key,
|
||||
sizeof(my_key),
|
||||
(lws_map_value_t)"4567", 4))
|
||||
/* fail */
|
||||
```
|
||||
|
||||
|
||||
In the case the key is a string, there is a ..._ks wrapper to simplify usage
|
||||
|
||||
```
|
||||
if (!lws_map_item_create_ks(map, "123", (lws_map_value_t)"4567", 4))
|
||||
/* fail */
|
||||
```
|
||||
|
||||
## Lookups in the map
|
||||
|
||||
You can retreive a pointer to an item in the map with a give key using
|
||||
|
||||
```
|
||||
lws_map_item_t *
|
||||
lws_map_item_lookup(lws_map_t *map, const lws_map_key_t key, size_t keylen);
|
||||
```
|
||||
|
||||
The item is opaque, but there are accessors
|
||||
|
||||
|Accessor|Function|
|
||||
|---|---|
|
||||
|`lws_map_item_key(lws_map_item_t *_item)`|get a pointer to the item key|
|
||||
|`lws_map_item_value(lws_map_item_t *_item)`|get a pointer to the item value|
|
||||
|`lws_map_item_key_len(lws_map_item_t *_item)`|get the key length|
|
||||
|`lws_map_item_value_len(lws_map_item_t *_item)`|get the value length|
|
||||
|
||||
Again there is a ..._ks() helper to simplify C strings as keys
|
||||
|
||||
```
|
||||
item = lws_map_item_lookup_ks(map, "abc");
|
||||
if (!item)
|
||||
/* fail */
|
||||
```
|
|
@ -571,6 +571,8 @@ struct lws_vhost;
|
|||
struct lws;
|
||||
|
||||
#include <libwebsockets/lws-dll2.h>
|
||||
#include <libwebsockets/lws-map.h>
|
||||
|
||||
#include <libwebsockets/lws-fault-injection.h>
|
||||
#include <libwebsockets/lws-timeout-timer.h>
|
||||
#include <libwebsockets/lws-cache-ttl.h>
|
||||
|
|
188
include/libwebsockets/lws-map.h
Normal file
188
include/libwebsockets/lws-map.h
Normal file
|
@ -0,0 +1,188 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/** \defgroup lws_map generic map apis
|
||||
* ##Generic map structures and apis
|
||||
* \ingroup lwsapi
|
||||
*
|
||||
* lws_map
|
||||
*
|
||||
* Discrete owner object represents the whole map, created with key-specific
|
||||
* ops for hashing the key to a uint32_t and comparing two keys. Owns a list
|
||||
* of hash tables whose size / modulo it set at creation time.
|
||||
*
|
||||
* Items in the map are contained in a lws_map_item_t that is indexed in a
|
||||
* hash table.
|
||||
*
|
||||
* It's difficult to make a single compact map abstraction that fits all cases,
|
||||
* this is useful to the extent you have the memory to trade off the number of
|
||||
* hashtables needed for the amount of items and the lookup latency limit for
|
||||
* your application, typically for hundreds or low thousands of items.
|
||||
*/
|
||||
//@{
|
||||
|
||||
typedef struct lws_map lws_map_t;
|
||||
typedef struct lws_map_item lws_map_item_t;
|
||||
|
||||
typedef void * lws_map_key_t;
|
||||
typedef void * lws_map_value_t;
|
||||
typedef uint32_t lws_map_hash_t;
|
||||
|
||||
typedef lws_map_hash_t (*lws_map_hash_from_key_t)(const lws_map_key_t key,
|
||||
size_t kl);
|
||||
typedef int (*lws_map_compare_key_t)(const lws_map_key_t key1, size_t kl1,
|
||||
const lws_map_value_t key2, size_t kl2);
|
||||
typedef void * (*lws_map_alloc_t)(struct lws_map *mo, size_t x);
|
||||
typedef void (*lws_map_free_t)(void *);
|
||||
|
||||
/*
|
||||
* Creation parameters for the map, copied into the map owner
|
||||
*/
|
||||
|
||||
typedef struct lws_map_info {
|
||||
lws_map_hash_from_key_t _hash;
|
||||
lws_map_compare_key_t _compare;
|
||||
lws_map_alloc_t _alloc; /* NULL = lws_malloc */
|
||||
lws_map_free_t _free; /* NULL = lws_free */
|
||||
|
||||
void *opaque;
|
||||
/**< &lwsac if using lwsac allocator */
|
||||
void *aux;
|
||||
/**< chunk size if using lwsac allocator */
|
||||
/**< this can be used by the alloc handler, eg for lws_ac */
|
||||
size_t modulo;
|
||||
/**< number of hashed owner lists to create */
|
||||
} lws_map_info_t;
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN const void *
|
||||
lws_map_item_key(lws_map_item_t *_item);
|
||||
LWS_VISIBLE LWS_EXTERN const void *
|
||||
lws_map_item_value(lws_map_item_t *_item);
|
||||
LWS_VISIBLE LWS_EXTERN size_t
|
||||
lws_map_item_key_len(lws_map_item_t *_item);
|
||||
LWS_VISIBLE LWS_EXTERN size_t
|
||||
lws_map_item_value_len(lws_map_item_t *_item);
|
||||
|
||||
/*
|
||||
* Helpers for C string keys case
|
||||
*/
|
||||
|
||||
#define lws_map_item_create_ks(_map, _str, _v, _vl) \
|
||||
lws_map_item_create(_map, (const lws_map_key_t)_str, \
|
||||
strlen(_str), (const lws_map_value_t)_v, \
|
||||
_vl)
|
||||
#define lws_map_item_lookup_ks(_map, _str) \
|
||||
lws_map_item_lookup(_map, (const lws_map_key_t)_str, strlen(_str))
|
||||
|
||||
/**
|
||||
* lws_map_create() - create a map object and hashtables on heap
|
||||
*
|
||||
* \param info: description of map to create
|
||||
*
|
||||
* Creates a map object on heap, using lws_malloc().
|
||||
*
|
||||
* \p info may be all zeros inside, if so, modulo defaults to 8, and the
|
||||
* operation callbacks default to using lws_malloc() / _free() for item alloc,
|
||||
* a default xor / shift based hash and simple linear memory key compare.
|
||||
*
|
||||
* For less typical use-cases, the provided \p info members can be tuned to
|
||||
* control how the allocation of mapped items is done, lws provides two exports
|
||||
* lws_map_alloc_lwsac() and lws_map_free_lwsac() that can be used for _alloc
|
||||
* and _free to have items allocated inside an lwsac.
|
||||
*
|
||||
* The map itself is created on the heap directly, the info._alloc() op is only
|
||||
* used when creating items.
|
||||
*
|
||||
* keys have individual memory sizes and do not need to all be the same length.
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN lws_map_t *
|
||||
lws_map_create(const lws_map_info_t *info);
|
||||
|
||||
/*
|
||||
* helpers that can be used for info._alloc and info._free if using lwsac
|
||||
* allocation for items, set info.opaque to point to the lwsac pointer, and
|
||||
* aux to (void *)chunksize, or leave zero / NULL for the default
|
||||
*/
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN void *
|
||||
lws_map_alloc_lwsac(struct lws_map *map, size_t x);
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN void
|
||||
lws_map_free_lwsac(void *v);
|
||||
|
||||
/**
|
||||
* lws_map_destroy() - deallocate all items and free map
|
||||
*
|
||||
* \param pmap: pointer to pointer map object to deallocate
|
||||
*
|
||||
* Frees all items in the map, using info._free(), and then frees the map
|
||||
* from heap directly. \p *pmap is set to NULL.
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN void
|
||||
lws_map_destroy(lws_map_t **pmap);
|
||||
|
||||
/**
|
||||
* lws_map_item_create() - allocate and map an item into an existing map
|
||||
*
|
||||
* \param map: the map to add items into
|
||||
* \param key: the key, may be any kind of object
|
||||
* \param keylen: the length of the key in bytes
|
||||
* \param value: the value, may be any kind of object
|
||||
* \param valuelen: the length of value
|
||||
*
|
||||
* Allocates space for the item, key and value using the map allocator, and
|
||||
* if non-NULL, copies the key and value into the item.
|
||||
*
|
||||
* If an item with the same key exists, it is removed and destroyed before
|
||||
* creating and adding the new one.
|
||||
*/
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN 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_item_destroy() - remove item from map and free
|
||||
*
|
||||
* \param item: the item in the map to remove and free
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN void
|
||||
lws_map_item_destroy(lws_map_item_t *item);
|
||||
|
||||
/**
|
||||
* lws_map_item_lookup() - look for a item with the given key in the map
|
||||
*
|
||||
* \param map: the map
|
||||
* \param key: the key to look for
|
||||
* \param keylen: the length of the key to look for
|
||||
*
|
||||
* Searches for the key in the map, using the map's key hash and key compare
|
||||
* functions.
|
||||
*/
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN lws_map_item_t *
|
||||
lws_map_item_lookup(lws_map_t *map, const lws_map_key_t key, size_t keylen);
|
||||
|
||||
//@}
|
|
@ -29,6 +29,7 @@ list(APPEND SOURCES
|
|||
core/buflist.c
|
||||
core/context.c
|
||||
core/lws_dll2.c
|
||||
core/lws_map.c
|
||||
core/libwebsockets.c
|
||||
core/logs.c
|
||||
)
|
||||
|
|
266
lib/core/lws_map.c
Normal file
266
lib/core/lws_map.c
Normal file
|
@ -0,0 +1,266 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* 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;
|
||||
}
|
17
minimal-examples/api-tests/api-test-lws_map/CMakeLists.txt
Normal file
17
minimal-examples/api-tests/api-test-lws_map/CMakeLists.txt
Normal file
|
@ -0,0 +1,17 @@
|
|||
project(lws-api-test-lws_map C)
|
||||
cmake_minimum_required(VERSION 2.8.12)
|
||||
find_package(libwebsockets CONFIG REQUIRED)
|
||||
list(APPEND CMAKE_MODULE_PATH ${LWS_CMAKE_DIR})
|
||||
include(CheckCSourceCompiles)
|
||||
|
||||
|
||||
add_executable(${PROJECT_NAME} main.c)
|
||||
add_test(NAME api-test-lws_map COMMAND lws-api-test-lws_map)
|
||||
|
||||
if (websockets_shared)
|
||||
target_link_libraries(${PROJECT_NAME} websockets_shared ${LIBWEBSOCKETS_DEP_LIBS})
|
||||
add_dependencies(${PROJECT_NAME} websockets_shared)
|
||||
else()
|
||||
target_link_libraries(${PROJECT_NAME} websockets ${LIBWEBSOCKETS_DEP_LIBS})
|
||||
endif()
|
||||
|
264
minimal-examples/api-tests/api-test-lws_map/main.c
Normal file
264
minimal-examples/api-tests/api-test-lws_map/main.c
Normal file
|
@ -0,0 +1,264 @@
|
|||
/*
|
||||
* lws-api-test-lws_map
|
||||
*
|
||||
* Written in 2010-2021 by Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This file is made available under the Creative Commons CC0 1.0
|
||||
* Universal Public Domain Dedication.
|
||||
*
|
||||
* unit tests for lws_map
|
||||
*/
|
||||
|
||||
#include <libwebsockets.h>
|
||||
|
||||
/* custom key and comparator for test 3 */
|
||||
|
||||
typedef struct mykey {
|
||||
int key;
|
||||
} mykey_t;
|
||||
|
||||
static int
|
||||
compare_mykey_t(const lws_map_key_t key1, size_t kl1,
|
||||
const lws_map_value_t key2, size_t kl2)
|
||||
{
|
||||
const mykey_t *m1 = (mykey_t *)key1, *m2 = (mykey_t *)key2;
|
||||
|
||||
return m1->key != m2->key;
|
||||
}
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
int e = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE,
|
||||
expected = 4, pass = 0;
|
||||
mykey_t k1 = { .key = 123 }, k2 = { .key = 234 }, k3 = { .key = 999 };
|
||||
struct lwsac *ac = NULL;
|
||||
lws_map_item_t *item;
|
||||
lws_map_info_t info;
|
||||
lws_map_t *map;
|
||||
const char *p;
|
||||
|
||||
if ((p = lws_cmdline_option(argc, argv, "-d")))
|
||||
logs = atoi(p);
|
||||
|
||||
lws_set_log_level(logs, NULL);
|
||||
lwsl_user("LWS API selftest: lws_map\n");
|
||||
|
||||
/* Test 1: string keys */
|
||||
|
||||
lwsl_user("%s: test1\n", __func__);
|
||||
memset(&info, 0, sizeof(info));
|
||||
map = lws_map_create(&info);
|
||||
if (!map) {
|
||||
e++;
|
||||
goto end_t1;
|
||||
}
|
||||
if (!lws_map_item_create_ks(map, "abc", (lws_map_value_t)"def", 3)) {
|
||||
e++;
|
||||
goto end_t1;
|
||||
}
|
||||
if (!lws_map_item_create_ks(map, "123", (lws_map_value_t)"4567", 4)) {
|
||||
e++;
|
||||
goto end_t1;
|
||||
}
|
||||
item = lws_map_item_lookup_ks(map, "abc");
|
||||
if (!item) {
|
||||
e++;
|
||||
goto end_t1;
|
||||
}
|
||||
|
||||
if (lws_map_item_value_len(item) != 3 ||
|
||||
memcmp(lws_map_item_value(item), "def", 3)) {
|
||||
e++;
|
||||
goto end_t1;
|
||||
}
|
||||
|
||||
item = lws_map_item_lookup_ks(map, "123");
|
||||
if (!item) {
|
||||
e++;
|
||||
goto end_t1;
|
||||
}
|
||||
|
||||
if (lws_map_item_value_len(item) != 4 ||
|
||||
memcmp(lws_map_item_value(item), "4567", 4)) {
|
||||
e++;
|
||||
goto end_t1;
|
||||
}
|
||||
|
||||
item = lws_map_item_lookup_ks(map, "nope");
|
||||
if (item) {
|
||||
e++;
|
||||
goto end_t1;
|
||||
}
|
||||
|
||||
pass++;
|
||||
|
||||
end_t1:
|
||||
lws_map_destroy(&map);
|
||||
|
||||
/* Test 2: Use lwsac item allocators */
|
||||
|
||||
lwsl_user("%s: test2\n", __func__);
|
||||
memset(&info, 0, sizeof(info));
|
||||
info._alloc = lws_map_alloc_lwsac;
|
||||
info._free = lws_map_free_lwsac;
|
||||
info.opaque = (void *)∾
|
||||
|
||||
map = lws_map_create(&info);
|
||||
if (!map) {
|
||||
e++;
|
||||
goto end_t2;
|
||||
}
|
||||
if (!lws_map_item_create_ks(map, "abc", "def", 3)) {
|
||||
e++;
|
||||
goto end_t2;
|
||||
}
|
||||
if (!lws_map_item_create_ks(map, "123", "4567", 4)) {
|
||||
e++;
|
||||
goto end_t2;
|
||||
}
|
||||
item = lws_map_item_lookup_ks(map, "abc");
|
||||
if (!item) {
|
||||
e++;
|
||||
goto end_t2;
|
||||
}
|
||||
|
||||
if (lws_map_item_value_len(item) != 3 ||
|
||||
memcmp(lws_map_item_value(item), "def", 3)) {
|
||||
e++;
|
||||
goto end_t2;
|
||||
}
|
||||
|
||||
item = lws_map_item_lookup_ks(map, "123");
|
||||
if (!item) {
|
||||
e++;
|
||||
goto end_t2;
|
||||
}
|
||||
|
||||
if (lws_map_item_value_len(item) != 4 ||
|
||||
memcmp(lws_map_item_value(item), "4567", 4)) {
|
||||
e++;
|
||||
goto end_t2;
|
||||
}
|
||||
|
||||
item = lws_map_item_lookup_ks(map, "nope");
|
||||
if (item) {
|
||||
e++;
|
||||
goto end_t2;
|
||||
}
|
||||
|
||||
pass++;
|
||||
|
||||
end_t2:
|
||||
lws_map_destroy(&map);
|
||||
lwsac_free(&ac);
|
||||
|
||||
/* Test 3: custom key object and comparator */
|
||||
|
||||
lwsl_user("%s: test3\n", __func__);
|
||||
memset(&info, 0, sizeof(info));
|
||||
info._compare = compare_mykey_t;
|
||||
|
||||
map = lws_map_create(&info);
|
||||
if (!map) {
|
||||
e++;
|
||||
goto end_t3;
|
||||
}
|
||||
if (!lws_map_item_create(map, (lws_map_key_t)&k1, sizeof(k1),
|
||||
(lws_map_value_t)"def", 3)) {
|
||||
lwsl_err("%s: t3; a\n", __func__);
|
||||
e++;
|
||||
goto end_t3;
|
||||
}
|
||||
if (!lws_map_item_create(map, (lws_map_key_t)&k2, sizeof(k2),
|
||||
(lws_map_value_t)"4567", 4)) {
|
||||
lwsl_err("%s: t3; b\n", __func__);
|
||||
e++;
|
||||
goto end_t3;
|
||||
}
|
||||
item = lws_map_item_lookup(map, (lws_map_key_t)&k1, sizeof(k1));
|
||||
if (!item) {
|
||||
lwsl_err("%s: t3; c\n", __func__);
|
||||
e++;
|
||||
goto end_t3;
|
||||
}
|
||||
|
||||
if (lws_map_item_value_len(item) != 3 ||
|
||||
memcmp(lws_map_item_value(item), "def", 3)) {
|
||||
lwsl_err("%s: t3; d\n", __func__);
|
||||
e++;
|
||||
goto end_t3;
|
||||
}
|
||||
|
||||
item = lws_map_item_lookup(map, (lws_map_key_t)&k2, sizeof(k2));
|
||||
if (!item) {
|
||||
lwsl_err("%s: t3; e\n", __func__);
|
||||
e++;
|
||||
goto end_t3;
|
||||
}
|
||||
|
||||
if (lws_map_item_value_len(item) != 4 ||
|
||||
memcmp(lws_map_item_value(item), "4567", 4)) {
|
||||
lwsl_err("%s: t3; f\n", __func__);
|
||||
e++;
|
||||
goto end_t3;
|
||||
}
|
||||
|
||||
item = lws_map_item_lookup(map, (lws_map_key_t)&k3, sizeof(k3));
|
||||
if (item) {
|
||||
lwsl_err("%s: t3; g\n", __func__);
|
||||
e++;
|
||||
goto end_t3;
|
||||
}
|
||||
|
||||
pass++;
|
||||
|
||||
end_t3:
|
||||
lws_map_destroy(&map);
|
||||
|
||||
/* Test 4: same key items */
|
||||
|
||||
lwsl_user("%s: test4\n", __func__);
|
||||
memset(&info, 0, sizeof(info));
|
||||
map = lws_map_create(&info);
|
||||
if (!map) {
|
||||
e++;
|
||||
goto end_t4;
|
||||
}
|
||||
if (!lws_map_item_create_ks(map, "abc", (lws_map_value_t)"def", 3)) {
|
||||
e++;
|
||||
goto end_t4;
|
||||
}
|
||||
if (!lws_map_item_create_ks(map, "abc", (lws_map_value_t)"4567", 4)) {
|
||||
e++;
|
||||
goto end_t4;
|
||||
}
|
||||
item = lws_map_item_lookup_ks(map, "abc");
|
||||
if (!item) {
|
||||
e++;
|
||||
goto end_t4;
|
||||
}
|
||||
|
||||
if (lws_map_item_value_len(item) != 4 ||
|
||||
memcmp(lws_map_item_value(item), "4567", 4)) {
|
||||
e++;
|
||||
goto end_t4;
|
||||
}
|
||||
|
||||
pass++;
|
||||
|
||||
end_t4:
|
||||
lws_map_destroy(&map);
|
||||
|
||||
if (e)
|
||||
goto bail;
|
||||
|
||||
lwsl_user("Completed: PASS %d / %d\n", pass, expected);
|
||||
|
||||
return 0;
|
||||
|
||||
bail:
|
||||
lwsl_user("Completed: FAIL, passed %d / %d (e %d)\n", pass,
|
||||
expected, e);
|
||||
|
||||
return 1;
|
||||
}
|
Loading…
Add table
Reference in a new issue