idl.c

来自「OpenLdap是LDAP的开源项目」· C语言 代码 · 共 1,264 行 · 第 1/2 页

C
1,264
字号
/* idl.c - ldap id list handling routines *//* $OpenLDAP: pkg/ldap/servers/slapd/back-ldbm/idl.c,v 1.86.2.4 2007/01/02 21:44:02 kurt Exp $ *//* This work is part of OpenLDAP Software <http://www.openldap.org/>. * * Copyright 1998-2007 The OpenLDAP Foundation. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted only as authorized by the OpenLDAP * Public License. * * A copy of this license is available in the file LICENSE in the * top-level directory of the distribution or, alternatively, at * <http://www.OpenLDAP.org/license.html>. */#include "portable.h"#include <stdio.h>#include <ac/string.h>#include <ac/socket.h>#include "slap.h"#include "back-ldbm.h"static ID_BLOCK* idl_dup( ID_BLOCK *idl );static void cont_alloc( Datum *cont, Datum *key ){	ldbm_datum_init( *cont );	cont->dsize = 1 + sizeof(ID) + key->dsize;	cont->dptr = ch_malloc( cont->dsize );	* (unsigned char *) cont->dptr = SLAP_INDEX_CONT_PREFIX;	AC_MEMCPY( &((unsigned char *)cont->dptr)[1 + sizeof(ID)],		key->dptr, key->dsize );}static void cont_id( Datum *cont, ID id ){	unsigned int i;	for( i=1; i <= sizeof(id); i++) {		((unsigned char *)cont->dptr)[i] = (unsigned char)(id & 0xFF);		id >>= 8;	}}static void cont_free( Datum *cont ){	ch_free( cont->dptr );}#ifdef LDBM_DEBUG_IDLstatic void idl_check(ID_BLOCK *idl){	int i, max;	ID_BLOCK last;	if( ID_BLOCK_ALLIDS(idl) )	{		return;	}#ifndef USE_INDIRECT_NIDS	if( ID_BLOCK_INDIRECT(idl) )	{		for ( max = 0; !ID_BLOCK_NOID(idl, max); max++ ) ;	} else#endif	{		max = ID_BLOCK_NIDS(idl);	}	if ( max <= 1 )	{		return;	}	for( last = ID_BLOCK_ID(idl, 0), i = 1;		i < max;		last = ID_BLOCK_ID(idl, i), i++ )	{		assert (last < ID_BLOCK_ID(idl, i) );	}}#endif/* Allocate an ID_BLOCK with room for nids ids */ID_BLOCK *idl_alloc( unsigned int nids ){	ID_BLOCK	*new;	/* nmax + nids + space for the ids */	new = (ID_BLOCK *) ch_calloc( (ID_BLOCK_IDS_OFFSET + nids), sizeof(ID) );	ID_BLOCK_NMAX(new) = nids;	ID_BLOCK_NIDS(new) = 0;	return( new );}/* Allocate an empty ALLIDS ID_BLOCK */ID_BLOCK	*idl_allids( Backend *be ){	ID_BLOCK	*idl;	ID		id;	idl = idl_alloc( 0 );	ID_BLOCK_NMAX(idl) = ID_BLOCK_ALLIDS_VALUE;	if ( next_id_get( be, &id ) ) {		idl_free( idl );		return NULL;	}	ID_BLOCK_NIDS(idl) = id;	return( idl );}/* Free an ID_BLOCK */voididl_free( ID_BLOCK *idl ){	if ( idl == NULL ) {		Debug( LDAP_DEBUG_TRACE,			"idl_free: called with NULL pointer\n",			0, 0, 0 );		return;	}	free( (char *) idl );}/* Fetch an single ID_BLOCK from the cache */static ID_BLOCK *idl_fetch_one(    Backend		*be,    DBCache	*db,    Datum		key){	Datum	data;	ID_BLOCK	*idl;	/* Debug( LDAP_DEBUG_TRACE, "=> idl_fetch_one\n", 0, 0, 0 ); */	data = ldbm_cache_fetch( db, key );	if( data.dptr == NULL ) {		return NULL;	}	idl = (ID_BLOCK *) data.dptr;	if ( ID_BLOCK_ALLIDS(idl) ) {		/* make sure we have the current value of highest id */		idl = idl_allids( be );	} else {		idl = idl_dup((ID_BLOCK *) data.dptr);	}	ldbm_datum_free( db->dbc_db, data );	return idl;}/* Fetch a set of ID_BLOCKs from the cache *	if not INDIRECT *		if block return is an ALLIDS block, *			return an new ALLIDS block *		otherwise *			return block *	construct super block from all blocks referenced by INDIRECT block *	return super block */ID_BLOCK *idl_fetch(    Backend		*be,    DBCache	*db,    Datum		key){	Datum	data;	ID_BLOCK	*idl;	ID_BLOCK	**tmp;	unsigned	i, nids, nblocks;	idl = idl_fetch_one( be, db, key );	if ( idl == NULL ) {		return NULL;	}	if ( ID_BLOCK_ALLIDS(idl) ) {		/* all ids block */		return( idl );	}	if ( ! ID_BLOCK_INDIRECT( idl ) ) {		/* regular block */		return( idl );	}	/*	 * this is an indirect block which points to other blocks.	 * we need to read in all the blocks it points to and construct	 * a big id list containing all the ids, which we will return.	 */#ifndef USE_INDIRECT_NIDS	/* count the number of blocks & allocate space for pointers to them */	for ( nblocks = 0; !ID_BLOCK_NOID(idl, nblocks); nblocks++ )		;	/* NULL */#else	nblocks = ID_BLOCK_NIDS(idl);#endif	tmp = (ID_BLOCK **) ch_malloc( nblocks * sizeof(ID_BLOCK *) );	/* read in all the blocks */	cont_alloc( &data, &key );	nids = 0;	for ( i = 0; i < nblocks; i++ ) {		cont_id( &data, ID_BLOCK_ID(idl, i) );		if ( (tmp[i] = idl_fetch_one( be, db, data )) == NULL ) {			Debug( LDAP_DEBUG_ANY,			    "idl_fetch: one returned NULL\n", 0, 0, 0 );			continue;		}		nids += ID_BLOCK_NIDS(tmp[i]);	}	cont_free( &data );	idl_free( idl );	/* allocate space for the big block */	idl = idl_alloc( nids );	ID_BLOCK_NIDS(idl) = nids;	nids = 0;	/* copy in all the ids from the component blocks */	for ( i = 0; i < nblocks; i++ ) {		if ( tmp[i] == NULL ) {			continue;		}		AC_MEMCPY(			(char *) &ID_BLOCK_ID(idl, nids),			(char *) &ID_BLOCK_ID(tmp[i], 0),			ID_BLOCK_NIDS(tmp[i]) * sizeof(ID) );		nids += ID_BLOCK_NIDS(tmp[i]);		idl_free( tmp[i] );	}	free( (char *) tmp );	assert( ID_BLOCK_NIDS(idl) == nids );#ifdef LDBM_DEBUG_IDL	idl_check(idl);#endif	Debug( LDAP_DEBUG_TRACE, "<= idl_fetch %ld ids (%ld max)\n",	       ID_BLOCK_NIDS(idl), ID_BLOCK_NMAXN(idl), 0 );	return( idl );}/* store a single block */static intidl_store(    Backend		*be,    DBCache	*db,    Datum		key,     ID_BLOCK		*idl){	int	rc, flags;	Datum	data;#ifdef LDBM_DEBUG_IDL	idl_check(idl);#endif	ldbm_datum_init( data );	/* Debug( LDAP_DEBUG_TRACE, "=> idl_store\n", 0, 0, 0 ); */	data.dptr = (char *) idl;	data.dsize = (ID_BLOCK_IDS_OFFSET + ID_BLOCK_NMAXN(idl)) * sizeof(ID);		flags = LDBM_REPLACE;	rc = ldbm_cache_store( db, key, data, flags );	/* Debug( LDAP_DEBUG_TRACE, "<= idl_store %d\n", rc, 0, 0 ); */	return( rc );}/* Binary search for id in block, return index *    an index is always returned, even with no match. If no * match, the returned index is the insertion point. */static unsigned intidl_find(    ID_BLOCK	*b,    ID		id){	int lo=0, hi=ID_BLOCK_NIDS(b)-1, nr=0;	for (;lo<=hi;)	{	    nr = ( lo + hi ) / 2;	    if (ID_BLOCK_ID(b, nr) == id)	    	break;	    if (ID_BLOCK_ID(b, nr) > id)	    	hi = nr - 1;	    else	    	lo = nr + 1;	}	return nr;}/* split the block at id  *	locate ID greater than or equal to id. */static voididl_split_block(    ID_BLOCK	*b,    ID		id,    ID_BLOCK	**right,    ID_BLOCK	**left){	unsigned int	nr, nl;	/* find where to split the block */	nr = idl_find(b, id);	if ( ID_BLOCK_ID(b,nr) < id )		nr++;	nl = ID_BLOCK_NIDS(b) - nr;	*right = idl_alloc( nr == 0 ? 1 : nr );	*left = idl_alloc( nl + (nr == 0 ? 0 : 1));	/*	 * everything before the id being inserted in the first block	 * unless there is nothing, in which case the id being inserted	 * goes there.	 */	if ( nr == 0 ) {		ID_BLOCK_NIDS(*right) = 1;		ID_BLOCK_ID(*right, 0) = id;	} else {		AC_MEMCPY(			(char *) &ID_BLOCK_ID(*right, 0),			(char *) &ID_BLOCK_ID(b, 0),			nr * sizeof(ID) );		ID_BLOCK_NIDS(*right) = nr;		ID_BLOCK_ID(*left, 0) = id;	}	/* the id being inserted & everything after in the second block */	AC_MEMCPY(		(char *) &ID_BLOCK_ID(*left, (nr == 0 ? 0 : 1)),	    (char *) &ID_BLOCK_ID(b, nr),		nl * sizeof(ID) );	ID_BLOCK_NIDS(*left) = nl + (nr == 0 ? 0 : 1);#ifdef LDBM_DEBUG_IDL	idl_check(*right);	idl_check(*left);#endif}/* * idl_change_first - called when an indirect block's first key has * changed, meaning it needs to be stored under a new key, and the * header block pointing to it needs updating. */static intidl_change_first(    Backend		*be,    DBCache	*db,    Datum		hkey,		/* header block key	*/    ID_BLOCK		*h,		/* header block		*/    int			pos,		/* pos in h to update	*/    Datum		bkey,		/* data block key	*/    ID_BLOCK		*b		/* data block		*/){	int	rc;	/* Debug( LDAP_DEBUG_TRACE, "=> idl_change_first\n", 0, 0, 0 ); */	/* delete old key block */	if ( (rc = ldbm_cache_delete( db, bkey )) != 0 ) {		Debug( LDAP_DEBUG_ANY,		    "idl_change_first: ldbm_cache_delete returned %d\n",			rc, 0, 0 );		return( rc );	}	/* write block with new key */	cont_id( &bkey, ID_BLOCK_ID(b, 0) );	if ( (rc = idl_store( be, db, bkey, b )) != 0 ) {		Debug( LDAP_DEBUG_ANY,		    "idl_change_first: idl_store returned %d\n", rc, 0, 0 );		return( rc );	}	/* update + write indirect header block */	ID_BLOCK_ID(h, pos) = ID_BLOCK_ID(b, 0);	if ( (rc = idl_store( be, db, hkey, h )) != 0 ) {		Debug( LDAP_DEBUG_ANY,		    "idl_change_first: idl_store returned %d\n", rc, 0, 0 );		return( rc );	}	return( 0 );}intidl_insert_key(    Backend		*be,    DBCache	*db,    Datum		key,    ID			id){	int	i, j, first, rc = 0;	ID_BLOCK	*idl, *tmp, *tmp2, *tmp3;	Datum	k2;	if ( (idl = idl_fetch_one( be, db, key )) == NULL ) {		idl = idl_alloc( 1 );		ID_BLOCK_ID(idl, ID_BLOCK_NIDS(idl)++) = id;		rc = idl_store( be, db, key, idl );		idl_free( idl );		return( rc );	}	if ( ID_BLOCK_ALLIDS( idl ) ) {		/* ALLIDS */		idl_free( idl );		return 0;	}	if ( ! ID_BLOCK_INDIRECT( idl ) ) {		/* regular block */		switch ( idl_insert( &idl, id, db->dbc_maxids ) ) {		case 0:		/* id inserted - store the updated block */		case 1:			rc = idl_store( be, db, key, idl );			break;		case 2:		/* id already there - nothing to do */			rc = 0;			break;		case 3:		/* id not inserted - block must be split */			/* check threshold for marking this an all-id block */			if ( db->dbc_maxindirect < 2 ) {				idl_free( idl );				idl = idl_allids( be );				rc = idl_store( be, db, key, idl );				break;			}			idl_split_block( idl, id, &tmp, &tmp2 );			idl_free( idl );			/* create the header indirect block */#ifndef USE_INDIRECT_NIDS			idl = idl_alloc( 3 );			ID_BLOCK_NMAX(idl) = 3;			ID_BLOCK_NIDS(idl) = ID_BLOCK_INDIRECT_VALUE;			ID_BLOCK_ID(idl, 0) = ID_BLOCK_ID(tmp, 0);			ID_BLOCK_ID(idl, 1) = ID_BLOCK_ID(tmp2, 0);			ID_BLOCK_ID(idl, 2) = NOID;#else			idl = idl_alloc( 2 );			ID_BLOCK_NMAX(idl) = 2 | ID_BLOCK_INDIRECT_VALUE;			ID_BLOCK_NIDS(idl) = 2;			ID_BLOCK_ID(idl, 0) = ID_BLOCK_ID(tmp, 0);			ID_BLOCK_ID(idl, 1) = ID_BLOCK_ID(tmp2, 0);#endif			/* store it */			rc = idl_store( be, db, key, idl );			cont_alloc( &k2, &key );			cont_id( &k2, ID_BLOCK_ID(tmp, 0) );			rc = idl_store( be, db, k2, tmp );			cont_id( &k2, ID_BLOCK_ID(tmp2, 0) );			rc = idl_store( be, db, k2, tmp2 );			cont_free( &k2 );			idl_free( tmp );			idl_free( tmp2 );			break;		}		idl_free( idl );		return( rc );	}	/*	 * this is an indirect block which points to other blocks.	 * we need to read in the block into which the id should be	 * inserted, then insert the id and store the block.  we might	 * have to split the block if it is full, which means we also	 * need to write a new "header" block.	 */#ifndef USE_INDIRECT_NIDS	/* select the block to try inserting into *//* XXX linear search XXX */	for ( i = 0; !ID_BLOCK_NOID(idl, i) && id >= ID_BLOCK_ID(idl, i); i++ )		;	/* NULL */#else	i = idl_find(idl, id);	if (ID_BLOCK_ID(idl, i) <= id)		i++;#endif	if ( i != 0 ) {		i--;		first = 0;	} else {		first = 1;	}	/* At this point, the following condition must be true:	 * ID_BLOCK_ID(idl, i) <= id && id < ID_BLOCK_ID(idl, i+1)	 * except when i is the first or the last block.	 */	/* get the block */	cont_alloc( &k2, &key );	cont_id( &k2, ID_BLOCK_ID(idl, i) );	if ( (tmp = idl_fetch_one( be, db, k2 )) == NULL ) {		Debug( LDAP_DEBUG_ANY, "idl_insert_key: nonexistent continuation block\n",		    0, 0, 0 );		cont_free( &k2 );		idl_free( idl );		return( -1 );	}	/* insert the id */	switch ( idl_insert( &tmp, id, db->dbc_maxids ) ) {	case 0:		/* id inserted ok */		if ( (rc = idl_store( be, db, k2, tmp )) != 0 ) {			Debug( LDAP_DEBUG_ANY,			    "idl_insert_key: idl_store returned %d\n", rc, 0, 0 );		}		break;	case 1:		/* id inserted - first id in block has changed */		/*		 * key for this block has changed, so we have to		 * write the block under the new key, delete the		 * old key block + update and write the indirect		 * header block.		 */		rc = idl_change_first( be, db, key, idl, i, k2, tmp );		break;	case 2:		/* id not inserted - already there, do nothing */		rc = 0;		break;	case 3:		/* id not inserted - block is full */		/*		 * first, see if it will fit in the next block,		 * without splitting, unless we're trying to insert		 * into the beginning of the first block.		 */#ifndef USE_INDIRECT_NIDS		/* is there a next block? */		if ( !first && !ID_BLOCK_NOID(idl, i + 1) ) {#else		if ( !first && (unsigned long)(i + 1) < ID_BLOCK_NIDS(idl) ) {#endif			Datum k3;			/* read it in */			cont_alloc( &k3, &key );			cont_id( &k3, ID_BLOCK_ID(idl, i + 1) );			if ( (tmp2 = idl_fetch_one( be, db, k3 )) == NULL ) {				Debug( LDAP_DEBUG_ANY,				    "idl_insert_key: idl_fetch_one returned NULL\n",				    0, 0, 0 );				/* split the original block */				cont_free( &k3 );				goto split;			}			/* If the new id is less than the last id in the			 * current block, it must not be put into the next			 * block. Push the last id of the current block			 * into the next block instead.			 */			if (id < ID_BLOCK_ID(tmp, ID_BLOCK_NIDS(tmp) - 1)) {			    ID id2 = ID_BLOCK_ID(tmp, ID_BLOCK_NIDS(tmp) - 1);			    --ID_BLOCK_NIDS(tmp);			    /* This must succeed since we just popped one			     * ID off the end of it.			     */			    rc = idl_insert( &tmp, id, db->dbc_maxids );

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?