215 lines
5.1 KiB
C
215 lines
5.1 KiB
C
/* Copyright (c) 2017 - 2022 LiteSpeed Technologies Inc. See LICENSE. */
|
|
/*
|
|
* lsquic_stock_shi.c
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/queue.h>
|
|
#include <time.h>
|
|
|
|
#include "lsquic.h"
|
|
#include "lsquic_stock_shi.h"
|
|
#include "lsquic_malo.h"
|
|
#include "lsquic_hash.h"
|
|
|
|
struct stock_shared_hash
|
|
{
|
|
TAILQ_HEAD(, hash_elem) lru_elems;
|
|
struct lsquic_hash *lhash;
|
|
struct malo *malo;
|
|
};
|
|
|
|
|
|
struct key
|
|
{
|
|
void *buf;
|
|
unsigned sz;
|
|
};
|
|
|
|
|
|
struct hash_elem
|
|
{
|
|
TAILQ_ENTRY(hash_elem) next_lru_he;
|
|
struct lsquic_hash_elem lhash_elem;
|
|
void *data;
|
|
time_t expiry; /* If not 0, the element is on LRU list */
|
|
struct key key;
|
|
unsigned data_sz;
|
|
};
|
|
|
|
|
|
static void
|
|
free_key_data (struct hash_elem *he)
|
|
{
|
|
free(he->key.buf);
|
|
}
|
|
|
|
|
|
static void
|
|
delete_expired_elements (struct stock_shared_hash *hash)
|
|
{
|
|
struct hash_elem *he;
|
|
time_t now = time(NULL);
|
|
while ((he = TAILQ_FIRST(&hash->lru_elems)))
|
|
{
|
|
if (he->expiry < now)
|
|
{
|
|
lsquic_hash_erase(hash->lhash, &he->lhash_elem);
|
|
if (he->expiry)
|
|
TAILQ_REMOVE(&hash->lru_elems, he, next_lru_he);
|
|
free_key_data(he);
|
|
lsquic_malo_put(he);
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
static int
|
|
stock_shi_insert (void *hash_ctx, void *key, unsigned key_sz,
|
|
void *data, unsigned data_sz, time_t expiry)
|
|
{
|
|
struct stock_shared_hash *const hash = hash_ctx;
|
|
struct hash_elem *he;
|
|
|
|
/* Potential optimization: do not exire on every insert. Use case:
|
|
* if many insert occur in a row, it is not efficient to perform
|
|
* this check every time. Can add a counter in hash.
|
|
*/
|
|
if (!TAILQ_EMPTY(&hash->lru_elems))
|
|
delete_expired_elements(hash);
|
|
|
|
he = lsquic_malo_get(hash->malo);
|
|
if (!he)
|
|
return -1;
|
|
he->key.buf = malloc(key_sz + data_sz + 1);
|
|
if (!he->key.buf)
|
|
{
|
|
lsquic_malo_put(he);
|
|
return -1;
|
|
}
|
|
memmove(he->key.buf, key, key_sz);
|
|
((char *)(he->key.buf))[key_sz] = 0;
|
|
he->key.sz = key_sz;
|
|
he->data = he->key.buf + he->key.sz + 1;
|
|
memmove(he->data, data, data_sz);
|
|
he->data_sz = data_sz;
|
|
he->expiry = expiry;
|
|
memset(&he->lhash_elem, 0, sizeof(he->lhash_elem));
|
|
if (lsquic_hash_insert(hash->lhash, he->key.buf,
|
|
he->key.sz, he, &he->lhash_elem))
|
|
{
|
|
if (expiry)
|
|
TAILQ_INSERT_TAIL(&hash->lru_elems, he, next_lru_he);
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
lsquic_malo_put(he);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
|
|
static int
|
|
stock_shi_lookup (void *hash_ctx, const void *key, unsigned key_sz,
|
|
void **data, unsigned *data_sz)
|
|
{
|
|
struct stock_shared_hash *const hash = hash_ctx;
|
|
struct hash_elem *he;
|
|
struct lsquic_hash_elem *el;
|
|
|
|
if (!TAILQ_EMPTY(&hash->lru_elems))
|
|
delete_expired_elements(hash);
|
|
|
|
el = lsquic_hash_find(hash->lhash, key, key_sz);
|
|
if (!el)
|
|
return 0; /* 0: not found */
|
|
he = lsquic_hashelem_getdata(el);
|
|
*data = he->data;
|
|
*data_sz = he->data_sz;
|
|
return 1; /* 1: found */
|
|
}
|
|
|
|
|
|
static int
|
|
stock_shi_delete (void *hash_ctx, const void *key, unsigned key_sz)
|
|
{
|
|
struct stock_shared_hash *const hash = hash_ctx;
|
|
struct lsquic_hash_elem *el;
|
|
struct hash_elem *he;
|
|
|
|
if (!TAILQ_EMPTY(&hash->lru_elems))
|
|
delete_expired_elements(hash);
|
|
|
|
el = lsquic_hash_find(hash->lhash, key, key_sz);
|
|
if (!el)
|
|
return -1;
|
|
|
|
he = lsquic_hashelem_getdata(el);
|
|
lsquic_hash_erase(hash->lhash, el);
|
|
if (he->expiry)
|
|
TAILQ_REMOVE(&hash->lru_elems, he, next_lru_he);
|
|
|
|
free_key_data(he);
|
|
lsquic_malo_put(he);
|
|
return 0;
|
|
}
|
|
|
|
|
|
struct stock_shared_hash *
|
|
lsquic_stock_shared_hash_new (void)
|
|
{
|
|
struct malo *malo;
|
|
struct stock_shared_hash *hash;
|
|
|
|
malo = lsquic_malo_create(sizeof(struct hash_elem));
|
|
if (!malo)
|
|
return NULL;
|
|
|
|
hash = lsquic_malo_get(malo);
|
|
if (!hash)
|
|
{ /* This would be really odd, but let's check this for completeness. */
|
|
lsquic_malo_destroy(malo);
|
|
return NULL;
|
|
}
|
|
|
|
hash->malo = malo;
|
|
hash->lhash = lsquic_hash_create();
|
|
TAILQ_INIT(&hash->lru_elems);
|
|
return hash;
|
|
}
|
|
|
|
|
|
void
|
|
lsquic_stock_shared_hash_destroy (struct stock_shared_hash *hash)
|
|
{
|
|
struct hash_elem *he;
|
|
struct lsquic_hash_elem *el;
|
|
for (el = lsquic_hash_first(hash->lhash); el;
|
|
el = lsquic_hash_next(hash->lhash))
|
|
{
|
|
he = lsquic_hashelem_getdata(el);
|
|
free_key_data(he);
|
|
/* No need to lsquic_malo_put(he) here */
|
|
}
|
|
lsquic_hash_destroy(hash->lhash);
|
|
lsquic_malo_destroy(hash->malo);
|
|
}
|
|
|
|
|
|
const struct lsquic_shared_hash_if stock_shi =
|
|
{
|
|
.shi_insert = stock_shi_insert,
|
|
.shi_delete = stock_shi_delete,
|
|
.shi_lookup = stock_shi_lookup,
|
|
};
|
|
|
|
|
|
/* Need this to save one malloc using malo: */
|
|
typedef char hash_not_larger_than_hash_elem [
|
|
(sizeof(struct stock_shared_hash) <= sizeof(struct hash_elem)) ? 1 : -1];
|