1158115Sume/*-
2158115Sume * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
3158115Sume * All rights reserved.
4158115Sume *
5158115Sume * Redistribution and use in source and binary forms, with or without
6158115Sume * modification, are permitted provided that the following conditions
7158115Sume * are met:
8158115Sume * 1. Redistributions of source code must retain the above copyright
9158115Sume *    notice, this list of conditions and the following disclaimer.
10158115Sume * 2. Redistributions in binary form must reproduce the above copyright
11158115Sume *    notice, this list of conditions and the following disclaimer in the
12158115Sume *    documentation and/or other materials provided with the distribution.
13158115Sume *
14158115Sume * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15158115Sume * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16158115Sume * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17158115Sume * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18158115Sume * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19158115Sume * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20158115Sume * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21158115Sume * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22158115Sume * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23158115Sume * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24158115Sume * SUCH DAMAGE.
25158115Sume *
26158115Sume */
27158115Sume
28158115Sume#include <sys/cdefs.h>
29158115Sume__FBSDID("$FreeBSD$");
30158115Sume
31158115Sume#include <sys/time.h>
32194093Sdes
33158115Sume#include <assert.h>
34158115Sume#include <stdlib.h>
35158115Sume#include <string.h>
36194093Sdes
37158115Sume#include "cachelib.h"
38158115Sume#include "debug.h"
39158115Sume
40158115Sume#define INITIAL_ENTRIES_CAPACITY 32
41158115Sume#define ENTRIES_CAPACITY_STEP 32
42158115Sume
43158115Sume#define STRING_SIMPLE_HASH_BODY(in_var, var, a, M)		\
44158115Sume	for ((var) = 0; *(in_var) != '\0'; ++(in_var))		\
45158115Sume		(var) = ((a)*(var) + *(in_var)) % (M)
46158115Sume
47158115Sume#define STRING_SIMPLE_MP2_HASH_BODY(in_var, var, a, M)		\
48158115Sume	for ((var) = 0; *(in_var) != 0; ++(in_var))		\
49158115Sume		(var) = ((a)*(var) + *(in_var)) & (M - 1)
50158115Sume
51158115Sumestatic int cache_elemsize_common_continue_func(struct cache_common_entry_ *,
52158115Sume	struct cache_policy_item_ *);
53158115Sumestatic int cache_lifetime_common_continue_func(struct cache_common_entry_ *,
54158115Sume	struct cache_policy_item_ *);
55158115Sumestatic void clear_cache_entry(struct cache_entry_ *);
56158115Sumestatic void destroy_cache_entry(struct cache_entry_ *);
57158115Sumestatic void destroy_cache_mp_read_session(struct cache_mp_read_session_ *);
58158115Sumestatic void destroy_cache_mp_write_session(struct cache_mp_write_session_ *);
59158115Sumestatic int entries_bsearch_cmp_func(const void *, const void *);
60158115Sumestatic int entries_qsort_cmp_func(const void *, const void *);
61158115Sumestatic struct cache_entry_ ** find_cache_entry_p(struct cache_ *,
62158115Sume	const char *);
63158115Sumestatic void flush_cache_entry(struct cache_entry_ *);
64158115Sumestatic void flush_cache_policy(struct cache_common_entry_ *,
65158115Sume	struct cache_policy_ *, struct cache_policy_ *,
66158115Sume		int (*)(struct cache_common_entry_ *,
67158115Sume		struct cache_policy_item_ *));
68158115Sumestatic int ht_items_cmp_func(const void *, const void *);
69158115Sumestatic int ht_items_fixed_size_left_cmp_func(const void *, const void *);
70158115Sumestatic hashtable_index_t ht_item_hash_func(const void *, size_t);
71158115Sume
72158115Sume/*
73158115Sume * Hashing and comparing routines, that are used with the hash tables
74158115Sume */
75158115Sumestatic int
76158115Sumeht_items_cmp_func(const void *p1, const void *p2)
77158115Sume{
78158115Sume    	struct cache_ht_item_data_ *hp1, *hp2;
79158115Sume	size_t min_size;
80158115Sume	int result;
81158115Sume
82158115Sume	hp1 = (struct cache_ht_item_data_ *)p1;
83158115Sume	hp2 = (struct cache_ht_item_data_ *)p2;
84158115Sume
85158115Sume	assert(hp1->key != NULL);
86158115Sume	assert(hp2->key != NULL);
87158115Sume
88158115Sume	if (hp1->key_size != hp2->key_size) {
89158115Sume		min_size = (hp1->key_size < hp2->key_size) ? hp1->key_size :
90158115Sume			hp2->key_size;
91158115Sume		result = memcmp(hp1->key, hp2->key, min_size);
92158115Sume
93158115Sume		if (result == 0)
94158115Sume			return ((hp1->key_size < hp2->key_size) ? -1 : 1);
95158115Sume		else
96158115Sume			return (result);
97158115Sume	} else
98158115Sume		return (memcmp(hp1->key, hp2->key, hp1->key_size));
99158115Sume}
100158115Sume
101158115Sumestatic int
102158115Sumeht_items_fixed_size_left_cmp_func(const void *p1, const void *p2)
103158115Sume{
104158115Sume    	struct cache_ht_item_data_ *hp1, *hp2;
105158115Sume	size_t min_size;
106158115Sume	int result;
107158115Sume
108158115Sume	hp1 = (struct cache_ht_item_data_ *)p1;
109158115Sume	hp2 = (struct cache_ht_item_data_ *)p2;
110158115Sume
111158115Sume	assert(hp1->key != NULL);
112158115Sume	assert(hp2->key != NULL);
113158115Sume
114158115Sume	if (hp1->key_size != hp2->key_size) {
115158115Sume		min_size = (hp1->key_size < hp2->key_size) ? hp1->key_size :
116158115Sume			hp2->key_size;
117158115Sume		result = memcmp(hp1->key, hp2->key, min_size);
118158115Sume
119158115Sume		if (result == 0)
120158115Sume			if (min_size == hp1->key_size)
121158115Sume			    return (0);
122158115Sume			else
123158115Sume			    return ((hp1->key_size < hp2->key_size) ? -1 : 1);
124158115Sume		else
125158115Sume			return (result);
126158115Sume	} else
127158115Sume		return (memcmp(hp1->key, hp2->key, hp1->key_size));
128158115Sume}
129158115Sume
130158115Sumestatic hashtable_index_t
131158115Sumeht_item_hash_func(const void *p, size_t cache_entries_size)
132158115Sume{
133158115Sume    	struct cache_ht_item_data_ *hp;
134158115Sume	size_t i;
135158115Sume
136158115Sume	hashtable_index_t retval;
137158115Sume
138158115Sume	hp = (struct cache_ht_item_data_ *)p;
139158115Sume	assert(hp->key != NULL);
140158115Sume
141158115Sume	retval = 0;
142158115Sume	for (i = 0; i < hp->key_size; ++i)
143158115Sume	    retval = (127 * retval + (unsigned char)hp->key[i]) %
144158115Sume		cache_entries_size;
145158115Sume
146158115Sume	return retval;
147158115Sume}
148158115Sume
149194095SdesHASHTABLE_PROTOTYPE(cache_ht_, cache_ht_item_, struct cache_ht_item_data_);
150158115SumeHASHTABLE_GENERATE(cache_ht_, cache_ht_item_, struct cache_ht_item_data_, data,
151158115Sume	ht_item_hash_func, ht_items_cmp_func);
152158115Sume
153158115Sume/*
154158115Sume * Routines to sort and search the entries by name
155158115Sume */
156158115Sumestatic int
157158115Sumeentries_bsearch_cmp_func(const void *key, const void *ent)
158158115Sume{
159158115Sume
160158115Sume	assert(key != NULL);
161158115Sume	assert(ent != NULL);
162158115Sume
163158115Sume	return (strcmp((char const *)key,
164158115Sume		(*(struct cache_entry_ const **)ent)->name));
165158115Sume}
166158115Sume
167158115Sumestatic int
168158115Sumeentries_qsort_cmp_func(const void *e1, const void *e2)
169158115Sume{
170158115Sume
171158115Sume	assert(e1 != NULL);
172158115Sume	assert(e2 != NULL);
173158115Sume
174158115Sume	return (strcmp((*(struct cache_entry_ const **)e1)->name,
175158115Sume		(*(struct cache_entry_ const **)e2)->name));
176158115Sume}
177158115Sume
178158115Sumestatic struct cache_entry_ **
179158115Sumefind_cache_entry_p(struct cache_ *the_cache, const char *entry_name)
180158115Sume{
181158115Sume
182158115Sume	return ((struct cache_entry_ **)(bsearch(entry_name, the_cache->entries,
183158115Sume		the_cache->entries_size, sizeof(struct cache_entry_ *),
184158115Sume		entries_bsearch_cmp_func)));
185158115Sume}
186158115Sume
187158115Sumestatic void
188158115Sumedestroy_cache_mp_write_session(struct cache_mp_write_session_ *ws)
189158115Sume{
190158115Sume
191158115Sume	struct cache_mp_data_item_	*data_item;
192158115Sume
193158115Sume	TRACE_IN(destroy_cache_mp_write_session);
194158115Sume	assert(ws != NULL);
195158115Sume	while (!TAILQ_EMPTY(&ws->items)) {
196158115Sume		data_item = TAILQ_FIRST(&ws->items);
197158115Sume		TAILQ_REMOVE(&ws->items, data_item, entries);
198158115Sume		free(data_item->value);
199158115Sume		free(data_item);
200158115Sume	}
201158115Sume
202158115Sume	free(ws);
203158115Sume	TRACE_OUT(destroy_cache_mp_write_session);
204158115Sume}
205158115Sume
206158115Sumestatic void
207158115Sumedestroy_cache_mp_read_session(struct cache_mp_read_session_ *rs)
208158115Sume{
209158115Sume
210158115Sume	TRACE_IN(destroy_cache_mp_read_session);
211158115Sume	assert(rs != NULL);
212158115Sume	free(rs);
213158115Sume	TRACE_OUT(destroy_cache_mp_read_session);
214158115Sume}
215158115Sume
216158115Sumestatic void
217158115Sumedestroy_cache_entry(struct cache_entry_ *entry)
218158115Sume{
219158115Sume	struct cache_common_entry_	*common_entry;
220158115Sume	struct cache_mp_entry_		*mp_entry;
221158115Sume	struct cache_mp_read_session_	*rs;
222158115Sume	struct cache_mp_write_session_	*ws;
223158115Sume	struct cache_ht_item_ *ht_item;
224158115Sume	struct cache_ht_item_data_ *ht_item_data;
225158115Sume
226158115Sume	TRACE_IN(destroy_cache_entry);
227158115Sume	assert(entry != NULL);
228158115Sume
229158115Sume	if (entry->params->entry_type == CET_COMMON) {
230158115Sume		common_entry = (struct cache_common_entry_ *)entry;
231158115Sume
232158115Sume		HASHTABLE_FOREACH(&(common_entry->items), ht_item) {
233158115Sume			HASHTABLE_ENTRY_FOREACH(ht_item, data, ht_item_data)
234158115Sume			{
235158115Sume				free(ht_item_data->key);
236158115Sume				free(ht_item_data->value);
237158115Sume			}
238158115Sume			HASHTABLE_ENTRY_CLEAR(ht_item, data);
239158115Sume		}
240158115Sume
241158115Sume		HASHTABLE_DESTROY(&(common_entry->items), data);
242158115Sume
243158115Sume		/* FIFO policy is always first */
244158115Sume		destroy_cache_fifo_policy(common_entry->policies[0]);
245158115Sume		switch (common_entry->common_params.policy) {
246158115Sume		case CPT_LRU:
247158115Sume			destroy_cache_lru_policy(common_entry->policies[1]);
248158115Sume			break;
249158115Sume		case CPT_LFU:
250158115Sume			destroy_cache_lfu_policy(common_entry->policies[1]);
251158115Sume			break;
252158115Sume		default:
253158115Sume		break;
254158115Sume		}
255158115Sume		free(common_entry->policies);
256158115Sume	} else {
257158115Sume		mp_entry = (struct cache_mp_entry_ *)entry;
258158115Sume
259158115Sume		while (!TAILQ_EMPTY(&mp_entry->ws_head)) {
260158115Sume			ws = TAILQ_FIRST(&mp_entry->ws_head);
261158115Sume			TAILQ_REMOVE(&mp_entry->ws_head, ws, entries);
262158115Sume			destroy_cache_mp_write_session(ws);
263158115Sume		}
264158115Sume
265158115Sume		while (!TAILQ_EMPTY(&mp_entry->rs_head)) {
266158115Sume			rs = TAILQ_FIRST(&mp_entry->rs_head);
267158115Sume			TAILQ_REMOVE(&mp_entry->rs_head, rs, entries);
268158115Sume			destroy_cache_mp_read_session(rs);
269158115Sume		}
270158115Sume
271158115Sume		if (mp_entry->completed_write_session != NULL)
272158115Sume			destroy_cache_mp_write_session(
273158115Sume				mp_entry->completed_write_session);
274158115Sume
275158115Sume		if (mp_entry->pending_write_session != NULL)
276158115Sume			destroy_cache_mp_write_session(
277158115Sume				mp_entry->pending_write_session);
278158115Sume	}
279158115Sume
280158115Sume	free(entry->name);
281158115Sume	free(entry);
282158115Sume	TRACE_OUT(destroy_cache_entry);
283158115Sume}
284158115Sume
285158115Sumestatic void
286158115Sumeclear_cache_entry(struct cache_entry_ *entry)
287158115Sume{
288158115Sume	struct cache_mp_entry_		*mp_entry;
289158115Sume	struct cache_common_entry_	*common_entry;
290158115Sume	struct cache_ht_item_ *ht_item;
291158115Sume	struct cache_ht_item_data_ *ht_item_data;
292158115Sume	struct cache_policy_ *policy;
293158115Sume	struct cache_policy_item_ *item, *next_item;
294158115Sume	size_t entry_size;
295194095Sdes	unsigned int i;
296158115Sume
297158115Sume	if (entry->params->entry_type == CET_COMMON) {
298158115Sume		common_entry = (struct cache_common_entry_ *)entry;
299158115Sume
300158115Sume		entry_size = 0;
301158115Sume		HASHTABLE_FOREACH(&(common_entry->items), ht_item) {
302158115Sume			HASHTABLE_ENTRY_FOREACH(ht_item, data, ht_item_data)
303158115Sume			{
304158115Sume				free(ht_item_data->key);
305158115Sume				free(ht_item_data->value);
306158115Sume			}
307158115Sume			entry_size += HASHTABLE_ENTRY_SIZE(ht_item, data);
308158115Sume			HASHTABLE_ENTRY_CLEAR(ht_item, data);
309158115Sume		}
310158115Sume
311158115Sume		common_entry->items_size -= entry_size;
312158115Sume		for (i = 0; i < common_entry->policies_size; ++i) {
313158115Sume			policy = common_entry->policies[i];
314158115Sume
315158115Sume			next_item = NULL;
316158115Sume			item = policy->get_first_item_func(policy);
317158115Sume			while (item != NULL) {
318158115Sume				next_item = policy->get_next_item_func(policy,
319158115Sume			    		item);
320158115Sume				policy->remove_item_func(policy, item);
321158115Sume				policy->destroy_item_func(item);
322158115Sume				item = next_item;
323158115Sume			}
324158115Sume		}
325158115Sume	} else {
326158115Sume		mp_entry = (struct cache_mp_entry_ *)entry;
327158115Sume
328158115Sume		if (mp_entry->rs_size == 0) {
329158115Sume			if (mp_entry->completed_write_session != NULL) {
330158115Sume				destroy_cache_mp_write_session(
331158115Sume					mp_entry->completed_write_session);
332158115Sume				mp_entry->completed_write_session = NULL;
333158115Sume			}
334158115Sume
335158115Sume			memset(&mp_entry->creation_time, 0,
336158115Sume				sizeof(struct timeval));
337158115Sume			memset(&mp_entry->last_request_time, 0,
338158115Sume				sizeof(struct timeval));
339158115Sume		}
340158115Sume	}
341158115Sume}
342158115Sume
343158115Sume/*
344158115Sume * When passed to the flush_cache_policy, ensures that all old elements are
345158115Sume * deleted.
346158115Sume */
347158115Sumestatic int
348158115Sumecache_lifetime_common_continue_func(struct cache_common_entry_ *entry,
349158115Sume	struct cache_policy_item_ *item)
350158115Sume{
351158115Sume
352158115Sume	return ((item->last_request_time.tv_sec - item->creation_time.tv_sec >
353158115Sume		entry->common_params.max_lifetime.tv_sec) ? 1: 0);
354158115Sume}
355158115Sume
356158115Sume/*
357158115Sume * When passed to the flush_cache_policy, ensures that all elements, that
358158115Sume * exceed the size limit, are deleted.
359158115Sume */
360158115Sumestatic int
361158115Sumecache_elemsize_common_continue_func(struct cache_common_entry_ *entry,
362158115Sume	struct cache_policy_item_ *item)
363158115Sume{
364158115Sume
365158115Sume	return ((entry->items_size > entry->common_params.satisf_elemsize) ? 1
366158115Sume    		: 0);
367158115Sume}
368158115Sume
369158115Sume/*
370158115Sume * Removes the elements from the cache entry, while the continue_func returns 1.
371158115Sume */
372158115Sumestatic void
373158115Sumeflush_cache_policy(struct cache_common_entry_ *entry,
374158115Sume	struct cache_policy_ *policy,
375158115Sume	struct cache_policy_ *connected_policy,
376158115Sume	int (*continue_func)(struct cache_common_entry_ *,
377158115Sume		struct cache_policy_item_ *))
378158115Sume{
379158115Sume	struct cache_policy_item_ *item, *next_item, *connected_item;
380158115Sume	struct cache_ht_item_ *ht_item;
381158115Sume	struct cache_ht_item_data_ *ht_item_data, ht_key;
382158115Sume	hashtable_index_t hash;
383158115Sume
384158115Sume	assert(policy != NULL);
385158115Sume
386158115Sume	next_item = NULL;
387158115Sume	item = policy->get_first_item_func(policy);
388158115Sume	while ((item != NULL) && (continue_func(entry, item) == 1)) {
389158115Sume		next_item = policy->get_next_item_func(policy, item);
390158115Sume
391158115Sume		connected_item = item->connected_item;
392158115Sume		policy->remove_item_func(policy, item);
393158115Sume
394158115Sume		memset(&ht_key, 0, sizeof(struct cache_ht_item_data_));
395158115Sume		ht_key.key = item->key;
396158115Sume		ht_key.key_size = item->key_size;
397158115Sume
398158115Sume		hash = HASHTABLE_CALCULATE_HASH(cache_ht_, &entry->items,
399158115Sume			&ht_key);
400158115Sume		assert(hash < HASHTABLE_ENTRIES_COUNT(&entry->items));
401158115Sume
402158115Sume		ht_item = HASHTABLE_GET_ENTRY(&(entry->items), hash);
403158115Sume		ht_item_data = HASHTABLE_ENTRY_FIND(cache_ht_, ht_item,
404158115Sume			&ht_key);
405158115Sume		assert(ht_item_data != NULL);
406158115Sume		free(ht_item_data->key);
407158115Sume		free(ht_item_data->value);
408158115Sume		HASHTABLE_ENTRY_REMOVE(cache_ht_, ht_item, ht_item_data);
409158115Sume		--entry->items_size;
410158115Sume
411158115Sume		policy->destroy_item_func(item);
412158115Sume
413158115Sume		if (connected_item != NULL) {
414158115Sume			connected_policy->remove_item_func(connected_policy,
415158115Sume				connected_item);
416158115Sume			connected_policy->destroy_item_func(connected_item);
417158115Sume		}
418158115Sume
419158115Sume		item = next_item;
420158115Sume	}
421158115Sume}
422158115Sume
423158115Sumestatic void
424158115Sumeflush_cache_entry(struct cache_entry_ *entry)
425158115Sume{
426158115Sume	struct cache_mp_entry_		*mp_entry;
427158115Sume	struct cache_common_entry_	*common_entry;
428158115Sume	struct cache_policy_ *policy, *connected_policy;
429158115Sume
430158115Sume	connected_policy = NULL;
431158115Sume	if (entry->params->entry_type == CET_COMMON) {
432158115Sume		common_entry = (struct cache_common_entry_ *)entry;
433158115Sume		if ((common_entry->common_params.max_lifetime.tv_sec != 0) ||
434158115Sume		    (common_entry->common_params.max_lifetime.tv_usec != 0)) {
435158115Sume
436158115Sume			policy = common_entry->policies[0];
437158115Sume			if (common_entry->policies_size > 1)
438158115Sume				connected_policy = common_entry->policies[1];
439158115Sume
440158115Sume			flush_cache_policy(common_entry, policy,
441158115Sume				connected_policy,
442158115Sume				cache_lifetime_common_continue_func);
443158115Sume		}
444158115Sume
445158115Sume
446158115Sume		if ((common_entry->common_params.max_elemsize != 0) &&
447158115Sume			common_entry->items_size >
448158115Sume			common_entry->common_params.max_elemsize) {
449158115Sume
450158115Sume			if (common_entry->policies_size > 1) {
451158115Sume				policy = common_entry->policies[1];
452158115Sume				connected_policy = common_entry->policies[0];
453158115Sume			} else {
454158115Sume				policy = common_entry->policies[0];
455158115Sume				connected_policy = NULL;
456158115Sume			}
457158115Sume
458158115Sume			flush_cache_policy(common_entry, policy,
459158115Sume				connected_policy,
460158115Sume				cache_elemsize_common_continue_func);
461158115Sume		}
462158115Sume	} else {
463158115Sume		mp_entry = (struct cache_mp_entry_ *)entry;
464158115Sume
465158115Sume		if ((mp_entry->mp_params.max_lifetime.tv_sec != 0)
466158115Sume			|| (mp_entry->mp_params.max_lifetime.tv_usec != 0)) {
467158115Sume
468158115Sume			if (mp_entry->last_request_time.tv_sec -
469158115Sume				mp_entry->last_request_time.tv_sec >
470158115Sume				mp_entry->mp_params.max_lifetime.tv_sec)
471158115Sume				clear_cache_entry(entry);
472158115Sume		}
473158115Sume	}
474158115Sume}
475158115Sume
476158115Sumestruct cache_ *
477158115Sumeinit_cache(struct cache_params const *params)
478158115Sume{
479158115Sume	struct cache_ *retval;
480158115Sume
481158115Sume	TRACE_IN(init_cache);
482158115Sume	assert(params != NULL);
483158115Sume
484194104Sdes	retval = calloc(1, sizeof(*retval));
485158115Sume	assert(retval != NULL);
486158115Sume
487158115Sume	assert(params != NULL);
488158115Sume	memcpy(&retval->params, params, sizeof(struct cache_params));
489158115Sume
490194104Sdes	retval->entries = calloc(1,
491194104Sdes		sizeof(*retval->entries) * INITIAL_ENTRIES_CAPACITY);
492158115Sume	assert(retval->entries != NULL);
493158115Sume
494158115Sume	retval->entries_capacity = INITIAL_ENTRIES_CAPACITY;
495158115Sume	retval->entries_size = 0;
496158115Sume
497158115Sume	TRACE_OUT(init_cache);
498158115Sume	return (retval);
499158115Sume}
500158115Sume
501158115Sumevoid
502158115Sumedestroy_cache(struct cache_ *the_cache)
503158115Sume{
504158115Sume
505158115Sume	TRACE_IN(destroy_cache);
506158115Sume	assert(the_cache != NULL);
507158115Sume
508158115Sume	if (the_cache->entries != NULL) {
509158115Sume		size_t i;
510158115Sume		for (i = 0; i < the_cache->entries_size; ++i)
511158115Sume			destroy_cache_entry(the_cache->entries[i]);
512158115Sume
513158115Sume		free(the_cache->entries);
514158115Sume	}
515158115Sume
516158115Sume	free(the_cache);
517158115Sume	TRACE_OUT(destroy_cache);
518158115Sume}
519158115Sume
520158115Sumeint
521158115Sumeregister_cache_entry(struct cache_ *the_cache,
522158115Sume	struct cache_entry_params const *params)
523158115Sume{
524158115Sume	int policies_size;
525158115Sume	size_t entry_name_size;
526158115Sume	struct cache_common_entry_	*new_common_entry;
527158115Sume	struct cache_mp_entry_		*new_mp_entry;
528158115Sume
529158115Sume	TRACE_IN(register_cache_entry);
530158115Sume	assert(the_cache != NULL);
531158115Sume
532158115Sume	if (find_cache_entry(the_cache, params->entry_name) != NULL) {
533158115Sume		TRACE_OUT(register_cache_entry);
534158115Sume		return (-1);
535158115Sume	}
536158115Sume
537158115Sume	if (the_cache->entries_size == the_cache->entries_capacity) {
538158115Sume		struct cache_entry_ **new_entries;
539158115Sume		size_t	new_capacity;
540158115Sume
541158115Sume		new_capacity = the_cache->entries_capacity +
542158115Sume			ENTRIES_CAPACITY_STEP;
543194104Sdes		new_entries = calloc(1,
544194104Sdes			sizeof(*new_entries) * new_capacity);
545158115Sume		assert(new_entries != NULL);
546158115Sume
547158115Sume		memcpy(new_entries, the_cache->entries,
548158115Sume			sizeof(struct cache_entry_ *)
549158115Sume			* the_cache->entries_size);
550158115Sume
551158115Sume		free(the_cache->entries);
552158115Sume		the_cache->entries = new_entries;
553158115Sume	}
554158115Sume
555184189Sdelphij	entry_name_size = strlen(params->entry_name) + 1;
556158115Sume	switch (params->entry_type)
557158115Sume	{
558158115Sume	case CET_COMMON:
559194104Sdes		new_common_entry = calloc(1,
560194104Sdes			sizeof(*new_common_entry));
561158115Sume		assert(new_common_entry != NULL);
562158115Sume
563158115Sume		memcpy(&new_common_entry->common_params, params,
564158115Sume			sizeof(struct common_cache_entry_params));
565158115Sume		new_common_entry->params =
566158115Sume		  (struct cache_entry_params *)&new_common_entry->common_params;
567158115Sume
568194104Sdes		new_common_entry->common_params.cep.entry_name = calloc(1,
569184189Sdelphij			entry_name_size);
570194097Sdes		assert(new_common_entry->common_params.cep.entry_name != NULL);
571194097Sdes		strlcpy(new_common_entry->common_params.cep.entry_name,
572158115Sume			params->entry_name, entry_name_size);
573158115Sume		new_common_entry->name =
574194097Sdes			new_common_entry->common_params.cep.entry_name;
575158115Sume
576158115Sume		HASHTABLE_INIT(&(new_common_entry->items),
577158115Sume			struct cache_ht_item_data_, data,
578158115Sume			new_common_entry->common_params.cache_entries_size);
579158115Sume
580158115Sume		if (new_common_entry->common_params.policy == CPT_FIFO)
581158115Sume			policies_size = 1;
582158115Sume		else
583158115Sume			policies_size = 2;
584158115Sume
585194104Sdes		new_common_entry->policies = calloc(1,
586194104Sdes			sizeof(*new_common_entry->policies) * policies_size);
587158115Sume		assert(new_common_entry->policies != NULL);
588158115Sume
589158115Sume		new_common_entry->policies_size = policies_size;
590158115Sume		new_common_entry->policies[0] = init_cache_fifo_policy();
591158115Sume
592158115Sume		if (policies_size > 1) {
593158115Sume			switch (new_common_entry->common_params.policy) {
594158115Sume			case CPT_LRU:
595158115Sume				new_common_entry->policies[1] =
596158115Sume					init_cache_lru_policy();
597158115Sume			break;
598158115Sume			case CPT_LFU:
599158115Sume				new_common_entry->policies[1] =
600158115Sume					init_cache_lfu_policy();
601158115Sume			break;
602158115Sume			default:
603158115Sume			break;
604158115Sume			}
605158115Sume		}
606158115Sume
607158115Sume		new_common_entry->get_time_func =
608158115Sume			the_cache->params.get_time_func;
609158115Sume		the_cache->entries[the_cache->entries_size++] =
610158115Sume			(struct cache_entry_ *)new_common_entry;
611158115Sume		break;
612158115Sume	case CET_MULTIPART:
613194104Sdes		new_mp_entry = calloc(1,
614194104Sdes			sizeof(*new_mp_entry));
615158115Sume		assert(new_mp_entry != NULL);
616158115Sume
617158115Sume		memcpy(&new_mp_entry->mp_params, params,
618158115Sume			sizeof(struct mp_cache_entry_params));
619158115Sume		new_mp_entry->params =
620158115Sume			(struct cache_entry_params *)&new_mp_entry->mp_params;
621158115Sume
622194104Sdes		new_mp_entry->mp_params.cep.entry_name = calloc(1,
623184189Sdelphij			entry_name_size);
624194097Sdes		assert(new_mp_entry->mp_params.cep.entry_name != NULL);
625194097Sdes		strlcpy(new_mp_entry->mp_params.cep.entry_name, params->entry_name,
626158115Sume			entry_name_size);
627194097Sdes		new_mp_entry->name = new_mp_entry->mp_params.cep.entry_name;
628158115Sume
629158115Sume		TAILQ_INIT(&new_mp_entry->ws_head);
630158115Sume		TAILQ_INIT(&new_mp_entry->rs_head);
631158115Sume
632158115Sume		new_mp_entry->get_time_func = the_cache->params.get_time_func;
633158115Sume		the_cache->entries[the_cache->entries_size++] =
634158115Sume			(struct cache_entry_ *)new_mp_entry;
635158115Sume		break;
636158115Sume	}
637158115Sume
638158115Sume
639158115Sume	qsort(the_cache->entries, the_cache->entries_size,
640158115Sume		sizeof(struct cache_entry_ *), entries_qsort_cmp_func);
641158115Sume
642158115Sume	TRACE_OUT(register_cache_entry);
643158115Sume	return (0);
644158115Sume}
645158115Sume
646158115Sumeint
647158115Sumeunregister_cache_entry(struct cache_ *the_cache, const char *entry_name)
648158115Sume{
649158115Sume	struct cache_entry_ **del_ent;
650158115Sume
651158115Sume	TRACE_IN(unregister_cache_entry);
652158115Sume	assert(the_cache != NULL);
653158115Sume
654158115Sume	del_ent = find_cache_entry_p(the_cache, entry_name);
655158115Sume	if (del_ent != NULL) {
656158115Sume		destroy_cache_entry(*del_ent);
657158115Sume		--the_cache->entries_size;
658158115Sume
659158115Sume		memmove(del_ent, del_ent + 1,
660158115Sume			(&(the_cache->entries[--the_cache->entries_size]) -
661158115Sume	    		del_ent) * sizeof(struct cache_entry_ *));
662158115Sume
663158115Sume		TRACE_OUT(unregister_cache_entry);
664158115Sume		return (0);
665158115Sume	} else {
666158115Sume		TRACE_OUT(unregister_cache_entry);
667158115Sume		return (-1);
668158115Sume	}
669158115Sume}
670158115Sume
671158115Sumestruct cache_entry_ *
672158115Sumefind_cache_entry(struct cache_ *the_cache, const char *entry_name)
673158115Sume{
674158115Sume	struct cache_entry_ **result;
675158115Sume
676158115Sume	TRACE_IN(find_cache_entry);
677158115Sume	result = find_cache_entry_p(the_cache, entry_name);
678158115Sume
679158115Sume	if (result == NULL) {
680158115Sume		TRACE_OUT(find_cache_entry);
681158115Sume		return (NULL);
682158115Sume	} else {
683158115Sume		TRACE_OUT(find_cache_entry);
684158115Sume		return (*result);
685158115Sume	}
686158115Sume}
687158115Sume
688158115Sume/*
689158115Sume * Tries to read the element with the specified key from the cache. If the
690158115Sume * value_size is too small, it will be filled with the proper number, and
691158115Sume * the user will need to call cache_read again with the value buffer, that
692158115Sume * is large enough.
693158115Sume * Function returns 0 on success, -1 on error, and -2 if the value_size is too
694158115Sume * small.
695158115Sume */
696158115Sumeint
697158115Sumecache_read(struct cache_entry_ *entry, const char *key, size_t key_size,
698158115Sume	char *value, size_t *value_size)
699158115Sume{
700158115Sume	struct cache_common_entry_	*common_entry;
701158115Sume	struct cache_ht_item_data_	item_data, *find_res;
702158115Sume	struct cache_ht_item_		*item;
703158115Sume	hashtable_index_t	hash;
704158115Sume	struct cache_policy_item_ *connected_item;
705158115Sume
706158115Sume	TRACE_IN(cache_read);
707158115Sume	assert(entry != NULL);
708158115Sume	assert(key != NULL);
709158115Sume	assert(value_size != NULL);
710158115Sume	assert(entry->params->entry_type == CET_COMMON);
711158115Sume
712158115Sume	common_entry = (struct cache_common_entry_ *)entry;
713158115Sume
714158115Sume	memset(&item_data, 0, sizeof(struct cache_ht_item_data_));
715158115Sume	/* can't avoid the cast here */
716158115Sume	item_data.key = (char *)key;
717158115Sume	item_data.key_size = key_size;
718158115Sume
719158115Sume	hash = HASHTABLE_CALCULATE_HASH(cache_ht_, &common_entry->items,
720158115Sume		&item_data);
721158115Sume	assert(hash < HASHTABLE_ENTRIES_COUNT(&common_entry->items));
722158115Sume
723158115Sume	item = HASHTABLE_GET_ENTRY(&(common_entry->items), hash);
724158115Sume	find_res = HASHTABLE_ENTRY_FIND(cache_ht_, item, &item_data);
725158115Sume	if (find_res == NULL) {
726158115Sume		TRACE_OUT(cache_read);
727158115Sume		return (-1);
728158115Sume	}
729238094Sse	/* pretend that entry was not found if confidence is below threshold*/
730238094Sse	if (find_res->confidence <
731238094Sse	    common_entry->common_params.confidence_threshold) {
732238094Sse		TRACE_OUT(cache_read);
733238094Sse		return (-1);
734238094Sse	}
735158115Sume
736158115Sume	if ((common_entry->common_params.max_lifetime.tv_sec != 0) ||
737158115Sume		(common_entry->common_params.max_lifetime.tv_usec != 0)) {
738158115Sume
739158115Sume		if (find_res->fifo_policy_item->last_request_time.tv_sec -
740158115Sume			find_res->fifo_policy_item->creation_time.tv_sec >
741158115Sume			common_entry->common_params.max_lifetime.tv_sec) {
742158115Sume
743158115Sume			free(find_res->key);
744158115Sume			free(find_res->value);
745158115Sume
746158115Sume			connected_item =
747158115Sume			    find_res->fifo_policy_item->connected_item;
748158115Sume			if (connected_item != NULL) {
749158115Sume				common_entry->policies[1]->remove_item_func(
750158115Sume					common_entry->policies[1],
751158115Sume			    		connected_item);
752158115Sume				common_entry->policies[1]->destroy_item_func(
753158115Sume					connected_item);
754158115Sume			}
755158115Sume
756158115Sume			common_entry->policies[0]->remove_item_func(
757158115Sume				common_entry->policies[0],
758158115Sume					find_res->fifo_policy_item);
759158115Sume			common_entry->policies[0]->destroy_item_func(
760158115Sume				find_res->fifo_policy_item);
761158115Sume
762158115Sume			HASHTABLE_ENTRY_REMOVE(cache_ht_, item, find_res);
763158115Sume			--common_entry->items_size;
764158115Sume		}
765158115Sume	}
766158115Sume
767158115Sume	if ((*value_size < find_res->value_size) || (value == NULL)) {
768158115Sume		*value_size = find_res->value_size;
769158115Sume		TRACE_OUT(cache_read);
770158115Sume		return (-2);
771158115Sume	}
772158115Sume
773158115Sume	*value_size = find_res->value_size;
774158115Sume	memcpy(value, find_res->value, find_res->value_size);
775158115Sume
776158115Sume	++find_res->fifo_policy_item->request_count;
777158115Sume	common_entry->get_time_func(
778158115Sume		&find_res->fifo_policy_item->last_request_time);
779158115Sume	common_entry->policies[0]->update_item_func(common_entry->policies[0],
780158115Sume		find_res->fifo_policy_item);
781158115Sume
782158115Sume	if (find_res->fifo_policy_item->connected_item != NULL) {
783158115Sume		connected_item = find_res->fifo_policy_item->connected_item;
784158115Sume		memcpy(&connected_item->last_request_time,
785158115Sume			&find_res->fifo_policy_item->last_request_time,
786158115Sume			sizeof(struct timeval));
787158115Sume		connected_item->request_count =
788158115Sume			find_res->fifo_policy_item->request_count;
789158115Sume
790158115Sume		common_entry->policies[1]->update_item_func(
791158115Sume			common_entry->policies[1], connected_item);
792158115Sume	}
793158115Sume
794158115Sume	TRACE_OUT(cache_read);
795158115Sume	return (0);
796158115Sume}
797158115Sume
798158115Sume/*
799158115Sume * Writes the value with the specified key into the cache entry.
800158115Sume * Functions returns 0 on success, and -1 on error.
801158115Sume */
802158115Sumeint
803158115Sumecache_write(struct cache_entry_ *entry, const char *key, size_t key_size,
804158115Sume    	char const *value, size_t value_size)
805158115Sume{
806158115Sume	struct cache_common_entry_	*common_entry;
807158115Sume	struct cache_ht_item_data_	item_data, *find_res;
808158115Sume	struct cache_ht_item_		*item;
809158115Sume	hashtable_index_t	hash;
810158115Sume
811158115Sume	struct cache_policy_		*policy, *connected_policy;
812158115Sume	struct cache_policy_item_	*policy_item;
813158115Sume	struct cache_policy_item_	*connected_policy_item;
814158115Sume
815158115Sume	TRACE_IN(cache_write);
816158115Sume	assert(entry != NULL);
817158115Sume	assert(key != NULL);
818158115Sume	assert(value != NULL);
819158115Sume	assert(entry->params->entry_type == CET_COMMON);
820158115Sume
821158115Sume	common_entry = (struct cache_common_entry_ *)entry;
822158115Sume
823158115Sume	memset(&item_data, 0, sizeof(struct cache_ht_item_data_));
824158115Sume	/* can't avoid the cast here */
825158115Sume	item_data.key = (char *)key;
826158115Sume	item_data.key_size = key_size;
827158115Sume
828158115Sume	hash = HASHTABLE_CALCULATE_HASH(cache_ht_, &common_entry->items,
829158115Sume		&item_data);
830158115Sume	assert(hash < HASHTABLE_ENTRIES_COUNT(&common_entry->items));
831158115Sume
832158115Sume	item = HASHTABLE_GET_ENTRY(&(common_entry->items), hash);
833158115Sume	find_res = HASHTABLE_ENTRY_FIND(cache_ht_, item, &item_data);
834158115Sume	if (find_res != NULL) {
835238094Sse		if (find_res->confidence < common_entry->common_params.confidence_threshold) {
836238094Sse		  	/* duplicate entry is no error, if confidence is low */
837238094Sse			if ((find_res->value_size == value_size) &&
838238094Sse			    (memcmp(find_res->value, value, value_size) == 0)) {
839238094Sse				/* increase confidence on exact match (key and values) */
840238094Sse				find_res->confidence++;
841238094Sse			} else {
842238094Sse				/* create new entry with low confidence, if value changed */
843238094Sse				free(item_data.value);
844238094Sse				item_data.value = malloc(value_size);
845238094Sse				assert(item_data.value != NULL);
846238094Sse				memcpy(item_data.value, value, value_size);
847238094Sse				item_data.value_size = value_size;
848238094Sse				find_res->confidence = 1;
849238094Sse			}
850238094Sse			TRACE_OUT(cache_write);
851238094Sse			return (0);
852238094Sse		}
853158115Sume		TRACE_OUT(cache_write);
854158115Sume		return (-1);
855158115Sume	}
856158115Sume
857194104Sdes	item_data.key = malloc(key_size);
858158115Sume	memcpy(item_data.key, key, key_size);
859158115Sume
860194104Sdes	item_data.value = malloc(value_size);
861158115Sume	assert(item_data.value != NULL);
862158115Sume
863158115Sume	memcpy(item_data.value, value, value_size);
864158115Sume	item_data.value_size = value_size;
865158115Sume
866238094Sse	item_data.confidence = 1;
867238094Sse
868158115Sume	policy_item = common_entry->policies[0]->create_item_func();
869158115Sume	policy_item->key = item_data.key;
870158115Sume	policy_item->key_size = item_data.key_size;
871158115Sume	common_entry->get_time_func(&policy_item->creation_time);
872158115Sume
873158115Sume	if (common_entry->policies_size > 1) {
874158115Sume		connected_policy_item =
875158115Sume			common_entry->policies[1]->create_item_func();
876158115Sume		memcpy(&connected_policy_item->creation_time,
877158115Sume			&policy_item->creation_time,
878158115Sume			sizeof(struct timeval));
879158115Sume		connected_policy_item->key = policy_item->key;
880158115Sume		connected_policy_item->key_size = policy_item->key_size;
881158115Sume
882158115Sume		connected_policy_item->connected_item = policy_item;
883158115Sume		policy_item->connected_item = connected_policy_item;
884158115Sume	}
885158115Sume
886158115Sume	item_data.fifo_policy_item = policy_item;
887158115Sume
888158115Sume	common_entry->policies[0]->add_item_func(common_entry->policies[0],
889158115Sume		policy_item);
890158115Sume	if (common_entry->policies_size > 1)
891158115Sume		common_entry->policies[1]->add_item_func(
892158115Sume			common_entry->policies[1], connected_policy_item);
893158115Sume
894158115Sume	HASHTABLE_ENTRY_STORE(cache_ht_, item, &item_data);
895158115Sume	++common_entry->items_size;
896158115Sume
897158115Sume	if ((common_entry->common_params.max_elemsize != 0) &&
898158115Sume		(common_entry->items_size >
899158115Sume		common_entry->common_params.max_elemsize)) {
900158115Sume		if (common_entry->policies_size > 1) {
901158115Sume			policy = common_entry->policies[1];
902158115Sume			connected_policy = common_entry->policies[0];
903158115Sume		} else {
904158115Sume			policy = common_entry->policies[0];
905158115Sume			connected_policy = NULL;
906158115Sume		}
907158115Sume
908158115Sume		flush_cache_policy(common_entry, policy, connected_policy,
909158115Sume			cache_elemsize_common_continue_func);
910158115Sume	}
911158115Sume
912158115Sume	TRACE_OUT(cache_write);
913158115Sume	return (0);
914158115Sume}
915158115Sume
916158115Sume/*
917158115Sume * Initializes the write session for the specified multipart entry. This
918158115Sume * session then should be filled with data either committed or abandoned by
919158115Sume * using close_cache_mp_write_session or abandon_cache_mp_write_session
920158115Sume * respectively.
921158115Sume * Returns NULL on errors (when there are too many opened write sessions for
922158115Sume * the entry).
923158115Sume */
924158115Sumestruct cache_mp_write_session_ *
925158115Sumeopen_cache_mp_write_session(struct cache_entry_ *entry)
926158115Sume{
927158115Sume	struct cache_mp_entry_	*mp_entry;
928158115Sume	struct cache_mp_write_session_	*retval;
929158115Sume
930158115Sume	TRACE_IN(open_cache_mp_write_session);
931158115Sume	assert(entry != NULL);
932158115Sume	assert(entry->params->entry_type == CET_MULTIPART);
933158115Sume	mp_entry = (struct cache_mp_entry_ *)entry;
934158115Sume
935158115Sume	if ((mp_entry->mp_params.max_sessions > 0) &&
936158115Sume		(mp_entry->ws_size == mp_entry->mp_params.max_sessions)) {
937158115Sume		TRACE_OUT(open_cache_mp_write_session);
938158115Sume		return (NULL);
939158115Sume	}
940158115Sume
941194104Sdes	retval = calloc(1,
942194104Sdes		sizeof(*retval));
943158115Sume	assert(retval != NULL);
944158115Sume
945158115Sume	TAILQ_INIT(&retval->items);
946158115Sume	retval->parent_entry = mp_entry;
947158115Sume
948158115Sume	TAILQ_INSERT_HEAD(&mp_entry->ws_head, retval, entries);
949158115Sume	++mp_entry->ws_size;
950158115Sume
951158115Sume	TRACE_OUT(open_cache_mp_write_session);
952158115Sume	return (retval);
953158115Sume}
954158115Sume
955158115Sume/*
956158115Sume * Writes data to the specified session. Return 0 on success and -1 on errors
957158115Sume * (when write session size limit is exceeded).
958158115Sume */
959158115Sumeint
960158115Sumecache_mp_write(struct cache_mp_write_session_ *ws, char *data,
961158115Sume	size_t data_size)
962158115Sume{
963158115Sume	struct cache_mp_data_item_	*new_item;
964158115Sume
965158115Sume	TRACE_IN(cache_mp_write);
966158115Sume	assert(ws != NULL);
967158115Sume	assert(ws->parent_entry != NULL);
968158115Sume	assert(ws->parent_entry->params->entry_type == CET_MULTIPART);
969158115Sume
970158115Sume	if ((ws->parent_entry->mp_params.max_elemsize > 0) &&
971158115Sume		(ws->parent_entry->mp_params.max_elemsize == ws->items_size)) {
972158115Sume		TRACE_OUT(cache_mp_write);
973158115Sume		return (-1);
974158115Sume	}
975158115Sume
976194104Sdes	new_item = calloc(1,
977194104Sdes		sizeof(*new_item));
978158115Sume	assert(new_item != NULL);
979158115Sume
980194104Sdes	new_item->value = malloc(data_size);
981158115Sume	assert(new_item->value != NULL);
982158115Sume	memcpy(new_item->value, data, data_size);
983158115Sume	new_item->value_size = data_size;
984158115Sume
985158115Sume	TAILQ_INSERT_TAIL(&ws->items, new_item, entries);
986158115Sume	++ws->items_size;
987158115Sume
988158115Sume	TRACE_OUT(cache_mp_write);
989158115Sume	return (0);
990158115Sume}
991158115Sume
992158115Sume/*
993158115Sume * Abandons the write session and frees all the connected resources.
994158115Sume */
995158115Sumevoid
996158115Sumeabandon_cache_mp_write_session(struct cache_mp_write_session_ *ws)
997158115Sume{
998158115Sume
999158115Sume	TRACE_IN(abandon_cache_mp_write_session);
1000158115Sume	assert(ws != NULL);
1001158115Sume	assert(ws->parent_entry != NULL);
1002158115Sume	assert(ws->parent_entry->params->entry_type == CET_MULTIPART);
1003158115Sume
1004158115Sume	TAILQ_REMOVE(&ws->parent_entry->ws_head, ws, entries);
1005158115Sume	--ws->parent_entry->ws_size;
1006158115Sume
1007158115Sume	destroy_cache_mp_write_session(ws);
1008158115Sume	TRACE_OUT(abandon_cache_mp_write_session);
1009158115Sume}
1010158115Sume
1011158115Sume/*
1012158115Sume * Commits the session to the entry, for which it was created.
1013158115Sume */
1014158115Sumevoid
1015158115Sumeclose_cache_mp_write_session(struct cache_mp_write_session_ *ws)
1016158115Sume{
1017158115Sume
1018158115Sume	TRACE_IN(close_cache_mp_write_session);
1019158115Sume	assert(ws != NULL);
1020158115Sume	assert(ws->parent_entry != NULL);
1021158115Sume	assert(ws->parent_entry->params->entry_type == CET_MULTIPART);
1022158115Sume
1023158115Sume	TAILQ_REMOVE(&ws->parent_entry->ws_head, ws, entries);
1024158115Sume	--ws->parent_entry->ws_size;
1025158115Sume
1026158115Sume	if (ws->parent_entry->completed_write_session == NULL) {
1027158115Sume		/*
1028158115Sume		 * If there is no completed session yet, this will be the one
1029158115Sume		 */
1030158115Sume		ws->parent_entry->get_time_func(
1031158115Sume	    		&ws->parent_entry->creation_time);
1032158115Sume		ws->parent_entry->completed_write_session = ws;
1033158115Sume	} else {
1034158115Sume		/*
1035158115Sume		 * If there is a completed session, then we'll save our session
1036158115Sume		 * as a pending session. If there is already a pending session,
1037158115Sume		 * it would be destroyed.
1038158115Sume		 */
1039158115Sume		if (ws->parent_entry->pending_write_session != NULL)
1040158115Sume			destroy_cache_mp_write_session(
1041158115Sume				ws->parent_entry->pending_write_session);
1042158115Sume
1043158115Sume		ws->parent_entry->pending_write_session = ws;
1044158115Sume	}
1045158115Sume	TRACE_OUT(close_cache_mp_write_session);
1046158115Sume}
1047158115Sume
1048158115Sume/*
1049158115Sume * Opens read session for the specified entry. Returns NULL on errors (when
1050158115Sume * there are no data in the entry, or the data are obsolete).
1051158115Sume */
1052158115Sumestruct cache_mp_read_session_ *
1053158115Sumeopen_cache_mp_read_session(struct cache_entry_ *entry)
1054158115Sume{
1055158115Sume	struct cache_mp_entry_			*mp_entry;
1056158115Sume	struct cache_mp_read_session_	*retval;
1057158115Sume
1058158115Sume	TRACE_IN(open_cache_mp_read_session);
1059158115Sume	assert(entry != NULL);
1060158115Sume	assert(entry->params->entry_type == CET_MULTIPART);
1061158115Sume	mp_entry = (struct cache_mp_entry_ *)entry;
1062158115Sume
1063158115Sume	if (mp_entry->completed_write_session == NULL) {
1064158115Sume		TRACE_OUT(open_cache_mp_read_session);
1065158115Sume		return (NULL);
1066158115Sume	}
1067158115Sume
1068158115Sume	if ((mp_entry->mp_params.max_lifetime.tv_sec != 0)
1069158115Sume		|| (mp_entry->mp_params.max_lifetime.tv_usec != 0)) {
1070158115Sume		if (mp_entry->last_request_time.tv_sec -
1071158115Sume			mp_entry->last_request_time.tv_sec >
1072158115Sume			mp_entry->mp_params.max_lifetime.tv_sec) {
1073158115Sume			flush_cache_entry(entry);
1074158115Sume			TRACE_OUT(open_cache_mp_read_session);
1075158115Sume			return (NULL);
1076158115Sume		}
1077158115Sume	}
1078158115Sume
1079194104Sdes	retval = calloc(1,
1080194104Sdes		sizeof(*retval));
1081158115Sume	assert(retval != NULL);
1082158115Sume
1083158115Sume	retval->parent_entry = mp_entry;
1084158115Sume	retval->current_item = TAILQ_FIRST(
1085158115Sume		&mp_entry->completed_write_session->items);
1086158115Sume
1087158115Sume	TAILQ_INSERT_HEAD(&mp_entry->rs_head, retval, entries);
1088158115Sume	++mp_entry->rs_size;
1089158115Sume
1090158115Sume	mp_entry->get_time_func(&mp_entry->last_request_time);
1091158115Sume	TRACE_OUT(open_cache_mp_read_session);
1092158115Sume	return (retval);
1093158115Sume}
1094158115Sume
1095158115Sume/*
1096158115Sume * Reads the data from the read session - step by step.
1097158115Sume * Returns 0 on success, -1 on error (when there are no more data), and -2 if
1098158115Sume * the data_size is too small.  In the last case, data_size would be filled
1099158115Sume * the proper value.
1100158115Sume */
1101158115Sumeint
1102158115Sumecache_mp_read(struct cache_mp_read_session_ *rs, char *data, size_t *data_size)
1103158115Sume{
1104158115Sume
1105158115Sume	TRACE_IN(cache_mp_read);
1106158115Sume	assert(rs != NULL);
1107158115Sume
1108158115Sume	if (rs->current_item == NULL) {
1109158115Sume		TRACE_OUT(cache_mp_read);
1110158115Sume		return (-1);
1111158115Sume	}
1112158115Sume
1113158115Sume	if (rs->current_item->value_size > *data_size) {
1114158115Sume		*data_size = rs->current_item->value_size;
1115158115Sume		if (data == NULL) {
1116158115Sume			TRACE_OUT(cache_mp_read);
1117158115Sume			return (0);
1118158115Sume		}
1119158115Sume
1120158115Sume		TRACE_OUT(cache_mp_read);
1121158115Sume		return (-2);
1122158115Sume	}
1123158115Sume
1124158115Sume	*data_size = rs->current_item->value_size;
1125158115Sume	memcpy(data, rs->current_item->value, rs->current_item->value_size);
1126158115Sume	rs->current_item = TAILQ_NEXT(rs->current_item, entries);
1127158115Sume
1128158115Sume	TRACE_OUT(cache_mp_read);
1129158115Sume	return (0);
1130158115Sume}
1131158115Sume
1132158115Sume/*
1133158115Sume * Closes the read session. If there are no more read sessions and there is
1134158115Sume * a pending write session, it will be committed and old
1135158115Sume * completed_write_session will be destroyed.
1136158115Sume */
1137158115Sumevoid
1138158115Sumeclose_cache_mp_read_session(struct cache_mp_read_session_ *rs)
1139158115Sume{
1140158115Sume
1141158115Sume	TRACE_IN(close_cache_mp_read_session);
1142158115Sume	assert(rs != NULL);
1143158115Sume	assert(rs->parent_entry != NULL);
1144158115Sume
1145158115Sume	TAILQ_REMOVE(&rs->parent_entry->rs_head, rs, entries);
1146158115Sume	--rs->parent_entry->rs_size;
1147158115Sume
1148158115Sume	if ((rs->parent_entry->rs_size == 0) &&
1149158115Sume		(rs->parent_entry->pending_write_session != NULL)) {
1150158115Sume		destroy_cache_mp_write_session(
1151158115Sume			rs->parent_entry->completed_write_session);
1152158115Sume		rs->parent_entry->completed_write_session =
1153158115Sume			rs->parent_entry->pending_write_session;
1154158115Sume		rs->parent_entry->pending_write_session = NULL;
1155158115Sume	}
1156158115Sume
1157158115Sume	destroy_cache_mp_read_session(rs);
1158158115Sume	TRACE_OUT(close_cache_mp_read_session);
1159158115Sume}
1160158115Sume
1161158115Sumeint
1162158115Sumetransform_cache_entry(struct cache_entry_ *entry,
1163158115Sume	enum cache_transformation_t transformation)
1164158115Sume{
1165158115Sume
1166158115Sume	TRACE_IN(transform_cache_entry);
1167158115Sume	switch (transformation) {
1168158115Sume	case CTT_CLEAR:
1169158115Sume		clear_cache_entry(entry);
1170158115Sume		TRACE_OUT(transform_cache_entry);
1171158115Sume		return (0);
1172158115Sume	case CTT_FLUSH:
1173158115Sume		flush_cache_entry(entry);
1174158115Sume		TRACE_OUT(transform_cache_entry);
1175158115Sume		return (0);
1176158115Sume	default:
1177158115Sume		TRACE_OUT(transform_cache_entry);
1178158115Sume		return (-1);
1179158115Sume	}
1180158115Sume}
1181158115Sume
1182158115Sumeint
1183158115Sumetransform_cache_entry_part(struct cache_entry_ *entry,
1184158115Sume	enum cache_transformation_t transformation, const char *key_part,
1185158115Sume	size_t key_part_size, enum part_position_t part_position)
1186158115Sume{
1187158115Sume	struct cache_common_entry_ *common_entry;
1188158115Sume	struct cache_ht_item_ *ht_item;
1189158115Sume	struct cache_ht_item_data_ *ht_item_data, ht_key;
1190158115Sume
1191158115Sume	struct cache_policy_item_ *item, *connected_item;
1192158115Sume
1193158115Sume	TRACE_IN(transform_cache_entry_part);
1194158115Sume	if (entry->params->entry_type != CET_COMMON) {
1195158115Sume		TRACE_OUT(transform_cache_entry_part);
1196158115Sume		return (-1);
1197158115Sume	}
1198158115Sume
1199158115Sume	if (transformation != CTT_CLEAR) {
1200158115Sume		TRACE_OUT(transform_cache_entry_part);
1201158115Sume		return (-1);
1202158115Sume	}
1203158115Sume
1204158115Sume	memset(&ht_key, 0, sizeof(struct cache_ht_item_data_));
1205158115Sume	ht_key.key = (char *)key_part;	/* can't avoid casting here */
1206158115Sume	ht_key.key_size = key_part_size;
1207158115Sume
1208158115Sume	common_entry = (struct cache_common_entry_ *)entry;
1209158115Sume	HASHTABLE_FOREACH(&(common_entry->items), ht_item) {
1210158115Sume		do {
1211158115Sume			ht_item_data = HASHTABLE_ENTRY_FIND_SPECIAL(cache_ht_,
1212158115Sume				ht_item, &ht_key,
1213158115Sume				ht_items_fixed_size_left_cmp_func);
1214158115Sume
1215158115Sume			if (ht_item_data != NULL) {
1216158115Sume			    item = ht_item_data->fifo_policy_item;
1217158115Sume			    connected_item = item->connected_item;
1218158115Sume
1219158115Sume			    common_entry->policies[0]->remove_item_func(
1220158115Sume				common_entry->policies[0],
1221158115Sume				item);
1222158115Sume
1223158115Sume			    free(ht_item_data->key);
1224158115Sume			    free(ht_item_data->value);
1225158115Sume			    HASHTABLE_ENTRY_REMOVE(cache_ht_, ht_item,
1226158115Sume				ht_item_data);
1227158115Sume			    --common_entry->items_size;
1228158115Sume
1229158115Sume			    common_entry->policies[0]->destroy_item_func(
1230158115Sume				item);
1231158115Sume			    if (common_entry->policies_size == 2) {
1232158115Sume				common_entry->policies[1]->remove_item_func(
1233158115Sume				    common_entry->policies[1],
1234158115Sume				    connected_item);
1235158115Sume				common_entry->policies[1]->destroy_item_func(
1236158115Sume				    connected_item);
1237158115Sume			    }
1238158115Sume			}
1239158115Sume		} while (ht_item_data != NULL);
1240158115Sume	}
1241158115Sume
1242158115Sume	TRACE_OUT(transform_cache_entry_part);
1243158115Sume	return (0);
1244158115Sume}
1245