1#define _POSIX_C_SOURCE 200809L
16#define CWIST_QUERY_MAP_DEFAULT_SIZE 16
28static void *query_alloc(
void *arena,
size_t size) {
36static void query_release(
void *arena,
void *ptr) {
42static char *query_strdup(
void *arena,
const char *str) {
43 if (!str)
return NULL;
45 char *copy = (
char *)query_alloc(arena, len + 1);
46 if (copy)
memcpy(copy, str, len + 1);
67 query_release(arena,
map);
83 void *arena =
map->arena;
85 query_release(arena,
map->buckets);
86 query_release(arena,
map);
95 for (
size_t i = 0;
i <
map->size;
i++) {
99 query_release(
map->arena,
curr->key);
100 query_release(
map->arena,
curr->value);
101 query_release(
map->arena,
curr);
118 if (
map->size &&
map->count + 1 <=
map->size -
map->size / 4)
return;
123 if (!buckets)
return;
125 for (
size_t i = 0;
i <
map->size;
i++) {
131 buckets[index] =
curr;
135 query_release(
map->arena,
map->buckets);
136 map->buckets = buckets;
147 if (!
map || !key || !value)
return;
150 if (!
map->size)
return;
154 size_t index =
hash %
map->size;
160 char *copy = query_strdup(
map->arena, value);
162 query_release(
map->arena,
curr->value);
170 if (
map->count + 1 >
map->size -
map->size / 4) {
176 node->
key = query_strdup(
map->arena, key);
177 node->
value = query_strdup(
map->arena, value);
180 query_release(
map->arena, node->
key);
181 query_release(
map->arena, node->
value);
182 query_release(
map->arena, node);
185 node->
next =
map->buckets[index];
186 map->buckets[index] = node;
200 size_t index =
hash %
map->size;
221 if (!
map || !key || !
map->size)
return;
223 size_t index =
hash %
map->size;
234 query_release(
map->arena,
curr->key);
235 query_release(
map->arena,
curr->value);
236 query_release(
map->arena,
curr);
253 if (!
map || !cb)
return;
254 for (
size_t i = 0;
i <
map->size;
i++) {
265static const uint8_t url_decode_hex[256] = {
266 [
'0'] = 0, 1, 2, 3, 4, 5, 6, 7,
267 [
'8'] = 8, 9, [
'A'] = 10, 11, 12, 13, 14, 15,
268 [
'a'] = 10, 11, 12, 13, 14, 15,
279static char *url_decode(
void *arena,
const char *src) {
280 if (!src)
return NULL;
283 char *out = (
char *)query_alloc(arena, len + 1);
284 if (!out)
return NULL;
300 }
else if (
i + 2 < len &&
isxdigit((
unsigned char)src[
i + 1]) &&
302 out[
j++] = (
char)((url_decode_hex[(
unsigned char)src[
i + 1]] << 4) |
303 url_decode_hex[(
unsigned char)src[
i + 2]]);
357 query_release(
map->arena, buffer);
Memory allocation wrappers backed by libttak.
void cwist_free(void *ptr)
Free memory obtained via cwist_alloc/cwist_strdup/etc.
void * cwist_alloc(size_t size)
Allocate zeroed memory tracked by libttak.
Request-scoped arena allocator backed by libttak arena generations.
struct cwist_arena cwist_arena_t
void * cwist_arena_alloc(cwist_arena_t *arena, size_t size)
Bump-allocate size bytes from the active generation.
bool cwist_arena_owns(const cwist_arena_t *arena, const void *ptr)
Check whether ptr lies inside the active generation buffer.
uint64_t siphash24(const void *src, size_t len, const uint8_t key[16])
Compute SipHash-2-4 over a byte range using a 16-byte secret key.
void cwist_generate_hash_seed(uint8_t key[16])
Generates a random SipHash seed.
Query string parser and map.
void(* cwist_query_map_iter_func)(const char *key, const char *value, void *ctx)
Iterator callback signature.
void cwist_query_map_delete(cwist_query_map *map, const char *key)
Remove a key/value pair from the map if present.
const char * cwist_query_map_get(cwist_query_map *map, const char *key)
Look up a decoded query parameter by key.
cwist_query_map * cwist_query_map_create_in_arena(void *arena)
Allocate a query map within a memory arena to avoid GC/heap allocations.
#define CWIST_QUERY_MAP_DEFAULT_SIZE
void cwist_query_map_set(cwist_query_map *map, const char *key, const char *value)
Insert or replace a decoded query parameter in the map.
void cwist_query_map_foreach(cwist_query_map *map, cwist_query_map_iter_func cb, void *ctx)
Invoke a callback for every key/value pair stored in the map.
void cwist_query_map_clear(cwist_query_map *map)
Remove every key/value pair while keeping the bucket array allocated.
cwist_query_map * cwist_query_map_create(void)
Allocate a query map and seed its SipHash key material.
void cwist_query_map_destroy(cwist_query_map *map)
Release every bucket node and the map container itself.
void cwist_query_map_parse(cwist_query_map *map, const char *raw_query)
Parse a raw a=1&b=2 query string into the existing map.
struct cwist_query_bucket * next