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/>.
23 static struct memcache
*global_cache
;
25 struct memcache_element
{
26 struct rb_node rb_node
;
27 struct memcache_element
*prev
, *next
;
28 size_t keylength
, valuelength
;
29 uint8 n
; /* This is really an enum, but save memory */
30 char data
[1]; /* placeholder for offsetof */
34 struct memcache_element
*mru
, *lru
;
40 static void memcache_element_parse(struct memcache_element
*e
,
41 DATA_BLOB
*key
, DATA_BLOB
*value
);
43 static bool memcache_is_talloc(enum memcache_number n
)
49 case PDB_GETPWSID_CACHE
:
50 case SINGLETON_CACHE_TALLOC
:
61 static int memcache_destructor(struct memcache
*cache
) {
62 struct memcache_element
*e
, *next
;
64 for (e
= cache
->mru
; e
!= NULL
; e
= next
) {
66 if (memcache_is_talloc((enum memcache_number
)e
->n
)
67 && (e
->valuelength
== sizeof(void *))) {
70 memcache_element_parse(e
, &key
, &value
);
71 memcpy(&ptr
, value
.data
, sizeof(ptr
));
79 struct memcache
*memcache_init(TALLOC_CTX
*mem_ctx
, size_t max_size
)
81 struct memcache
*result
;
83 result
= TALLOC_ZERO_P(mem_ctx
, struct memcache
);
87 result
->max_size
= max_size
;
88 talloc_set_destructor(result
, memcache_destructor
);
92 void memcache_set_global(struct memcache
*cache
)
94 TALLOC_FREE(global_cache
);
98 static struct memcache_element
*memcache_node2elem(struct rb_node
*node
)
100 return (struct memcache_element
*)
101 ((char *)node
- offsetof(struct memcache_element
, rb_node
));
104 static void memcache_element_parse(struct memcache_element
*e
,
105 DATA_BLOB
*key
, DATA_BLOB
*value
)
107 key
->data
= ((uint8
*)e
) + offsetof(struct memcache_element
, data
);
108 key
->length
= e
->keylength
;
109 value
->data
= key
->data
+ e
->keylength
;
110 value
->length
= e
->valuelength
;
113 static size_t memcache_element_size(size_t key_length
, size_t value_length
)
115 return sizeof(struct memcache_element
) - 1 + key_length
+ value_length
;
118 static int memcache_compare(struct memcache_element
*e
, enum memcache_number n
,
121 DATA_BLOB this_key
, this_value
;
123 if ((int)e
->n
< (int)n
) return -1;
124 if ((int)e
->n
> (int)n
) return 1;
126 if (e
->keylength
< key
.length
) return -1;
127 if (e
->keylength
> key
.length
) return 1;
129 memcache_element_parse(e
, &this_key
, &this_value
);
130 return memcmp(this_key
.data
, key
.data
, key
.length
);
133 static struct memcache_element
*memcache_find(
134 struct memcache
*cache
, enum memcache_number n
, DATA_BLOB key
)
136 struct rb_node
*node
;
138 node
= cache
->tree
.rb_node
;
140 while (node
!= NULL
) {
141 struct memcache_element
*elem
= memcache_node2elem(node
);
144 cmp
= memcache_compare(elem
, n
, key
);
148 node
= (cmp
< 0) ? node
->rb_left
: node
->rb_right
;
154 bool memcache_lookup(struct memcache
*cache
, enum memcache_number n
,
155 DATA_BLOB key
, DATA_BLOB
*value
)
157 struct memcache_element
*e
;
160 cache
= global_cache
;
166 e
= memcache_find(cache
, n
, key
);
171 if (cache
->size
!= 0) {
173 * Do LRU promotion only when we will ever shrink
175 if (e
== cache
->lru
) {
176 cache
->lru
= e
->prev
;
178 DLIST_PROMOTE(cache
->mru
, e
);
179 if (cache
->mru
== NULL
) {
184 memcache_element_parse(e
, &key
, value
);
188 void *memcache_lookup_talloc(struct memcache
*cache
, enum memcache_number n
,
194 if (!memcache_lookup(cache
, n
, key
, &value
)) {
198 if (value
.length
!= sizeof(result
)) {
202 memcpy(&result
, value
.data
, sizeof(result
));
207 static void memcache_delete_element(struct memcache
*cache
,
208 struct memcache_element
*e
)
210 rb_erase(&e
->rb_node
, &cache
->tree
);
212 if (e
== cache
->lru
) {
213 cache
->lru
= e
->prev
;
215 DLIST_REMOVE(cache
->mru
, e
);
217 cache
->size
-= memcache_element_size(e
->keylength
, e
->valuelength
);
222 static void memcache_trim(struct memcache
*cache
)
224 if (cache
->max_size
== 0) {
228 while ((cache
->size
> cache
->max_size
) && (cache
->lru
!= NULL
)) {
229 memcache_delete_element(cache
, cache
->lru
);
233 void memcache_delete(struct memcache
*cache
, enum memcache_number n
,
236 struct memcache_element
*e
;
239 cache
= global_cache
;
245 e
= memcache_find(cache
, n
, key
);
250 memcache_delete_element(cache
, e
);
253 void memcache_add(struct memcache
*cache
, enum memcache_number n
,
254 DATA_BLOB key
, DATA_BLOB value
)
256 struct memcache_element
*e
;
258 struct rb_node
*parent
;
259 DATA_BLOB cache_key
, cache_value
;
263 cache
= global_cache
;
269 if (key
.length
== 0) {
273 e
= memcache_find(cache
, n
, key
);
276 memcache_element_parse(e
, &cache_key
, &cache_value
);
278 if (value
.length
<= cache_value
.length
) {
280 * We can reuse the existing record
282 memcpy(cache_value
.data
, value
.data
, value
.length
);
283 e
->valuelength
= value
.length
;
287 memcache_delete_element(cache
, e
);
290 element_size
= memcache_element_size(key
.length
, value
.length
);
293 e
= (struct memcache_element
*)SMB_MALLOC(element_size
);
296 DEBUG(0, ("malloc failed\n"));
301 e
->keylength
= key
.length
;
302 e
->valuelength
= value
.length
;
304 memcache_element_parse(e
, &cache_key
, &cache_value
);
305 memcpy(cache_key
.data
, key
.data
, key
.length
);
306 memcpy(cache_value
.data
, value
.data
, value
.length
);
309 p
= &cache
->tree
.rb_node
;
312 struct memcache_element
*elem
= memcache_node2elem(*p
);
317 cmp
= memcache_compare(elem
, n
, key
);
319 p
= (cmp
< 0) ? &(*p
)->rb_left
: &(*p
)->rb_right
;
322 rb_link_node(&e
->rb_node
, parent
, p
);
323 rb_insert_color(&e
->rb_node
, &cache
->tree
);
325 DLIST_ADD(cache
->mru
, e
);
326 if (cache
->lru
== NULL
) {
330 cache
->size
+= element_size
;
331 memcache_trim(cache
);
334 void memcache_add_talloc(struct memcache
*cache
, enum memcache_number n
,
335 DATA_BLOB key
, void *ptr
)
337 memcache_add(cache
, n
, key
, data_blob_const(&ptr
, sizeof(ptr
)));
340 void memcache_flush(struct memcache
*cache
, enum memcache_number n
)
342 struct rb_node
*node
;
345 cache
= global_cache
;
352 * Find the smallest element of number n
355 node
= cache
->tree
.rb_node
;
361 struct memcache_element
*elem
= memcache_node2elem(node
);
362 struct rb_node
*next
;
364 if ((int)elem
->n
< (int)n
) {
365 next
= node
->rb_right
;
368 next
= node
->rb_left
;
376 node
= rb_next(node
);
381 while (node
!= NULL
) {
382 struct memcache_element
*e
= memcache_node2elem(node
);
383 struct rb_node
*next
= rb_next(node
);
385 memcache_delete_element(cache
, e
);