1d4afb5ceSopenharmony_ci/*
2d4afb5ceSopenharmony_ci * libwebsockets - small server side websockets and web server implementation
3d4afb5ceSopenharmony_ci *
4d4afb5ceSopenharmony_ci * Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
5d4afb5ceSopenharmony_ci *
6d4afb5ceSopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a copy
7d4afb5ceSopenharmony_ci * of this software and associated documentation files (the "Software"), to
8d4afb5ceSopenharmony_ci * deal in the Software without restriction, including without limitation the
9d4afb5ceSopenharmony_ci * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10d4afb5ceSopenharmony_ci * sell copies of the Software, and to permit persons to whom the Software is
11d4afb5ceSopenharmony_ci * furnished to do so, subject to the following conditions:
12d4afb5ceSopenharmony_ci *
13d4afb5ceSopenharmony_ci * The above copyright notice and this permission notice shall be included in
14d4afb5ceSopenharmony_ci * all copies or substantial portions of the Software.
15d4afb5ceSopenharmony_ci *
16d4afb5ceSopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17d4afb5ceSopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18d4afb5ceSopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19d4afb5ceSopenharmony_ci * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20d4afb5ceSopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21d4afb5ceSopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22d4afb5ceSopenharmony_ci * IN THE SOFTWARE.
23d4afb5ceSopenharmony_ci */
24d4afb5ceSopenharmony_ci
25d4afb5ceSopenharmony_ci#include <private-lib-core.h>
26d4afb5ceSopenharmony_ci#include "private-lib-misc-cache-ttl.h"
27d4afb5ceSopenharmony_ci
28d4afb5ceSopenharmony_ci#if defined(write)
29d4afb5ceSopenharmony_ci#undef write
30d4afb5ceSopenharmony_ci#endif
31d4afb5ceSopenharmony_ci
32d4afb5ceSopenharmony_cistatic void
33d4afb5ceSopenharmony_ciupdate_sul(lws_cache_ttl_lru_t_heap_t *cache);
34d4afb5ceSopenharmony_ci
35d4afb5ceSopenharmony_cistatic int
36d4afb5ceSopenharmony_cilws_cache_heap_invalidate(struct lws_cache_ttl_lru *_c, const char *key);
37d4afb5ceSopenharmony_ci
38d4afb5ceSopenharmony_cistatic int
39d4afb5ceSopenharmony_cisort_expiry(const lws_dll2_t *a, const lws_dll2_t *b)
40d4afb5ceSopenharmony_ci{
41d4afb5ceSopenharmony_ci	const lws_cache_ttl_item_heap_t
42d4afb5ceSopenharmony_ci		*c = lws_container_of(a, lws_cache_ttl_item_heap_t, list_expiry),
43d4afb5ceSopenharmony_ci		*d = lws_container_of(b, lws_cache_ttl_item_heap_t, list_expiry);
44d4afb5ceSopenharmony_ci
45d4afb5ceSopenharmony_ci	if (c->expiry > d->expiry)
46d4afb5ceSopenharmony_ci		return 1;
47d4afb5ceSopenharmony_ci	if (c->expiry < d->expiry)
48d4afb5ceSopenharmony_ci		return -1;
49d4afb5ceSopenharmony_ci
50d4afb5ceSopenharmony_ci	return 0;
51d4afb5ceSopenharmony_ci}
52d4afb5ceSopenharmony_ci
53d4afb5ceSopenharmony_cistatic void
54d4afb5ceSopenharmony_ci_lws_cache_heap_item_destroy(lws_cache_ttl_lru_t_heap_t *cache,
55d4afb5ceSopenharmony_ci			     lws_cache_ttl_item_heap_t *item)
56d4afb5ceSopenharmony_ci{
57d4afb5ceSopenharmony_ci	lwsl_cache("%s: %s (%s)\n", __func__, cache->cache.info.name,
58d4afb5ceSopenharmony_ci			(const char *)&item[1] + item->size);
59d4afb5ceSopenharmony_ci
60d4afb5ceSopenharmony_ci	lws_dll2_remove(&item->list_expiry);
61d4afb5ceSopenharmony_ci	lws_dll2_remove(&item->list_lru);
62d4afb5ceSopenharmony_ci
63d4afb5ceSopenharmony_ci	cache->cache.current_footprint -= item->size;
64d4afb5ceSopenharmony_ci
65d4afb5ceSopenharmony_ci	update_sul(cache);
66d4afb5ceSopenharmony_ci
67d4afb5ceSopenharmony_ci	if (cache->cache.info.cb)
68d4afb5ceSopenharmony_ci		cache->cache.info.cb((void *)((uint8_t *)&item[1]), item->size);
69d4afb5ceSopenharmony_ci
70d4afb5ceSopenharmony_ci	lws_free(item);
71d4afb5ceSopenharmony_ci}
72d4afb5ceSopenharmony_ci
73d4afb5ceSopenharmony_cistatic void
74d4afb5ceSopenharmony_cilws_cache_heap_item_destroy(lws_cache_ttl_lru_t_heap_t *cache,
75d4afb5ceSopenharmony_ci			    lws_cache_ttl_item_heap_t *item, int parent_too)
76d4afb5ceSopenharmony_ci{
77d4afb5ceSopenharmony_ci	struct lws_cache_ttl_lru *backing = &cache->cache;
78d4afb5ceSopenharmony_ci	const char *tag = ((const char *)&item[1]) + item->size;
79d4afb5ceSopenharmony_ci
80d4afb5ceSopenharmony_ci	/*
81d4afb5ceSopenharmony_ci	 * We're destroying a normal item?
82d4afb5ceSopenharmony_ci	 */
83d4afb5ceSopenharmony_ci
84d4afb5ceSopenharmony_ci	if (*tag == META_ITEM_LEADING)
85d4afb5ceSopenharmony_ci		/* no, nothing to check here then */
86d4afb5ceSopenharmony_ci		goto post;
87d4afb5ceSopenharmony_ci
88d4afb5ceSopenharmony_ci	if (backing->info.parent)
89d4afb5ceSopenharmony_ci		backing = backing->info.parent;
90d4afb5ceSopenharmony_ci
91d4afb5ceSopenharmony_ci	/*
92d4afb5ceSopenharmony_ci	 * We need to check any cached meta-results from lookups that
93d4afb5ceSopenharmony_ci	 * include this normal item, and if any, invalidate the meta-results
94d4afb5ceSopenharmony_ci	 * since they have to be recalculated before being used again.
95d4afb5ceSopenharmony_ci	 */
96d4afb5ceSopenharmony_ci
97d4afb5ceSopenharmony_ci	lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
98d4afb5ceSopenharmony_ci				   cache->items_lru.head) {
99d4afb5ceSopenharmony_ci		lws_cache_ttl_item_heap_t *i = lws_container_of(d,
100d4afb5ceSopenharmony_ci						lws_cache_ttl_item_heap_t,
101d4afb5ceSopenharmony_ci						list_lru);
102d4afb5ceSopenharmony_ci		const char *iname = ((const char *)&item[1]) + item->size;
103d4afb5ceSopenharmony_ci		uint8_t *pay = (uint8_t *)&item[1], *end = pay + item->size;
104d4afb5ceSopenharmony_ci
105d4afb5ceSopenharmony_ci		if (*iname == META_ITEM_LEADING) {
106d4afb5ceSopenharmony_ci			size_t taglen = strlen(iname);
107d4afb5ceSopenharmony_ci
108d4afb5ceSopenharmony_ci			/*
109d4afb5ceSopenharmony_ci			 * If the item about to be destroyed makes an
110d4afb5ceSopenharmony_ci			 * appearance on the meta results list, we must kill
111d4afb5ceSopenharmony_ci			 * the meta result item to force recalc next time
112d4afb5ceSopenharmony_ci			 */
113d4afb5ceSopenharmony_ci
114d4afb5ceSopenharmony_ci			while (pay < end) {
115d4afb5ceSopenharmony_ci				uint32_t tlen = lws_ser_ru32be(pay + 4);
116d4afb5ceSopenharmony_ci
117d4afb5ceSopenharmony_ci				if (tlen == taglen &&
118d4afb5ceSopenharmony_ci				    !strcmp((const char *)pay + 8, iname)) {
119d4afb5ceSopenharmony_ci#if defined(_DEBUG)
120d4afb5ceSopenharmony_ci					/*
121d4afb5ceSopenharmony_ci					 * Sanity check that the item tag is
122d4afb5ceSopenharmony_ci					 * really a match for that meta results
123d4afb5ceSopenharmony_ci					 * item
124d4afb5ceSopenharmony_ci					 */
125d4afb5ceSopenharmony_ci
126d4afb5ceSopenharmony_ci					assert (!backing->info.ops->tag_match(
127d4afb5ceSopenharmony_ci						 backing, iname + 1, tag, 1));
128d4afb5ceSopenharmony_ci#endif
129d4afb5ceSopenharmony_ci					_lws_cache_heap_item_destroy(cache, i);
130d4afb5ceSopenharmony_ci					break;
131d4afb5ceSopenharmony_ci				}
132d4afb5ceSopenharmony_ci				pay += 8 + tlen + 1;
133d4afb5ceSopenharmony_ci			}
134d4afb5ceSopenharmony_ci
135d4afb5ceSopenharmony_ci#if defined(_DEBUG)
136d4afb5ceSopenharmony_ci			/*
137d4afb5ceSopenharmony_ci			 * Sanity check that the item tag really isn't a match
138d4afb5ceSopenharmony_ci			 * for that meta results item
139d4afb5ceSopenharmony_ci			 */
140d4afb5ceSopenharmony_ci
141d4afb5ceSopenharmony_ci			assert (backing->info.ops->tag_match(backing, iname + 1,
142d4afb5ceSopenharmony_ci							  tag, 1));
143d4afb5ceSopenharmony_ci#endif
144d4afb5ceSopenharmony_ci		}
145d4afb5ceSopenharmony_ci
146d4afb5ceSopenharmony_ci	} lws_end_foreach_dll_safe(d, d1);
147d4afb5ceSopenharmony_ci
148d4afb5ceSopenharmony_cipost:
149d4afb5ceSopenharmony_ci	_lws_cache_heap_item_destroy(cache, item);
150d4afb5ceSopenharmony_ci}
151d4afb5ceSopenharmony_ci
152d4afb5ceSopenharmony_cistatic void
153d4afb5ceSopenharmony_cilws_cache_item_evict_lru(lws_cache_ttl_lru_t_heap_t *cache)
154d4afb5ceSopenharmony_ci{
155d4afb5ceSopenharmony_ci	lws_cache_ttl_item_heap_t *ei;
156d4afb5ceSopenharmony_ci
157d4afb5ceSopenharmony_ci	if (!cache->items_lru.head)
158d4afb5ceSopenharmony_ci		return;
159d4afb5ceSopenharmony_ci
160d4afb5ceSopenharmony_ci	ei = lws_container_of(cache->items_lru.head,
161d4afb5ceSopenharmony_ci			      lws_cache_ttl_item_heap_t, list_lru);
162d4afb5ceSopenharmony_ci
163d4afb5ceSopenharmony_ci	lws_cache_heap_item_destroy(cache, ei, 0);
164d4afb5ceSopenharmony_ci}
165d4afb5ceSopenharmony_ci
166d4afb5ceSopenharmony_ci/*
167d4afb5ceSopenharmony_ci * We need to weed out expired entries in the backing file
168d4afb5ceSopenharmony_ci */
169d4afb5ceSopenharmony_ci
170d4afb5ceSopenharmony_cistatic void
171d4afb5ceSopenharmony_ciexpiry_cb(lws_sorted_usec_list_t *sul)
172d4afb5ceSopenharmony_ci{
173d4afb5ceSopenharmony_ci	lws_cache_ttl_lru_t_heap_t *cache = lws_container_of(sul,
174d4afb5ceSopenharmony_ci					lws_cache_ttl_lru_t_heap_t, cache.sul);
175d4afb5ceSopenharmony_ci	lws_usec_t now = lws_now_usecs();
176d4afb5ceSopenharmony_ci
177d4afb5ceSopenharmony_ci	lwsl_cache("%s: %s\n", __func__, cache->cache.info.name);
178d4afb5ceSopenharmony_ci
179d4afb5ceSopenharmony_ci	while (cache->items_expiry.head) {
180d4afb5ceSopenharmony_ci		lws_cache_ttl_item_heap_t *item;
181d4afb5ceSopenharmony_ci
182d4afb5ceSopenharmony_ci		item = lws_container_of(cache->items_expiry.head,
183d4afb5ceSopenharmony_ci					lws_cache_ttl_item_heap_t, list_expiry);
184d4afb5ceSopenharmony_ci
185d4afb5ceSopenharmony_ci		if (item->expiry > now)
186d4afb5ceSopenharmony_ci			return;
187d4afb5ceSopenharmony_ci
188d4afb5ceSopenharmony_ci		lws_cache_heap_item_destroy(cache, item, 1);
189d4afb5ceSopenharmony_ci	}
190d4afb5ceSopenharmony_ci}
191d4afb5ceSopenharmony_ci
192d4afb5ceSopenharmony_ci/*
193d4afb5ceSopenharmony_ci * Let's figure out what the earliest next expiry is
194d4afb5ceSopenharmony_ci */
195d4afb5ceSopenharmony_ci
196d4afb5ceSopenharmony_cistatic int
197d4afb5ceSopenharmony_ciearliest_expiry(lws_cache_ttl_lru_t_heap_t *cache, lws_usec_t *pearliest)
198d4afb5ceSopenharmony_ci{
199d4afb5ceSopenharmony_ci	lws_cache_ttl_item_heap_t *item;
200d4afb5ceSopenharmony_ci
201d4afb5ceSopenharmony_ci	if (!cache->items_expiry.head)
202d4afb5ceSopenharmony_ci		return 1;
203d4afb5ceSopenharmony_ci
204d4afb5ceSopenharmony_ci	item = lws_container_of(cache->items_expiry.head,
205d4afb5ceSopenharmony_ci				lws_cache_ttl_item_heap_t, list_expiry);
206d4afb5ceSopenharmony_ci
207d4afb5ceSopenharmony_ci	*pearliest = item->expiry;
208d4afb5ceSopenharmony_ci
209d4afb5ceSopenharmony_ci	return 0;
210d4afb5ceSopenharmony_ci}
211d4afb5ceSopenharmony_ci
212d4afb5ceSopenharmony_cistatic void
213d4afb5ceSopenharmony_ciupdate_sul(lws_cache_ttl_lru_t_heap_t *cache)
214d4afb5ceSopenharmony_ci{
215d4afb5ceSopenharmony_ci	lws_usec_t earliest;
216d4afb5ceSopenharmony_ci
217d4afb5ceSopenharmony_ci	/* weed out any newly-expired */
218d4afb5ceSopenharmony_ci	expiry_cb(&cache->cache.sul);
219d4afb5ceSopenharmony_ci
220d4afb5ceSopenharmony_ci	/* figure out the next soonest expiring item */
221d4afb5ceSopenharmony_ci	if (earliest_expiry(cache, &earliest)) {
222d4afb5ceSopenharmony_ci		lws_sul_cancel(&cache->cache.sul);
223d4afb5ceSopenharmony_ci		return;
224d4afb5ceSopenharmony_ci	}
225d4afb5ceSopenharmony_ci
226d4afb5ceSopenharmony_ci	lwsl_debug("%s: setting exp %llu\n", __func__,
227d4afb5ceSopenharmony_ci			(unsigned long long)earliest);
228d4afb5ceSopenharmony_ci
229d4afb5ceSopenharmony_ci	if (earliest)
230d4afb5ceSopenharmony_ci		lws_cache_schedule(&cache->cache, expiry_cb, earliest);
231d4afb5ceSopenharmony_ci}
232d4afb5ceSopenharmony_ci
233d4afb5ceSopenharmony_cistatic lws_cache_ttl_item_heap_t *
234d4afb5ceSopenharmony_cilws_cache_heap_specific(lws_cache_ttl_lru_t_heap_t *cache,
235d4afb5ceSopenharmony_ci			const char *specific_key)
236d4afb5ceSopenharmony_ci{
237d4afb5ceSopenharmony_ci	lws_start_foreach_dll(struct lws_dll2 *, d, cache->items_lru.head) {
238d4afb5ceSopenharmony_ci		lws_cache_ttl_item_heap_t *item = lws_container_of(d,
239d4afb5ceSopenharmony_ci						lws_cache_ttl_item_heap_t,
240d4afb5ceSopenharmony_ci						list_lru);
241d4afb5ceSopenharmony_ci		const char *iname = ((const char *)&item[1]) + item->size;
242d4afb5ceSopenharmony_ci
243d4afb5ceSopenharmony_ci		if (!strcmp(specific_key, iname))
244d4afb5ceSopenharmony_ci			return item;
245d4afb5ceSopenharmony_ci
246d4afb5ceSopenharmony_ci	} lws_end_foreach_dll(d);
247d4afb5ceSopenharmony_ci
248d4afb5ceSopenharmony_ci	return NULL;
249d4afb5ceSopenharmony_ci}
250d4afb5ceSopenharmony_ci
251d4afb5ceSopenharmony_cistatic int
252d4afb5ceSopenharmony_cilws_cache_heap_tag_match(struct lws_cache_ttl_lru *cache, const char *wc,
253d4afb5ceSopenharmony_ci				const char *tag, char lookup_rules)
254d4afb5ceSopenharmony_ci{
255d4afb5ceSopenharmony_ci	return lws_strcmp_wildcard(wc, strlen(wc), tag, strlen(tag));
256d4afb5ceSopenharmony_ci}
257d4afb5ceSopenharmony_ci
258d4afb5ceSopenharmony_cistatic int
259d4afb5ceSopenharmony_cilws_cache_heap_lookup(struct lws_cache_ttl_lru *_c, const char *wildcard_key,
260d4afb5ceSopenharmony_ci		      lws_dll2_owner_t *results_owner)
261d4afb5ceSopenharmony_ci{
262d4afb5ceSopenharmony_ci	lws_cache_ttl_lru_t_heap_t *cache = (lws_cache_ttl_lru_t_heap_t *)_c;
263d4afb5ceSopenharmony_ci	size_t sklen = strlen(wildcard_key);
264d4afb5ceSopenharmony_ci
265d4afb5ceSopenharmony_ci	lws_start_foreach_dll(struct lws_dll2 *, d, cache->items_lru.head) {
266d4afb5ceSopenharmony_ci		lws_cache_ttl_item_heap_t *item = lws_container_of(d,
267d4afb5ceSopenharmony_ci						lws_cache_ttl_item_heap_t,
268d4afb5ceSopenharmony_ci						list_lru);
269d4afb5ceSopenharmony_ci		const char *iname = ((const char *)&item[1]) + item->size;
270d4afb5ceSopenharmony_ci
271d4afb5ceSopenharmony_ci		if (!lws_strcmp_wildcard(wildcard_key, sklen, iname,
272d4afb5ceSopenharmony_ci					 strlen(iname))) {
273d4afb5ceSopenharmony_ci			size_t ilen = strlen(iname);
274d4afb5ceSopenharmony_ci			lws_cache_match_t *m;
275d4afb5ceSopenharmony_ci			char hit = 0;
276d4afb5ceSopenharmony_ci
277d4afb5ceSopenharmony_ci			/*
278d4afb5ceSopenharmony_ci			 * It musn't already be on the list from an earlier
279d4afb5ceSopenharmony_ci			 * cache level
280d4afb5ceSopenharmony_ci			 */
281d4afb5ceSopenharmony_ci
282d4afb5ceSopenharmony_ci			lws_start_foreach_dll(struct lws_dll2 *, e,
283d4afb5ceSopenharmony_ci					results_owner->head) {
284d4afb5ceSopenharmony_ci				lws_cache_match_t *i = lws_container_of(e,
285d4afb5ceSopenharmony_ci							lws_cache_match_t, list);
286d4afb5ceSopenharmony_ci				if (i->tag_size == ilen &&
287d4afb5ceSopenharmony_ci				    !strcmp(iname, ((const char *)&i[1]))) {
288d4afb5ceSopenharmony_ci					hit = 1;
289d4afb5ceSopenharmony_ci					break;
290d4afb5ceSopenharmony_ci				}
291d4afb5ceSopenharmony_ci			} lws_end_foreach_dll(e);
292d4afb5ceSopenharmony_ci
293d4afb5ceSopenharmony_ci			if (!hit) {
294d4afb5ceSopenharmony_ci
295d4afb5ceSopenharmony_ci				/*
296d4afb5ceSopenharmony_ci				 * it's unique, instantiate a record for it
297d4afb5ceSopenharmony_ci				 */
298d4afb5ceSopenharmony_ci
299d4afb5ceSopenharmony_ci				m = lws_fi(&_c->info.cx->fic,
300d4afb5ceSopenharmony_ci					   "cache_lookup_oom") ? NULL :
301d4afb5ceSopenharmony_ci					lws_malloc(sizeof(*m) + ilen + 1,
302d4afb5ceSopenharmony_ci						   __func__);
303d4afb5ceSopenharmony_ci				if (!m) {
304d4afb5ceSopenharmony_ci					lws_cache_clear_matches(results_owner);
305d4afb5ceSopenharmony_ci					return 1;
306d4afb5ceSopenharmony_ci				}
307d4afb5ceSopenharmony_ci
308d4afb5ceSopenharmony_ci				memset(&m->list, 0, sizeof(m->list));
309d4afb5ceSopenharmony_ci				m->tag_size = ilen;
310d4afb5ceSopenharmony_ci				memcpy(&m[1], iname, ilen + 1);
311d4afb5ceSopenharmony_ci
312d4afb5ceSopenharmony_ci				lws_dll2_add_tail(&m->list, results_owner);
313d4afb5ceSopenharmony_ci			}
314d4afb5ceSopenharmony_ci		}
315d4afb5ceSopenharmony_ci
316d4afb5ceSopenharmony_ci	} lws_end_foreach_dll(d);
317d4afb5ceSopenharmony_ci
318d4afb5ceSopenharmony_ci	return 0;
319d4afb5ceSopenharmony_ci}
320d4afb5ceSopenharmony_ci
321d4afb5ceSopenharmony_cistatic int
322d4afb5ceSopenharmony_cilws_cache_heap_write(struct lws_cache_ttl_lru *_c, const char *specific_key,
323d4afb5ceSopenharmony_ci		     const uint8_t *source, size_t size, lws_usec_t expiry,
324d4afb5ceSopenharmony_ci		     void **ppvoid)
325d4afb5ceSopenharmony_ci{
326d4afb5ceSopenharmony_ci	lws_cache_ttl_lru_t_heap_t *cache = (lws_cache_ttl_lru_t_heap_t *)_c;
327d4afb5ceSopenharmony_ci	struct lws_cache_ttl_lru *backing = _c;
328d4afb5ceSopenharmony_ci	lws_cache_ttl_item_heap_t *item, *ei;
329d4afb5ceSopenharmony_ci	size_t kl = strlen(specific_key);
330d4afb5ceSopenharmony_ci	char *p;
331d4afb5ceSopenharmony_ci
332d4afb5ceSopenharmony_ci	lwsl_cache("%s: %s: len %d\n", __func__, _c->info.name, (int)size);
333d4afb5ceSopenharmony_ci
334d4afb5ceSopenharmony_ci	/*
335d4afb5ceSopenharmony_ci	 * Is this new tag going to invalidate any existing cached meta-results?
336d4afb5ceSopenharmony_ci	 *
337d4afb5ceSopenharmony_ci	 * If so, let's destroy any of those first to recover the heap
338d4afb5ceSopenharmony_ci	 */
339d4afb5ceSopenharmony_ci
340d4afb5ceSopenharmony_ci	if (backing->info.parent)
341d4afb5ceSopenharmony_ci		backing = backing->info.parent;
342d4afb5ceSopenharmony_ci
343d4afb5ceSopenharmony_ci	lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
344d4afb5ceSopenharmony_ci				   cache->items_lru.head) {
345d4afb5ceSopenharmony_ci		lws_cache_ttl_item_heap_t *i = lws_container_of(d,
346d4afb5ceSopenharmony_ci						lws_cache_ttl_item_heap_t,
347d4afb5ceSopenharmony_ci						list_lru);
348d4afb5ceSopenharmony_ci		const char *iname = ((const char *)&i[1]) + i->size;
349d4afb5ceSopenharmony_ci
350d4afb5ceSopenharmony_ci		if (*iname == META_ITEM_LEADING) {
351d4afb5ceSopenharmony_ci
352d4afb5ceSopenharmony_ci			/*
353d4afb5ceSopenharmony_ci			 * If the item about to be added would match any cached
354d4afb5ceSopenharmony_ci			 * results from before it was added, we have to
355d4afb5ceSopenharmony_ci			 * invalidate them.  To check this, we have to use the
356d4afb5ceSopenharmony_ci			 * matching rules at the backing store level
357d4afb5ceSopenharmony_ci			 */
358d4afb5ceSopenharmony_ci
359d4afb5ceSopenharmony_ci			if (!strcmp(iname + 1, specific_key))
360d4afb5ceSopenharmony_ci				_lws_cache_heap_item_destroy(cache, i);
361d4afb5ceSopenharmony_ci		}
362d4afb5ceSopenharmony_ci
363d4afb5ceSopenharmony_ci	} lws_end_foreach_dll_safe(d, d1);
364d4afb5ceSopenharmony_ci
365d4afb5ceSopenharmony_ci
366d4afb5ceSopenharmony_ci	/*
367d4afb5ceSopenharmony_ci	 * Keep us under the limit if possible... note this will always allow
368d4afb5ceSopenharmony_ci	 * caching a single large item even if it is above the limits
369d4afb5ceSopenharmony_ci	 */
370d4afb5ceSopenharmony_ci
371d4afb5ceSopenharmony_ci	while ((cache->cache.info.max_footprint &&
372d4afb5ceSopenharmony_ci	        cache->cache.current_footprint + size >
373d4afb5ceSopenharmony_ci					     cache->cache.info.max_footprint) ||
374d4afb5ceSopenharmony_ci	       (cache->cache.info.max_items &&
375d4afb5ceSopenharmony_ci		cache->items_lru.count + 1 > cache->cache.info.max_items))
376d4afb5ceSopenharmony_ci		lws_cache_item_evict_lru(cache);
377d4afb5ceSopenharmony_ci
378d4afb5ceSopenharmony_ci	/* remove any existing entry of the same key */
379d4afb5ceSopenharmony_ci
380d4afb5ceSopenharmony_ci	lws_cache_heap_invalidate(&cache->cache, specific_key);
381d4afb5ceSopenharmony_ci
382d4afb5ceSopenharmony_ci	item = lws_fi(&_c->info.cx->fic, "cache_write_oom") ? NULL :
383d4afb5ceSopenharmony_ci			lws_malloc(sizeof(*item) + kl + 1u + size, __func__);
384d4afb5ceSopenharmony_ci	if (!item)
385d4afb5ceSopenharmony_ci		return 1;
386d4afb5ceSopenharmony_ci
387d4afb5ceSopenharmony_ci	cache->cache.current_footprint += item->size;
388d4afb5ceSopenharmony_ci
389d4afb5ceSopenharmony_ci	/* only need to zero down our item object */
390d4afb5ceSopenharmony_ci	memset(item, 0, sizeof(*item));
391d4afb5ceSopenharmony_ci
392d4afb5ceSopenharmony_ci	p = (char *)&item[1];
393d4afb5ceSopenharmony_ci	if (ppvoid)
394d4afb5ceSopenharmony_ci		*ppvoid = p;
395d4afb5ceSopenharmony_ci
396d4afb5ceSopenharmony_ci	/* copy the payload into place */
397d4afb5ceSopenharmony_ci	if (source)
398d4afb5ceSopenharmony_ci		memcpy(p, source, size);
399d4afb5ceSopenharmony_ci
400d4afb5ceSopenharmony_ci	/* copy the key string into place, with terminating NUL */
401d4afb5ceSopenharmony_ci	memcpy(p + size, specific_key, kl + 1);
402d4afb5ceSopenharmony_ci
403d4afb5ceSopenharmony_ci	item->expiry = expiry;
404d4afb5ceSopenharmony_ci	item->key_len = kl;
405d4afb5ceSopenharmony_ci	item->size = size;
406d4afb5ceSopenharmony_ci
407d4afb5ceSopenharmony_ci	if (expiry) {
408d4afb5ceSopenharmony_ci		/* adding to expiry is optional, on nonzero expiry */
409d4afb5ceSopenharmony_ci		lws_dll2_add_sorted(&item->list_expiry, &cache->items_expiry,
410d4afb5ceSopenharmony_ci				    sort_expiry);
411d4afb5ceSopenharmony_ci		ei = lws_container_of(cache->items_expiry.head,
412d4afb5ceSopenharmony_ci				      lws_cache_ttl_item_heap_t, list_expiry);
413d4afb5ceSopenharmony_ci		lwsl_debug("%s: setting exp %llu\n", __func__,
414d4afb5ceSopenharmony_ci						(unsigned long long)ei->expiry);
415d4afb5ceSopenharmony_ci		lws_cache_schedule(&cache->cache, expiry_cb, ei->expiry);
416d4afb5ceSopenharmony_ci	}
417d4afb5ceSopenharmony_ci
418d4afb5ceSopenharmony_ci	/* always add outselves to head of lru list */
419d4afb5ceSopenharmony_ci	lws_dll2_add_head(&item->list_lru, &cache->items_lru);
420d4afb5ceSopenharmony_ci
421d4afb5ceSopenharmony_ci	return 0;
422d4afb5ceSopenharmony_ci}
423d4afb5ceSopenharmony_ci
424d4afb5ceSopenharmony_cistatic int
425d4afb5ceSopenharmony_cilws_cache_heap_get(struct lws_cache_ttl_lru *_c, const char *specific_key,
426d4afb5ceSopenharmony_ci		   const void **pdata, size_t *psize)
427d4afb5ceSopenharmony_ci{
428d4afb5ceSopenharmony_ci	lws_cache_ttl_lru_t_heap_t *cache = (lws_cache_ttl_lru_t_heap_t *)_c;
429d4afb5ceSopenharmony_ci	lws_cache_ttl_item_heap_t *item;
430d4afb5ceSopenharmony_ci
431d4afb5ceSopenharmony_ci	item = lws_cache_heap_specific(cache, specific_key);
432d4afb5ceSopenharmony_ci	if (!item)
433d4afb5ceSopenharmony_ci		return 1;
434d4afb5ceSopenharmony_ci
435d4afb5ceSopenharmony_ci	/* we are using it, move it to lru head */
436d4afb5ceSopenharmony_ci	lws_dll2_remove(&item->list_lru);
437d4afb5ceSopenharmony_ci	lws_dll2_add_head(&item->list_lru, &cache->items_lru);
438d4afb5ceSopenharmony_ci
439d4afb5ceSopenharmony_ci	if (pdata) {
440d4afb5ceSopenharmony_ci		*pdata = (const void *)&item[1];
441d4afb5ceSopenharmony_ci		*psize = item->size;
442d4afb5ceSopenharmony_ci	}
443d4afb5ceSopenharmony_ci
444d4afb5ceSopenharmony_ci	return 0;
445d4afb5ceSopenharmony_ci}
446d4afb5ceSopenharmony_ci
447d4afb5ceSopenharmony_cistatic int
448d4afb5ceSopenharmony_cilws_cache_heap_invalidate(struct lws_cache_ttl_lru *_c, const char *specific_key)
449d4afb5ceSopenharmony_ci{
450d4afb5ceSopenharmony_ci	lws_cache_ttl_lru_t_heap_t *cache = (lws_cache_ttl_lru_t_heap_t *)_c;
451d4afb5ceSopenharmony_ci	struct lws_cache_ttl_lru *backing = _c;
452d4afb5ceSopenharmony_ci	lws_cache_ttl_item_heap_t *item;
453d4afb5ceSopenharmony_ci	const void *user;
454d4afb5ceSopenharmony_ci	size_t size;
455d4afb5ceSopenharmony_ci
456d4afb5ceSopenharmony_ci	if (lws_cache_heap_get(_c, specific_key, &user, &size))
457d4afb5ceSopenharmony_ci		return 0;
458d4afb5ceSopenharmony_ci
459d4afb5ceSopenharmony_ci	if (backing->info.parent)
460d4afb5ceSopenharmony_ci		backing = backing->info.parent;
461d4afb5ceSopenharmony_ci
462d4afb5ceSopenharmony_ci	item = (lws_cache_ttl_item_heap_t *)(((uint8_t *)user) - sizeof(*item));
463d4afb5ceSopenharmony_ci
464d4afb5ceSopenharmony_ci	/*
465d4afb5ceSopenharmony_ci	 * We must invalidate any cached results that would have included this
466d4afb5ceSopenharmony_ci	 */
467d4afb5ceSopenharmony_ci
468d4afb5ceSopenharmony_ci	lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
469d4afb5ceSopenharmony_ci				   cache->items_lru.head) {
470d4afb5ceSopenharmony_ci		lws_cache_ttl_item_heap_t *i = lws_container_of(d,
471d4afb5ceSopenharmony_ci						lws_cache_ttl_item_heap_t,
472d4afb5ceSopenharmony_ci						list_lru);
473d4afb5ceSopenharmony_ci		const char *iname = ((const char *)&i[1]) + i->size;
474d4afb5ceSopenharmony_ci
475d4afb5ceSopenharmony_ci		if (*iname == META_ITEM_LEADING) {
476d4afb5ceSopenharmony_ci
477d4afb5ceSopenharmony_ci			/*
478d4afb5ceSopenharmony_ci			 * If the item about to be added would match any cached
479d4afb5ceSopenharmony_ci			 * results from before it was added, we have to
480d4afb5ceSopenharmony_ci			 * invalidate them.  To check this, we have to use the
481d4afb5ceSopenharmony_ci			 * matching rules at the backing store level
482d4afb5ceSopenharmony_ci			 */
483d4afb5ceSopenharmony_ci
484d4afb5ceSopenharmony_ci			if (!backing->info.ops->tag_match(backing, iname + 1,
485d4afb5ceSopenharmony_ci							  specific_key, 1))
486d4afb5ceSopenharmony_ci				_lws_cache_heap_item_destroy(cache, i);
487d4afb5ceSopenharmony_ci		}
488d4afb5ceSopenharmony_ci
489d4afb5ceSopenharmony_ci	} lws_end_foreach_dll_safe(d, d1);
490d4afb5ceSopenharmony_ci
491d4afb5ceSopenharmony_ci	lws_cache_heap_item_destroy(cache, item, 0);
492d4afb5ceSopenharmony_ci
493d4afb5ceSopenharmony_ci	return 0;
494d4afb5ceSopenharmony_ci}
495d4afb5ceSopenharmony_ci
496d4afb5ceSopenharmony_cistatic struct lws_cache_ttl_lru *
497d4afb5ceSopenharmony_cilws_cache_heap_create(const struct lws_cache_creation_info *info)
498d4afb5ceSopenharmony_ci{
499d4afb5ceSopenharmony_ci	lws_cache_ttl_lru_t_heap_t *cache;
500d4afb5ceSopenharmony_ci
501d4afb5ceSopenharmony_ci	assert(info->cx);
502d4afb5ceSopenharmony_ci	assert(info->name);
503d4afb5ceSopenharmony_ci
504d4afb5ceSopenharmony_ci	cache = lws_fi(&info->cx->fic, "cache_createfail") ? NULL :
505d4afb5ceSopenharmony_ci					lws_zalloc(sizeof(*cache), __func__);
506d4afb5ceSopenharmony_ci	if (!cache)
507d4afb5ceSopenharmony_ci		return NULL;
508d4afb5ceSopenharmony_ci
509d4afb5ceSopenharmony_ci	cache->cache.info = *info;
510d4afb5ceSopenharmony_ci	if (info->parent)
511d4afb5ceSopenharmony_ci		info->parent->child = &cache->cache;
512d4afb5ceSopenharmony_ci
513d4afb5ceSopenharmony_ci	// lwsl_cache("%s: create %s\n", __func__, info->name);
514d4afb5ceSopenharmony_ci
515d4afb5ceSopenharmony_ci	return (struct lws_cache_ttl_lru *)cache;
516d4afb5ceSopenharmony_ci}
517d4afb5ceSopenharmony_ci
518d4afb5ceSopenharmony_cistatic int
519d4afb5ceSopenharmony_cidestroy_dll(struct lws_dll2 *d, void *user)
520d4afb5ceSopenharmony_ci{
521d4afb5ceSopenharmony_ci	lws_cache_ttl_lru_t *_c = (struct lws_cache_ttl_lru *)user;
522d4afb5ceSopenharmony_ci	lws_cache_ttl_lru_t_heap_t *cache = (lws_cache_ttl_lru_t_heap_t *)_c;
523d4afb5ceSopenharmony_ci	lws_cache_ttl_item_heap_t *item =
524d4afb5ceSopenharmony_ci		       lws_container_of(d, lws_cache_ttl_item_heap_t, list_lru);
525d4afb5ceSopenharmony_ci
526d4afb5ceSopenharmony_ci	lws_cache_heap_item_destroy(cache, item, 0);
527d4afb5ceSopenharmony_ci
528d4afb5ceSopenharmony_ci	return 0;
529d4afb5ceSopenharmony_ci}
530d4afb5ceSopenharmony_ci
531d4afb5ceSopenharmony_cistatic int
532d4afb5ceSopenharmony_cilws_cache_heap_expunge(struct lws_cache_ttl_lru *_c)
533d4afb5ceSopenharmony_ci{
534d4afb5ceSopenharmony_ci	lws_cache_ttl_lru_t_heap_t *cache = (lws_cache_ttl_lru_t_heap_t *)_c;
535d4afb5ceSopenharmony_ci
536d4afb5ceSopenharmony_ci	lws_dll2_foreach_safe(&cache->items_lru, cache, destroy_dll);
537d4afb5ceSopenharmony_ci
538d4afb5ceSopenharmony_ci	return 0;
539d4afb5ceSopenharmony_ci}
540d4afb5ceSopenharmony_ci
541d4afb5ceSopenharmony_cistatic void
542d4afb5ceSopenharmony_cilws_cache_heap_destroy(struct lws_cache_ttl_lru **_cache)
543d4afb5ceSopenharmony_ci{
544d4afb5ceSopenharmony_ci	lws_cache_ttl_lru_t *c = *_cache;
545d4afb5ceSopenharmony_ci	lws_cache_ttl_lru_t_heap_t *cache = (lws_cache_ttl_lru_t_heap_t *)c;
546d4afb5ceSopenharmony_ci
547d4afb5ceSopenharmony_ci	if (!cache)
548d4afb5ceSopenharmony_ci		return;
549d4afb5ceSopenharmony_ci
550d4afb5ceSopenharmony_ci	lws_sul_cancel(&c->sul);
551d4afb5ceSopenharmony_ci
552d4afb5ceSopenharmony_ci	lws_dll2_foreach_safe(&cache->items_lru, cache, destroy_dll);
553d4afb5ceSopenharmony_ci
554d4afb5ceSopenharmony_ci	lws_free_set_NULL(*_cache);
555d4afb5ceSopenharmony_ci}
556d4afb5ceSopenharmony_ci
557d4afb5ceSopenharmony_ci#if defined(_DEBUG)
558d4afb5ceSopenharmony_cistatic int
559d4afb5ceSopenharmony_cidump_dll(struct lws_dll2 *d, void *user)
560d4afb5ceSopenharmony_ci{
561d4afb5ceSopenharmony_ci	lws_cache_ttl_item_heap_t *item =
562d4afb5ceSopenharmony_ci		       lws_container_of(d, lws_cache_ttl_item_heap_t, list_lru);
563d4afb5ceSopenharmony_ci
564d4afb5ceSopenharmony_ci	lwsl_cache("  %s: size %d, exp %llu\n",
565d4afb5ceSopenharmony_ci		   (const char *)&item[1] + item->size,
566d4afb5ceSopenharmony_ci		   (int)item->size, (unsigned long long)item->expiry);
567d4afb5ceSopenharmony_ci
568d4afb5ceSopenharmony_ci	lwsl_hexdump_cache((const char *)&item[1], item->size);
569d4afb5ceSopenharmony_ci
570d4afb5ceSopenharmony_ci	return 0;
571d4afb5ceSopenharmony_ci}
572d4afb5ceSopenharmony_ci
573d4afb5ceSopenharmony_cistatic void
574d4afb5ceSopenharmony_cilws_cache_heap_debug_dump(struct lws_cache_ttl_lru *_c)
575d4afb5ceSopenharmony_ci{
576d4afb5ceSopenharmony_ci	lws_cache_ttl_lru_t_heap_t *cache = (lws_cache_ttl_lru_t_heap_t *)_c;
577d4afb5ceSopenharmony_ci#if !defined(LWS_WITH_NO_LOGS)
578d4afb5ceSopenharmony_ci	lws_cache_ttl_item_heap_t *item = NULL;
579d4afb5ceSopenharmony_ci
580d4afb5ceSopenharmony_ci	lws_dll2_t *d = cache->items_expiry.head;
581d4afb5ceSopenharmony_ci
582d4afb5ceSopenharmony_ci	if (d)
583d4afb5ceSopenharmony_ci		item = lws_container_of(d, lws_cache_ttl_item_heap_t,
584d4afb5ceSopenharmony_ci						list_expiry);
585d4afb5ceSopenharmony_ci
586d4afb5ceSopenharmony_ci	lwsl_cache("%s: %s: items %d, earliest %llu\n", __func__,
587d4afb5ceSopenharmony_ci			cache->cache.info.name, (int)cache->items_lru.count,
588d4afb5ceSopenharmony_ci			item ? (unsigned long long)item->expiry : 0ull);
589d4afb5ceSopenharmony_ci#endif
590d4afb5ceSopenharmony_ci
591d4afb5ceSopenharmony_ci	lws_dll2_foreach_safe(&cache->items_lru, cache, dump_dll);
592d4afb5ceSopenharmony_ci}
593d4afb5ceSopenharmony_ci#endif
594d4afb5ceSopenharmony_ci
595d4afb5ceSopenharmony_ciconst struct lws_cache_ops lws_cache_ops_heap = {
596d4afb5ceSopenharmony_ci	.create			= lws_cache_heap_create,
597d4afb5ceSopenharmony_ci	.destroy		= lws_cache_heap_destroy,
598d4afb5ceSopenharmony_ci	.expunge		= lws_cache_heap_expunge,
599d4afb5ceSopenharmony_ci
600d4afb5ceSopenharmony_ci	.write			= lws_cache_heap_write,
601d4afb5ceSopenharmony_ci	.tag_match		= lws_cache_heap_tag_match,
602d4afb5ceSopenharmony_ci	.lookup			= lws_cache_heap_lookup,
603d4afb5ceSopenharmony_ci	.invalidate		= lws_cache_heap_invalidate,
604d4afb5ceSopenharmony_ci	.get			= lws_cache_heap_get,
605d4afb5ceSopenharmony_ci#if defined(_DEBUG)
606d4afb5ceSopenharmony_ci	.debug_dump		= lws_cache_heap_debug_dump,
607d4afb5ceSopenharmony_ci#endif
608d4afb5ceSopenharmony_ci};
609