dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / libldap5 / sources / ldap / common / memcache.c
bloba9d4597bb52c6289f5ae2d121cb02da9d54bad7f
1 /*
2 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
5 #pragma ident "%Z%%M% %I% %E% SMI"
7 /*
8 * The contents of this file are subject to the Netscape Public
9 * License Version 1.1 (the "License"); you may not use this file
10 * except in compliance with the License. You may obtain a copy of
11 * the License at http://www.mozilla.org/NPL/
13 * Software distributed under the License is distributed on an "AS
14 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
15 * implied. See the License for the specific language governing
16 * rights and limitations under the License.
18 * The Original Code is Mozilla Communicator client code, released
19 * March 31, 1998.
21 * The Initial Developer of the Original Code is Netscape
22 * Communications Corporation. Portions created by Netscape are
23 * Copyright (C) 1998-1999 Netscape Communications Corporation. All
24 * Rights Reserved.
26 * Contributor(s):
30 * memcache.c - routines that implement an in-memory cache.
32 * To Do: 1) ber_dup_ext().
33 * 2) referrals and reference?
36 #include <assert.h>
37 #include "ldap-int.h"
40 * Extra size allocated to BerElement.
41 * XXXmcs: must match EXBUFSIZ in liblber/io.c?
43 #define EXTRA_SIZE 1024
45 /* Mode constants for function memcache_access() */
46 #define MEMCACHE_ACCESS_ADD 0
47 #define MEMCACHE_ACCESS_APPEND 1
48 #define MEMCACHE_ACCESS_APPEND_LAST 2
49 #define MEMCACHE_ACCESS_FIND 3
50 #define MEMCACHE_ACCESS_DELETE 4
51 #define MEMCACHE_ACCESS_DELETE_ALL 5
52 #define MEMCACHE_ACCESS_UPDATE 6
53 #define MEMCACHE_ACCESS_FLUSH 7
54 #define MEMCACHE_ACCESS_FLUSH_ALL 8
55 #define MEMCACHE_ACCESS_FLUSH_LRU 9
57 /* Mode constants for function memcache_adj_size */
58 #define MEMCACHE_SIZE_DEDUCT 0
59 #define MEMCACHE_SIZE_ADD 1
61 #define MEMCACHE_SIZE_ENTRIES 1
62 #define MEMCACHE_SIZE_NON_ENTRIES 2
64 /* Size used for calculation if given size of cache is 0 */
65 #define MEMCACHE_DEF_SIZE 131072 /* 128K bytes */
67 /* Index into different list structure */
68 #define LIST_TTL 0
69 #define LIST_LRU 1
70 #define LIST_TMP 2
71 #define LIST_TOTAL 3
74 static char *emptyStr = "";
76 /* Macros to make code more readable */
77 #define NSLDAPI_VALID_MEMCACHE_POINTER( cp ) ( (cp) != NULL )
78 #define NSLDAPI_STR_NONNULL( s ) ( (s) ? (s) : emptyStr )
79 #define NSLDAPI_SAFE_STRLEN( s ) ( (s) ? strlen((s)) + 1 : 1 )
81 /* Macros dealing with mutex */
82 #define LDAP_MEMCACHE_MUTEX_LOCK( c ) \
83 if ( (c) && ((c)->ldmemc_lock_fns).ltf_mutex_lock ) { \
84 ((c)->ldmemc_lock_fns).ltf_mutex_lock( (c)->ldmemc_lock ); \
87 #define LDAP_MEMCACHE_MUTEX_UNLOCK( c ) \
88 if ( (c) && ((c)->ldmemc_lock_fns).ltf_mutex_unlock ) { \
89 ((c)->ldmemc_lock_fns).ltf_mutex_unlock( (c)->ldmemc_lock ); \
92 #define LDAP_MEMCACHE_MUTEX_ALLOC( c ) \
93 ((c) && ((c)->ldmemc_lock_fns).ltf_mutex_alloc ? \
94 ((c)->ldmemc_lock_fns).ltf_mutex_alloc() : NULL)
96 #define LDAP_MEMCACHE_MUTEX_FREE( c ) \
97 if ( (c) && ((c)->ldmemc_lock_fns).ltf_mutex_free ) { \
98 ((c)->ldmemc_lock_fns).ltf_mutex_free( (c)->ldmemc_lock ); \
101 /* Macros used for triming unnecessary spaces in a basedn */
102 #define NSLDAPI_IS_SPACE( c ) \
103 (((c) == ' ') || ((c) == '\t') || ((c) == '\n'))
105 #define NSLDAPI_IS_SEPARATER( c ) \
106 ((c) == ',')
108 /* Hash table callback function pointer definition */
109 typedef int (*HashFuncPtr)(int table_size, void *key);
110 typedef int (*PutDataPtr)(void **ppTableData, void *key, void *pData);
111 typedef int (*GetDataPtr)(void *pTableData, void *key, void **ppData);
112 typedef int (*RemoveDataPtr)(void **ppTableData, void *key, void **ppData);
113 typedef int (*MiscFuncPtr)(void **ppTableData, void *key, void *pData);
114 typedef void (*ClrTableNodePtr)(void **ppTableData, void *pData);
116 /* Structure of a node in a hash table */
117 typedef struct HashTableNode_struct {
118 void *pData;
119 } HashTableNode;
121 /* Structure of a hash table */
122 typedef struct HashTable_struct {
123 HashTableNode *table;
124 int size;
125 HashFuncPtr hashfunc;
126 PutDataPtr putdata;
127 GetDataPtr getdata;
128 MiscFuncPtr miscfunc;
129 RemoveDataPtr removedata;
130 ClrTableNodePtr clrtablenode;
131 } HashTable;
133 /* Structure uniquely identifies a search request */
134 typedef struct ldapmemcacheReqId_struct {
135 LDAP *ldmemcrid_ld;
136 int ldmemcrid_msgid;
137 } ldapmemcacheReqId;
139 /* Structure representing a ldap handle associated to memcache */
140 typedef struct ldapmemcacheld_struct {
141 LDAP *ldmemcl_ld;
142 struct ldapmemcacheld_struct *ldmemcl_next;
143 } ldapmemcacheld;
145 /* Structure representing header of a search result */
146 typedef struct ldapmemcacheRes_struct {
147 char *ldmemcr_basedn;
148 unsigned long ldmemcr_crc_key;
149 unsigned long ldmemcr_resSize;
150 unsigned long ldmemcr_timestamp;
151 LDAPMessage *ldmemcr_resHead;
152 LDAPMessage *ldmemcr_resTail;
153 ldapmemcacheReqId ldmemcr_req_id;
154 struct ldapmemcacheRes_struct *ldmemcr_next[LIST_TOTAL];
155 struct ldapmemcacheRes_struct *ldmemcr_prev[LIST_TOTAL];
156 struct ldapmemcacheRes_struct *ldmemcr_htable_next;
157 } ldapmemcacheRes;
159 /* Structure for memcache statistics */
160 typedef struct ldapmemcacheStats_struct {
161 unsigned long ldmemcstat_tries;
162 unsigned long ldmemcstat_hits;
163 } ldapmemcacheStats;
165 /* Structure of a memcache object */
166 struct ldapmemcache {
167 unsigned long ldmemc_ttl;
168 unsigned long ldmemc_size;
169 unsigned long ldmemc_size_used;
170 unsigned long ldmemc_size_entries;
171 char **ldmemc_basedns;
172 void *ldmemc_lock;
173 ldapmemcacheld *ldmemc_lds;
174 HashTable *ldmemc_resTmp;
175 HashTable *ldmemc_resLookup;
176 ldapmemcacheRes *ldmemc_resHead[LIST_TOTAL];
177 ldapmemcacheRes *ldmemc_resTail[LIST_TOTAL];
178 struct ldap_thread_fns ldmemc_lock_fns;
179 ldapmemcacheStats ldmemc_stats;
182 /* Function prototypes */
183 static int memcache_exist(LDAP *ld);
184 static int memcache_add_to_ld(LDAP *ld, int msgid, LDAPMessage *pMsg);
185 static int memcache_compare_dn(const char *main_dn, const char *dn, int scope);
186 static int memcache_dup_message(LDAPMessage *res, int msgid, int fromcache,
187 LDAPMessage **ppResCopy, unsigned long *pSize);
188 static BerElement* memcache_ber_dup(BerElement* pBer, unsigned long *pSize);
190 static void memcache_trim_basedn_spaces(char *basedn);
191 static int memcache_validate_basedn(LDAPMemCache *cache, const char *basedn);
192 static int memcache_get_ctrls_len(LDAPControl **ctrls);
193 static void memcache_append_ctrls(char *buf, LDAPControl **serverCtrls,
194 LDAPControl **clientCtrls);
195 static int memcache_adj_size(LDAPMemCache *cache, unsigned long size,
196 int usageFlags, int bAdd);
197 static int memcache_free_entry(LDAPMemCache *cache, ldapmemcacheRes *pRes);
198 static int memcache_expired(LDAPMemCache *cache, ldapmemcacheRes *pRes,
199 unsigned long curTime);
200 static int memcache_add_to_list(LDAPMemCache *cache, ldapmemcacheRes *pRes,
201 int index);
202 static int memcache_add_res_to_list(ldapmemcacheRes *pRes, LDAPMessage *pMsg,
203 unsigned long size);
204 static int memcache_free_from_list(LDAPMemCache *cache, ldapmemcacheRes *pRes,
205 int index);
206 static int memcache_search(LDAP *ld, unsigned long key, LDAPMessage **ppRes);
207 static int memcache_add(LDAP *ld, unsigned long key, int msgid,
208 const char *basedn);
209 static int memcache_append(LDAP *ld, int msgid, LDAPMessage *pRes);
210 static int memcache_append_last(LDAP *ld, int msgid, LDAPMessage *pRes);
211 static int memcache_remove(LDAP *ld, int msgid);
212 #if 0 /* function not used */
213 static int memcache_remove_all(LDAP *ld);
214 #endif /* 0 */
215 static int memcache_access(LDAPMemCache *cache, int mode,
216 void *pData1, void *pData2, void *pData3);
217 #ifdef LDAP_DEBUG
218 static void memcache_print_list( LDAPMemCache *cache, int index );
219 static void memcache_report_statistics( LDAPMemCache *cache );
220 #endif /* LDAP_DEBUG */
222 static int htable_calculate_size(int sizelimit);
223 static int htable_sizeinbytes(HashTable *pTable);
224 static int htable_put(HashTable *pTable, void *key, void *pData);
225 static int htable_get(HashTable *pTable, void *key, void **ppData);
226 static int htable_misc(HashTable *pTable, void *key, void *pData);
227 static int htable_remove(HashTable *pTable, void *key, void **ppData);
228 static int htable_removeall(HashTable *pTable, void *pData);
229 static int htable_create(int size_limit, HashFuncPtr hashf,
230 PutDataPtr putDataf, GetDataPtr getDataf,
231 RemoveDataPtr removeDataf, ClrTableNodePtr clrNodef,
232 MiscFuncPtr miscOpf, HashTable **ppTable);
233 static int htable_free(HashTable *pTable);
235 static int msgid_hashf(int table_size, void *key);
236 static int msgid_putdata(void **ppTableData, void *key, void *pData);
237 static int msgid_getdata(void *pTableData, void *key, void **ppData);
238 static int msgid_removedata(void **ppTableData, void *key, void **ppData);
239 static int msgid_clear_ld_items(void **ppTableData, void *key, void *pData);
240 static void msgid_clearnode(void **ppTableData, void *pData);
242 static int attrkey_hashf(int table_size, void *key);
243 static int attrkey_putdata(void **ppTableData, void *key, void *pData);
244 static int attrkey_getdata(void *pTableData, void *key, void **ppData);
245 static int attrkey_removedata(void **ppTableData, void *key, void **ppData);
246 static void attrkey_clearnode(void **ppTableData, void *pData);
248 static unsigned long crc32_convert(char *buf, int len);
250 /* Create a memcache object. */
252 LDAP_CALL
253 ldap_memcache_init( unsigned long ttl, unsigned long size,
254 char **baseDNs, struct ldap_thread_fns *thread_fns,
255 LDAPMemCache **cachep )
257 unsigned long total_size = 0;
259 LDAPDebug( LDAP_DEBUG_TRACE, "ldap_memcache_init\n", 0, 0, 0 );
261 if ( cachep == NULL ) {
262 return( LDAP_PARAM_ERROR );
265 if ((*cachep = (LDAPMemCache*)NSLDAPI_CALLOC(1,
266 sizeof(LDAPMemCache))) == NULL) {
267 return ( LDAP_NO_MEMORY );
270 total_size += sizeof(LDAPMemCache);
272 (*cachep)->ldmemc_ttl = ttl;
273 (*cachep)->ldmemc_size = size;
274 (*cachep)->ldmemc_lds = NULL;
276 /* Non-zero default size needed for calculating size of hash tables */
277 size = (size ? size : MEMCACHE_DEF_SIZE);
279 if (thread_fns) {
280 memcpy(&((*cachep)->ldmemc_lock_fns), thread_fns,
281 sizeof(struct ldap_thread_fns));
282 } else {
283 memset(&((*cachep)->ldmemc_lock_fns), 0,
284 sizeof(struct ldap_thread_fns));
287 (*cachep)->ldmemc_lock = LDAP_MEMCACHE_MUTEX_ALLOC( *cachep );
289 /* Cache basedns */
290 if (baseDNs != NULL) {
292 int i;
294 for (i = 0; baseDNs[i]; i++) {
298 (*cachep)->ldmemc_basedns = (char**)NSLDAPI_CALLOC(i + 1,
299 sizeof(char*));
301 if ((*cachep)->ldmemc_basedns == NULL) {
302 ldap_memcache_destroy(*cachep);
303 *cachep = NULL;
304 return ( LDAP_NO_MEMORY );
307 total_size += (i + 1) * sizeof(char*);
309 for (i = 0; baseDNs[i]; i++) {
310 (*cachep)->ldmemc_basedns[i] = nsldapi_strdup(baseDNs[i]);
311 if ((*cachep)->ldmemc_basedns[i] == NULL)
312 return ( LDAP_NO_MEMORY );
313 total_size += strlen(baseDNs[i]) + 1;
316 (*cachep)->ldmemc_basedns[i] = NULL;
319 /* Create hash table for temporary cache */
320 if (htable_create(size, msgid_hashf, msgid_putdata, msgid_getdata,
321 msgid_removedata, msgid_clearnode, msgid_clear_ld_items,
322 &((*cachep)->ldmemc_resTmp)) != LDAP_SUCCESS) {
323 ldap_memcache_destroy(*cachep);
324 *cachep = NULL;
325 return( LDAP_NO_MEMORY );
328 total_size += htable_sizeinbytes((*cachep)->ldmemc_resTmp);
330 /* Create hash table for primary cache */
331 if (htable_create(size, attrkey_hashf, attrkey_putdata,
332 attrkey_getdata, attrkey_removedata, attrkey_clearnode,
333 NULL, &((*cachep)->ldmemc_resLookup)) != LDAP_SUCCESS) {
334 ldap_memcache_destroy(*cachep);
335 *cachep = NULL;
336 return( LDAP_NO_MEMORY );
339 total_size += htable_sizeinbytes((*cachep)->ldmemc_resLookup);
341 /* See if there is enough room so far */
342 if (memcache_adj_size(*cachep, total_size, MEMCACHE_SIZE_NON_ENTRIES,
343 MEMCACHE_SIZE_ADD) != LDAP_SUCCESS) {
344 ldap_memcache_destroy(*cachep);
345 *cachep = NULL;
346 return( LDAP_SIZELIMIT_EXCEEDED );
349 LDAPDebug( LDAP_DEBUG_TRACE, "ldap_memcache_init new cache 0x%x\n",
350 *cachep, 0, 0 );
352 return( LDAP_SUCCESS );
355 /* Associates a ldap handle to a memcache object. */
357 LDAP_CALL
358 ldap_memcache_set( LDAP *ld, LDAPMemCache *cache )
360 int nRes = LDAP_SUCCESS;
362 LDAPDebug( LDAP_DEBUG_TRACE, "ldap_memcache_set\n", 0, 0, 0 );
364 if ( !NSLDAPI_VALID_LDAP_POINTER( ld ) )
365 return( LDAP_PARAM_ERROR );
367 LDAP_MUTEX_LOCK( ld, LDAP_MEMCACHE_LOCK );
369 if (ld->ld_memcache != cache) {
371 LDAPMemCache *c = ld->ld_memcache;
372 ldapmemcacheld *pCur = NULL;
373 ldapmemcacheld *pPrev = NULL;
375 /* First dissociate handle from old cache */
377 LDAP_MEMCACHE_MUTEX_LOCK( c );
379 pCur = (c ? c->ldmemc_lds : NULL);
380 for (; pCur; pCur = pCur->ldmemcl_next) {
381 if (pCur->ldmemcl_ld == ld)
382 break;
383 pPrev = pCur;
386 if (pCur) {
388 ldapmemcacheReqId reqid;
390 reqid.ldmemcrid_ld = ld;
391 reqid.ldmemcrid_msgid = -1;
392 htable_misc(c->ldmemc_resTmp, (void*)&reqid, (void*)c);
394 if (pPrev)
395 pPrev->ldmemcl_next = pCur->ldmemcl_next;
396 else
397 c->ldmemc_lds = pCur->ldmemcl_next;
398 NSLDAPI_FREE(pCur);
399 pCur = NULL;
401 memcache_adj_size(c, sizeof(ldapmemcacheld),
402 MEMCACHE_SIZE_NON_ENTRIES, MEMCACHE_SIZE_DEDUCT);
405 LDAP_MEMCACHE_MUTEX_UNLOCK( c );
407 ld->ld_memcache = NULL;
409 /* Exit if no new cache is specified */
410 if (cache == NULL) {
411 LDAP_MUTEX_UNLOCK( ld, LDAP_MEMCACHE_LOCK );
412 return( LDAP_SUCCESS );
415 /* Then associate handle with new cache */
417 LDAP_MEMCACHE_MUTEX_LOCK( cache );
419 if ((nRes = memcache_adj_size(cache, sizeof(ldapmemcacheld),
420 MEMCACHE_SIZE_NON_ENTRIES, MEMCACHE_SIZE_ADD)) != LDAP_SUCCESS) {
421 LDAP_MEMCACHE_MUTEX_UNLOCK( cache );
422 LDAP_MUTEX_UNLOCK( ld, LDAP_MEMCACHE_LOCK );
423 return nRes;
426 pCur = (ldapmemcacheld*)NSLDAPI_CALLOC(1, sizeof(ldapmemcacheld));
427 if (pCur == NULL) {
428 memcache_adj_size(cache, sizeof(ldapmemcacheld),
429 MEMCACHE_SIZE_NON_ENTRIES, MEMCACHE_SIZE_DEDUCT);
430 nRes = LDAP_NO_MEMORY;
431 } else {
432 pCur->ldmemcl_ld = ld;
433 pCur->ldmemcl_next = cache->ldmemc_lds;
434 cache->ldmemc_lds = pCur;
435 ld->ld_memcache = cache;
438 LDAP_MEMCACHE_MUTEX_UNLOCK( cache );
441 LDAP_MUTEX_UNLOCK( ld, LDAP_MEMCACHE_LOCK );
443 return nRes;
446 /* Retrieves memcache with which the ldap handle has been associated. */
448 LDAP_CALL
449 ldap_memcache_get( LDAP *ld, LDAPMemCache **cachep )
451 LDAPDebug( LDAP_DEBUG_TRACE, "ldap_memcache_get ld: 0x%x\n", ld, 0, 0 );
453 if ( !NSLDAPI_VALID_LDAP_POINTER( ld ) || cachep == NULL ) {
454 return( LDAP_PARAM_ERROR );
457 LDAP_MUTEX_LOCK( ld, LDAP_MEMCACHE_LOCK );
458 *cachep = ld->ld_memcache;
459 LDAP_MUTEX_UNLOCK( ld, LDAP_MEMCACHE_LOCK );
461 return( LDAP_SUCCESS );
465 * Function that stays inside libldap and proactively expires items from
466 * the given cache. This should be called from a newly created thread since
467 * it will not return until after ldap_memcache_destroy() is called.
469 void
470 LDAP_CALL
471 ldap_memcache_update( LDAPMemCache *cache )
473 LDAPDebug( LDAP_DEBUG_TRACE, "ldap_memcache_update: cache 0x%x\n",
474 cache, 0, 0 );
476 if ( !NSLDAPI_VALID_MEMCACHE_POINTER( cache )) {
477 return;
480 LDAP_MEMCACHE_MUTEX_LOCK( cache );
481 memcache_access(cache, MEMCACHE_ACCESS_UPDATE, NULL, NULL, NULL);
482 LDAP_MEMCACHE_MUTEX_UNLOCK( cache );
485 /* Removes specified entries from given memcache. */
486 void
487 LDAP_CALL
488 ldap_memcache_flush( LDAPMemCache *cache, char *dn, int scope )
490 LDAPDebug( LDAP_DEBUG_TRACE,
491 "ldap_memcache_flush( cache: 0x%x, dn: %s, scope: %d)\n",
492 cache, ( dn == NULL ) ? "(null)" : dn, scope );
494 if ( !NSLDAPI_VALID_MEMCACHE_POINTER( cache )) {
495 return;
498 LDAP_MEMCACHE_MUTEX_LOCK( cache );
500 if (!dn) {
501 memcache_access(cache, MEMCACHE_ACCESS_FLUSH_ALL, NULL, NULL, NULL);
502 } else {
503 memcache_access(cache, MEMCACHE_ACCESS_FLUSH,
504 (void*)dn, (void*)(uintptr_t)scope, NULL);
507 LDAP_MEMCACHE_MUTEX_UNLOCK( cache );
510 /* Destroys the given memcache. */
511 void
512 LDAP_CALL
513 ldap_memcache_destroy( LDAPMemCache *cache )
515 int i = 0;
516 unsigned long size = sizeof(LDAPMemCache);
517 ldapmemcacheld *pNode = NULL, *pNextNode = NULL;
519 LDAPDebug( LDAP_DEBUG_TRACE, "ldap_memcache_destroy( 0x%x )\n",
520 cache, 0, 0 );
522 if ( !NSLDAPI_VALID_MEMCACHE_POINTER( cache )) {
523 return;
526 /* Dissociate all ldap handes from this cache. */
527 LDAP_MEMCACHE_MUTEX_LOCK( cache );
529 for (pNode = cache->ldmemc_lds; pNode; pNode = pNextNode, i++) {
530 LDAP_MUTEX_LOCK( pNode->ldmemcl_ld, LDAP_MEMCACHE_LOCK );
531 cache->ldmemc_lds = pNode->ldmemcl_next;
532 pNode->ldmemcl_ld->ld_memcache = NULL;
533 LDAP_MUTEX_UNLOCK( pNode->ldmemcl_ld, LDAP_MEMCACHE_LOCK );
534 pNextNode = pNode->ldmemcl_next;
535 NSLDAPI_FREE(pNode);
538 size += i * sizeof(ldapmemcacheld);
540 LDAP_MEMCACHE_MUTEX_UNLOCK( cache );
542 /* Free array of basedns */
543 if (cache->ldmemc_basedns) {
544 for (i = 0; cache->ldmemc_basedns[i]; i++) {
545 size += strlen(cache->ldmemc_basedns[i]) + 1;
546 NSLDAPI_FREE(cache->ldmemc_basedns[i]);
548 size += (i + 1) * sizeof(char*);
549 NSLDAPI_FREE(cache->ldmemc_basedns);
552 /* Free hash table used for temporary cache */
553 if (cache->ldmemc_resTmp) {
554 size += htable_sizeinbytes(cache->ldmemc_resTmp);
555 memcache_access(cache, MEMCACHE_ACCESS_DELETE_ALL, NULL, NULL, NULL);
556 htable_free(cache->ldmemc_resTmp);
559 /* Free hash table used for primary cache */
560 if (cache->ldmemc_resLookup) {
561 size += htable_sizeinbytes(cache->ldmemc_resLookup);
562 memcache_access(cache, MEMCACHE_ACCESS_FLUSH_ALL, NULL, NULL, NULL);
563 htable_free(cache->ldmemc_resLookup);
566 memcache_adj_size(cache, size, MEMCACHE_SIZE_NON_ENTRIES,
567 MEMCACHE_SIZE_DEDUCT);
569 LDAP_MEMCACHE_MUTEX_FREE( cache );
571 NSLDAPI_FREE(cache);
574 /************************* Internal API Functions ****************************/
576 /* Creates an integer key by applying the Cyclic Reduntency Check algorithm on
577 a long string formed by concatenating all the search parameters plus the
578 current bind DN. The key is used in the cache for looking up cached
579 entries. It is assumed that the CRC algorithm will generate
580 different integers from different byte strings. */
582 ldap_memcache_createkey(LDAP *ld, const char *base, int scope,
583 const char *filter, char **attrs,
584 int attrsonly, LDAPControl **serverctrls,
585 LDAPControl **clientctrls, unsigned long *keyp)
587 int nRes, i, j, i_smallest;
588 int len;
589 int defport;
590 char buf[50];
591 char *tmp, *defhost, *binddn, *keystr, *tmpbase;
593 if ( !NSLDAPI_VALID_LDAP_POINTER( ld ) || (keyp == NULL) )
594 return( LDAP_PARAM_ERROR );
596 *keyp = 0;
598 if (!memcache_exist(ld))
599 return( LDAP_LOCAL_ERROR );
601 LDAP_MUTEX_LOCK( ld, LDAP_MEMCACHE_LOCK );
602 LDAP_MEMCACHE_MUTEX_LOCK( ld->ld_memcache );
603 nRes = memcache_validate_basedn(ld->ld_memcache, base);
604 LDAP_MEMCACHE_MUTEX_UNLOCK( ld->ld_memcache );
605 LDAP_MUTEX_UNLOCK( ld, LDAP_MEMCACHE_LOCK );
607 if (nRes != LDAP_SUCCESS)
608 return nRes;
610 defhost = NSLDAPI_STR_NONNULL(ld->ld_defhost);
611 defport = ld->ld_defport;
612 tmpbase = nsldapi_strdup(NSLDAPI_STR_NONNULL(base));
613 if (tmpbase == NULL)
614 return( LDAP_LOCAL_ERROR );
615 memcache_trim_basedn_spaces(tmpbase);
617 if ((binddn = nsldapi_get_binddn(ld)) == NULL)
618 binddn = "";
620 sprintf(buf, "%i\n%i\n%i\n", defport, scope, (attrsonly ? 1 : 0));
621 len = NSLDAPI_SAFE_STRLEN(buf) + NSLDAPI_SAFE_STRLEN(tmpbase) +
622 NSLDAPI_SAFE_STRLEN(filter) + NSLDAPI_SAFE_STRLEN(defhost) +
623 NSLDAPI_SAFE_STRLEN(binddn);
625 if (attrs) {
626 for (i = 0; attrs[i]; i++) {
628 for (i_smallest = j = i; attrs[j]; j++) {
629 if (strcasecmp(attrs[i_smallest], attrs[j]) > 0)
630 i_smallest = j;
633 if (i != i_smallest) {
634 tmp = attrs[i];
635 attrs[i] = attrs[i_smallest];
636 attrs[i_smallest] = tmp;
639 len += NSLDAPI_SAFE_STRLEN(attrs[i]);
641 } else {
642 len += 1;
645 len += memcache_get_ctrls_len(serverctrls) +
646 memcache_get_ctrls_len(clientctrls) + 1;
648 if ((keystr = (char*)NSLDAPI_CALLOC(len, sizeof(char))) == NULL) {
649 if (defhost != emptyStr)
650 NSLDAPI_FREE(defhost);
651 NSLDAPI_FREE(tmpbase);
652 return( LDAP_NO_MEMORY );
655 sprintf(keystr, "%s\n%s\n%s\n%s\n%s\n", binddn, tmpbase,
656 NSLDAPI_STR_NONNULL(defhost), NSLDAPI_STR_NONNULL(filter),
657 NSLDAPI_STR_NONNULL(buf));
659 if (attrs) {
660 for (i = 0; attrs[i]; i++) {
661 strcat(keystr, NSLDAPI_STR_NONNULL(attrs[i]));
662 strcat(keystr, "\n");
664 } else {
665 strcat(keystr, "\n");
668 for (tmp = keystr; *tmp;
669 *tmp += (*tmp >= 'a' && *tmp <= 'z' ? 'A'-'a' : 0), tmp++) {
673 memcache_append_ctrls(keystr, serverctrls, clientctrls);
675 /* CRC algorithm */
676 *keyp = crc32_convert(keystr, len);
678 NSLDAPI_FREE(keystr);
679 NSLDAPI_FREE(tmpbase);
681 return LDAP_SUCCESS;
684 /* Searches the cache for the right cached entries, and if found, attaches
685 them to the given ldap handle. This function relies on locking by the
686 caller. */
688 ldap_memcache_result(LDAP *ld, int msgid, unsigned long key)
690 int nRes;
691 LDAPMessage *pMsg = NULL;
693 LDAPDebug( LDAP_DEBUG_TRACE,
694 "ldap_memcache_result( ld: 0x%x, msgid: %d, key: 0x%8.8lx)\n",
695 ld, msgid, key );
697 if ( !NSLDAPI_VALID_LDAP_POINTER( ld ) || (msgid < 0) ) {
698 return( LDAP_PARAM_ERROR );
701 if (!memcache_exist(ld)) {
702 return( LDAP_LOCAL_ERROR );
705 LDAP_MUTEX_LOCK( ld, LDAP_MEMCACHE_LOCK );
706 LDAP_MEMCACHE_MUTEX_LOCK( ld->ld_memcache );
708 /* Search the cache and append the results to ld if found */
709 ++ld->ld_memcache->ldmemc_stats.ldmemcstat_tries;
710 if ((nRes = memcache_search(ld, key, &pMsg)) == LDAP_SUCCESS) {
711 nRes = memcache_add_to_ld(ld, msgid, pMsg);
712 ++ld->ld_memcache->ldmemc_stats.ldmemcstat_hits;
713 LDAPDebug( LDAP_DEBUG_TRACE,
714 "ldap_memcache_result: key 0x%8.8lx found in cache\n",
715 key, 0, 0 );
716 } else {
717 LDAPDebug( LDAP_DEBUG_TRACE,
718 "ldap_memcache_result: key 0x%8.8lx not found in cache\n",
719 key, 0, 0 );
722 #ifdef LDAP_DEBUG
723 memcache_print_list( ld->ld_memcache, LIST_LRU );
724 memcache_report_statistics( ld->ld_memcache );
725 #endif /* LDAP_DEBUG */
727 LDAP_MEMCACHE_MUTEX_UNLOCK( ld->ld_memcache );
728 LDAP_MUTEX_UNLOCK( ld, LDAP_MEMCACHE_LOCK );
730 return nRes;
733 /* Creates a new header in the cache so that entries arriving from the
734 directory server can later be cached under the header. */
736 ldap_memcache_new(LDAP *ld, int msgid, unsigned long key, const char *basedn)
738 int nRes;
740 if ( !NSLDAPI_VALID_LDAP_POINTER( ld ) ) {
741 return( LDAP_PARAM_ERROR );
744 LDAP_MUTEX_LOCK( ld, LDAP_MEMCACHE_LOCK );
746 if (!memcache_exist(ld)) {
747 LDAP_MUTEX_UNLOCK( ld, LDAP_MEMCACHE_LOCK );
748 return( LDAP_LOCAL_ERROR );
751 LDAP_MEMCACHE_MUTEX_LOCK( ld->ld_memcache );
752 nRes = memcache_add(ld, key, msgid, basedn);
753 LDAP_MEMCACHE_MUTEX_UNLOCK( ld->ld_memcache );
754 LDAP_MUTEX_UNLOCK( ld, LDAP_MEMCACHE_LOCK );
756 return nRes;
759 /* Appends a chain of entries to an existing cache header. Parameter "bLast"
760 indicates whether there will be more entries arriving for the search in
761 question. */
763 ldap_memcache_append(LDAP *ld, int msgid, int bLast, LDAPMessage *result)
765 int nRes = LDAP_SUCCESS;
767 LDAPDebug( LDAP_DEBUG_TRACE, "ldap_memcache_append( ld: 0x%x, ", ld, 0, 0 );
768 LDAPDebug( LDAP_DEBUG_TRACE, "msgid %d, bLast: %d, result: 0x%x)\n",
769 msgid, bLast, result );
771 if ( !NSLDAPI_VALID_LDAP_POINTER( ld ) || !result ) {
772 return( LDAP_PARAM_ERROR );
775 LDAP_MUTEX_LOCK( ld, LDAP_MEMCACHE_LOCK );
777 if (!memcache_exist(ld)) {
778 LDAP_MUTEX_UNLOCK( ld, LDAP_MEMCACHE_LOCK );
779 return( LDAP_LOCAL_ERROR );
782 LDAP_MEMCACHE_MUTEX_LOCK( ld->ld_memcache );
784 if (!bLast)
785 nRes = memcache_append(ld, msgid, result);
786 else
787 nRes = memcache_append_last(ld, msgid, result);
789 LDAPDebug( LDAP_DEBUG_TRACE,
790 "ldap_memcache_append: %s result for msgid %d\n",
791 ( nRes == LDAP_SUCCESS ) ? "added" : "failed to add", msgid , 0 );
793 LDAP_MEMCACHE_MUTEX_UNLOCK( ld->ld_memcache );
794 LDAP_MUTEX_UNLOCK( ld, LDAP_MEMCACHE_LOCK );
796 return nRes;
799 /* Removes partially cached results for a search as a result of calling
800 ldap_abandon() by the client. */
802 ldap_memcache_abandon(LDAP *ld, int msgid)
804 int nRes;
806 if ( !NSLDAPI_VALID_LDAP_POINTER( ld ) || (msgid < 0) ) {
807 return( LDAP_PARAM_ERROR );
810 LDAP_MUTEX_LOCK( ld, LDAP_MEMCACHE_LOCK );
812 if (!memcache_exist(ld)) {
813 LDAP_MUTEX_UNLOCK( ld, LDAP_MEMCACHE_LOCK );
814 return( LDAP_LOCAL_ERROR );
817 LDAP_MEMCACHE_MUTEX_LOCK( ld->ld_memcache );
818 nRes = memcache_remove(ld, msgid);
819 LDAP_MEMCACHE_MUTEX_UNLOCK( ld->ld_memcache );
820 LDAP_MUTEX_UNLOCK( ld, LDAP_MEMCACHE_LOCK );
822 return nRes;
825 /*************************** helper functions *******************************/
827 /* Removes extraneous spaces in a basedn so that basedns differ by only those
828 spaces will be treated as equal. Extraneous spaces are those that
829 precedes the basedn and those that follow a comma. */
831 * XXXmcs: this is a bit too agressive... we need to deal with the fact that
832 * commas and spaces may be quoted, in which case it is wrong to remove them.
834 static void
835 memcache_trim_basedn_spaces(char *basedn)
837 char *pRead, *pWrite;
839 if (!basedn)
840 return;
842 for (pWrite = pRead = basedn; *pRead; ) {
843 for (; *pRead && NSLDAPI_IS_SPACE(*pRead); pRead++) {
846 for (; *pRead && !NSLDAPI_IS_SEPARATER(*pRead);
847 *(pWrite++) = *(pRead++)) {
850 *(pWrite++) = (*pRead ? *(pRead++) : *pRead);
854 /* Verifies whether the results of a search should be cached or not by
855 checking if the search's basedn falls under any of the basedns for which
856 the memcache is responsible. */
857 static int
858 memcache_validate_basedn(LDAPMemCache *cache, const char *basedn)
860 int i;
862 if ( cache->ldmemc_basedns == NULL ) {
863 return( LDAP_SUCCESS );
866 #if 1
867 if (basedn == NULL) {
868 basedn = "";
870 #else
871 /* XXXmcs: I do not understand this code... */
872 if (basedn == NULL)
873 return (cache->ldmemc_basedns && cache->ldmemc_basedns[0] ?
874 LDAP_OPERATIONS_ERROR : LDAP_SUCCESS);
876 #endif
878 for (i = 0; cache->ldmemc_basedns[i]; i++) {
879 if (memcache_compare_dn(basedn, cache->ldmemc_basedns[i],
880 LDAP_SCOPE_SUBTREE) == LDAP_COMPARE_TRUE) {
881 return( LDAP_SUCCESS );
885 return( LDAP_OPERATIONS_ERROR );
888 /* Calculates the length of the buffer needed to concatenate the contents of
889 a ldap control. */
890 static int
891 memcache_get_ctrls_len(LDAPControl **ctrls)
893 int len = 0, i;
895 if (ctrls) {
896 for (i = 0; ctrls[i]; i++) {
897 len += strlen(NSLDAPI_STR_NONNULL(ctrls[i]->ldctl_oid)) +
898 (ctrls[i]->ldctl_value).bv_len + 4;
902 return len;
905 /* Contenates the contents of client and server controls to a buffer. */
906 static void
907 memcache_append_ctrls(char *buf, LDAPControl **serverCtrls,
908 LDAPControl **clientCtrls)
910 int i, j;
911 char *pCh = buf + strlen(buf);
912 LDAPControl **ctrls;
914 for (j = 0; j < 2; j++) {
916 if ((ctrls = (j ? clientCtrls : serverCtrls)) == NULL)
917 continue;
919 for (i = 0; ctrls[i]; i++) {
920 sprintf(pCh, "%s\n", NSLDAPI_STR_NONNULL(ctrls[i]->ldctl_oid));
921 pCh += strlen(NSLDAPI_STR_NONNULL(ctrls[i]->ldctl_oid)) + 1;
922 if ((ctrls[i]->ldctl_value).bv_len > 0) {
923 memcpy(pCh, (ctrls[i]->ldctl_value).bv_val,
924 (ctrls[i]->ldctl_value).bv_len);
925 pCh += (ctrls[i]->ldctl_value).bv_len;
927 sprintf(pCh, "\n%i\n", (ctrls[i]->ldctl_iscritical ? 1 : 0));
928 pCh += 3;
933 /* Increases or decreases the size (in bytes) the given memcache currently
934 uses. If the size goes over the limit, the function returns an error. */
935 static int
936 memcache_adj_size(LDAPMemCache *cache, unsigned long size,
937 int usageFlags, int bAdd)
939 LDAPDebug( LDAP_DEBUG_TRACE,
940 "memcache_adj_size: attempting to %s %ld %s bytes...\n",
941 bAdd ? "add" : "remove", size,
942 ( usageFlags & MEMCACHE_SIZE_ENTRIES ) ? "entry" : "non-entry" );
944 if (bAdd) {
945 cache->ldmemc_size_used += size;
946 if ((cache->ldmemc_size > 0) &&
947 (cache->ldmemc_size_used > cache->ldmemc_size)) {
949 if (size > cache->ldmemc_size_entries) {
950 cache->ldmemc_size_used -= size;
951 LDAPDebug( LDAP_DEBUG_TRACE,
952 "memcache_adj_size: failed (size > size_entries %ld).\n",
953 cache->ldmemc_size_entries, 0, 0 );
954 return( LDAP_SIZELIMIT_EXCEEDED );
957 while (cache->ldmemc_size_used > cache->ldmemc_size) {
958 if (memcache_access(cache, MEMCACHE_ACCESS_FLUSH_LRU,
959 NULL, NULL, NULL) != LDAP_SUCCESS) {
960 cache->ldmemc_size_used -= size;
961 LDAPDebug( LDAP_DEBUG_TRACE,
962 "memcache_adj_size: failed (LRU flush failed).\n",
963 0, 0, 0 );
964 return( LDAP_SIZELIMIT_EXCEEDED );
968 if (usageFlags & MEMCACHE_SIZE_ENTRIES)
969 cache->ldmemc_size_entries += size;
970 } else {
971 cache->ldmemc_size_used -= size;
972 assert(cache->ldmemc_size_used >= 0);
973 if (usageFlags & MEMCACHE_SIZE_ENTRIES)
974 cache->ldmemc_size_entries -= size;
977 #ifdef LDAP_DEBUG
978 if ( cache->ldmemc_size == 0 ) { /* no size limit */
979 LDAPDebug( LDAP_DEBUG_TRACE,
980 "memcache_adj_size: succeeded (new size: %ld bytes).\n",
981 cache->ldmemc_size_used, 0, 0 );
982 } else {
983 LDAPDebug( LDAP_DEBUG_TRACE,
984 "memcache_adj_size: succeeded (new size: %ld bytes, "
985 "free space: %ld bytes).\n", cache->ldmemc_size_used,
986 cache->ldmemc_size - cache->ldmemc_size_used, 0 );
988 #endif /* LDAP_DEBUG */
990 return( LDAP_SUCCESS );
993 /* Searches the cache for results for a particular search identified by
994 parameter "key", which was generated ldap_memcache_createkey(). */
995 static int
996 memcache_search(LDAP *ld, unsigned long key, LDAPMessage **ppRes)
998 int nRes;
999 ldapmemcacheRes *pRes;
1001 *ppRes = NULL;
1003 if (!memcache_exist(ld))
1004 return LDAP_LOCAL_ERROR;
1006 nRes = memcache_access(ld->ld_memcache, MEMCACHE_ACCESS_FIND,
1007 (void*)&key, (void*)(&pRes), NULL);
1009 if (nRes != LDAP_SUCCESS)
1010 return nRes;
1012 *ppRes = pRes->ldmemcr_resHead;
1013 assert((pRes->ldmemcr_req_id).ldmemcrid_msgid == -1);
1015 return( LDAP_SUCCESS );
1018 /* Adds a new header into the cache as a place holder for entries
1019 arriving later. */
1020 static int
1021 memcache_add(LDAP *ld, unsigned long key, int msgid,
1022 const char *basedn)
1024 ldapmemcacheReqId reqid;
1026 if (!memcache_exist(ld))
1027 return LDAP_LOCAL_ERROR;
1029 reqid.ldmemcrid_msgid = msgid;
1030 reqid.ldmemcrid_ld = ld;
1032 return memcache_access(ld->ld_memcache, MEMCACHE_ACCESS_ADD,
1033 (void*)&key, (void*)&reqid, (void*)basedn);
1036 /* Appends search entries arriving from the dir server to the cache. */
1037 static int
1038 memcache_append(LDAP *ld, int msgid, LDAPMessage *pRes)
1040 ldapmemcacheReqId reqid;
1042 if (!memcache_exist(ld))
1043 return LDAP_LOCAL_ERROR;
1045 reqid.ldmemcrid_msgid = msgid;
1046 reqid.ldmemcrid_ld = ld;
1048 return memcache_access(ld->ld_memcache, MEMCACHE_ACCESS_APPEND,
1049 (void*)&reqid, (void*)pRes, NULL);
1052 /* Same as memcache_append(), but the entries being appended are the
1053 last from the dir server. Once all entries for a search have arrived,
1054 the entries are moved from secondary to primary cache, and a time
1055 stamp is given to the entries. */
1056 static int
1057 memcache_append_last(LDAP *ld, int msgid, LDAPMessage *pRes)
1059 ldapmemcacheReqId reqid;
1061 if (!memcache_exist(ld))
1062 return LDAP_LOCAL_ERROR;
1064 reqid.ldmemcrid_msgid = msgid;
1065 reqid.ldmemcrid_ld = ld;
1067 return memcache_access(ld->ld_memcache, MEMCACHE_ACCESS_APPEND_LAST,
1068 (void*)&reqid, (void*)pRes, NULL);
1071 /* Removes entries from the temporary cache. */
1072 static int
1073 memcache_remove(LDAP *ld, int msgid)
1075 ldapmemcacheReqId reqid;
1077 if (!memcache_exist(ld))
1078 return LDAP_LOCAL_ERROR;
1080 reqid.ldmemcrid_msgid = msgid;
1081 reqid.ldmemcrid_ld = ld;
1083 return memcache_access(ld->ld_memcache, MEMCACHE_ACCESS_DELETE,
1084 (void*)&reqid, NULL, NULL);
1087 #if 0 /* this function is not used */
1088 /* Wipes out everything in the temporary cache directory. */
1089 static int
1090 memcache_remove_all(LDAP *ld)
1092 if (!memcache_exist(ld))
1093 return LDAP_LOCAL_ERROR;
1095 return memcache_access(ld->ld_memcache, MEMCACHE_ACCESS_DELETE_ALL,
1096 NULL, NULL, NULL);
1098 #endif /* 0 */
1100 /* Returns TRUE or FALSE */
1101 static int
1102 memcache_exist(LDAP *ld)
1104 return (ld->ld_memcache != NULL);
1107 /* Attaches cached entries to an ldap handle. */
1108 static int
1109 memcache_add_to_ld(LDAP *ld, int msgid, LDAPMessage *pMsg)
1111 int nRes = LDAP_SUCCESS;
1112 LDAPMessage **r;
1113 LDAPMessage *pCopy;
1115 nRes = memcache_dup_message(pMsg, msgid, 1, &pCopy, NULL);
1116 if (nRes != LDAP_SUCCESS)
1117 return nRes;
1119 for (r = &(ld->ld_responses); *r; r = &((*r)->lm_next))
1120 if ((*r)->lm_msgid == msgid)
1121 break;
1123 if (*r)
1124 for (r = &((*r)->lm_chain); *r; r = &((*r)->lm_chain)) {
1128 *r = pCopy;
1130 return nRes;
1133 /* Check if main_dn is included in {dn, scope} */
1134 static int
1135 memcache_compare_dn(const char *main_dn, const char *dn, int scope)
1137 int nRes;
1138 char **components = NULL;
1139 char **main_components = NULL;
1141 components = ldap_explode_dn(dn, 0);
1142 main_components = ldap_explode_dn(main_dn, 0);
1144 if (!components || !main_components) {
1145 nRes = LDAP_COMPARE_TRUE;
1147 else {
1149 int i, main_i;
1151 main_i = ldap_count_values(main_components) - 1;
1152 i = ldap_count_values(components) - 1;
1154 for (; i >= 0 && main_i >= 0; i--, main_i--) {
1155 if (strcasecmp(main_components[main_i], components[i]))
1156 break;
1159 if (i >= 0 && main_i >= 0) {
1160 nRes = LDAP_COMPARE_FALSE;
1162 else if (i < 0 && main_i < 0) {
1163 if (scope != LDAP_SCOPE_ONELEVEL)
1164 nRes = LDAP_COMPARE_TRUE;
1165 else
1166 nRes = LDAP_COMPARE_FALSE;
1168 else if (main_i < 0) {
1169 nRes = LDAP_COMPARE_FALSE;
1171 else {
1172 if (scope == LDAP_SCOPE_BASE)
1173 nRes = LDAP_COMPARE_FALSE;
1174 else if (scope == LDAP_SCOPE_SUBTREE)
1175 nRes = LDAP_COMPARE_TRUE;
1176 else if (main_i == 0)
1177 nRes = LDAP_COMPARE_TRUE;
1178 else
1179 nRes = LDAP_COMPARE_FALSE;
1183 if (components)
1184 ldap_value_free(components);
1186 if (main_components)
1187 ldap_value_free(main_components);
1189 return nRes;
1192 /* Dup a complete separate copy of a berelement, including the buffers
1193 the berelement points to. */
1194 static BerElement*
1195 memcache_ber_dup(BerElement* pBer, unsigned long *pSize)
1197 BerElement *p = ber_dup(pBer);
1199 *pSize = 0;
1201 if (p) {
1203 *pSize += sizeof(BerElement) + EXTRA_SIZE;
1205 if (p->ber_len <= EXTRA_SIZE) {
1206 p->ber_flags |= LBER_FLAG_NO_FREE_BUFFER;
1207 p->ber_buf = (char*)p + sizeof(BerElement);
1208 } else {
1209 p->ber_flags &= ~LBER_FLAG_NO_FREE_BUFFER;
1210 p->ber_buf = (char*)NSLDAPI_CALLOC(1, p->ber_len);
1211 *pSize += (p->ber_buf ? p->ber_len : 0);
1214 if (p->ber_buf) {
1215 p->ber_ptr = p->ber_buf + (pBer->ber_ptr - pBer->ber_buf);
1216 p->ber_end = p->ber_buf + p->ber_len;
1217 memcpy(p->ber_buf, pBer->ber_buf, p->ber_len);
1218 } else {
1219 ber_free(p, 0);
1220 p = NULL;
1221 *pSize = 0;
1225 return p;
1228 /* Dup a entry or a chain of entries. */
1229 static int
1230 memcache_dup_message(LDAPMessage *res, int msgid, int fromcache,
1231 LDAPMessage **ppResCopy, unsigned long *pSize)
1233 int nRes = LDAP_SUCCESS;
1234 unsigned long ber_size;
1235 LDAPMessage *pCur;
1236 LDAPMessage **ppCurNew;
1238 *ppResCopy = NULL;
1240 if (pSize)
1241 *pSize = 0;
1243 /* Make a copy of res */
1244 for (pCur = res, ppCurNew = ppResCopy; pCur;
1245 pCur = pCur->lm_chain, ppCurNew = &((*ppCurNew)->lm_chain)) {
1247 if ((*ppCurNew = (LDAPMessage*)NSLDAPI_CALLOC(1,
1248 sizeof(LDAPMessage))) == NULL) {
1249 nRes = LDAP_NO_MEMORY;
1250 break;
1253 memcpy(*ppCurNew, pCur, sizeof(LDAPMessage));
1254 (*ppCurNew)->lm_next = NULL;
1255 (*ppCurNew)->lm_ber = memcache_ber_dup(pCur->lm_ber, &ber_size);
1256 (*ppCurNew)->lm_msgid = msgid;
1257 (*ppCurNew)->lm_fromcache = (fromcache != 0);
1259 if (pSize)
1260 *pSize += sizeof(LDAPMessage) + ber_size;
1263 if ((nRes != LDAP_SUCCESS) && (*ppResCopy != NULL)) {
1264 ldap_msgfree(*ppResCopy);
1265 *ppResCopy = NULL;
1266 if (pSize)
1267 *pSize = 0;
1270 return nRes;
1273 /************************* Cache Functions ***********************/
1275 /* Frees a cache header. */
1276 static int
1277 memcache_free_entry(LDAPMemCache *cache, ldapmemcacheRes *pRes)
1279 if (pRes) {
1281 unsigned long size = sizeof(ldapmemcacheRes);
1283 if (pRes->ldmemcr_basedn) {
1284 size += strlen(pRes->ldmemcr_basedn) + 1;
1285 NSLDAPI_FREE(pRes->ldmemcr_basedn);
1288 if (pRes->ldmemcr_resHead) {
1289 size += pRes->ldmemcr_resSize;
1290 ldap_msgfree(pRes->ldmemcr_resHead);
1293 NSLDAPI_FREE(pRes);
1295 memcache_adj_size(cache, size, MEMCACHE_SIZE_ENTRIES,
1296 MEMCACHE_SIZE_DEDUCT);
1299 return( LDAP_SUCCESS );
1302 /* Detaches a cache header from the list of headers. */
1303 static int
1304 memcache_free_from_list(LDAPMemCache *cache, ldapmemcacheRes *pRes, int index)
1306 if (pRes->ldmemcr_prev[index])
1307 pRes->ldmemcr_prev[index]->ldmemcr_next[index] =
1308 pRes->ldmemcr_next[index];
1310 if (pRes->ldmemcr_next[index])
1311 pRes->ldmemcr_next[index]->ldmemcr_prev[index] =
1312 pRes->ldmemcr_prev[index];
1314 if (cache->ldmemc_resHead[index] == pRes)
1315 cache->ldmemc_resHead[index] = pRes->ldmemcr_next[index];
1317 if (cache->ldmemc_resTail[index] == pRes)
1318 cache->ldmemc_resTail[index] = pRes->ldmemcr_prev[index];
1320 pRes->ldmemcr_prev[index] = NULL;
1321 pRes->ldmemcr_next[index] = NULL;
1323 return( LDAP_SUCCESS );
1326 /* Inserts a new cache header to a list of headers. */
1327 static int
1328 memcache_add_to_list(LDAPMemCache *cache, ldapmemcacheRes *pRes, int index)
1330 if (cache->ldmemc_resHead[index])
1331 cache->ldmemc_resHead[index]->ldmemcr_prev[index] = pRes;
1332 else
1333 cache->ldmemc_resTail[index] = pRes;
1335 pRes->ldmemcr_prev[index] = NULL;
1336 pRes->ldmemcr_next[index] = cache->ldmemc_resHead[index];
1337 cache->ldmemc_resHead[index] = pRes;
1339 return( LDAP_SUCCESS );
1342 /* Appends a chain of entries to the given cache header. */
1343 static int
1344 memcache_add_res_to_list(ldapmemcacheRes *pRes, LDAPMessage *pMsg,
1345 unsigned long size)
1347 if (pRes->ldmemcr_resTail)
1348 pRes->ldmemcr_resTail->lm_chain = pMsg;
1349 else
1350 pRes->ldmemcr_resHead = pMsg;
1352 for (pRes->ldmemcr_resTail = pMsg;
1353 pRes->ldmemcr_resTail->lm_chain;
1354 pRes->ldmemcr_resTail = pRes->ldmemcr_resTail->lm_chain) {
1358 pRes->ldmemcr_resSize += size;
1360 return( LDAP_SUCCESS );
1364 #ifdef LDAP_DEBUG
1365 static void
1366 memcache_print_list( LDAPMemCache *cache, int index )
1368 char *name;
1369 ldapmemcacheRes *restmp;
1371 switch( index ) {
1372 case LIST_TTL:
1373 name = "TTL";
1374 break;
1375 case LIST_LRU:
1376 name = "LRU";
1377 break;
1378 case LIST_TMP:
1379 name = "TMP";
1380 break;
1381 case LIST_TOTAL:
1382 name = "TOTAL";
1383 break;
1384 default:
1385 name = "unknown";
1388 LDAPDebug( LDAP_DEBUG_TRACE, "memcache 0x%x %s list:\n",
1389 cache, name, 0 );
1390 for ( restmp = cache->ldmemc_resHead[index]; restmp != NULL;
1391 restmp = restmp->ldmemcr_next[index] ) {
1392 LDAPDebug( LDAP_DEBUG_TRACE,
1393 " key: 0x%8.8lx, ld: 0x%x, msgid: %d\n",
1394 restmp->ldmemcr_crc_key,
1395 restmp->ldmemcr_req_id.ldmemcrid_ld,
1396 restmp->ldmemcr_req_id.ldmemcrid_msgid );
1398 LDAPDebug( LDAP_DEBUG_TRACE, "memcache 0x%x end of %s list.\n",
1399 cache, name, 0 );
1401 #endif /* LDAP_DEBUG */
1403 /* Tells whether a cached result has expired. */
1404 static int
1405 memcache_expired(LDAPMemCache *cache, ldapmemcacheRes *pRes,
1406 unsigned long curTime)
1408 if (!cache->ldmemc_ttl)
1409 return 0;
1411 return ((unsigned long)difftime(
1412 (time_t)curTime,
1413 (time_t)(pRes->ldmemcr_timestamp)) >=
1414 cache->ldmemc_ttl);
1417 /* Operates the cache in a central place. */
1418 static int
1419 memcache_access(LDAPMemCache *cache, int mode,
1420 void *pData1, void *pData2, void *pData3)
1422 int nRes = LDAP_SUCCESS;
1423 unsigned long size = 0;
1425 /* Add a new cache header to the cache. */
1426 if (mode == MEMCACHE_ACCESS_ADD) {
1427 unsigned long key = *((unsigned long*)pData1);
1428 char *basedn = (char*)pData3;
1429 ldapmemcacheRes *pRes = NULL;
1431 nRes = htable_get(cache->ldmemc_resTmp, pData2, (void**)&pRes);
1432 if (nRes == LDAP_SUCCESS)
1433 return( LDAP_ALREADY_EXISTS );
1435 pRes = (ldapmemcacheRes*)NSLDAPI_CALLOC(1, sizeof(ldapmemcacheRes));
1436 if (pRes == NULL)
1437 return( LDAP_NO_MEMORY );
1439 pRes->ldmemcr_crc_key = key;
1440 pRes->ldmemcr_req_id = *((ldapmemcacheReqId*)pData2);
1441 pRes->ldmemcr_basedn = (basedn ? nsldapi_strdup(basedn) : NULL);
1443 size += sizeof(ldapmemcacheRes) + strlen(basedn) + 1;
1444 nRes = memcache_adj_size(cache, size, MEMCACHE_SIZE_ENTRIES,
1445 MEMCACHE_SIZE_ADD);
1446 if (nRes == LDAP_SUCCESS)
1447 nRes = htable_put(cache->ldmemc_resTmp, pData2, (void*)pRes);
1448 if (nRes == LDAP_SUCCESS)
1449 memcache_add_to_list(cache, pRes, LIST_TMP);
1450 else
1451 memcache_free_entry(cache, pRes);
1453 /* Append entries to an existing cache header. */
1454 else if ((mode == MEMCACHE_ACCESS_APPEND) ||
1455 (mode == MEMCACHE_ACCESS_APPEND_LAST)) {
1457 LDAPMessage *pMsg = (LDAPMessage*)pData2;
1458 LDAPMessage *pCopy = NULL;
1459 ldapmemcacheRes *pRes = NULL;
1461 nRes = htable_get(cache->ldmemc_resTmp, pData1, (void**)&pRes);
1462 if (nRes != LDAP_SUCCESS)
1463 return nRes;
1465 nRes = memcache_dup_message(pMsg, pMsg->lm_msgid, 0, &pCopy, &size);
1466 if (nRes != LDAP_SUCCESS) {
1467 nRes = htable_remove(cache->ldmemc_resTmp, pData1, NULL);
1468 assert(nRes == LDAP_SUCCESS);
1469 memcache_free_from_list(cache, pRes, LIST_TMP);
1470 memcache_free_entry(cache, pRes);
1471 return nRes;
1474 nRes = memcache_adj_size(cache, size, MEMCACHE_SIZE_ENTRIES,
1475 MEMCACHE_SIZE_ADD);
1476 if (nRes != LDAP_SUCCESS) {
1477 ldap_msgfree(pCopy);
1478 nRes = htable_remove(cache->ldmemc_resTmp, pData1, NULL);
1479 assert(nRes == LDAP_SUCCESS);
1480 memcache_free_from_list(cache, pRes, LIST_TMP);
1481 memcache_free_entry(cache, pRes);
1482 return nRes;
1485 memcache_add_res_to_list(pRes, pCopy, size);
1487 if (mode == MEMCACHE_ACCESS_APPEND)
1488 return( LDAP_SUCCESS );
1490 nRes = htable_remove(cache->ldmemc_resTmp, pData1, NULL);
1491 assert(nRes == LDAP_SUCCESS);
1492 memcache_free_from_list(cache, pRes, LIST_TMP);
1493 (pRes->ldmemcr_req_id).ldmemcrid_ld = NULL;
1494 (pRes->ldmemcr_req_id).ldmemcrid_msgid = -1;
1495 pRes->ldmemcr_timestamp = (unsigned long)time(NULL);
1497 if ((nRes = htable_put(cache->ldmemc_resLookup,
1498 (void*)&(pRes->ldmemcr_crc_key),
1499 (void*)pRes)) == LDAP_SUCCESS) {
1500 memcache_add_to_list(cache, pRes, LIST_TTL);
1501 memcache_add_to_list(cache, pRes, LIST_LRU);
1502 } else {
1503 memcache_free_entry(cache, pRes);
1506 /* Search for cached entries for a particular search. */
1507 else if (mode == MEMCACHE_ACCESS_FIND) {
1509 ldapmemcacheRes **ppRes = (ldapmemcacheRes**)pData2;
1511 nRes = htable_get(cache->ldmemc_resLookup, pData1, (void**)ppRes);
1512 if (nRes != LDAP_SUCCESS)
1513 return nRes;
1515 if (!memcache_expired(cache, *ppRes, (unsigned long)time(0))) {
1516 memcache_free_from_list(cache, *ppRes, LIST_LRU);
1517 memcache_add_to_list(cache, *ppRes, LIST_LRU);
1518 return( LDAP_SUCCESS );
1521 nRes = htable_remove(cache->ldmemc_resLookup, pData1, NULL);
1522 assert(nRes == LDAP_SUCCESS);
1523 memcache_free_from_list(cache, *ppRes, LIST_TTL);
1524 memcache_free_from_list(cache, *ppRes, LIST_LRU);
1525 memcache_free_entry(cache, *ppRes);
1526 nRes = LDAP_NO_SUCH_OBJECT;
1527 *ppRes = NULL;
1529 /* Remove cached entries in the temporary cache. */
1530 else if (mode == MEMCACHE_ACCESS_DELETE) {
1532 ldapmemcacheRes *pCurRes = NULL;
1534 if ((nRes = htable_remove(cache->ldmemc_resTmp, pData1,
1535 (void**)&pCurRes)) == LDAP_SUCCESS) {
1536 memcache_free_from_list(cache, pCurRes, LIST_TMP);
1537 memcache_free_entry(cache, pCurRes);
1540 /* Wipe out the temporary cache. */
1541 else if (mode == MEMCACHE_ACCESS_DELETE_ALL) {
1543 nRes = htable_removeall(cache->ldmemc_resTmp, (void*)cache);
1545 /* Remove expired entries from primary cache. */
1546 else if (mode == MEMCACHE_ACCESS_UPDATE) {
1548 ldapmemcacheRes *pCurRes = cache->ldmemc_resTail[LIST_TTL];
1549 unsigned long curTime = (unsigned long)time(NULL);
1551 for (; pCurRes; pCurRes = cache->ldmemc_resTail[LIST_TTL]) {
1553 if (!memcache_expired(cache, pCurRes, curTime))
1554 break;
1556 nRes = htable_remove(cache->ldmemc_resLookup,
1557 (void*)&(pCurRes->ldmemcr_crc_key), NULL);
1558 assert(nRes == LDAP_SUCCESS);
1559 memcache_free_from_list(cache, pCurRes, LIST_TTL);
1560 memcache_free_from_list(cache, pCurRes, LIST_LRU);
1561 memcache_free_entry(cache, pCurRes);
1564 /* Wipe out the primary cache. */
1565 else if (mode == MEMCACHE_ACCESS_FLUSH_ALL) {
1567 ldapmemcacheRes *pCurRes = cache->ldmemc_resHead[LIST_TTL];
1569 nRes = htable_removeall(cache->ldmemc_resLookup, (void*)cache);
1571 for (; pCurRes; pCurRes = cache->ldmemc_resHead[LIST_TTL]) {
1572 memcache_free_from_list(cache, pCurRes, LIST_LRU);
1573 cache->ldmemc_resHead[LIST_TTL] =
1574 cache->ldmemc_resHead[LIST_TTL]->ldmemcr_next[LIST_TTL];
1575 memcache_free_entry(cache, pCurRes);
1577 cache->ldmemc_resTail[LIST_TTL] = NULL;
1579 /* Remove cached entries in both primary and temporary cache. */
1580 else if (mode == MEMCACHE_ACCESS_FLUSH) {
1582 int i, list_id, bDone;
1583 int scope = (int)(uintptr_t)pData2;
1584 char *dn = (char*)pData1;
1585 char *dnTmp;
1586 BerElement ber;
1587 LDAPMessage *pMsg;
1588 ldapmemcacheRes *pRes;
1590 if (cache->ldmemc_basedns) {
1591 for (i = 0; cache->ldmemc_basedns[i]; i++) {
1592 if ((memcache_compare_dn(cache->ldmemc_basedns[i], dn,
1593 LDAP_SCOPE_SUBTREE) == LDAP_COMPARE_TRUE) ||
1594 (memcache_compare_dn(dn, cache->ldmemc_basedns[i],
1595 LDAP_SCOPE_SUBTREE) == LDAP_COMPARE_TRUE))
1596 break;
1598 if (cache->ldmemc_basedns[i] == NULL)
1599 return( LDAP_SUCCESS );
1602 for (i = 0; i < 2; i++) {
1604 list_id = (i == 0 ? LIST_TTL : LIST_TMP);
1606 for (pRes = cache->ldmemc_resHead[list_id]; pRes != NULL;
1607 pRes = pRes->ldmemcr_next[list_id]) {
1609 if ((memcache_compare_dn(pRes->ldmemcr_basedn, dn,
1610 LDAP_SCOPE_SUBTREE) != LDAP_COMPARE_TRUE) &&
1611 (memcache_compare_dn(dn, pRes->ldmemcr_basedn,
1612 LDAP_SCOPE_SUBTREE) != LDAP_COMPARE_TRUE))
1613 continue;
1615 for (pMsg = pRes->ldmemcr_resHead, bDone = 0;
1616 !bDone && pMsg; pMsg = pMsg->lm_chain) {
1618 if (!NSLDAPI_IS_SEARCH_ENTRY( pMsg->lm_msgtype ))
1619 continue;
1621 ber = *(pMsg->lm_ber);
1622 if (ber_scanf(&ber, "{a", &dnTmp) != LBER_ERROR) {
1623 bDone = (memcache_compare_dn(dnTmp, dn, scope) ==
1624 LDAP_COMPARE_TRUE);
1625 ldap_memfree(dnTmp);
1629 if (!bDone)
1630 continue;
1632 if (list_id == LIST_TTL) {
1633 nRes = htable_remove(cache->ldmemc_resLookup,
1634 (void*)&(pRes->ldmemcr_crc_key), NULL);
1635 assert(nRes == LDAP_SUCCESS);
1636 memcache_free_from_list(cache, pRes, LIST_TTL);
1637 memcache_free_from_list(cache, pRes, LIST_LRU);
1638 } else {
1639 nRes = htable_remove(cache->ldmemc_resTmp,
1640 (void*)&(pRes->ldmemcr_req_id), NULL);
1641 assert(nRes == LDAP_SUCCESS);
1642 memcache_free_from_list(cache, pRes, LIST_TMP);
1644 memcache_free_entry(cache, pRes);
1648 /* Flush least recently used entries from cache */
1649 else if (mode == MEMCACHE_ACCESS_FLUSH_LRU) {
1651 ldapmemcacheRes *pRes = cache->ldmemc_resTail[LIST_LRU];
1653 if (pRes == NULL)
1654 return LDAP_NO_SUCH_OBJECT;
1656 LDAPDebug( LDAP_DEBUG_TRACE,
1657 "memcache_access FLUSH_LRU: removing key 0x%8.8lx\n",
1658 pRes->ldmemcr_crc_key, 0, 0 );
1659 nRes = htable_remove(cache->ldmemc_resLookup,
1660 (void*)&(pRes->ldmemcr_crc_key), NULL);
1661 assert(nRes == LDAP_SUCCESS);
1662 memcache_free_from_list(cache, pRes, LIST_TTL);
1663 memcache_free_from_list(cache, pRes, LIST_LRU);
1664 memcache_free_entry(cache, pRes);
1666 /* Unknown command */
1667 else {
1668 nRes = LDAP_PARAM_ERROR;
1671 return nRes;
1675 #ifdef LDAP_DEBUG
1676 static void
1677 memcache_report_statistics( LDAPMemCache *cache )
1679 unsigned long hitrate;
1681 if ( cache->ldmemc_stats.ldmemcstat_tries == 0 ) {
1682 hitrate = 0;
1683 } else {
1684 hitrate = ( 100L * cache->ldmemc_stats.ldmemcstat_hits ) /
1685 cache->ldmemc_stats.ldmemcstat_tries;
1687 LDAPDebug( LDAP_DEBUG_STATS, "memcache 0x%x:\n", cache, 0, 0 );
1688 LDAPDebug( LDAP_DEBUG_STATS, " tries: %ld hits: %ld hitrate: %ld%%\n",
1689 cache->ldmemc_stats.ldmemcstat_tries,
1690 cache->ldmemc_stats.ldmemcstat_hits, hitrate );
1691 if ( cache->ldmemc_size <= 0 ) { /* no size limit */
1692 LDAPDebug( LDAP_DEBUG_STATS, " memory bytes used: %ld\n",
1693 cache->ldmemc_size_used, 0, 0 );
1694 } else {
1695 LDAPDebug( LDAP_DEBUG_STATS, " memory bytes used: %ld free: %ld\n",
1696 cache->ldmemc_size_used,
1697 cache->ldmemc_size - cache->ldmemc_size_used, 0 );
1700 #endif /* LDAP_DEBUG */
1702 /************************ Hash Table Functions *****************************/
1704 /* Calculates size (# of entries) of hash table given the size limit for
1705 the cache. */
1706 static int
1707 htable_calculate_size(int sizelimit)
1709 int i, j;
1710 int size = (int)(((double)sizelimit /
1711 (double)(sizeof(BerElement) + EXTRA_SIZE)) / 1.5);
1713 /* Get a prime # */
1714 size = (size & 0x1 ? size : size + 1);
1715 for (i = 3, j = size / 2; i < j; i++) {
1716 if ((size % i) == 0) {
1717 size += 2;
1718 i = 3;
1719 j = size / 2;
1723 return size;
1726 /* Returns the size in bytes of the given hash table. */
1727 static int
1728 htable_sizeinbytes(HashTable *pTable)
1730 if (!pTable)
1731 return 0;
1733 return (pTable->size * sizeof(HashTableNode));
1736 /* Inserts an item into the hash table. */
1737 static int
1738 htable_put(HashTable *pTable, void *key, void *pData)
1740 int index = pTable->hashfunc(pTable->size, key);
1742 if (index >= 0 && index < pTable->size)
1743 return pTable->putdata(&(pTable->table[index].pData), key, pData);
1745 return( LDAP_OPERATIONS_ERROR );
1748 /* Retrieves an item from the hash table. */
1749 static int
1750 htable_get(HashTable *pTable, void *key, void **ppData)
1752 int index = pTable->hashfunc(pTable->size, key);
1754 *ppData = NULL;
1756 if (index >= 0 && index < pTable->size)
1757 return pTable->getdata(pTable->table[index].pData, key, ppData);
1759 return( LDAP_OPERATIONS_ERROR );
1762 /* Performs a miscellaneous operation on a hash table entry. */
1763 static int
1764 htable_misc(HashTable *pTable, void *key, void *pData)
1766 if (pTable->miscfunc) {
1767 int index = pTable->hashfunc(pTable->size, key);
1768 if (index >= 0 && index < pTable->size)
1769 return pTable->miscfunc(&(pTable->table[index].pData), key, pData);
1772 return( LDAP_OPERATIONS_ERROR );
1775 /* Removes an item from the hash table. */
1776 static int
1777 htable_remove(HashTable *pTable, void *key, void **ppData)
1779 int index = pTable->hashfunc(pTable->size, key);
1781 if (ppData)
1782 *ppData = NULL;
1784 if (index >= 0 && index < pTable->size)
1785 return pTable->removedata(&(pTable->table[index].pData), key, ppData);
1787 return( LDAP_OPERATIONS_ERROR );
1790 /* Removes everything in the hash table. */
1791 static int
1792 htable_removeall(HashTable *pTable, void *pData)
1794 int i;
1796 for (i = 0; i < pTable->size; i++)
1797 pTable->clrtablenode(&(pTable->table[i].pData), pData);
1799 return( LDAP_SUCCESS );
1802 /* Creates a new hash table. */
1803 static int
1804 htable_create(int size_limit, HashFuncPtr hashf,
1805 PutDataPtr putDataf, GetDataPtr getDataf,
1806 RemoveDataPtr removeDataf, ClrTableNodePtr clrNodef,
1807 MiscFuncPtr miscOpf, HashTable **ppTable)
1809 size_limit = htable_calculate_size(size_limit);
1811 if ((*ppTable = (HashTable*)NSLDAPI_CALLOC(1, sizeof(HashTable))) == NULL)
1812 return( LDAP_NO_MEMORY );
1814 (*ppTable)->table = (HashTableNode*)NSLDAPI_CALLOC(size_limit,
1815 sizeof(HashTableNode));
1816 if ((*ppTable)->table == NULL) {
1817 NSLDAPI_FREE(*ppTable);
1818 *ppTable = NULL;
1819 return( LDAP_NO_MEMORY );
1822 (*ppTable)->size = size_limit;
1823 (*ppTable)->hashfunc = hashf;
1824 (*ppTable)->putdata = putDataf;
1825 (*ppTable)->getdata = getDataf;
1826 (*ppTable)->miscfunc = miscOpf;
1827 (*ppTable)->removedata = removeDataf;
1828 (*ppTable)->clrtablenode = clrNodef;
1830 return( LDAP_SUCCESS );
1833 /* Destroys a hash table. */
1834 static int
1835 htable_free(HashTable *pTable)
1837 NSLDAPI_FREE(pTable->table);
1838 NSLDAPI_FREE(pTable);
1839 return( LDAP_SUCCESS );
1842 /**************** Hash table callbacks for temporary cache ****************/
1844 /* Hash function */
1845 static int
1846 msgid_hashf(int table_size, void *key)
1848 uint_t code = (uint_t)(uintptr_t)((ldapmemcacheReqId*)key)->ldmemcrid_ld;
1849 return (((code << 20) + (code >> 12)) % table_size);
1852 /* Called by hash table to insert an item. */
1853 static int
1854 msgid_putdata(void **ppTableData, void *key, void *pData)
1856 ldapmemcacheReqId *pReqId = (ldapmemcacheReqId*)key;
1857 ldapmemcacheRes *pRes = (ldapmemcacheRes*)pData;
1858 ldapmemcacheRes **ppHead = (ldapmemcacheRes**)ppTableData;
1859 ldapmemcacheRes *pCurRes = *ppHead;
1860 ldapmemcacheRes *pPrev = NULL;
1862 for (; pCurRes; pCurRes = pCurRes->ldmemcr_htable_next) {
1863 if ((pCurRes->ldmemcr_req_id).ldmemcrid_ld == pReqId->ldmemcrid_ld)
1864 break;
1865 pPrev = pCurRes;
1868 if (pCurRes) {
1869 for (; pCurRes; pCurRes = pCurRes->ldmemcr_next[LIST_TTL]) {
1870 if ((pCurRes->ldmemcr_req_id).ldmemcrid_msgid ==
1871 pReqId->ldmemcrid_msgid)
1872 return( LDAP_ALREADY_EXISTS );
1873 pPrev = pCurRes;
1875 pPrev->ldmemcr_next[LIST_TTL] = pRes;
1876 pRes->ldmemcr_prev[LIST_TTL] = pPrev;
1877 pRes->ldmemcr_next[LIST_TTL] = NULL;
1878 } else {
1879 if (pPrev)
1880 pPrev->ldmemcr_htable_next = pRes;
1881 else
1882 *ppHead = pRes;
1883 pRes->ldmemcr_htable_next = NULL;
1886 return( LDAP_SUCCESS );
1889 /* Called by hash table to retrieve an item. */
1890 static int
1891 msgid_getdata(void *pTableData, void *key, void **ppData)
1893 ldapmemcacheReqId *pReqId = (ldapmemcacheReqId*)key;
1894 ldapmemcacheRes *pCurRes = (ldapmemcacheRes*)pTableData;
1896 *ppData = NULL;
1898 for (; pCurRes; pCurRes = pCurRes->ldmemcr_htable_next) {
1899 if ((pCurRes->ldmemcr_req_id).ldmemcrid_ld == pReqId->ldmemcrid_ld)
1900 break;
1903 if (!pCurRes)
1904 return( LDAP_NO_SUCH_OBJECT );
1906 for (; pCurRes; pCurRes = pCurRes->ldmemcr_next[LIST_TTL]) {
1907 if ((pCurRes->ldmemcr_req_id).ldmemcrid_msgid ==
1908 pReqId->ldmemcrid_msgid) {
1909 *ppData = (void*)pCurRes;
1910 return( LDAP_SUCCESS );
1914 return( LDAP_NO_SUCH_OBJECT );
1917 /* Called by hash table to remove an item. */
1918 static int
1919 msgid_removedata(void **ppTableData, void *key, void **ppData)
1921 ldapmemcacheRes *pHead = *((ldapmemcacheRes**)ppTableData);
1922 ldapmemcacheRes *pCurRes = NULL;
1923 ldapmemcacheRes *pPrev = NULL;
1924 ldapmemcacheReqId *pReqId = (ldapmemcacheReqId*)key;
1926 if (ppData)
1927 *ppData = NULL;
1929 for (; pHead; pHead = pHead->ldmemcr_htable_next) {
1930 if ((pHead->ldmemcr_req_id).ldmemcrid_ld == pReqId->ldmemcrid_ld)
1931 break;
1932 pPrev = pHead;
1935 if (!pHead)
1936 return( LDAP_NO_SUCH_OBJECT );
1938 for (pCurRes = pHead; pCurRes; pCurRes = pCurRes->ldmemcr_next[LIST_TTL]) {
1939 if ((pCurRes->ldmemcr_req_id).ldmemcrid_msgid ==
1940 pReqId->ldmemcrid_msgid)
1941 break;
1944 if (!pCurRes)
1945 return( LDAP_NO_SUCH_OBJECT );
1947 if (ppData) {
1948 pCurRes->ldmemcr_next[LIST_TTL] = NULL;
1949 pCurRes->ldmemcr_prev[LIST_TTL] = NULL;
1950 pCurRes->ldmemcr_htable_next = NULL;
1951 *ppData = (void*)pCurRes;
1954 if (pCurRes != pHead) {
1955 if (pCurRes->ldmemcr_prev[LIST_TTL])
1956 pCurRes->ldmemcr_prev[LIST_TTL]->ldmemcr_next[LIST_TTL] =
1957 pCurRes->ldmemcr_next[LIST_TTL];
1958 if (pCurRes->ldmemcr_next[LIST_TTL])
1959 pCurRes->ldmemcr_next[LIST_TTL]->ldmemcr_prev[LIST_TTL] =
1960 pCurRes->ldmemcr_prev[LIST_TTL];
1961 return( LDAP_SUCCESS );
1964 if (pPrev) {
1965 if (pHead->ldmemcr_next[LIST_TTL]) {
1966 pPrev->ldmemcr_htable_next = pHead->ldmemcr_next[LIST_TTL];
1967 pHead->ldmemcr_next[LIST_TTL]->ldmemcr_htable_next =
1968 pHead->ldmemcr_htable_next;
1969 } else {
1970 pPrev->ldmemcr_htable_next = pHead->ldmemcr_htable_next;
1972 } else {
1973 if (pHead->ldmemcr_next[LIST_TTL]) {
1974 *((ldapmemcacheRes**)ppTableData) = pHead->ldmemcr_next[LIST_TTL];
1975 pHead->ldmemcr_next[LIST_TTL]->ldmemcr_htable_next =
1976 pHead->ldmemcr_htable_next;
1977 } else {
1978 *((ldapmemcacheRes**)ppTableData) = pHead->ldmemcr_htable_next;
1982 return( LDAP_SUCCESS );
1985 /* Called by hash table to remove all cached entries associated to searches
1986 being performed using the given ldap handle. */
1987 static int
1988 msgid_clear_ld_items(void **ppTableData, void *key, void *pData)
1990 LDAPMemCache *cache = (LDAPMemCache*)pData;
1991 ldapmemcacheRes *pHead = *((ldapmemcacheRes**)ppTableData);
1992 ldapmemcacheRes *pPrev = NULL;
1993 ldapmemcacheRes *pCurRes = NULL;
1994 ldapmemcacheReqId *pReqId = (ldapmemcacheReqId*)key;
1996 for (; pHead; pHead = pHead->ldmemcr_htable_next) {
1997 if ((pHead->ldmemcr_req_id).ldmemcrid_ld == pReqId->ldmemcrid_ld)
1998 break;
1999 pPrev = pHead;
2002 if (!pHead)
2003 return( LDAP_NO_SUCH_OBJECT );
2005 if (pPrev)
2006 pPrev->ldmemcr_htable_next = pHead->ldmemcr_htable_next;
2007 else
2008 *((ldapmemcacheRes**)ppTableData) = pHead->ldmemcr_htable_next;
2010 for (pCurRes = pHead; pHead; pCurRes = pHead) {
2011 pHead = pHead->ldmemcr_next[LIST_TTL];
2012 memcache_free_from_list(cache, pCurRes, LIST_TMP);
2013 memcache_free_entry(cache, pCurRes);
2016 return( LDAP_SUCCESS );
2019 /* Called by hash table for removing all items in the table. */
2020 static void
2021 msgid_clearnode(void **ppTableData, void *pData)
2023 LDAPMemCache *cache = (LDAPMemCache*)pData;
2024 ldapmemcacheRes **ppHead = (ldapmemcacheRes**)ppTableData;
2025 ldapmemcacheRes *pSubHead = *ppHead;
2026 ldapmemcacheRes *pCurRes = NULL;
2028 for (; *ppHead; pSubHead = *ppHead) {
2029 ppHead = &((*ppHead)->ldmemcr_htable_next);
2030 for (pCurRes = pSubHead; pSubHead; pCurRes = pSubHead) {
2031 pSubHead = pSubHead->ldmemcr_next[LIST_TTL];
2032 memcache_free_from_list(cache, pCurRes, LIST_TMP);
2033 memcache_free_entry(cache, pCurRes);
2038 /********************* Hash table for primary cache ************************/
2040 /* Hash function */
2041 static int
2042 attrkey_hashf(int table_size, void *key)
2044 return ((*((unsigned long*)key)) % table_size);
2047 /* Called by hash table to insert an item. */
2048 static int
2049 attrkey_putdata(void **ppTableData, void *key, void *pData)
2051 unsigned long attrkey = *((unsigned long*)key);
2052 ldapmemcacheRes **ppHead = (ldapmemcacheRes**)ppTableData;
2053 ldapmemcacheRes *pRes = *ppHead;
2055 for (; pRes; pRes = pRes->ldmemcr_htable_next) {
2056 if (pRes->ldmemcr_crc_key == attrkey)
2057 return( LDAP_ALREADY_EXISTS );
2060 pRes = (ldapmemcacheRes*)pData;
2061 pRes->ldmemcr_htable_next = *ppHead;
2062 *ppHead = pRes;
2064 return( LDAP_SUCCESS );
2067 /* Called by hash table to retrieve an item. */
2068 static int
2069 attrkey_getdata(void *pTableData, void *key, void **ppData)
2071 unsigned long attrkey = *((unsigned long*)key);
2072 ldapmemcacheRes *pRes = (ldapmemcacheRes*)pTableData;
2074 for (; pRes; pRes = pRes->ldmemcr_htable_next) {
2075 if (pRes->ldmemcr_crc_key == attrkey) {
2076 *ppData = (void*)pRes;
2077 return( LDAP_SUCCESS );
2081 *ppData = NULL;
2083 return( LDAP_NO_SUCH_OBJECT );
2086 /* Called by hash table to remove an item. */
2087 static int
2088 attrkey_removedata(void **ppTableData, void *key, void **ppData)
2090 unsigned long attrkey = *((unsigned long*)key);
2091 ldapmemcacheRes **ppHead = (ldapmemcacheRes**)ppTableData;
2092 ldapmemcacheRes *pRes = *ppHead;
2093 ldapmemcacheRes *pPrev = NULL;
2095 for (; pRes; pRes = pRes->ldmemcr_htable_next) {
2096 if (pRes->ldmemcr_crc_key == attrkey) {
2097 if (ppData)
2098 *ppData = (void*)pRes;
2099 if (pPrev)
2100 pPrev->ldmemcr_htable_next = pRes->ldmemcr_htable_next;
2101 else
2102 *ppHead = pRes->ldmemcr_htable_next;
2103 pRes->ldmemcr_htable_next = NULL;
2104 return( LDAP_SUCCESS );
2106 pPrev = pRes;
2109 if (ppData)
2110 *ppData = NULL;
2112 return( LDAP_NO_SUCH_OBJECT );
2115 /* Called by hash table for removing all items in the table. */
2116 static void
2117 attrkey_clearnode(void **ppTableData, void *pData)
2119 ldapmemcacheRes **ppHead = (ldapmemcacheRes**)ppTableData;
2120 ldapmemcacheRes *pRes = *ppHead;
2122 (void)pData;
2124 for (; *ppHead; pRes = *ppHead) {
2125 ppHead = &((*ppHead)->ldmemcr_htable_next);
2126 pRes->ldmemcr_htable_next = NULL;
2130 /***************************** CRC algorithm ********************************/
2132 /* From http://www.faqs.org/faqs/compression-faq/part1/section-25.html */
2135 * Build auxiliary table for parallel byte-at-a-time CRC-32.
2137 #define NSLDAPI_CRC32_POLY 0x04c11db7 /* AUTODIN II, Ethernet, & FDDI */
2139 static unsigned long crc32_table[256] = {
2140 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
2141 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
2142 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7,
2143 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
2144 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3,
2145 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
2146 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef,
2147 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
2148 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb,
2149 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
2150 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
2151 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
2152 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4,
2153 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
2154 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08,
2155 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
2156 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc,
2157 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
2158 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050,
2159 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
2160 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
2161 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
2162 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1,
2163 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
2164 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5,
2165 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
2166 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9,
2167 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
2168 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd,
2169 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
2170 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
2171 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
2172 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2,
2173 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
2174 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
2175 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
2176 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a,
2177 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
2178 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676,
2179 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
2180 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
2181 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
2182 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 };
2184 /* Initialized first time "crc32()" is called. If you prefer, you can
2185 * statically initialize it at compile time. [Another exercise.]
2188 static unsigned long
2189 crc32_convert(char *buf, int len)
2191 char *p;
2192 #ifdef OSF1V4D
2193 unsigned int crc;
2194 #else
2195 unsigned long crc; /* FIXME: this is not 32-bits on all platforms! */
2196 #endif /* OSF1V4D */
2198 crc = 0xffffffff; /* preload shift register, per CRC-32 spec */
2199 for (p = buf; len > 0; ++p, --len)
2200 crc = ((crc << 8) ^ crc32_table[(crc >> 24) ^ *p]) & 0xffffffff;
2202 return (unsigned long) ~crc; /* transmit complement, per CRC-32 spec */