nlookup.9 - document nlookup_init_root
[dragonfly.git] / usr.sbin / nscd / cachelib.c
blob4abd1f87ddc9a58c367e61b1072a55e9cd32900b
1 /*-
2 * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
3 * All rights reserved.
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.
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.
26 * $FreeBSD: src/usr.sbin/nscd/cachelib.c,v 1.4 2008/10/23 00:31:15 delphij Exp $
29 #include <sys/time.h>
30 #include <assert.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include "cachelib.h"
34 #include "debug.h"
36 #define INITIAL_ENTRIES_CAPACITY 32
37 #define ENTRIES_CAPACITY_STEP 32
39 #define STRING_SIMPLE_HASH_BODY(in_var, var, a, M) \
40 for ((var) = 0; *(in_var) != '\0'; ++(in_var)) \
41 (var) = ((a)*(var) + *(in_var)) % (M)
43 #define STRING_SIMPLE_MP2_HASH_BODY(in_var, var, a, M) \
44 for ((var) = 0; *(in_var) != 0; ++(in_var)) \
45 (var) = ((a)*(var) + *(in_var)) & (M - 1)
47 static int cache_elemsize_common_continue_func(struct cache_common_entry_ *,
48 struct cache_policy_item_ *);
49 static int cache_lifetime_common_continue_func(struct cache_common_entry_ *,
50 struct cache_policy_item_ *);
51 static void clear_cache_entry(struct cache_entry_ *);
52 static void destroy_cache_entry(struct cache_entry_ *);
53 static void destroy_cache_mp_read_session(struct cache_mp_read_session_ *);
54 static void destroy_cache_mp_write_session(struct cache_mp_write_session_ *);
55 static int entries_bsearch_cmp_func(const void *, const void *);
56 static int entries_qsort_cmp_func(const void *, const void *);
57 static struct cache_entry_ ** find_cache_entry_p(struct cache_ *,
58 const char *);
59 static void flush_cache_entry(struct cache_entry_ *);
60 static void flush_cache_policy(struct cache_common_entry_ *,
61 struct cache_policy_ *, struct cache_policy_ *,
62 int (*)(struct cache_common_entry_ *,
63 struct cache_policy_item_ *));
64 static int ht_items_cmp_func(const void *, const void *);
65 static int ht_items_fixed_size_left_cmp_func(const void *, const void *);
66 static hashtable_index_t ht_item_hash_func(const void *, size_t);
69 * Hashing and comparing routines, that are used with the hash tables
71 static int
72 ht_items_cmp_func(const void *p1, const void *p2)
74 struct cache_ht_item_data_ *hp1, *hp2;
75 size_t min_size;
76 int result;
78 hp1 = (struct cache_ht_item_data_ *)p1;
79 hp2 = (struct cache_ht_item_data_ *)p2;
81 assert(hp1->key != NULL);
82 assert(hp2->key != NULL);
84 if (hp1->key_size != hp2->key_size) {
85 min_size = (hp1->key_size < hp2->key_size) ? hp1->key_size :
86 hp2->key_size;
87 result = memcmp(hp1->key, hp2->key, min_size);
89 if (result == 0)
90 return ((hp1->key_size < hp2->key_size) ? -1 : 1);
91 else
92 return (result);
93 } else
94 return (memcmp(hp1->key, hp2->key, hp1->key_size));
97 static int
98 ht_items_fixed_size_left_cmp_func(const void *p1, const void *p2)
100 struct cache_ht_item_data_ *hp1, *hp2;
101 size_t min_size;
102 int result;
104 hp1 = (struct cache_ht_item_data_ *)p1;
105 hp2 = (struct cache_ht_item_data_ *)p2;
107 assert(hp1->key != NULL);
108 assert(hp2->key != NULL);
110 if (hp1->key_size != hp2->key_size) {
111 min_size = (hp1->key_size < hp2->key_size) ? hp1->key_size :
112 hp2->key_size;
113 result = memcmp(hp1->key, hp2->key, min_size);
115 if (result == 0)
116 if (min_size == hp1->key_size)
117 return (0);
118 else
119 return ((hp1->key_size < hp2->key_size) ? -1 : 1);
120 else
121 return (result);
122 } else
123 return (memcmp(hp1->key, hp2->key, hp1->key_size));
126 static hashtable_index_t
127 ht_item_hash_func(const void *p, size_t cache_entries_size)
129 struct cache_ht_item_data_ *hp;
130 size_t i;
132 hashtable_index_t retval;
134 hp = (struct cache_ht_item_data_ *)p;
135 assert(hp->key != NULL);
137 retval = 0;
138 for (i = 0; i < hp->key_size; ++i)
139 retval = (127 * retval + (unsigned char)hp->key[i]) %
140 cache_entries_size;
142 return retval;
145 HASHTABLE_GENERATE(cache_ht_, cache_ht_item_, struct cache_ht_item_data_, data,
146 ht_item_hash_func, ht_items_cmp_func);
149 * Routines to sort and search the entries by name
151 static int
152 entries_bsearch_cmp_func(const void *key, const void *ent)
155 assert(key != NULL);
156 assert(ent != NULL);
158 return (strcmp((char const *)key,
159 (*(struct cache_entry_ const **)ent)->name));
162 static int
163 entries_qsort_cmp_func(const void *e1, const void *e2)
166 assert(e1 != NULL);
167 assert(e2 != NULL);
169 return (strcmp((*(struct cache_entry_ const **)e1)->name,
170 (*(struct cache_entry_ const **)e2)->name));
173 static struct cache_entry_ **
174 find_cache_entry_p(struct cache_ *the_cache, const char *entry_name)
177 return ((struct cache_entry_ **)(bsearch(entry_name, the_cache->entries,
178 the_cache->entries_size, sizeof(struct cache_entry_ *),
179 entries_bsearch_cmp_func)));
182 static void
183 destroy_cache_mp_write_session(struct cache_mp_write_session_ *ws)
186 struct cache_mp_data_item_ *data_item;
188 TRACE_IN(destroy_cache_mp_write_session);
189 assert(ws != NULL);
190 while (!TAILQ_EMPTY(&ws->items)) {
191 data_item = TAILQ_FIRST(&ws->items);
192 TAILQ_REMOVE(&ws->items, data_item, entries);
193 free(data_item->value);
194 free(data_item);
197 free(ws);
198 TRACE_OUT(destroy_cache_mp_write_session);
201 static void
202 destroy_cache_mp_read_session(struct cache_mp_read_session_ *rs)
205 TRACE_IN(destroy_cache_mp_read_session);
206 assert(rs != NULL);
207 free(rs);
208 TRACE_OUT(destroy_cache_mp_read_session);
211 static void
212 destroy_cache_entry(struct cache_entry_ *entry)
214 struct cache_common_entry_ *common_entry;
215 struct cache_mp_entry_ *mp_entry;
216 struct cache_mp_read_session_ *rs;
217 struct cache_mp_write_session_ *ws;
218 struct cache_ht_item_ *ht_item;
219 struct cache_ht_item_data_ *ht_item_data;
221 TRACE_IN(destroy_cache_entry);
222 assert(entry != NULL);
224 if (entry->params->entry_type == CET_COMMON) {
225 common_entry = (struct cache_common_entry_ *)entry;
227 HASHTABLE_FOREACH(&(common_entry->items), ht_item) {
228 HASHTABLE_ENTRY_FOREACH(ht_item, data, ht_item_data)
230 free(ht_item_data->key);
231 free(ht_item_data->value);
233 HASHTABLE_ENTRY_CLEAR(ht_item, data);
236 HASHTABLE_DESTROY(&(common_entry->items), data);
238 /* FIFO policy is always first */
239 destroy_cache_fifo_policy(common_entry->policies[0]);
240 switch (common_entry->common_params.policy) {
241 case CPT_LRU:
242 destroy_cache_lru_policy(common_entry->policies[1]);
243 break;
244 case CPT_LFU:
245 destroy_cache_lfu_policy(common_entry->policies[1]);
246 break;
247 default:
248 break;
250 free(common_entry->policies);
251 } else {
252 mp_entry = (struct cache_mp_entry_ *)entry;
254 while (!TAILQ_EMPTY(&mp_entry->ws_head)) {
255 ws = TAILQ_FIRST(&mp_entry->ws_head);
256 TAILQ_REMOVE(&mp_entry->ws_head, ws, entries);
257 destroy_cache_mp_write_session(ws);
260 while (!TAILQ_EMPTY(&mp_entry->rs_head)) {
261 rs = TAILQ_FIRST(&mp_entry->rs_head);
262 TAILQ_REMOVE(&mp_entry->rs_head, rs, entries);
263 destroy_cache_mp_read_session(rs);
266 if (mp_entry->completed_write_session != NULL)
267 destroy_cache_mp_write_session(
268 mp_entry->completed_write_session);
270 if (mp_entry->pending_write_session != NULL)
271 destroy_cache_mp_write_session(
272 mp_entry->pending_write_session);
275 free(entry->name);
276 free(entry);
277 TRACE_OUT(destroy_cache_entry);
280 static void
281 clear_cache_entry(struct cache_entry_ *entry)
283 struct cache_mp_entry_ *mp_entry;
284 struct cache_common_entry_ *common_entry;
285 struct cache_ht_item_ *ht_item;
286 struct cache_ht_item_data_ *ht_item_data;
287 struct cache_policy_ *policy;
288 struct cache_policy_item_ *item, *next_item;
289 size_t entry_size;
290 int i;
292 if (entry->params->entry_type == CET_COMMON) {
293 common_entry = (struct cache_common_entry_ *)entry;
295 entry_size = 0;
296 HASHTABLE_FOREACH(&(common_entry->items), ht_item) {
297 HASHTABLE_ENTRY_FOREACH(ht_item, data, ht_item_data)
299 free(ht_item_data->key);
300 free(ht_item_data->value);
302 entry_size += HASHTABLE_ENTRY_SIZE(ht_item, data);
303 HASHTABLE_ENTRY_CLEAR(ht_item, data);
306 common_entry->items_size -= entry_size;
307 for (i = 0; i < common_entry->policies_size; ++i) {
308 policy = common_entry->policies[i];
310 next_item = NULL;
311 item = policy->get_first_item_func(policy);
312 while (item != NULL) {
313 next_item = policy->get_next_item_func(policy,
314 item);
315 policy->remove_item_func(policy, item);
316 policy->destroy_item_func(item);
317 item = next_item;
320 } else {
321 mp_entry = (struct cache_mp_entry_ *)entry;
323 if (mp_entry->rs_size == 0) {
324 if (mp_entry->completed_write_session != NULL) {
325 destroy_cache_mp_write_session(
326 mp_entry->completed_write_session);
327 mp_entry->completed_write_session = NULL;
330 memset(&mp_entry->creation_time, 0,
331 sizeof(struct timeval));
332 memset(&mp_entry->last_request_time, 0,
333 sizeof(struct timeval));
339 * When passed to the flush_cache_policy, ensures that all old elements are
340 * deleted.
342 static int
343 cache_lifetime_common_continue_func(struct cache_common_entry_ *entry,
344 struct cache_policy_item_ *item)
347 return ((item->last_request_time.tv_sec - item->creation_time.tv_sec >
348 entry->common_params.max_lifetime.tv_sec) ? 1: 0);
352 * When passed to the flush_cache_policy, ensures that all elements, that
353 * exceed the size limit, are deleted.
355 static int
356 cache_elemsize_common_continue_func(struct cache_common_entry_ *entry,
357 struct cache_policy_item_ *item)
360 return ((entry->items_size > entry->common_params.satisf_elemsize) ? 1
361 : 0);
365 * Removes the elements from the cache entry, while the continue_func returns 1.
367 static void
368 flush_cache_policy(struct cache_common_entry_ *entry,
369 struct cache_policy_ *policy,
370 struct cache_policy_ *connected_policy,
371 int (*continue_func)(struct cache_common_entry_ *,
372 struct cache_policy_item_ *))
374 struct cache_policy_item_ *item, *next_item, *connected_item;
375 struct cache_ht_item_ *ht_item;
376 struct cache_ht_item_data_ *ht_item_data, ht_key;
377 hashtable_index_t hash;
379 assert(policy != NULL);
381 next_item = NULL;
382 item = policy->get_first_item_func(policy);
383 while ((item != NULL) && (continue_func(entry, item) == 1)) {
384 next_item = policy->get_next_item_func(policy, item);
386 connected_item = item->connected_item;
387 policy->remove_item_func(policy, item);
389 memset(&ht_key, 0, sizeof(struct cache_ht_item_data_));
390 ht_key.key = item->key;
391 ht_key.key_size = item->key_size;
393 hash = HASHTABLE_CALCULATE_HASH(cache_ht_, &entry->items,
394 &ht_key);
395 assert(hash >= 0);
396 assert(hash < HASHTABLE_ENTRIES_COUNT(&entry->items));
398 ht_item = HASHTABLE_GET_ENTRY(&(entry->items), hash);
399 ht_item_data = HASHTABLE_ENTRY_FIND(cache_ht_, ht_item,
400 &ht_key);
401 assert(ht_item_data != NULL);
402 free(ht_item_data->key);
403 free(ht_item_data->value);
404 HASHTABLE_ENTRY_REMOVE(cache_ht_, ht_item, ht_item_data);
405 --entry->items_size;
407 policy->destroy_item_func(item);
409 if (connected_item != NULL) {
410 connected_policy->remove_item_func(connected_policy,
411 connected_item);
412 connected_policy->destroy_item_func(connected_item);
415 item = next_item;
419 static void
420 flush_cache_entry(struct cache_entry_ *entry)
422 struct cache_mp_entry_ *mp_entry;
423 struct cache_common_entry_ *common_entry;
424 struct cache_policy_ *policy, *connected_policy;
426 connected_policy = NULL;
427 if (entry->params->entry_type == CET_COMMON) {
428 common_entry = (struct cache_common_entry_ *)entry;
429 if ((common_entry->common_params.max_lifetime.tv_sec != 0) ||
430 (common_entry->common_params.max_lifetime.tv_usec != 0)) {
432 policy = common_entry->policies[0];
433 if (common_entry->policies_size > 1)
434 connected_policy = common_entry->policies[1];
436 flush_cache_policy(common_entry, policy,
437 connected_policy,
438 cache_lifetime_common_continue_func);
442 if ((common_entry->common_params.max_elemsize != 0) &&
443 common_entry->items_size >
444 common_entry->common_params.max_elemsize) {
446 if (common_entry->policies_size > 1) {
447 policy = common_entry->policies[1];
448 connected_policy = common_entry->policies[0];
449 } else {
450 policy = common_entry->policies[0];
451 connected_policy = NULL;
454 flush_cache_policy(common_entry, policy,
455 connected_policy,
456 cache_elemsize_common_continue_func);
458 } else {
459 mp_entry = (struct cache_mp_entry_ *)entry;
461 if ((mp_entry->mp_params.max_lifetime.tv_sec != 0)
462 || (mp_entry->mp_params.max_lifetime.tv_usec != 0)) {
464 if (mp_entry->last_request_time.tv_sec -
465 mp_entry->last_request_time.tv_sec >
466 mp_entry->mp_params.max_lifetime.tv_sec)
467 clear_cache_entry(entry);
472 struct cache_ *
473 init_cache(struct cache_params const *params)
475 struct cache_ *retval;
477 TRACE_IN(init_cache);
478 assert(params != NULL);
480 retval = (struct cache_ *)calloc(1, sizeof(struct cache_));
481 assert(retval != NULL);
483 assert(params != NULL);
484 memcpy(&retval->params, params, sizeof(struct cache_params));
486 retval->entries = (struct cache_entry_ **)calloc(1,
487 sizeof(struct cache_entry_ *) * INITIAL_ENTRIES_CAPACITY);
488 assert(retval->entries != NULL);
490 retval->entries_capacity = INITIAL_ENTRIES_CAPACITY;
491 retval->entries_size = 0;
493 TRACE_OUT(init_cache);
494 return (retval);
497 void
498 destroy_cache(struct cache_ *the_cache)
501 TRACE_IN(destroy_cache);
502 assert(the_cache != NULL);
504 if (the_cache->entries != NULL) {
505 size_t i;
506 for (i = 0; i < the_cache->entries_size; ++i)
507 destroy_cache_entry(the_cache->entries[i]);
509 free(the_cache->entries);
512 free(the_cache);
513 TRACE_OUT(destroy_cache);
517 register_cache_entry(struct cache_ *the_cache,
518 struct cache_entry_params const *params)
520 int policies_size;
521 size_t entry_name_size;
522 struct cache_common_entry_ *new_common_entry;
523 struct cache_mp_entry_ *new_mp_entry;
525 TRACE_IN(register_cache_entry);
526 assert(the_cache != NULL);
528 if (find_cache_entry(the_cache, params->entry_name) != NULL) {
529 TRACE_OUT(register_cache_entry);
530 return (-1);
533 if (the_cache->entries_size == the_cache->entries_capacity) {
534 struct cache_entry_ **new_entries;
535 size_t new_capacity;
537 new_capacity = the_cache->entries_capacity +
538 ENTRIES_CAPACITY_STEP;
539 new_entries = (struct cache_entry_ **)calloc(1,
540 sizeof(struct cache_entry_ *) * new_capacity);
541 assert(new_entries != NULL);
543 memcpy(new_entries, the_cache->entries,
544 sizeof(struct cache_entry_ *)
545 * the_cache->entries_size);
547 free(the_cache->entries);
548 the_cache->entries = new_entries;
551 entry_name_size = strlen(params->entry_name) + 1;
552 switch (params->entry_type)
554 case CET_COMMON:
555 new_common_entry = (struct cache_common_entry_ *)calloc(1,
556 sizeof(struct cache_common_entry_));
557 assert(new_common_entry != NULL);
559 memcpy(&new_common_entry->common_params, params,
560 sizeof(struct common_cache_entry_params));
561 new_common_entry->params =
562 (struct cache_entry_params *)&new_common_entry->common_params;
564 new_common_entry->common_params.entry_name = (char *)calloc(1,
565 entry_name_size);
566 assert(new_common_entry->common_params.entry_name != NULL);
567 strlcpy(new_common_entry->common_params.entry_name,
568 params->entry_name, entry_name_size);
569 new_common_entry->name =
570 new_common_entry->common_params.entry_name;
572 HASHTABLE_INIT(&(new_common_entry->items),
573 struct cache_ht_item_data_, data,
574 new_common_entry->common_params.cache_entries_size);
576 if (new_common_entry->common_params.policy == CPT_FIFO)
577 policies_size = 1;
578 else
579 policies_size = 2;
581 new_common_entry->policies = (struct cache_policy_ **)calloc(1,
582 sizeof(struct cache_policy_ *) * policies_size);
583 assert(new_common_entry->policies != NULL);
585 new_common_entry->policies_size = policies_size;
586 new_common_entry->policies[0] = init_cache_fifo_policy();
588 if (policies_size > 1) {
589 switch (new_common_entry->common_params.policy) {
590 case CPT_LRU:
591 new_common_entry->policies[1] =
592 init_cache_lru_policy();
593 break;
594 case CPT_LFU:
595 new_common_entry->policies[1] =
596 init_cache_lfu_policy();
597 break;
598 default:
599 break;
603 new_common_entry->get_time_func =
604 the_cache->params.get_time_func;
605 the_cache->entries[the_cache->entries_size++] =
606 (struct cache_entry_ *)new_common_entry;
607 break;
608 case CET_MULTIPART:
609 new_mp_entry = (struct cache_mp_entry_ *)calloc(1,
610 sizeof(struct cache_mp_entry_));
611 assert(new_mp_entry != NULL);
613 memcpy(&new_mp_entry->mp_params, params,
614 sizeof(struct mp_cache_entry_params));
615 new_mp_entry->params =
616 (struct cache_entry_params *)&new_mp_entry->mp_params;
618 new_mp_entry->mp_params.entry_name = (char *)calloc(1,
619 entry_name_size);
620 assert(new_mp_entry->mp_params.entry_name != NULL);
621 strlcpy(new_mp_entry->mp_params.entry_name, params->entry_name,
622 entry_name_size);
623 new_mp_entry->name = new_mp_entry->mp_params.entry_name;
625 TAILQ_INIT(&new_mp_entry->ws_head);
626 TAILQ_INIT(&new_mp_entry->rs_head);
628 new_mp_entry->get_time_func = the_cache->params.get_time_func;
629 the_cache->entries[the_cache->entries_size++] =
630 (struct cache_entry_ *)new_mp_entry;
631 break;
635 qsort(the_cache->entries, the_cache->entries_size,
636 sizeof(struct cache_entry_ *), entries_qsort_cmp_func);
638 TRACE_OUT(register_cache_entry);
639 return (0);
643 unregister_cache_entry(struct cache_ *the_cache, const char *entry_name)
645 struct cache_entry_ **del_ent;
647 TRACE_IN(unregister_cache_entry);
648 assert(the_cache != NULL);
650 del_ent = find_cache_entry_p(the_cache, entry_name);
651 if (del_ent != NULL) {
652 destroy_cache_entry(*del_ent);
653 --the_cache->entries_size;
655 memmove(del_ent, del_ent + 1,
656 (&(the_cache->entries[--the_cache->entries_size]) -
657 del_ent) * sizeof(struct cache_entry_ *));
659 TRACE_OUT(unregister_cache_entry);
660 return (0);
661 } else {
662 TRACE_OUT(unregister_cache_entry);
663 return (-1);
667 struct cache_entry_ *
668 find_cache_entry(struct cache_ *the_cache, const char *entry_name)
670 struct cache_entry_ **result;
672 TRACE_IN(find_cache_entry);
673 result = find_cache_entry_p(the_cache, entry_name);
675 if (result == NULL) {
676 TRACE_OUT(find_cache_entry);
677 return (NULL);
678 } else {
679 TRACE_OUT(find_cache_entry);
680 return (*result);
685 * Tries to read the element with the specified key from the cache. If the
686 * value_size is too small, it will be filled with the proper number, and
687 * the user will need to call cache_read again with the value buffer, that
688 * is large enough.
689 * Function returns 0 on success, -1 on error, and -2 if the value_size is too
690 * small.
693 cache_read(struct cache_entry_ *entry, const char *key, size_t key_size,
694 char *value, size_t *value_size)
696 struct cache_common_entry_ *common_entry;
697 struct cache_ht_item_data_ item_data, *find_res;
698 struct cache_ht_item_ *item;
699 hashtable_index_t hash;
700 struct cache_policy_item_ *connected_item;
702 TRACE_IN(cache_read);
703 assert(entry != NULL);
704 assert(key != NULL);
705 assert(value_size != NULL);
706 assert(entry->params->entry_type == CET_COMMON);
708 common_entry = (struct cache_common_entry_ *)entry;
710 memset(&item_data, 0, sizeof(struct cache_ht_item_data_));
711 /* can't avoid the cast here */
712 item_data.key = (char *)key;
713 item_data.key_size = key_size;
715 hash = HASHTABLE_CALCULATE_HASH(cache_ht_, &common_entry->items,
716 &item_data);
717 assert(hash >= 0);
718 assert(hash < HASHTABLE_ENTRIES_COUNT(&common_entry->items));
720 item = HASHTABLE_GET_ENTRY(&(common_entry->items), hash);
721 find_res = HASHTABLE_ENTRY_FIND(cache_ht_, item, &item_data);
722 if (find_res == NULL) {
723 TRACE_OUT(cache_read);
724 return (-1);
727 if ((common_entry->common_params.max_lifetime.tv_sec != 0) ||
728 (common_entry->common_params.max_lifetime.tv_usec != 0)) {
730 if (find_res->fifo_policy_item->last_request_time.tv_sec -
731 find_res->fifo_policy_item->creation_time.tv_sec >
732 common_entry->common_params.max_lifetime.tv_sec) {
734 free(find_res->key);
735 free(find_res->value);
737 connected_item =
738 find_res->fifo_policy_item->connected_item;
739 if (connected_item != NULL) {
740 common_entry->policies[1]->remove_item_func(
741 common_entry->policies[1],
742 connected_item);
743 common_entry->policies[1]->destroy_item_func(
744 connected_item);
747 common_entry->policies[0]->remove_item_func(
748 common_entry->policies[0],
749 find_res->fifo_policy_item);
750 common_entry->policies[0]->destroy_item_func(
751 find_res->fifo_policy_item);
753 HASHTABLE_ENTRY_REMOVE(cache_ht_, item, find_res);
754 --common_entry->items_size;
758 if ((*value_size < find_res->value_size) || (value == NULL)) {
759 *value_size = find_res->value_size;
760 TRACE_OUT(cache_read);
761 return (-2);
764 *value_size = find_res->value_size;
765 memcpy(value, find_res->value, find_res->value_size);
767 ++find_res->fifo_policy_item->request_count;
768 common_entry->get_time_func(
769 &find_res->fifo_policy_item->last_request_time);
770 common_entry->policies[0]->update_item_func(common_entry->policies[0],
771 find_res->fifo_policy_item);
773 if (find_res->fifo_policy_item->connected_item != NULL) {
774 connected_item = find_res->fifo_policy_item->connected_item;
775 memcpy(&connected_item->last_request_time,
776 &find_res->fifo_policy_item->last_request_time,
777 sizeof(struct timeval));
778 connected_item->request_count =
779 find_res->fifo_policy_item->request_count;
781 common_entry->policies[1]->update_item_func(
782 common_entry->policies[1], connected_item);
785 TRACE_OUT(cache_read);
786 return (0);
790 * Writes the value with the specified key into the cache entry.
791 * Functions returns 0 on success, and -1 on error.
794 cache_write(struct cache_entry_ *entry, const char *key, size_t key_size,
795 char const *value, size_t value_size)
797 struct cache_common_entry_ *common_entry;
798 struct cache_ht_item_data_ item_data, *find_res;
799 struct cache_ht_item_ *item;
800 hashtable_index_t hash;
802 struct cache_policy_ *policy, *connected_policy;
803 struct cache_policy_item_ *policy_item;
804 struct cache_policy_item_ *connected_policy_item;
806 TRACE_IN(cache_write);
807 assert(entry != NULL);
808 assert(key != NULL);
809 assert(value != NULL);
810 assert(entry->params->entry_type == CET_COMMON);
812 common_entry = (struct cache_common_entry_ *)entry;
814 memset(&item_data, 0, sizeof(struct cache_ht_item_data_));
815 /* can't avoid the cast here */
816 item_data.key = (char *)key;
817 item_data.key_size = key_size;
819 hash = HASHTABLE_CALCULATE_HASH(cache_ht_, &common_entry->items,
820 &item_data);
821 assert(hash >= 0);
822 assert(hash < HASHTABLE_ENTRIES_COUNT(&common_entry->items));
824 item = HASHTABLE_GET_ENTRY(&(common_entry->items), hash);
825 find_res = HASHTABLE_ENTRY_FIND(cache_ht_, item, &item_data);
826 if (find_res != NULL) {
827 TRACE_OUT(cache_write);
828 return (-1);
831 item_data.key = (char *)malloc(key_size);
832 memcpy(item_data.key, key, key_size);
834 item_data.value = (char *)malloc(value_size);
835 assert(item_data.value != NULL);
837 memcpy(item_data.value, value, value_size);
838 item_data.value_size = value_size;
840 policy_item = common_entry->policies[0]->create_item_func();
841 policy_item->key = item_data.key;
842 policy_item->key_size = item_data.key_size;
843 common_entry->get_time_func(&policy_item->creation_time);
845 if (common_entry->policies_size > 1) {
846 connected_policy_item =
847 common_entry->policies[1]->create_item_func();
848 memcpy(&connected_policy_item->creation_time,
849 &policy_item->creation_time,
850 sizeof(struct timeval));
851 connected_policy_item->key = policy_item->key;
852 connected_policy_item->key_size = policy_item->key_size;
854 connected_policy_item->connected_item = policy_item;
855 policy_item->connected_item = connected_policy_item;
858 item_data.fifo_policy_item = policy_item;
860 common_entry->policies[0]->add_item_func(common_entry->policies[0],
861 policy_item);
862 if (common_entry->policies_size > 1)
863 common_entry->policies[1]->add_item_func(
864 common_entry->policies[1], connected_policy_item);
866 HASHTABLE_ENTRY_STORE(cache_ht_, item, &item_data);
867 ++common_entry->items_size;
869 if ((common_entry->common_params.max_elemsize != 0) &&
870 (common_entry->items_size >
871 common_entry->common_params.max_elemsize)) {
872 if (common_entry->policies_size > 1) {
873 policy = common_entry->policies[1];
874 connected_policy = common_entry->policies[0];
875 } else {
876 policy = common_entry->policies[0];
877 connected_policy = NULL;
880 flush_cache_policy(common_entry, policy, connected_policy,
881 cache_elemsize_common_continue_func);
884 TRACE_OUT(cache_write);
885 return (0);
889 * Initializes the write session for the specified multipart entry. This
890 * session then should be filled with data either committed or abandoned by
891 * using close_cache_mp_write_session or abandon_cache_mp_write_session
892 * respectively.
893 * Returns NULL on errors (when there are too many opened write sessions for
894 * the entry).
896 struct cache_mp_write_session_ *
897 open_cache_mp_write_session(struct cache_entry_ *entry)
899 struct cache_mp_entry_ *mp_entry;
900 struct cache_mp_write_session_ *retval;
902 TRACE_IN(open_cache_mp_write_session);
903 assert(entry != NULL);
904 assert(entry->params->entry_type == CET_MULTIPART);
905 mp_entry = (struct cache_mp_entry_ *)entry;
907 if ((mp_entry->mp_params.max_sessions > 0) &&
908 (mp_entry->ws_size == mp_entry->mp_params.max_sessions)) {
909 TRACE_OUT(open_cache_mp_write_session);
910 return (NULL);
913 retval = (struct cache_mp_write_session_ *)calloc(1,
914 sizeof(struct cache_mp_write_session_));
915 assert(retval != NULL);
917 TAILQ_INIT(&retval->items);
918 retval->parent_entry = mp_entry;
920 TAILQ_INSERT_HEAD(&mp_entry->ws_head, retval, entries);
921 ++mp_entry->ws_size;
923 TRACE_OUT(open_cache_mp_write_session);
924 return (retval);
928 * Writes data to the specified session. Return 0 on success and -1 on errors
929 * (when write session size limit is exceeded).
932 cache_mp_write(struct cache_mp_write_session_ *ws, char *data,
933 size_t data_size)
935 struct cache_mp_data_item_ *new_item;
937 TRACE_IN(cache_mp_write);
938 assert(ws != NULL);
939 assert(ws->parent_entry != NULL);
940 assert(ws->parent_entry->params->entry_type == CET_MULTIPART);
942 if ((ws->parent_entry->mp_params.max_elemsize > 0) &&
943 (ws->parent_entry->mp_params.max_elemsize == ws->items_size)) {
944 TRACE_OUT(cache_mp_write);
945 return (-1);
948 new_item = (struct cache_mp_data_item_ *)calloc(1,
949 sizeof(struct cache_mp_data_item_));
950 assert(new_item != NULL);
952 new_item->value = (char *)malloc(data_size);
953 assert(new_item->value != NULL);
954 memcpy(new_item->value, data, data_size);
955 new_item->value_size = data_size;
957 TAILQ_INSERT_TAIL(&ws->items, new_item, entries);
958 ++ws->items_size;
960 TRACE_OUT(cache_mp_write);
961 return (0);
965 * Abandons the write session and frees all the connected resources.
967 void
968 abandon_cache_mp_write_session(struct cache_mp_write_session_ *ws)
971 TRACE_IN(abandon_cache_mp_write_session);
972 assert(ws != NULL);
973 assert(ws->parent_entry != NULL);
974 assert(ws->parent_entry->params->entry_type == CET_MULTIPART);
976 TAILQ_REMOVE(&ws->parent_entry->ws_head, ws, entries);
977 --ws->parent_entry->ws_size;
979 destroy_cache_mp_write_session(ws);
980 TRACE_OUT(abandon_cache_mp_write_session);
984 * Commits the session to the entry, for which it was created.
986 void
987 close_cache_mp_write_session(struct cache_mp_write_session_ *ws)
990 TRACE_IN(close_cache_mp_write_session);
991 assert(ws != NULL);
992 assert(ws->parent_entry != NULL);
993 assert(ws->parent_entry->params->entry_type == CET_MULTIPART);
995 TAILQ_REMOVE(&ws->parent_entry->ws_head, ws, entries);
996 --ws->parent_entry->ws_size;
998 if (ws->parent_entry->completed_write_session == NULL) {
1000 * If there is no completed session yet, this will be the one
1002 ws->parent_entry->get_time_func(
1003 &ws->parent_entry->creation_time);
1004 ws->parent_entry->completed_write_session = ws;
1005 } else {
1007 * If there is a completed session, then we'll save our session
1008 * as a pending session. If there is already a pending session,
1009 * it would be destroyed.
1011 if (ws->parent_entry->pending_write_session != NULL)
1012 destroy_cache_mp_write_session(
1013 ws->parent_entry->pending_write_session);
1015 ws->parent_entry->pending_write_session = ws;
1017 TRACE_OUT(close_cache_mp_write_session);
1021 * Opens read session for the specified entry. Returns NULL on errors (when
1022 * there are no data in the entry, or the data are obsolete).
1024 struct cache_mp_read_session_ *
1025 open_cache_mp_read_session(struct cache_entry_ *entry)
1027 struct cache_mp_entry_ *mp_entry;
1028 struct cache_mp_read_session_ *retval;
1030 TRACE_IN(open_cache_mp_read_session);
1031 assert(entry != NULL);
1032 assert(entry->params->entry_type == CET_MULTIPART);
1033 mp_entry = (struct cache_mp_entry_ *)entry;
1035 if (mp_entry->completed_write_session == NULL) {
1036 TRACE_OUT(open_cache_mp_read_session);
1037 return (NULL);
1040 if ((mp_entry->mp_params.max_lifetime.tv_sec != 0)
1041 || (mp_entry->mp_params.max_lifetime.tv_usec != 0)) {
1042 if (mp_entry->last_request_time.tv_sec -
1043 mp_entry->last_request_time.tv_sec >
1044 mp_entry->mp_params.max_lifetime.tv_sec) {
1045 flush_cache_entry(entry);
1046 TRACE_OUT(open_cache_mp_read_session);
1047 return (NULL);
1051 retval = (struct cache_mp_read_session_ *)calloc(1,
1052 sizeof(struct cache_mp_read_session_));
1053 assert(retval != NULL);
1055 retval->parent_entry = mp_entry;
1056 retval->current_item = TAILQ_FIRST(
1057 &mp_entry->completed_write_session->items);
1059 TAILQ_INSERT_HEAD(&mp_entry->rs_head, retval, entries);
1060 ++mp_entry->rs_size;
1062 mp_entry->get_time_func(&mp_entry->last_request_time);
1063 TRACE_OUT(open_cache_mp_read_session);
1064 return (retval);
1068 * Reads the data from the read session - step by step.
1069 * Returns 0 on success, -1 on error (when there are no more data), and -2 if
1070 * the data_size is too small. In the last case, data_size would be filled
1071 * the proper value.
1074 cache_mp_read(struct cache_mp_read_session_ *rs, char *data, size_t *data_size)
1077 TRACE_IN(cache_mp_read);
1078 assert(rs != NULL);
1080 if (rs->current_item == NULL) {
1081 TRACE_OUT(cache_mp_read);
1082 return (-1);
1085 if (rs->current_item->value_size > *data_size) {
1086 *data_size = rs->current_item->value_size;
1087 if (data == NULL) {
1088 TRACE_OUT(cache_mp_read);
1089 return (0);
1092 TRACE_OUT(cache_mp_read);
1093 return (-2);
1096 *data_size = rs->current_item->value_size;
1097 memcpy(data, rs->current_item->value, rs->current_item->value_size);
1098 rs->current_item = TAILQ_NEXT(rs->current_item, entries);
1100 TRACE_OUT(cache_mp_read);
1101 return (0);
1105 * Closes the read session. If there are no more read sessions and there is
1106 * a pending write session, it will be committed and old
1107 * completed_write_session will be destroyed.
1109 void
1110 close_cache_mp_read_session(struct cache_mp_read_session_ *rs)
1113 TRACE_IN(close_cache_mp_read_session);
1114 assert(rs != NULL);
1115 assert(rs->parent_entry != NULL);
1117 TAILQ_REMOVE(&rs->parent_entry->rs_head, rs, entries);
1118 --rs->parent_entry->rs_size;
1120 if ((rs->parent_entry->rs_size == 0) &&
1121 (rs->parent_entry->pending_write_session != NULL)) {
1122 destroy_cache_mp_write_session(
1123 rs->parent_entry->completed_write_session);
1124 rs->parent_entry->completed_write_session =
1125 rs->parent_entry->pending_write_session;
1126 rs->parent_entry->pending_write_session = NULL;
1129 destroy_cache_mp_read_session(rs);
1130 TRACE_OUT(close_cache_mp_read_session);
1134 transform_cache_entry(struct cache_entry_ *entry,
1135 enum cache_transformation_t transformation)
1138 TRACE_IN(transform_cache_entry);
1139 switch (transformation) {
1140 case CTT_CLEAR:
1141 clear_cache_entry(entry);
1142 TRACE_OUT(transform_cache_entry);
1143 return (0);
1144 case CTT_FLUSH:
1145 flush_cache_entry(entry);
1146 TRACE_OUT(transform_cache_entry);
1147 return (0);
1148 default:
1149 TRACE_OUT(transform_cache_entry);
1150 return (-1);
1155 transform_cache_entry_part(struct cache_entry_ *entry,
1156 enum cache_transformation_t transformation, const char *key_part,
1157 size_t key_part_size, enum part_position_t part_position)
1159 struct cache_common_entry_ *common_entry;
1160 struct cache_ht_item_ *ht_item;
1161 struct cache_ht_item_data_ *ht_item_data, ht_key;
1163 struct cache_policy_item_ *item, *connected_item;
1165 TRACE_IN(transform_cache_entry_part);
1166 if (entry->params->entry_type != CET_COMMON) {
1167 TRACE_OUT(transform_cache_entry_part);
1168 return (-1);
1171 if (transformation != CTT_CLEAR) {
1172 TRACE_OUT(transform_cache_entry_part);
1173 return (-1);
1176 memset(&ht_key, 0, sizeof(struct cache_ht_item_data_));
1177 ht_key.key = (char *)key_part; /* can't avoid casting here */
1178 ht_key.key_size = key_part_size;
1180 common_entry = (struct cache_common_entry_ *)entry;
1181 HASHTABLE_FOREACH(&(common_entry->items), ht_item) {
1182 do {
1183 ht_item_data = HASHTABLE_ENTRY_FIND_SPECIAL(cache_ht_,
1184 ht_item, &ht_key,
1185 ht_items_fixed_size_left_cmp_func);
1187 if (ht_item_data != NULL) {
1188 item = ht_item_data->fifo_policy_item;
1189 connected_item = item->connected_item;
1191 common_entry->policies[0]->remove_item_func(
1192 common_entry->policies[0],
1193 item);
1195 free(ht_item_data->key);
1196 free(ht_item_data->value);
1197 HASHTABLE_ENTRY_REMOVE(cache_ht_, ht_item,
1198 ht_item_data);
1199 --common_entry->items_size;
1201 common_entry->policies[0]->destroy_item_func(
1202 item);
1203 if (common_entry->policies_size == 2) {
1204 common_entry->policies[1]->remove_item_func(
1205 common_entry->policies[1],
1206 connected_item);
1207 common_entry->policies[1]->destroy_item_func(
1208 connected_item);
1211 } while (ht_item_data != NULL);
1214 TRACE_OUT(transform_cache_entry_part);
1215 return (0);