+static char *
+bdb_show_key(
+ DBT *key,
+ char *buf )
+{
+ if ( key->size == 4 /* LUTIL_HASH_BYTES */ ) {
+ unsigned char *c = key->data;
+ sprintf( buf, "[%02x%02x%02x%02x]", c[0], c[1], c[2], c[3] );
+ return buf;
+ } else {
+ return key->data;
+ }
+}
+
+/* Find a db/key pair in the IDL cache. If ids is non-NULL,
+ * copy the cached IDL into it, otherwise just return the status.
+ */
+int
+bdb_idl_cache_get(
+ struct bdb_info *bdb,
+ DB *db,
+ DBT *key,
+ ID *ids )
+{
+ bdb_idl_cache_entry_t idl_tmp;
+ bdb_idl_cache_entry_t *matched_idl_entry;
+ int rc = LDAP_NO_SUCH_OBJECT;
+
+ DBT2bv( key, &idl_tmp.kstr );
+ idl_tmp.db = db;
+ ldap_pvt_thread_rdwr_rlock( &bdb->bi_idl_tree_rwlock );
+ matched_idl_entry = avl_find( bdb->bi_idl_tree, &idl_tmp,
+ bdb_idl_entry_cmp );
+ if ( matched_idl_entry != NULL ) {
+ if ( matched_idl_entry->idl && ids )
+ BDB_IDL_CPY( ids, matched_idl_entry->idl );
+ matched_idl_entry->idl_flags |= CACHE_ENTRY_REFERENCED;
+ if ( matched_idl_entry->idl )
+ rc = LDAP_SUCCESS;
+ else
+ rc = DB_NOTFOUND;
+ }
+ ldap_pvt_thread_rdwr_runlock( &bdb->bi_idl_tree_rwlock );
+
+ return rc;
+}
+
+void
+bdb_idl_cache_put(
+ struct bdb_info *bdb,
+ DB *db,
+ DBT *key,
+ ID *ids,
+ int rc )
+{
+ bdb_idl_cache_entry_t idl_tmp;
+ bdb_idl_cache_entry_t *ee, *eprev;
+
+ if ( rc == DB_NOTFOUND || BDB_IDL_IS_ZERO( ids ))
+ return;
+
+ DBT2bv( key, &idl_tmp.kstr );
+
+ ee = (bdb_idl_cache_entry_t *) ch_malloc(
+ sizeof( bdb_idl_cache_entry_t ) );
+ ee->db = db;
+ ee->idl = (ID*) ch_malloc( BDB_IDL_SIZEOF ( ids ) );
+ BDB_IDL_CPY( ee->idl, ids );
+
+ ee->idl_lru_prev = NULL;
+ ee->idl_lru_next = NULL;
+ ee->idl_flags = 0;
+ ber_dupbv( &ee->kstr, &idl_tmp.kstr );
+ ldap_pvt_thread_rdwr_wlock( &bdb->bi_idl_tree_rwlock );
+ if ( avl_insert( &bdb->bi_idl_tree, (caddr_t) ee,
+ bdb_idl_entry_cmp, avl_dup_error ))
+ {
+ ch_free( ee->kstr.bv_val );
+ ch_free( ee->idl );
+ ch_free( ee );
+ ldap_pvt_thread_rdwr_wunlock( &bdb->bi_idl_tree_rwlock );
+ return;
+ }
+ ldap_pvt_thread_mutex_lock( &bdb->bi_idl_tree_lrulock );
+ /* LRU_ADD */
+ if ( bdb->bi_idl_lru_head ) {
+ assert( bdb->bi_idl_lru_tail != NULL );
+ assert( bdb->bi_idl_lru_head->idl_lru_prev != NULL );
+ assert( bdb->bi_idl_lru_head->idl_lru_next != NULL );
+
+ ee->idl_lru_next = bdb->bi_idl_lru_head;
+ ee->idl_lru_prev = bdb->bi_idl_lru_head->idl_lru_prev;
+ bdb->bi_idl_lru_head->idl_lru_prev->idl_lru_next = ee;
+ bdb->bi_idl_lru_head->idl_lru_prev = ee;
+ } else {
+ ee->idl_lru_next = ee->idl_lru_prev = ee;
+ bdb->bi_idl_lru_tail = ee;
+ }
+ bdb->bi_idl_lru_head = ee;
+
+ if ( ++bdb->bi_idl_cache_size > bdb->bi_idl_cache_max_size ) {
+ int i;
+ ee = bdb->bi_idl_lru_tail;
+ for ( i = 0; ee != NULL && i < 10; i++, ee = eprev ) {
+ eprev = ee->idl_lru_prev;
+ if ( eprev == ee ) {
+ eprev = NULL;
+ }
+ if ( ee->idl_flags & CACHE_ENTRY_REFERENCED ) {
+ ee->idl_flags ^= CACHE_ENTRY_REFERENCED;
+ continue;
+ }
+ if ( avl_delete( &bdb->bi_idl_tree, (caddr_t) ee,
+ bdb_idl_entry_cmp ) == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "=> bdb_idl_cache_put: "
+ "AVL delete failed\n",
+ 0, 0, 0 );
+ }
+ IDL_LRU_DELETE( bdb, ee );
+ i++;
+ --bdb->bi_idl_cache_size;
+ ch_free( ee->kstr.bv_val );
+ ch_free( ee->idl );
+ ch_free( ee );
+ }
+ bdb->bi_idl_lru_tail = eprev;
+ assert( bdb->bi_idl_lru_tail != NULL
+ || bdb->bi_idl_lru_head == NULL );
+ }
+ ldap_pvt_thread_mutex_unlock( &bdb->bi_idl_tree_lrulock );
+ ldap_pvt_thread_rdwr_wunlock( &bdb->bi_idl_tree_rwlock );
+}
+
+void
+bdb_idl_cache_del(
+ struct bdb_info *bdb,
+ DB *db,
+ DBT *key )
+{
+ bdb_idl_cache_entry_t *matched_idl_entry, idl_tmp;
+ DBT2bv( key, &idl_tmp.kstr );
+ idl_tmp.db = db;
+ ldap_pvt_thread_rdwr_wlock( &bdb->bi_idl_tree_rwlock );
+ matched_idl_entry = avl_find( bdb->bi_idl_tree, &idl_tmp,
+ bdb_idl_entry_cmp );
+ if ( matched_idl_entry != NULL ) {
+ if ( avl_delete( &bdb->bi_idl_tree, (caddr_t) matched_idl_entry,
+ bdb_idl_entry_cmp ) == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "=> bdb_idl_cache_del: "
+ "AVL delete failed\n",
+ 0, 0, 0 );
+ }
+ --bdb->bi_idl_cache_size;
+ ldap_pvt_thread_mutex_lock( &bdb->bi_idl_tree_lrulock );
+ IDL_LRU_DELETE( bdb, matched_idl_entry );
+ ldap_pvt_thread_mutex_unlock( &bdb->bi_idl_tree_lrulock );
+ free( matched_idl_entry->kstr.bv_val );
+ if ( matched_idl_entry->idl )
+ free( matched_idl_entry->idl );
+ free( matched_idl_entry );
+ }
+ ldap_pvt_thread_rdwr_wunlock( &bdb->bi_idl_tree_rwlock );
+}
+
+void
+bdb_idl_cache_add_id(
+ struct bdb_info *bdb,
+ DB *db,
+ DBT *key,
+ ID id )
+{
+ bdb_idl_cache_entry_t *cache_entry, idl_tmp;
+ DBT2bv( key, &idl_tmp.kstr );
+ idl_tmp.db = db;
+ ldap_pvt_thread_rdwr_wlock( &bdb->bi_idl_tree_rwlock );
+ cache_entry = avl_find( bdb->bi_idl_tree, &idl_tmp,
+ bdb_idl_entry_cmp );
+ if ( cache_entry != NULL ) {
+ if ( !BDB_IDL_IS_RANGE( cache_entry->idl ) &&
+ cache_entry->idl[0] < BDB_IDL_DB_MAX ) {
+ size_t s = BDB_IDL_SIZEOF( cache_entry->idl ) + sizeof(ID);
+ cache_entry->idl = ch_realloc( cache_entry->idl, s );
+ }
+ bdb_idl_insert( cache_entry->idl, id );
+ }
+ ldap_pvt_thread_rdwr_wunlock( &bdb->bi_idl_tree_rwlock );
+}
+
+void
+bdb_idl_cache_del_id(
+ struct bdb_info *bdb,
+ DB *db,
+ DBT *key,
+ ID id )
+{
+ bdb_idl_cache_entry_t *cache_entry, idl_tmp;
+ DBT2bv( key, &idl_tmp.kstr );
+ idl_tmp.db = db;
+ ldap_pvt_thread_rdwr_wlock( &bdb->bi_idl_tree_rwlock );
+ cache_entry = avl_find( bdb->bi_idl_tree, &idl_tmp,
+ bdb_idl_entry_cmp );
+ if ( cache_entry != NULL ) {
+ bdb_idl_delete( cache_entry->idl, id );
+ if ( cache_entry->idl[0] == 0 ) {
+ if ( avl_delete( &bdb->bi_idl_tree, (caddr_t) cache_entry,
+ bdb_idl_entry_cmp ) == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "=> bdb_idl_cache_del: "
+ "AVL delete failed\n",
+ 0, 0, 0 );
+ }
+ --bdb->bi_idl_cache_size;
+ ldap_pvt_thread_mutex_lock( &bdb->bi_idl_tree_lrulock );
+ IDL_LRU_DELETE( bdb, cache_entry );
+ ldap_pvt_thread_mutex_unlock( &bdb->bi_idl_tree_lrulock );
+ free( cache_entry->kstr.bv_val );
+ free( cache_entry->idl );
+ free( cache_entry );
+ }
+ }
+ ldap_pvt_thread_rdwr_wunlock( &bdb->bi_idl_tree_rwlock );
+}
+