lsquic_stock_shi.c revision fb3e20e0
1/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc.  See LICENSE. */
2/*
3 * lsquic_stock_shi.c
4 */
5
6#include <errno.h>
7#include <stdlib.h>
8#include <string.h>
9#include <sys/queue.h>
10#include <time.h>
11
12#include "lsquic.h"
13#include "lsquic_stock_shi.h"
14#include "lsquic_malo.h"
15#include "lsquic_hash.h"
16
17struct stock_shared_hash
18{
19    TAILQ_HEAD(, hash_elem)     lru_elems;
20    struct lsquic_hash         *lhash;
21    struct malo                *malo;
22};
23
24
25struct key
26{
27    void         *buf;
28    unsigned      sz;
29};
30
31
32struct hash_elem
33{
34    TAILQ_ENTRY(hash_elem)  next_lru_he;
35    struct lsquic_hash_elem lhash_elem;
36    void                   *data;
37    time_t                  expiry;         /* If not 0, the element is on LRU list */
38    struct key              key;
39    unsigned                data_sz;
40};
41
42
43static void
44free_key_data (struct hash_elem *he)
45{
46    if (he->data_sz)
47        free(he->data);
48    free(he->key.buf);
49}
50
51
52static void
53delete_expired_elements (struct stock_shared_hash *hash)
54{
55    struct hash_elem *he;
56    time_t now = time(NULL);
57    while ((he = TAILQ_FIRST(&hash->lru_elems)))
58    {
59        if (he->expiry < now)
60        {
61            lsquic_hash_erase(hash->lhash, &he->lhash_elem);
62            if (he->expiry)
63                TAILQ_REMOVE(&hash->lru_elems, he, next_lru_he);
64            free_key_data(he);
65            lsquic_malo_put(he);
66        }
67        else
68            break;
69    }
70}
71
72
73static int
74stock_shi_insert (void *hash_ctx, void *key, unsigned key_sz,
75                  void *data, unsigned data_sz, time_t expiry)
76{
77    struct stock_shared_hash *const hash = hash_ctx;
78    struct hash_elem *he;
79
80    /* Potential optimization: do not exire on every insert.  Use case:
81     * if many insert occur in a row, it is not efficient to perform
82     * this check every time.  Can add a counter in hash.
83     */
84    if (!TAILQ_EMPTY(&hash->lru_elems))
85        delete_expired_elements(hash);
86
87    he = lsquic_malo_get(hash->malo);
88    if (!he)
89        return -1;
90    he->key.buf = key;
91    he->key.sz  = key_sz;
92    he->data    = data;
93    he->data_sz = data_sz;
94    he->expiry = expiry;
95    memset(&he->lhash_elem, 0, sizeof(he->lhash_elem));
96    if (lsquic_hash_insert(hash->lhash, he->key.buf,
97                                            he->key.sz, he, &he->lhash_elem))
98    {
99        if (expiry)
100            TAILQ_INSERT_TAIL(&hash->lru_elems, he, next_lru_he);
101        return 0;
102    }
103    else
104    {
105        lsquic_malo_put(he);
106        return -1;
107    }
108}
109
110
111static int
112stock_shi_lookup (void *hash_ctx, const void *key, unsigned key_sz,
113                                  void     **data, unsigned *data_sz)
114{
115    struct stock_shared_hash *const hash = hash_ctx;
116    struct hash_elem *he;
117    struct lsquic_hash_elem *el;
118
119    if (!TAILQ_EMPTY(&hash->lru_elems))
120        delete_expired_elements(hash);
121
122    el = lsquic_hash_find(hash->lhash, key, key_sz);
123    if (!el)
124        return 0;                                   /* 0: not found */
125    he = lsquic_hashelem_getdata(el);
126    *data    = he->data;
127    *data_sz = he->data_sz;
128    return 1;                                       /* 1: found */
129}
130
131
132static int
133stock_shi_delete (void *hash_ctx, const void *key, unsigned key_sz)
134{
135    struct stock_shared_hash *const hash = hash_ctx;
136    struct lsquic_hash_elem *el;
137    struct hash_elem *he;
138
139    if (!TAILQ_EMPTY(&hash->lru_elems))
140        delete_expired_elements(hash);
141
142    el = lsquic_hash_find(hash->lhash, key, key_sz);
143    if (!el)
144        return -1;
145
146    he = lsquic_hashelem_getdata(el);
147    lsquic_hash_erase(hash->lhash, el);
148    if (he->expiry)
149        TAILQ_REMOVE(&hash->lru_elems, he, next_lru_he);
150
151    free_key_data(he);
152    lsquic_malo_put(he);
153    return 0;
154}
155
156
157struct stock_shared_hash *
158lsquic_stock_shared_hash_new (void)
159{
160    struct malo *malo;
161    struct stock_shared_hash *hash;
162
163    malo = lsquic_malo_create(sizeof(struct hash_elem));
164    if (!malo)
165        return NULL;
166
167    hash = lsquic_malo_get(malo);
168    if (!hash)
169    {   /* This would be really odd, but let's check this for completeness. */
170        lsquic_malo_destroy(malo);
171        return NULL;
172    }
173
174    hash->malo = malo;
175    hash->lhash = lsquic_hash_create();
176    TAILQ_INIT(&hash->lru_elems);
177    return hash;
178}
179
180
181void
182lsquic_stock_shared_hash_destroy (struct stock_shared_hash *hash)
183{
184    struct hash_elem *he;
185    struct lsquic_hash_elem *el;
186    for (el = lsquic_hash_first(hash->lhash); el;
187                                        el = lsquic_hash_next(hash->lhash))
188    {
189        he = lsquic_hashelem_getdata(el);
190        free_key_data(he);
191        /* No need to lsquic_malo_put(he) here */
192    }
193    lsquic_hash_destroy(hash->lhash);
194    lsquic_malo_destroy(hash->malo);
195}
196
197
198const struct lsquic_shared_hash_if stock_shi =
199{
200    .shi_insert = stock_shi_insert,
201    .shi_delete = stock_shi_delete,
202    .shi_lookup = stock_shi_lookup,
203};
204
205
206/* Need this to save one malloc using malo: */
207typedef char hash_not_larger_than_hash_elem [
208            (sizeof(struct stock_shared_hash) <= sizeof(struct hash_elem)) ? 1 : -1];
209