2 Unix SMB/CIFS implementation.
4 Copyright (C) Volker Lendecke 2007
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "../lib/util/debug.h"
23 #include "../lib/util/samba_util.h"
24 #include "../lib/util/dlinklist.h"
25 #include "../lib/util/rbtree.h"
28 static struct memcache
*global_cache
;
30 struct memcache_talloc_value
{
35 struct memcache_element
{
36 struct rb_node rb_node
;
37 struct memcache_element
*prev
, *next
;
38 size_t keylength
, valuelength
;
39 uint8_t n
; /* This is really an enum, but save memory */
40 char data
[1]; /* placeholder for offsetof */
44 struct memcache_element
*mru
;
50 static void memcache_element_parse(struct memcache_element
*e
,
51 DATA_BLOB
*key
, DATA_BLOB
*value
);
53 static bool memcache_is_talloc(enum memcache_number n
)
59 case PDB_GETPWSID_CACHE
:
60 case SINGLETON_CACHE_TALLOC
:
61 case SHARE_MODE_LOCK_CACHE
:
63 case VIRUSFILTER_SCAN_RESULTS_CACHE_TALLOC
:
74 static int memcache_destructor(struct memcache
*cache
) {
75 struct memcache_element
*e
, *next
;
77 for (e
= cache
->mru
; e
!= NULL
; e
= next
) {
84 struct memcache
*memcache_init(TALLOC_CTX
*mem_ctx
, size_t max_size
)
86 struct memcache
*result
;
88 result
= talloc_zero(mem_ctx
, struct memcache
);
92 result
->max_size
= max_size
;
93 talloc_set_destructor(result
, memcache_destructor
);
97 void memcache_set_global(struct memcache
*cache
)
99 TALLOC_FREE(global_cache
);
100 global_cache
= cache
;
103 static struct memcache_element
*memcache_node2elem(struct rb_node
*node
)
105 return (struct memcache_element
*)
106 ((char *)node
- offsetof(struct memcache_element
, rb_node
));
109 static void memcache_element_parse(struct memcache_element
*e
,
110 DATA_BLOB
*key
, DATA_BLOB
*value
)
112 key
->data
= ((uint8_t *)e
) + offsetof(struct memcache_element
, data
);
113 key
->length
= e
->keylength
;
114 value
->data
= key
->data
+ e
->keylength
;
115 value
->length
= e
->valuelength
;
118 static size_t memcache_element_size(size_t key_length
, size_t value_length
)
120 return sizeof(struct memcache_element
) - 1 + key_length
+ value_length
;
123 static int memcache_compare(struct memcache_element
*e
, enum memcache_number n
,
126 DATA_BLOB this_key
, this_value
;
128 if ((int)e
->n
< (int)n
) return 1;
129 if ((int)e
->n
> (int)n
) return -1;
131 if (e
->keylength
< key
.length
) return 1;
132 if (e
->keylength
> key
.length
) return -1;
134 memcache_element_parse(e
, &this_key
, &this_value
);
135 return memcmp(this_key
.data
, key
.data
, key
.length
);
138 static struct memcache_element
*memcache_find(
139 struct memcache
*cache
, enum memcache_number n
, DATA_BLOB key
)
141 struct rb_node
*node
;
143 node
= cache
->tree
.rb_node
;
145 while (node
!= NULL
) {
146 struct memcache_element
*elem
= memcache_node2elem(node
);
149 cmp
= memcache_compare(elem
, n
, key
);
153 node
= (cmp
< 0) ? node
->rb_left
: node
->rb_right
;
159 bool memcache_lookup(struct memcache
*cache
, enum memcache_number n
,
160 DATA_BLOB key
, DATA_BLOB
*value
)
162 struct memcache_element
*e
;
165 cache
= global_cache
;
171 e
= memcache_find(cache
, n
, key
);
176 if (cache
->size
!= 0) {
177 DLIST_PROMOTE(cache
->mru
, e
);
180 memcache_element_parse(e
, &key
, value
);
184 void *memcache_lookup_talloc(struct memcache
*cache
, enum memcache_number n
,
188 struct memcache_talloc_value mtv
;
190 if (!memcache_lookup(cache
, n
, key
, &value
)) {
194 if (value
.length
!= sizeof(mtv
)) {
198 memcpy(&mtv
, value
.data
, sizeof(mtv
));
203 static void memcache_delete_element(struct memcache
*cache
,
204 struct memcache_element
*e
)
206 rb_erase(&e
->rb_node
, &cache
->tree
);
208 DLIST_REMOVE(cache
->mru
, e
);
210 if (memcache_is_talloc(e
->n
)) {
211 DATA_BLOB cache_key
, cache_value
;
212 struct memcache_talloc_value mtv
;
214 memcache_element_parse(e
, &cache_key
, &cache_value
);
215 SMB_ASSERT(cache_value
.length
== sizeof(mtv
));
216 memcpy(&mtv
, cache_value
.data
, sizeof(mtv
));
217 cache
->size
-= mtv
.len
;
218 TALLOC_FREE(mtv
.ptr
);
221 cache
->size
-= memcache_element_size(e
->keylength
, e
->valuelength
);
226 static void memcache_trim(struct memcache
*cache
, struct memcache_element
*e
)
228 struct memcache_element
*tail
= NULL
;
230 if (cache
->max_size
== 0) {
234 for (tail
= DLIST_TAIL(cache
->mru
);
235 (cache
->size
> cache
->max_size
) && (tail
!= NULL
);
236 tail
= DLIST_TAIL(cache
->mru
))
239 tail
= DLIST_PREV(tail
);
244 memcache_delete_element(cache
, tail
);
248 void memcache_delete(struct memcache
*cache
, enum memcache_number n
,
251 struct memcache_element
*e
;
254 cache
= global_cache
;
260 e
= memcache_find(cache
, n
, key
);
265 memcache_delete_element(cache
, e
);
268 bool memcache_add(struct memcache
*cache
, enum memcache_number n
,
269 DATA_BLOB key
, DATA_BLOB value
)
271 struct memcache_element
*e
;
273 struct rb_node
*parent
;
274 DATA_BLOB cache_key
, cache_value
;
278 cache
= global_cache
;
284 if (key
.length
== 0) {
288 e
= memcache_find(cache
, n
, key
);
291 memcache_element_parse(e
, &cache_key
, &cache_value
);
293 if (value
.length
<= cache_value
.length
) {
294 if (memcache_is_talloc(e
->n
)) {
295 struct memcache_talloc_value mtv
;
297 SMB_ASSERT(cache_value
.length
== sizeof(mtv
));
298 memcpy(&mtv
, cache_value
.data
, sizeof(mtv
));
299 cache
->size
-= mtv
.len
;
300 TALLOC_FREE(mtv
.ptr
);
303 * We can reuse the existing record
305 memcpy(cache_value
.data
, value
.data
, value
.length
);
306 e
->valuelength
= value
.length
;
308 if (memcache_is_talloc(e
->n
)) {
309 struct memcache_talloc_value mtv
;
311 SMB_ASSERT(cache_value
.length
== sizeof(mtv
));
312 memcpy(&mtv
, cache_value
.data
, sizeof(mtv
));
313 cache
->size
+= mtv
.len
;
318 memcache_delete_element(cache
, e
);
321 element_size
= memcache_element_size(key
.length
, value
.length
);
323 e
= talloc_size(cache
, element_size
);
325 DEBUG(0, ("talloc failed\n"));
328 talloc_set_type(e
, struct memcache_element
);
331 e
->keylength
= key
.length
;
332 e
->valuelength
= value
.length
;
334 memcache_element_parse(e
, &cache_key
, &cache_value
);
335 memcpy(cache_key
.data
, key
.data
, key
.length
);
336 memcpy(cache_value
.data
, value
.data
, value
.length
);
339 p
= &cache
->tree
.rb_node
;
342 struct memcache_element
*elem
= memcache_node2elem(*p
);
347 cmp
= memcache_compare(elem
, n
, key
);
349 p
= (cmp
< 0) ? &(*p
)->rb_left
: &(*p
)->rb_right
;
352 rb_link_node(&e
->rb_node
, parent
, p
);
353 rb_insert_color(&e
->rb_node
, &cache
->tree
);
355 DLIST_ADD(cache
->mru
, e
);
357 cache
->size
+= element_size
;
358 if (memcache_is_talloc(e
->n
)) {
359 struct memcache_talloc_value mtv
;
361 SMB_ASSERT(cache_value
.length
== sizeof(mtv
));
362 memcpy(&mtv
, cache_value
.data
, sizeof(mtv
));
363 cache
->size
+= mtv
.len
;
365 memcache_trim(cache
, e
);
370 bool memcache_add_talloc(struct memcache
*cache
, enum memcache_number n
,
371 DATA_BLOB key
, void *pptr
)
373 struct memcache_talloc_value mtv
;
374 void **ptr
= (void **)pptr
;
377 cache
= global_cache
;
383 mtv
.len
= talloc_total_size(*ptr
);
384 mtv
.ptr
= talloc_move(cache
, ptr
);
386 return memcache_add(cache
, n
, key
, data_blob_const(&mtv
, sizeof(mtv
)));
389 void memcache_flush(struct memcache
*cache
, enum memcache_number n
)
391 struct rb_node
*node
;
394 cache
= global_cache
;
401 * Find the smallest element of number n
404 node
= cache
->tree
.rb_node
;
410 * First, find *any* element of number n
414 struct memcache_element
*elem
= memcache_node2elem(node
);
415 struct rb_node
*next
;
417 if ((int)elem
->n
== (int)n
) {
421 if ((int)elem
->n
< (int)n
) {
422 next
= node
->rb_right
;
425 next
= node
->rb_left
;
434 * Then, find the leftmost element with number n
438 struct rb_node
*prev
= rb_prev(node
);
439 struct memcache_element
*elem
;
444 elem
= memcache_node2elem(prev
);
445 if ((int)elem
->n
!= (int)n
) {
451 while (node
!= NULL
) {
452 struct memcache_element
*e
= memcache_node2elem(node
);
453 struct rb_node
*next
= rb_next(node
);
459 memcache_delete_element(cache
, e
);
464 void gfree_memcache(void)
466 TALLOC_FREE(global_cache
);