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/>.
21
#include "../lib/util/rbtree.h"
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) {
71
struct memcache *memcache_init(TALLOC_CTX *mem_ctx, size_t max_size)
73
struct memcache *result;
75
result = TALLOC_ZERO_P(mem_ctx, struct memcache);
79
result->max_size = max_size;
80
talloc_set_destructor(result, memcache_destructor);
84
void memcache_set_global(struct memcache *cache)
86
TALLOC_FREE(global_cache);
90
static struct memcache_element *memcache_node2elem(struct rb_node *node)
92
return (struct memcache_element *)
93
((char *)node - offsetof(struct memcache_element, rb_node));
96
static void memcache_element_parse(struct memcache_element *e,
97
DATA_BLOB *key, DATA_BLOB *value)
99
key->data = ((uint8 *)e) + offsetof(struct memcache_element, data);
100
key->length = e->keylength;
101
value->data = key->data + e->keylength;
102
value->length = e->valuelength;
105
static size_t memcache_element_size(size_t key_length, size_t value_length)
107
return sizeof(struct memcache_element) - 1 + key_length + value_length;
110
static int memcache_compare(struct memcache_element *e, enum memcache_number n,
113
DATA_BLOB this_key, this_value;
115
if ((int)e->n < (int)n) return 1;
116
if ((int)e->n > (int)n) return -1;
118
if (e->keylength < key.length) return 1;
119
if (e->keylength > key.length) return -1;
121
memcache_element_parse(e, &this_key, &this_value);
122
return memcmp(this_key.data, key.data, key.length);
125
static struct memcache_element *memcache_find(
126
struct memcache *cache, enum memcache_number n, DATA_BLOB key)
128
struct rb_node *node;
130
node = cache->tree.rb_node;
132
while (node != NULL) {
133
struct memcache_element *elem = memcache_node2elem(node);
136
cmp = memcache_compare(elem, n, key);
140
node = (cmp < 0) ? node->rb_left : node->rb_right;
146
bool memcache_lookup(struct memcache *cache, enum memcache_number n,
147
DATA_BLOB key, DATA_BLOB *value)
149
struct memcache_element *e;
152
cache = global_cache;
158
e = memcache_find(cache, n, key);
163
if (cache->size != 0) {
165
* Do LRU promotion only when we will ever shrink
167
if (e == cache->lru) {
168
cache->lru = e->prev;
170
DLIST_PROMOTE(cache->mru, e);
171
if (cache->mru == NULL) {
176
memcache_element_parse(e, &key, value);
180
void *memcache_lookup_talloc(struct memcache *cache, enum memcache_number n,
186
if (!memcache_lookup(cache, n, key, &value)) {
190
if (value.length != sizeof(result)) {
194
memcpy(&result, value.data, sizeof(result));
199
static void memcache_delete_element(struct memcache *cache,
200
struct memcache_element *e)
202
rb_erase(&e->rb_node, &cache->tree);
204
if (e == cache->lru) {
205
cache->lru = e->prev;
207
DLIST_REMOVE(cache->mru, e);
209
if (memcache_is_talloc(e->n)) {
210
DATA_BLOB cache_key, cache_value;
213
memcache_element_parse(e, &cache_key, &cache_value);
214
SMB_ASSERT(cache_value.length == sizeof(ptr));
215
memcpy(&ptr, cache_value.data, sizeof(ptr));
219
cache->size -= memcache_element_size(e->keylength, e->valuelength);
224
static void memcache_trim(struct memcache *cache)
226
if (cache->max_size == 0) {
230
while ((cache->size > cache->max_size) && (cache->lru != NULL)) {
231
memcache_delete_element(cache, cache->lru);
235
void memcache_delete(struct memcache *cache, enum memcache_number n,
238
struct memcache_element *e;
241
cache = global_cache;
247
e = memcache_find(cache, n, key);
252
memcache_delete_element(cache, e);
255
void memcache_add(struct memcache *cache, enum memcache_number n,
256
DATA_BLOB key, DATA_BLOB value)
258
struct memcache_element *e;
260
struct rb_node *parent;
261
DATA_BLOB cache_key, cache_value;
265
cache = global_cache;
271
if (key.length == 0) {
275
e = memcache_find(cache, n, key);
278
memcache_element_parse(e, &cache_key, &cache_value);
280
if (value.length <= cache_value.length) {
281
if (memcache_is_talloc(e->n)) {
283
SMB_ASSERT(cache_value.length == sizeof(ptr));
284
memcpy(&ptr, cache_value.data, sizeof(ptr));
288
* We can reuse the existing record
290
memcpy(cache_value.data, value.data, value.length);
291
e->valuelength = value.length;
295
memcache_delete_element(cache, e);
298
element_size = memcache_element_size(key.length, value.length);
301
e = (struct memcache_element *)SMB_MALLOC(element_size);
304
DEBUG(0, ("malloc failed\n"));
309
e->keylength = key.length;
310
e->valuelength = value.length;
312
memcache_element_parse(e, &cache_key, &cache_value);
313
memcpy(cache_key.data, key.data, key.length);
314
memcpy(cache_value.data, value.data, value.length);
317
p = &cache->tree.rb_node;
320
struct memcache_element *elem = memcache_node2elem(*p);
325
cmp = memcache_compare(elem, n, key);
327
p = (cmp < 0) ? &(*p)->rb_left : &(*p)->rb_right;
330
rb_link_node(&e->rb_node, parent, p);
331
rb_insert_color(&e->rb_node, &cache->tree);
333
DLIST_ADD(cache->mru, e);
334
if (cache->lru == NULL) {
338
cache->size += element_size;
339
memcache_trim(cache);
342
void memcache_add_talloc(struct memcache *cache, enum memcache_number n,
343
DATA_BLOB key, void *pptr)
345
void **ptr = (void **)pptr;
349
cache = global_cache;
355
p = talloc_move(cache, ptr);
356
memcache_add(cache, n, key, data_blob_const(&p, sizeof(p)));
359
void memcache_flush(struct memcache *cache, enum memcache_number n)
361
struct rb_node *node;
364
cache = global_cache;
371
* Find the smallest element of number n
374
node = cache->tree.rb_node;
380
* First, find *any* element of number n
384
struct memcache_element *elem = memcache_node2elem(node);
385
struct rb_node *next;
387
if ((int)elem->n == (int)n) {
391
if ((int)elem->n < (int)n) {
392
next = node->rb_right;
395
next = node->rb_left;
408
* Then, find the leftmost element with number n
412
struct rb_node *prev = rb_prev(node);
413
struct memcache_element *elem;
418
elem = memcache_node2elem(prev);
419
if ((int)elem->n != (int)n) {
425
while (node != NULL) {
426
struct memcache_element *e = memcache_node2elem(node);
427
struct rb_node *next = rb_next(node);
433
memcache_delete_element(cache, e);