1/*-
2 * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 */
27
28#include <sys/cdefs.h>
29#include <sys/time.h>
30
31#include <assert.h>
32#include <stdlib.h>
33#include <string.h>
34
35#include "cachelib.h"
36#include "debug.h"
37
38#define INITIAL_ENTRIES_CAPACITY 32
39#define ENTRIES_CAPACITY_STEP 32
40
41#define STRING_SIMPLE_HASH_BODY(in_var, var, a, M)		\
42	for ((var) = 0; *(in_var) != '\0'; ++(in_var))		\
43		(var) = ((a)*(var) + *(in_var)) % (M)
44
45#define STRING_SIMPLE_MP2_HASH_BODY(in_var, var, a, M)		\
46	for ((var) = 0; *(in_var) != 0; ++(in_var))		\
47		(var) = ((a)*(var) + *(in_var)) & (M - 1)
48
49static int cache_elemsize_common_continue_func(struct cache_common_entry_ *,
50	struct cache_policy_item_ *);
51static int cache_lifetime_common_continue_func(struct cache_common_entry_ *,
52	struct cache_policy_item_ *);
53static void clear_cache_entry(struct cache_entry_ *);
54static void destroy_cache_entry(struct cache_entry_ *);
55static void destroy_cache_mp_read_session(struct cache_mp_read_session_ *);
56static void destroy_cache_mp_write_session(struct cache_mp_write_session_ *);
57static int entries_bsearch_cmp_func(const void *, const void *);
58static int entries_qsort_cmp_func(const void *, const void *);
59static struct cache_entry_ ** find_cache_entry_p(struct cache_ *,
60	const char *);
61static void flush_cache_entry(struct cache_entry_ *);
62static void flush_cache_policy(struct cache_common_entry_ *,
63	struct cache_policy_ *, struct cache_policy_ *,
64		int (*)(struct cache_common_entry_ *,
65		struct cache_policy_item_ *));
66static int ht_items_cmp_func(const void *, const void *);
67static int ht_items_fixed_size_left_cmp_func(const void *, const void *);
68static hashtable_index_t ht_item_hash_func(const void *, size_t);
69
70/*
71 * Hashing and comparing routines, that are used with the hash tables
72 */
73static int
74ht_items_cmp_func(const void *p1, const void *p2)
75{
76    	struct cache_ht_item_data_ *hp1, *hp2;
77	size_t min_size;
78	int result;
79
80	hp1 = (struct cache_ht_item_data_ *)p1;
81	hp2 = (struct cache_ht_item_data_ *)p2;
82
83	assert(hp1->key != NULL);
84	assert(hp2->key != NULL);
85
86	if (hp1->key_size != hp2->key_size) {
87		min_size = (hp1->key_size < hp2->key_size) ? hp1->key_size :
88			hp2->key_size;
89		result = memcmp(hp1->key, hp2->key, min_size);
90
91		if (result == 0)
92			return ((hp1->key_size < hp2->key_size) ? -1 : 1);
93		else
94			return (result);
95	} else
96		return (memcmp(hp1->key, hp2->key, hp1->key_size));
97}
98
99static int
100ht_items_fixed_size_left_cmp_func(const void *p1, const void *p2)
101{
102    	struct cache_ht_item_data_ *hp1, *hp2;
103	size_t min_size;
104	int result;
105
106	hp1 = (struct cache_ht_item_data_ *)p1;
107	hp2 = (struct cache_ht_item_data_ *)p2;
108
109	assert(hp1->key != NULL);
110	assert(hp2->key != NULL);
111
112	if (hp1->key_size != hp2->key_size) {
113		min_size = (hp1->key_size < hp2->key_size) ? hp1->key_size :
114			hp2->key_size;
115		result = memcmp(hp1->key, hp2->key, min_size);
116
117		if (result == 0)
118			if (min_size == hp1->key_size)
119			    return (0);
120			else
121			    return ((hp1->key_size < hp2->key_size) ? -1 : 1);
122		else
123			return (result);
124	} else
125		return (memcmp(hp1->key, hp2->key, hp1->key_size));
126}
127
128static hashtable_index_t
129ht_item_hash_func(const void *p, size_t cache_entries_size)
130{
131    	struct cache_ht_item_data_ *hp;
132	size_t i;
133
134	hashtable_index_t retval;
135
136	hp = (struct cache_ht_item_data_ *)p;
137	assert(hp->key != NULL);
138
139	retval = 0;
140	for (i = 0; i < hp->key_size; ++i)
141	    retval = (127 * retval + (unsigned char)hp->key[i]) %
142		cache_entries_size;
143
144	return retval;
145}
146
147HASHTABLE_PROTOTYPE(cache_ht_, cache_ht_item_, struct cache_ht_item_data_);
148HASHTABLE_GENERATE(cache_ht_, cache_ht_item_, struct cache_ht_item_data_, data,
149	ht_item_hash_func, ht_items_cmp_func);
150
151/*
152 * Routines to sort and search the entries by name
153 */
154static int
155entries_bsearch_cmp_func(const void *key, const void *ent)
156{
157
158	assert(key != NULL);
159	assert(ent != NULL);
160
161	return (strcmp((char const *)key,
162		(*(struct cache_entry_ const **)ent)->name));
163}
164
165static int
166entries_qsort_cmp_func(const void *e1, const void *e2)
167{
168
169	assert(e1 != NULL);
170	assert(e2 != NULL);
171
172	return (strcmp((*(struct cache_entry_ const **)e1)->name,
173		(*(struct cache_entry_ const **)e2)->name));
174}
175
176static struct cache_entry_ **
177find_cache_entry_p(struct cache_ *the_cache, const char *entry_name)
178{
179
180	return ((struct cache_entry_ **)(bsearch(entry_name, the_cache->entries,
181		the_cache->entries_size, sizeof(struct cache_entry_ *),
182		entries_bsearch_cmp_func)));
183}
184
185static void
186destroy_cache_mp_write_session(struct cache_mp_write_session_ *ws)
187{
188
189	struct cache_mp_data_item_	*data_item;
190
191	TRACE_IN(destroy_cache_mp_write_session);
192	assert(ws != NULL);
193	while (!TAILQ_EMPTY(&ws->items)) {
194		data_item = TAILQ_FIRST(&ws->items);
195		TAILQ_REMOVE(&ws->items, data_item, entries);
196		free(data_item->value);
197		free(data_item);
198	}
199
200	free(ws);
201	TRACE_OUT(destroy_cache_mp_write_session);
202}
203
204static void
205destroy_cache_mp_read_session(struct cache_mp_read_session_ *rs)
206{
207
208	TRACE_IN(destroy_cache_mp_read_session);
209	assert(rs != NULL);
210	free(rs);
211	TRACE_OUT(destroy_cache_mp_read_session);
212}
213
214static void
215destroy_cache_entry(struct cache_entry_ *entry)
216{
217	struct cache_common_entry_	*common_entry;
218	struct cache_mp_entry_		*mp_entry;
219	struct cache_mp_read_session_	*rs;
220	struct cache_mp_write_session_	*ws;
221	struct cache_ht_item_ *ht_item;
222	struct cache_ht_item_data_ *ht_item_data;
223
224	TRACE_IN(destroy_cache_entry);
225	assert(entry != NULL);
226
227	if (entry->params->entry_type == CET_COMMON) {
228		common_entry = (struct cache_common_entry_ *)entry;
229
230		HASHTABLE_FOREACH(&(common_entry->items), ht_item) {
231			HASHTABLE_ENTRY_FOREACH(ht_item, data, ht_item_data)
232			{
233				free(ht_item_data->key);
234				free(ht_item_data->value);
235			}
236			HASHTABLE_ENTRY_CLEAR(ht_item, data);
237		}
238
239		HASHTABLE_DESTROY(&(common_entry->items), data);
240
241		/* FIFO policy is always first */
242		destroy_cache_fifo_policy(common_entry->policies[0]);
243		switch (common_entry->common_params.policy) {
244		case CPT_LRU:
245			destroy_cache_lru_policy(common_entry->policies[1]);
246			break;
247		case CPT_LFU:
248			destroy_cache_lfu_policy(common_entry->policies[1]);
249			break;
250		default:
251		break;
252		}
253		free(common_entry->policies);
254	} else {
255		mp_entry = (struct cache_mp_entry_ *)entry;
256
257		while (!TAILQ_EMPTY(&mp_entry->ws_head)) {
258			ws = TAILQ_FIRST(&mp_entry->ws_head);
259			TAILQ_REMOVE(&mp_entry->ws_head, ws, entries);
260			destroy_cache_mp_write_session(ws);
261		}
262
263		while (!TAILQ_EMPTY(&mp_entry->rs_head)) {
264			rs = TAILQ_FIRST(&mp_entry->rs_head);
265			TAILQ_REMOVE(&mp_entry->rs_head, rs, entries);
266			destroy_cache_mp_read_session(rs);
267		}
268
269		if (mp_entry->completed_write_session != NULL)
270			destroy_cache_mp_write_session(
271				mp_entry->completed_write_session);
272
273		if (mp_entry->pending_write_session != NULL)
274			destroy_cache_mp_write_session(
275				mp_entry->pending_write_session);
276	}
277
278	free(entry->name);
279	free(entry);
280	TRACE_OUT(destroy_cache_entry);
281}
282
283static void
284clear_cache_entry(struct cache_entry_ *entry)
285{
286	struct cache_mp_entry_		*mp_entry;
287	struct cache_common_entry_	*common_entry;
288	struct cache_ht_item_ *ht_item;
289	struct cache_ht_item_data_ *ht_item_data;
290	struct cache_policy_ *policy;
291	struct cache_policy_item_ *item, *next_item;
292	size_t entry_size;
293	unsigned int i;
294
295	if (entry->params->entry_type == CET_COMMON) {
296		common_entry = (struct cache_common_entry_ *)entry;
297
298		entry_size = 0;
299		HASHTABLE_FOREACH(&(common_entry->items), ht_item) {
300			HASHTABLE_ENTRY_FOREACH(ht_item, data, ht_item_data)
301			{
302				free(ht_item_data->key);
303				free(ht_item_data->value);
304			}
305			entry_size += HASHTABLE_ENTRY_SIZE(ht_item, data);
306			HASHTABLE_ENTRY_CLEAR(ht_item, data);
307		}
308
309		common_entry->items_size -= entry_size;
310		for (i = 0; i < common_entry->policies_size; ++i) {
311			policy = common_entry->policies[i];
312
313			next_item = NULL;
314			item = policy->get_first_item_func(policy);
315			while (item != NULL) {
316				next_item = policy->get_next_item_func(policy,
317			    		item);
318				policy->remove_item_func(policy, item);
319				policy->destroy_item_func(item);
320				item = next_item;
321			}
322		}
323	} else {
324		mp_entry = (struct cache_mp_entry_ *)entry;
325
326		if (mp_entry->rs_size == 0) {
327			if (mp_entry->completed_write_session != NULL) {
328				destroy_cache_mp_write_session(
329					mp_entry->completed_write_session);
330				mp_entry->completed_write_session = NULL;
331			}
332
333			memset(&mp_entry->creation_time, 0,
334				sizeof(struct timeval));
335			memset(&mp_entry->last_request_time, 0,
336				sizeof(struct timeval));
337		}
338	}
339}
340
341/*
342 * When passed to the flush_cache_policy, ensures that all old elements are
343 * deleted.
344 */
345static int
346cache_lifetime_common_continue_func(struct cache_common_entry_ *entry,
347	struct cache_policy_item_ *item)
348{
349
350	return ((item->last_request_time.tv_sec - item->creation_time.tv_sec >
351		entry->common_params.max_lifetime.tv_sec) ? 1: 0);
352}
353
354/*
355 * When passed to the flush_cache_policy, ensures that all elements, that
356 * exceed the size limit, are deleted.
357 */
358static int
359cache_elemsize_common_continue_func(struct cache_common_entry_ *entry,
360	struct cache_policy_item_ *item)
361{
362
363	return ((entry->items_size > entry->common_params.satisf_elemsize) ? 1
364    		: 0);
365}
366
367/*
368 * Removes the elements from the cache entry, while the continue_func returns 1.
369 */
370static void
371flush_cache_policy(struct cache_common_entry_ *entry,
372	struct cache_policy_ *policy,
373	struct cache_policy_ *connected_policy,
374	int (*continue_func)(struct cache_common_entry_ *,
375		struct cache_policy_item_ *))
376{
377	struct cache_policy_item_ *item, *next_item, *connected_item;
378	struct cache_ht_item_ *ht_item;
379	struct cache_ht_item_data_ *ht_item_data, ht_key;
380	hashtable_index_t hash;
381
382	assert(policy != NULL);
383
384	next_item = NULL;
385	item = policy->get_first_item_func(policy);
386	while ((item != NULL) && (continue_func(entry, item) == 1)) {
387		next_item = policy->get_next_item_func(policy, item);
388
389		connected_item = item->connected_item;
390		policy->remove_item_func(policy, item);
391
392		memset(&ht_key, 0, sizeof(struct cache_ht_item_data_));
393		ht_key.key = item->key;
394		ht_key.key_size = item->key_size;
395
396		hash = HASHTABLE_CALCULATE_HASH(cache_ht_, &entry->items,
397			&ht_key);
398		assert(hash < HASHTABLE_ENTRIES_COUNT(&entry->items));
399
400		ht_item = HASHTABLE_GET_ENTRY(&(entry->items), hash);
401		ht_item_data = HASHTABLE_ENTRY_FIND(cache_ht_, ht_item,
402			&ht_key);
403		assert(ht_item_data != NULL);
404		free(ht_item_data->key);
405		free(ht_item_data->value);
406		HASHTABLE_ENTRY_REMOVE(cache_ht_, ht_item, ht_item_data);
407		--entry->items_size;
408
409		policy->destroy_item_func(item);
410
411		if (connected_item != NULL) {
412			connected_policy->remove_item_func(connected_policy,
413				connected_item);
414			connected_policy->destroy_item_func(connected_item);
415		}
416
417		item = next_item;
418	}
419}
420
421static void
422flush_cache_entry(struct cache_entry_ *entry)
423{
424	struct cache_mp_entry_		*mp_entry;
425	struct cache_common_entry_	*common_entry;
426	struct cache_policy_ *policy, *connected_policy;
427
428	connected_policy = NULL;
429	if (entry->params->entry_type == CET_COMMON) {
430		common_entry = (struct cache_common_entry_ *)entry;
431		if ((common_entry->common_params.max_lifetime.tv_sec != 0) ||
432		    (common_entry->common_params.max_lifetime.tv_usec != 0)) {
433
434			policy = common_entry->policies[0];
435			if (common_entry->policies_size > 1)
436				connected_policy = common_entry->policies[1];
437
438			flush_cache_policy(common_entry, policy,
439				connected_policy,
440				cache_lifetime_common_continue_func);
441		}
442
443
444		if ((common_entry->common_params.max_elemsize != 0) &&
445			common_entry->items_size >
446			common_entry->common_params.max_elemsize) {
447
448			if (common_entry->policies_size > 1) {
449				policy = common_entry->policies[1];
450				connected_policy = common_entry->policies[0];
451			} else {
452				policy = common_entry->policies[0];
453				connected_policy = NULL;
454			}
455
456			flush_cache_policy(common_entry, policy,
457				connected_policy,
458				cache_elemsize_common_continue_func);
459		}
460	} else {
461		mp_entry = (struct cache_mp_entry_ *)entry;
462
463		if ((mp_entry->mp_params.max_lifetime.tv_sec != 0)
464			|| (mp_entry->mp_params.max_lifetime.tv_usec != 0)) {
465
466			if (mp_entry->last_request_time.tv_sec -
467				mp_entry->last_request_time.tv_sec >
468				mp_entry->mp_params.max_lifetime.tv_sec)
469				clear_cache_entry(entry);
470		}
471	}
472}
473
474struct cache_ *
475init_cache(struct cache_params const *params)
476{
477	struct cache_ *retval;
478
479	TRACE_IN(init_cache);
480	assert(params != NULL);
481
482	retval = calloc(1, sizeof(*retval));
483	assert(retval != NULL);
484
485	assert(params != NULL);
486	memcpy(&retval->params, params, sizeof(struct cache_params));
487
488	retval->entries = calloc(INITIAL_ENTRIES_CAPACITY,
489		sizeof(*retval->entries));
490	assert(retval->entries != NULL);
491
492	retval->entries_capacity = INITIAL_ENTRIES_CAPACITY;
493	retval->entries_size = 0;
494
495	TRACE_OUT(init_cache);
496	return (retval);
497}
498
499void
500destroy_cache(struct cache_ *the_cache)
501{
502
503	TRACE_IN(destroy_cache);
504	assert(the_cache != NULL);
505
506	if (the_cache->entries != NULL) {
507		size_t i;
508		for (i = 0; i < the_cache->entries_size; ++i)
509			destroy_cache_entry(the_cache->entries[i]);
510
511		free(the_cache->entries);
512	}
513
514	free(the_cache);
515	TRACE_OUT(destroy_cache);
516}
517
518int
519register_cache_entry(struct cache_ *the_cache,
520	struct cache_entry_params const *params)
521{
522	int policies_size;
523	size_t entry_name_size;
524	struct cache_common_entry_	*new_common_entry;
525	struct cache_mp_entry_		*new_mp_entry;
526
527	TRACE_IN(register_cache_entry);
528	assert(the_cache != NULL);
529
530	if (find_cache_entry(the_cache, params->entry_name) != NULL) {
531		TRACE_OUT(register_cache_entry);
532		return (-1);
533	}
534
535	if (the_cache->entries_size == the_cache->entries_capacity) {
536		struct cache_entry_ **new_entries;
537		size_t	new_capacity;
538
539		new_capacity = the_cache->entries_capacity +
540			ENTRIES_CAPACITY_STEP;
541		new_entries = calloc(new_capacity,
542			sizeof(*new_entries));
543		assert(new_entries != NULL);
544
545		memcpy(new_entries, the_cache->entries,
546			sizeof(struct cache_entry_ *)
547			* the_cache->entries_size);
548
549		free(the_cache->entries);
550		the_cache->entries = new_entries;
551	}
552
553	entry_name_size = strlen(params->entry_name) + 1;
554	switch (params->entry_type)
555	{
556	case CET_COMMON:
557		new_common_entry = calloc(1,
558			sizeof(*new_common_entry));
559		assert(new_common_entry != NULL);
560
561		memcpy(&new_common_entry->common_params, params,
562			sizeof(struct common_cache_entry_params));
563		new_common_entry->params =
564		  (struct cache_entry_params *)&new_common_entry->common_params;
565
566		new_common_entry->common_params.cep.entry_name = calloc(1,
567			entry_name_size);
568		assert(new_common_entry->common_params.cep.entry_name != NULL);
569		strlcpy(new_common_entry->common_params.cep.entry_name,
570			params->entry_name, entry_name_size);
571		new_common_entry->name =
572			new_common_entry->common_params.cep.entry_name;
573
574		HASHTABLE_INIT(&(new_common_entry->items),
575			struct cache_ht_item_data_, data,
576			new_common_entry->common_params.cache_entries_size);
577
578		if (new_common_entry->common_params.policy == CPT_FIFO)
579			policies_size = 1;
580		else
581			policies_size = 2;
582
583		new_common_entry->policies = calloc(policies_size,
584			sizeof(*new_common_entry->policies));
585		assert(new_common_entry->policies != NULL);
586
587		new_common_entry->policies_size = policies_size;
588		new_common_entry->policies[0] = init_cache_fifo_policy();
589
590		if (policies_size > 1) {
591			switch (new_common_entry->common_params.policy) {
592			case CPT_LRU:
593				new_common_entry->policies[1] =
594					init_cache_lru_policy();
595			break;
596			case CPT_LFU:
597				new_common_entry->policies[1] =
598					init_cache_lfu_policy();
599			break;
600			default:
601			break;
602			}
603		}
604
605		new_common_entry->get_time_func =
606			the_cache->params.get_time_func;
607		the_cache->entries[the_cache->entries_size++] =
608			(struct cache_entry_ *)new_common_entry;
609		break;
610	case CET_MULTIPART:
611		new_mp_entry = calloc(1,
612			sizeof(*new_mp_entry));
613		assert(new_mp_entry != NULL);
614
615		memcpy(&new_mp_entry->mp_params, params,
616			sizeof(struct mp_cache_entry_params));
617		new_mp_entry->params =
618			(struct cache_entry_params *)&new_mp_entry->mp_params;
619
620		new_mp_entry->mp_params.cep.entry_name = calloc(1,
621			entry_name_size);
622		assert(new_mp_entry->mp_params.cep.entry_name != NULL);
623		strlcpy(new_mp_entry->mp_params.cep.entry_name, params->entry_name,
624			entry_name_size);
625		new_mp_entry->name = new_mp_entry->mp_params.cep.entry_name;
626
627		TAILQ_INIT(&new_mp_entry->ws_head);
628		TAILQ_INIT(&new_mp_entry->rs_head);
629
630		new_mp_entry->get_time_func = the_cache->params.get_time_func;
631		the_cache->entries[the_cache->entries_size++] =
632			(struct cache_entry_ *)new_mp_entry;
633		break;
634	}
635
636
637	qsort(the_cache->entries, the_cache->entries_size,
638		sizeof(struct cache_entry_ *), entries_qsort_cmp_func);
639
640	TRACE_OUT(register_cache_entry);
641	return (0);
642}
643
644int
645unregister_cache_entry(struct cache_ *the_cache, const char *entry_name)
646{
647	struct cache_entry_ **del_ent;
648
649	TRACE_IN(unregister_cache_entry);
650	assert(the_cache != NULL);
651
652	del_ent = find_cache_entry_p(the_cache, entry_name);
653	if (del_ent != NULL) {
654		destroy_cache_entry(*del_ent);
655		--the_cache->entries_size;
656
657		memmove(del_ent, del_ent + 1,
658			(&(the_cache->entries[--the_cache->entries_size]) -
659	    		del_ent) * sizeof(struct cache_entry_ *));
660
661		TRACE_OUT(unregister_cache_entry);
662		return (0);
663	} else {
664		TRACE_OUT(unregister_cache_entry);
665		return (-1);
666	}
667}
668
669struct cache_entry_ *
670find_cache_entry(struct cache_ *the_cache, const char *entry_name)
671{
672	struct cache_entry_ **result;
673
674	TRACE_IN(find_cache_entry);
675	result = find_cache_entry_p(the_cache, entry_name);
676
677	if (result == NULL) {
678		TRACE_OUT(find_cache_entry);
679		return (NULL);
680	} else {
681		TRACE_OUT(find_cache_entry);
682		return (*result);
683	}
684}
685
686/*
687 * Tries to read the element with the specified key from the cache. If the
688 * value_size is too small, it will be filled with the proper number, and
689 * the user will need to call cache_read again with the value buffer, that
690 * is large enough.
691 * Function returns 0 on success, -1 on error, and -2 if the value_size is too
692 * small.
693 */
694int
695cache_read(struct cache_entry_ *entry, const char *key, size_t key_size,
696	char *value, size_t *value_size)
697{
698	struct cache_common_entry_	*common_entry;
699	struct cache_ht_item_data_	item_data, *find_res;
700	struct cache_ht_item_		*item;
701	hashtable_index_t	hash;
702	struct cache_policy_item_ *connected_item;
703
704	TRACE_IN(cache_read);
705	assert(entry != NULL);
706	assert(key != NULL);
707	assert(value_size != NULL);
708	assert(entry->params->entry_type == CET_COMMON);
709
710	common_entry = (struct cache_common_entry_ *)entry;
711
712	memset(&item_data, 0, sizeof(struct cache_ht_item_data_));
713	/* can't avoid the cast here */
714	item_data.key = (char *)key;
715	item_data.key_size = key_size;
716
717	hash = HASHTABLE_CALCULATE_HASH(cache_ht_, &common_entry->items,
718		&item_data);
719	assert(hash < HASHTABLE_ENTRIES_COUNT(&common_entry->items));
720
721	item = HASHTABLE_GET_ENTRY(&(common_entry->items), hash);
722	find_res = HASHTABLE_ENTRY_FIND(cache_ht_, item, &item_data);
723	if (find_res == NULL) {
724		TRACE_OUT(cache_read);
725		return (-1);
726	}
727	/* pretend that entry was not found if confidence is below threshold*/
728	if (find_res->confidence <
729	    common_entry->common_params.confidence_threshold) {
730		TRACE_OUT(cache_read);
731		return (-1);
732	}
733
734	if ((common_entry->common_params.max_lifetime.tv_sec != 0) ||
735		(common_entry->common_params.max_lifetime.tv_usec != 0)) {
736
737		if (find_res->fifo_policy_item->last_request_time.tv_sec -
738			find_res->fifo_policy_item->creation_time.tv_sec >
739			common_entry->common_params.max_lifetime.tv_sec) {
740
741			free(find_res->key);
742			free(find_res->value);
743
744			connected_item =
745			    find_res->fifo_policy_item->connected_item;
746			if (connected_item != NULL) {
747				common_entry->policies[1]->remove_item_func(
748					common_entry->policies[1],
749			    		connected_item);
750				common_entry->policies[1]->destroy_item_func(
751					connected_item);
752			}
753
754			common_entry->policies[0]->remove_item_func(
755				common_entry->policies[0],
756					find_res->fifo_policy_item);
757			common_entry->policies[0]->destroy_item_func(
758				find_res->fifo_policy_item);
759
760			HASHTABLE_ENTRY_REMOVE(cache_ht_, item, find_res);
761			--common_entry->items_size;
762		}
763	}
764
765	if ((*value_size < find_res->value_size) || (value == NULL)) {
766		*value_size = find_res->value_size;
767		TRACE_OUT(cache_read);
768		return (-2);
769	}
770
771	*value_size = find_res->value_size;
772	memcpy(value, find_res->value, find_res->value_size);
773
774	++find_res->fifo_policy_item->request_count;
775	common_entry->get_time_func(
776		&find_res->fifo_policy_item->last_request_time);
777	common_entry->policies[0]->update_item_func(common_entry->policies[0],
778		find_res->fifo_policy_item);
779
780	if (find_res->fifo_policy_item->connected_item != NULL) {
781		connected_item = find_res->fifo_policy_item->connected_item;
782		memcpy(&connected_item->last_request_time,
783			&find_res->fifo_policy_item->last_request_time,
784			sizeof(struct timeval));
785		connected_item->request_count =
786			find_res->fifo_policy_item->request_count;
787
788		common_entry->policies[1]->update_item_func(
789			common_entry->policies[1], connected_item);
790	}
791
792	TRACE_OUT(cache_read);
793	return (0);
794}
795
796/*
797 * Writes the value with the specified key into the cache entry.
798 * Functions returns 0 on success, and -1 on error.
799 */
800int
801cache_write(struct cache_entry_ *entry, const char *key, size_t key_size,
802    	char const *value, size_t value_size)
803{
804	struct cache_common_entry_	*common_entry;
805	struct cache_ht_item_data_	item_data, *find_res;
806	struct cache_ht_item_		*item;
807	hashtable_index_t	hash;
808
809	struct cache_policy_		*policy, *connected_policy;
810	struct cache_policy_item_	*policy_item;
811	struct cache_policy_item_	*connected_policy_item;
812
813	TRACE_IN(cache_write);
814	assert(entry != NULL);
815	assert(key != NULL);
816	assert(value != NULL);
817	assert(entry->params->entry_type == CET_COMMON);
818
819	common_entry = (struct cache_common_entry_ *)entry;
820
821	memset(&item_data, 0, sizeof(struct cache_ht_item_data_));
822	/* can't avoid the cast here */
823	item_data.key = (char *)key;
824	item_data.key_size = key_size;
825
826	hash = HASHTABLE_CALCULATE_HASH(cache_ht_, &common_entry->items,
827		&item_data);
828	assert(hash < HASHTABLE_ENTRIES_COUNT(&common_entry->items));
829
830	item = HASHTABLE_GET_ENTRY(&(common_entry->items), hash);
831	find_res = HASHTABLE_ENTRY_FIND(cache_ht_, item, &item_data);
832	if (find_res != NULL) {
833		if (find_res->confidence < common_entry->common_params.confidence_threshold) {
834		  	/* duplicate entry is no error, if confidence is low */
835			if ((find_res->value_size == value_size) &&
836			    (memcmp(find_res->value, value, value_size) == 0)) {
837				/* increase confidence on exact match (key and values) */
838				find_res->confidence++;
839			} else {
840				/* create new entry with low confidence, if value changed */
841				free(item_data.value);
842				item_data.value = malloc(value_size);
843				assert(item_data.value != NULL);
844				memcpy(item_data.value, value, value_size);
845				item_data.value_size = value_size;
846				find_res->confidence = 1;
847			}
848			TRACE_OUT(cache_write);
849			return (0);
850		}
851		TRACE_OUT(cache_write);
852		return (-1);
853	}
854
855	item_data.key = malloc(key_size);
856	memcpy(item_data.key, key, key_size);
857
858	item_data.value = malloc(value_size);
859	assert(item_data.value != NULL);
860
861	memcpy(item_data.value, value, value_size);
862	item_data.value_size = value_size;
863
864	item_data.confidence = 1;
865
866	policy_item = common_entry->policies[0]->create_item_func();
867	policy_item->key = item_data.key;
868	policy_item->key_size = item_data.key_size;
869	common_entry->get_time_func(&policy_item->creation_time);
870
871	if (common_entry->policies_size > 1) {
872		connected_policy_item =
873			common_entry->policies[1]->create_item_func();
874		memcpy(&connected_policy_item->creation_time,
875			&policy_item->creation_time,
876			sizeof(struct timeval));
877		connected_policy_item->key = policy_item->key;
878		connected_policy_item->key_size = policy_item->key_size;
879
880		connected_policy_item->connected_item = policy_item;
881		policy_item->connected_item = connected_policy_item;
882	}
883
884	item_data.fifo_policy_item = policy_item;
885
886	common_entry->policies[0]->add_item_func(common_entry->policies[0],
887		policy_item);
888	if (common_entry->policies_size > 1)
889		common_entry->policies[1]->add_item_func(
890			common_entry->policies[1], connected_policy_item);
891
892	HASHTABLE_ENTRY_STORE(cache_ht_, item, &item_data);
893	++common_entry->items_size;
894
895	if ((common_entry->common_params.max_elemsize != 0) &&
896		(common_entry->items_size >
897		common_entry->common_params.max_elemsize)) {
898		if (common_entry->policies_size > 1) {
899			policy = common_entry->policies[1];
900			connected_policy = common_entry->policies[0];
901		} else {
902			policy = common_entry->policies[0];
903			connected_policy = NULL;
904		}
905
906		flush_cache_policy(common_entry, policy, connected_policy,
907			cache_elemsize_common_continue_func);
908	}
909
910	TRACE_OUT(cache_write);
911	return (0);
912}
913
914/*
915 * Initializes the write session for the specified multipart entry. This
916 * session then should be filled with data either committed or abandoned by
917 * using close_cache_mp_write_session or abandon_cache_mp_write_session
918 * respectively.
919 * Returns NULL on errors (when there are too many opened write sessions for
920 * the entry).
921 */
922struct cache_mp_write_session_ *
923open_cache_mp_write_session(struct cache_entry_ *entry)
924{
925	struct cache_mp_entry_	*mp_entry;
926	struct cache_mp_write_session_	*retval;
927
928	TRACE_IN(open_cache_mp_write_session);
929	assert(entry != NULL);
930	assert(entry->params->entry_type == CET_MULTIPART);
931	mp_entry = (struct cache_mp_entry_ *)entry;
932
933	if ((mp_entry->mp_params.max_sessions > 0) &&
934		(mp_entry->ws_size == mp_entry->mp_params.max_sessions)) {
935		TRACE_OUT(open_cache_mp_write_session);
936		return (NULL);
937	}
938
939	retval = calloc(1,
940		sizeof(*retval));
941	assert(retval != NULL);
942
943	TAILQ_INIT(&retval->items);
944	retval->parent_entry = mp_entry;
945
946	TAILQ_INSERT_HEAD(&mp_entry->ws_head, retval, entries);
947	++mp_entry->ws_size;
948
949	TRACE_OUT(open_cache_mp_write_session);
950	return (retval);
951}
952
953/*
954 * Writes data to the specified session. Return 0 on success and -1 on errors
955 * (when write session size limit is exceeded).
956 */
957int
958cache_mp_write(struct cache_mp_write_session_ *ws, char *data,
959	size_t data_size)
960{
961	struct cache_mp_data_item_	*new_item;
962
963	TRACE_IN(cache_mp_write);
964	assert(ws != NULL);
965	assert(ws->parent_entry != NULL);
966	assert(ws->parent_entry->params->entry_type == CET_MULTIPART);
967
968	if ((ws->parent_entry->mp_params.max_elemsize > 0) &&
969		(ws->parent_entry->mp_params.max_elemsize == ws->items_size)) {
970		TRACE_OUT(cache_mp_write);
971		return (-1);
972	}
973
974	new_item = calloc(1,
975		sizeof(*new_item));
976	assert(new_item != NULL);
977
978	new_item->value = malloc(data_size);
979	assert(new_item->value != NULL);
980	memcpy(new_item->value, data, data_size);
981	new_item->value_size = data_size;
982
983	TAILQ_INSERT_TAIL(&ws->items, new_item, entries);
984	++ws->items_size;
985
986	TRACE_OUT(cache_mp_write);
987	return (0);
988}
989
990/*
991 * Abandons the write session and frees all the connected resources.
992 */
993void
994abandon_cache_mp_write_session(struct cache_mp_write_session_ *ws)
995{
996
997	TRACE_IN(abandon_cache_mp_write_session);
998	assert(ws != NULL);
999	assert(ws->parent_entry != NULL);
1000	assert(ws->parent_entry->params->entry_type == CET_MULTIPART);
1001
1002	TAILQ_REMOVE(&ws->parent_entry->ws_head, ws, entries);
1003	--ws->parent_entry->ws_size;
1004
1005	destroy_cache_mp_write_session(ws);
1006	TRACE_OUT(abandon_cache_mp_write_session);
1007}
1008
1009/*
1010 * Commits the session to the entry, for which it was created.
1011 */
1012void
1013close_cache_mp_write_session(struct cache_mp_write_session_ *ws)
1014{
1015
1016	TRACE_IN(close_cache_mp_write_session);
1017	assert(ws != NULL);
1018	assert(ws->parent_entry != NULL);
1019	assert(ws->parent_entry->params->entry_type == CET_MULTIPART);
1020
1021	TAILQ_REMOVE(&ws->parent_entry->ws_head, ws, entries);
1022	--ws->parent_entry->ws_size;
1023
1024	if (ws->parent_entry->completed_write_session == NULL) {
1025		/*
1026		 * If there is no completed session yet, this will be the one
1027		 */
1028		ws->parent_entry->get_time_func(
1029	    		&ws->parent_entry->creation_time);
1030		ws->parent_entry->completed_write_session = ws;
1031	} else {
1032		/*
1033		 * If there is a completed session, then we'll save our session
1034		 * as a pending session. If there is already a pending session,
1035		 * it would be destroyed.
1036		 */
1037		if (ws->parent_entry->pending_write_session != NULL)
1038			destroy_cache_mp_write_session(
1039				ws->parent_entry->pending_write_session);
1040
1041		ws->parent_entry->pending_write_session = ws;
1042	}
1043	TRACE_OUT(close_cache_mp_write_session);
1044}
1045
1046/*
1047 * Opens read session for the specified entry. Returns NULL on errors (when
1048 * there are no data in the entry, or the data are obsolete).
1049 */
1050struct cache_mp_read_session_ *
1051open_cache_mp_read_session(struct cache_entry_ *entry)
1052{
1053	struct cache_mp_entry_			*mp_entry;
1054	struct cache_mp_read_session_	*retval;
1055
1056	TRACE_IN(open_cache_mp_read_session);
1057	assert(entry != NULL);
1058	assert(entry->params->entry_type == CET_MULTIPART);
1059	mp_entry = (struct cache_mp_entry_ *)entry;
1060
1061	if (mp_entry->completed_write_session == NULL) {
1062		TRACE_OUT(open_cache_mp_read_session);
1063		return (NULL);
1064	}
1065
1066	if ((mp_entry->mp_params.max_lifetime.tv_sec != 0)
1067		|| (mp_entry->mp_params.max_lifetime.tv_usec != 0)) {
1068		if (mp_entry->last_request_time.tv_sec -
1069			mp_entry->last_request_time.tv_sec >
1070			mp_entry->mp_params.max_lifetime.tv_sec) {
1071			flush_cache_entry(entry);
1072			TRACE_OUT(open_cache_mp_read_session);
1073			return (NULL);
1074		}
1075	}
1076
1077	retval = calloc(1,
1078		sizeof(*retval));
1079	assert(retval != NULL);
1080
1081	retval->parent_entry = mp_entry;
1082	retval->current_item = TAILQ_FIRST(
1083		&mp_entry->completed_write_session->items);
1084
1085	TAILQ_INSERT_HEAD(&mp_entry->rs_head, retval, entries);
1086	++mp_entry->rs_size;
1087
1088	mp_entry->get_time_func(&mp_entry->last_request_time);
1089	TRACE_OUT(open_cache_mp_read_session);
1090	return (retval);
1091}
1092
1093/*
1094 * Reads the data from the read session - step by step.
1095 * Returns 0 on success, -1 on error (when there are no more data), and -2 if
1096 * the data_size is too small.  In the last case, data_size would be filled
1097 * the proper value.
1098 */
1099int
1100cache_mp_read(struct cache_mp_read_session_ *rs, char *data, size_t *data_size)
1101{
1102
1103	TRACE_IN(cache_mp_read);
1104	assert(rs != NULL);
1105
1106	if (rs->current_item == NULL) {
1107		TRACE_OUT(cache_mp_read);
1108		return (-1);
1109	}
1110
1111	if (rs->current_item->value_size > *data_size) {
1112		*data_size = rs->current_item->value_size;
1113		if (data == NULL) {
1114			TRACE_OUT(cache_mp_read);
1115			return (0);
1116		}
1117
1118		TRACE_OUT(cache_mp_read);
1119		return (-2);
1120	}
1121
1122	*data_size = rs->current_item->value_size;
1123	memcpy(data, rs->current_item->value, rs->current_item->value_size);
1124	rs->current_item = TAILQ_NEXT(rs->current_item, entries);
1125
1126	TRACE_OUT(cache_mp_read);
1127	return (0);
1128}
1129
1130/*
1131 * Closes the read session. If there are no more read sessions and there is
1132 * a pending write session, it will be committed and old
1133 * completed_write_session will be destroyed.
1134 */
1135void
1136close_cache_mp_read_session(struct cache_mp_read_session_ *rs)
1137{
1138
1139	TRACE_IN(close_cache_mp_read_session);
1140	assert(rs != NULL);
1141	assert(rs->parent_entry != NULL);
1142
1143	TAILQ_REMOVE(&rs->parent_entry->rs_head, rs, entries);
1144	--rs->parent_entry->rs_size;
1145
1146	if ((rs->parent_entry->rs_size == 0) &&
1147		(rs->parent_entry->pending_write_session != NULL)) {
1148		destroy_cache_mp_write_session(
1149			rs->parent_entry->completed_write_session);
1150		rs->parent_entry->completed_write_session =
1151			rs->parent_entry->pending_write_session;
1152		rs->parent_entry->pending_write_session = NULL;
1153	}
1154
1155	destroy_cache_mp_read_session(rs);
1156	TRACE_OUT(close_cache_mp_read_session);
1157}
1158
1159int
1160transform_cache_entry(struct cache_entry_ *entry,
1161	enum cache_transformation_t transformation)
1162{
1163
1164	TRACE_IN(transform_cache_entry);
1165	switch (transformation) {
1166	case CTT_CLEAR:
1167		clear_cache_entry(entry);
1168		TRACE_OUT(transform_cache_entry);
1169		return (0);
1170	case CTT_FLUSH:
1171		flush_cache_entry(entry);
1172		TRACE_OUT(transform_cache_entry);
1173		return (0);
1174	default:
1175		TRACE_OUT(transform_cache_entry);
1176		return (-1);
1177	}
1178}
1179
1180int
1181transform_cache_entry_part(struct cache_entry_ *entry,
1182	enum cache_transformation_t transformation, const char *key_part,
1183	size_t key_part_size, enum part_position_t part_position)
1184{
1185	struct cache_common_entry_ *common_entry;
1186	struct cache_ht_item_ *ht_item;
1187	struct cache_ht_item_data_ *ht_item_data, ht_key;
1188
1189	struct cache_policy_item_ *item, *connected_item;
1190
1191	TRACE_IN(transform_cache_entry_part);
1192	if (entry->params->entry_type != CET_COMMON) {
1193		TRACE_OUT(transform_cache_entry_part);
1194		return (-1);
1195	}
1196
1197	if (transformation != CTT_CLEAR) {
1198		TRACE_OUT(transform_cache_entry_part);
1199		return (-1);
1200	}
1201
1202	memset(&ht_key, 0, sizeof(struct cache_ht_item_data_));
1203	ht_key.key = (char *)key_part;	/* can't avoid casting here */
1204	ht_key.key_size = key_part_size;
1205
1206	common_entry = (struct cache_common_entry_ *)entry;
1207	HASHTABLE_FOREACH(&(common_entry->items), ht_item) {
1208		do {
1209			ht_item_data = HASHTABLE_ENTRY_FIND_SPECIAL(cache_ht_,
1210				ht_item, &ht_key,
1211				ht_items_fixed_size_left_cmp_func);
1212
1213			if (ht_item_data != NULL) {
1214			    item = ht_item_data->fifo_policy_item;
1215			    connected_item = item->connected_item;
1216
1217			    common_entry->policies[0]->remove_item_func(
1218				common_entry->policies[0],
1219				item);
1220
1221			    free(ht_item_data->key);
1222			    free(ht_item_data->value);
1223			    HASHTABLE_ENTRY_REMOVE(cache_ht_, ht_item,
1224				ht_item_data);
1225			    --common_entry->items_size;
1226
1227			    common_entry->policies[0]->destroy_item_func(
1228				item);
1229			    if (common_entry->policies_size == 2) {
1230				common_entry->policies[1]->remove_item_func(
1231				    common_entry->policies[1],
1232				    connected_item);
1233				common_entry->policies[1]->destroy_item_func(
1234				    connected_item);
1235			    }
1236			}
1237		} while (ht_item_data != NULL);
1238	}
1239
1240	TRACE_OUT(transform_cache_entry_part);
1241	return (0);
1242}
1243