2014-12-07 16:13:51 +03:00
/*
2004-10-19 10:39:51 +04:00
Unix SMB / CIFS implementation .
very efficient functions to manage mapping a id ( such as a fnum ) to
a pointer . This is used for fnum and search id allocation .
Copyright ( C ) Andrew Tridgell 2004
2014-12-07 16:13:51 +03:00
This code is derived from lib / idr . c in the 2.6 Linux kernel , which was
2004-10-19 10:39:51 +04:00
written by Jim Houston jim . houston @ ccur . com , and is
Copyright ( C ) 2002 by Concurrent Computer Corporation
2014-12-07 16:13:51 +03:00
2004-10-19 10:39:51 +04:00
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
2008-09-30 18:17:02 +04:00
the Free Software Foundation ; either version 2 of the License , or
2004-10-19 10:39:51 +04:00
( at your option ) any later version .
2014-12-07 16:13:51 +03:00
2004-10-19 10:39:51 +04:00
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
2014-12-07 16:13:51 +03:00
2004-10-19 10:39:51 +04:00
You should have received a copy of the GNU General Public License
2007-07-10 06:07:03 +04:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2004-10-19 10:39:51 +04:00
*/
/*
see the section marked " public interface " below for documentation
*/
2006-02-28 16:12:39 +03:00
/**
* @ file
*/
2014-09-19 09:01:51 +04:00
# include "replace.h"
2015-07-12 09:30:36 +03:00
# include <talloc.h>
2014-09-19 09:01:51 +04:00
# include "debug.h"
# include "idtree.h"
2004-10-19 10:39:51 +04:00
# define IDR_BITS 5
# define IDR_FULL 0xfffffffful
2006-08-24 13:48:40 +04:00
#if 0 /* unused */
2004-10-19 10:39:51 +04:00
# define TOP_LEVEL_FULL (IDR_FULL >> 30)
2006-08-24 13:48:40 +04:00
# endif
2004-10-19 10:39:51 +04:00
# define IDR_SIZE (1 << IDR_BITS)
# define IDR_MASK ((1 << IDR_BITS)-1)
# define MAX_ID_SHIFT (sizeof(int)*8 - 1)
# define MAX_ID_BIT (1U << MAX_ID_SHIFT)
# define MAX_ID_MASK (MAX_ID_BIT - 1)
# define MAX_LEVEL (MAX_ID_SHIFT + IDR_BITS - 1) / IDR_BITS
# define IDR_FREE_MAX MAX_LEVEL + MAX_LEVEL
# define set_bit(bit, v) (v) |= (1<<(bit))
# define clear_bit(bit, v) (v) &= ~(1<<(bit))
2004-10-22 10:46:04 +04:00
# define test_bit(bit, v) ((v) & (1<<(bit)))
2014-12-07 16:13:51 +03:00
2004-10-19 10:39:51 +04:00
struct idr_layer {
uint32_t bitmap ;
struct idr_layer * ary [ IDR_SIZE ] ;
int count ;
} ;
2004-10-22 10:46:04 +04:00
struct idr_context {
2004-10-19 10:39:51 +04:00
struct idr_layer * top ;
struct idr_layer * id_free ;
int layers ;
int id_free_cnt ;
} ;
2004-10-22 10:46:04 +04:00
static struct idr_layer * alloc_layer ( struct idr_context * idp )
2004-10-19 10:39:51 +04:00
{
struct idr_layer * p ;
if ( ! ( p = idp - > id_free ) )
return NULL ;
idp - > id_free = p - > ary [ 0 ] ;
idp - > id_free_cnt - - ;
p - > ary [ 0 ] = NULL ;
return p ;
}
static int find_next_bit ( uint32_t bm , int maxid , int n )
{
2004-10-22 10:46:04 +04:00
while ( n < maxid & & ! test_bit ( n , bm ) ) n + + ;
2004-10-19 10:39:51 +04:00
return n ;
}
2004-10-22 10:46:04 +04:00
static void free_layer ( struct idr_context * idp , struct idr_layer * p )
2004-10-19 10:39:51 +04:00
{
p - > ary [ 0 ] = idp - > id_free ;
idp - > id_free = p ;
idp - > id_free_cnt + + ;
}
2004-10-22 10:46:04 +04:00
static int idr_pre_get ( struct idr_context * idp )
2004-10-19 10:39:51 +04:00
{
while ( idp - > id_free_cnt < IDR_FREE_MAX ) {
2009-02-02 14:14:45 +03:00
struct idr_layer * pn = talloc_zero ( idp , struct idr_layer ) ;
if ( pn = = NULL )
2004-10-19 10:39:51 +04:00
return ( 0 ) ;
2009-02-02 14:14:45 +03:00
free_layer ( idp , pn ) ;
2004-10-19 10:39:51 +04:00
}
return 1 ;
}
2004-10-22 10:46:04 +04:00
static int sub_alloc ( struct idr_context * idp , void * ptr , int * starting_id )
2004-10-19 10:39:51 +04:00
{
int n , m , sh ;
2009-02-02 14:14:45 +03:00
struct idr_layer * p , * pn ;
2010-12-06 06:05:50 +03:00
struct idr_layer * pa [ MAX_LEVEL + 1 ] ;
2010-10-05 06:36:19 +04:00
unsigned int l , id , oid ;
2004-10-19 10:39:51 +04:00
uint32_t bm ;
2006-03-13 09:58:09 +03:00
memset ( pa , 0 , sizeof ( pa ) ) ;
2004-10-19 10:39:51 +04:00
id = * starting_id ;
2008-09-30 18:14:31 +04:00
restart :
2004-10-19 10:39:51 +04:00
p = idp - > top ;
l = idp - > layers ;
pa [ l - - ] = NULL ;
while ( 1 ) {
/*
* We run around this while until we reach the leaf node . . .
*/
n = ( id > > ( IDR_BITS * l ) ) & IDR_MASK ;
bm = ~ p - > bitmap ;
m = find_next_bit ( bm , IDR_SIZE , n ) ;
if ( m = = IDR_SIZE ) {
/* no space available go back to previous layer. */
l + + ;
2008-09-30 18:14:31 +04:00
oid = id ;
2004-10-19 10:39:51 +04:00
id = ( id | ( ( 1 < < ( IDR_BITS * l ) ) - 1 ) ) + 1 ;
2008-09-30 18:14:31 +04:00
/* if already at the top layer, we need to grow */
2004-10-19 10:39:51 +04:00
if ( ! ( p = pa [ l ] ) ) {
* starting_id = id ;
return - 2 ;
}
2008-09-30 18:14:31 +04:00
/* If we need to go up one layer, continue the
* loop ; otherwise , restart from the top .
*/
sh = IDR_BITS * ( l + 1 ) ;
if ( oid > > sh = = id > > sh )
2014-08-06 05:57:06 +04:00
continue ;
2008-09-30 18:14:31 +04:00
else
goto restart ;
2004-10-19 10:39:51 +04:00
}
if ( m ! = n ) {
sh = IDR_BITS * l ;
id = ( ( id > > sh ) ^ n ^ m ) < < sh ;
}
if ( ( id > = MAX_ID_BIT ) | | ( id < 0 ) )
return - 1 ;
if ( l = = 0 )
break ;
/*
* Create the layer below if it is missing .
*/
if ( ! p - > ary [ m ] ) {
2009-02-02 14:14:45 +03:00
if ( ! ( pn = alloc_layer ( idp ) ) )
2004-10-19 10:39:51 +04:00
return - 1 ;
2009-02-02 14:14:45 +03:00
p - > ary [ m ] = pn ;
2004-10-19 10:39:51 +04:00
p - > count + + ;
}
pa [ l - - ] = p ;
p = p - > ary [ m ] ;
}
/*
* We have reached the leaf node , plant the
* users pointer and return the raw id .
*/
p - > ary [ m ] = ( struct idr_layer * ) ptr ;
set_bit ( m , p - > bitmap ) ;
p - > count + + ;
/*
* If this layer is full mark the bit in the layer above
* to show that this part of the radix tree is full .
* This may complete the layer above and require walking
* up the radix tree .
*/
n = id ;
while ( p - > bitmap = = IDR_FULL ) {
2015-11-25 14:20:23 +03:00
if ( l > = MAX_LEVEL ) {
2004-10-19 10:39:51 +04:00
break ;
2015-11-25 14:20:23 +03:00
}
p = pa [ + + l ] ;
if ( p = = NULL ) {
break ;
}
2004-10-19 10:39:51 +04:00
n = n > > IDR_BITS ;
set_bit ( ( n & IDR_MASK ) , p - > bitmap ) ;
}
return ( id ) ;
}
2004-10-22 10:46:04 +04:00
static int idr_get_new_above_int ( struct idr_context * idp , void * ptr , int starting_id )
2004-10-19 10:39:51 +04:00
{
2009-02-02 14:14:45 +03:00
struct idr_layer * p , * pn ;
2004-10-19 10:39:51 +04:00
int layers , v , id ;
idr_pre_get ( idp ) ;
2014-12-07 16:13:51 +03:00
2004-10-19 10:39:51 +04:00
id = starting_id ;
build_up :
p = idp - > top ;
layers = idp - > layers ;
if ( ! p ) {
if ( ! ( p = alloc_layer ( idp ) ) )
return - 1 ;
layers = 1 ;
}
/*
* Add a new layer to the top of the tree if the requested
* id is larger than the currently allocated space .
*/
while ( ( layers < MAX_LEVEL ) & & ( id > = ( 1 < < ( layers * IDR_BITS ) ) ) ) {
layers + + ;
if ( ! p - > count )
continue ;
2009-02-02 14:14:45 +03:00
if ( ! ( pn = alloc_layer ( idp ) ) ) {
2004-10-19 10:39:51 +04:00
/*
* The allocation failed . If we built part of
* the structure tear it down .
*/
2009-02-02 14:14:45 +03:00
for ( pn = p ; p & & p ! = idp - > top ; pn = p ) {
2004-10-19 10:39:51 +04:00
p = p - > ary [ 0 ] ;
2009-02-02 14:14:45 +03:00
pn - > ary [ 0 ] = NULL ;
pn - > bitmap = pn - > count = 0 ;
free_layer ( idp , pn ) ;
2004-10-19 10:39:51 +04:00
}
return - 1 ;
}
2009-02-02 14:14:45 +03:00
pn - > ary [ 0 ] = p ;
pn - > count = 1 ;
2004-10-19 10:39:51 +04:00
if ( p - > bitmap = = IDR_FULL )
2009-02-02 14:14:45 +03:00
set_bit ( 0 , pn - > bitmap ) ;
p = pn ;
2004-10-19 10:39:51 +04:00
}
idp - > top = p ;
idp - > layers = layers ;
v = sub_alloc ( idp , ptr , & id ) ;
if ( v = = - 2 )
goto build_up ;
return ( v ) ;
}
2004-10-22 10:46:04 +04:00
static int sub_remove ( struct idr_context * idp , int shift , int id )
2004-10-19 10:39:51 +04:00
{
struct idr_layer * p = idp - > top ;
2010-06-11 00:27:51 +04:00
struct idr_layer * * pa [ 1 + MAX_LEVEL ] ;
2004-10-19 10:39:51 +04:00
struct idr_layer * * * paa = & pa [ 0 ] ;
2004-10-22 10:46:04 +04:00
int n ;
2004-10-19 10:39:51 +04:00
* paa = NULL ;
* + + paa = & idp - > top ;
while ( ( shift > 0 ) & & p ) {
2004-10-22 10:46:04 +04:00
n = ( id > > shift ) & IDR_MASK ;
2004-10-19 10:39:51 +04:00
clear_bit ( n , p - > bitmap ) ;
* + + paa = & p - > ary [ n ] ;
p = p - > ary [ n ] ;
shift - = IDR_BITS ;
}
2004-10-22 10:46:04 +04:00
n = id & IDR_MASK ;
if ( p ! = NULL & & test_bit ( n , p - > bitmap ) ) {
2004-10-19 10:39:51 +04:00
clear_bit ( n , p - > bitmap ) ;
p - > ary [ n ] = NULL ;
while ( * paa & & ! - - ( ( * * paa ) - > count ) ) {
free_layer ( idp , * * paa ) ;
* * paa - - = NULL ;
}
if ( ! * paa )
idp - > layers = 0 ;
2004-10-22 10:46:04 +04:00
return 0 ;
2004-10-19 10:39:51 +04:00
}
2004-10-22 10:46:04 +04:00
return - 1 ;
2004-10-19 10:39:51 +04:00
}
2004-10-22 10:46:04 +04:00
static void * _idr_find ( struct idr_context * idp , int id )
2004-10-19 10:39:51 +04:00
{
int n ;
struct idr_layer * p ;
n = idp - > layers * IDR_BITS ;
p = idp - > top ;
/*
* This tests to see if bits outside the current tree are
* present . If so , tain ' t one of ours !
*/
2010-06-11 00:27:51 +04:00
if ( n + IDR_BITS < 31 & &
( ( id & ~ ( ~ 0 < < MAX_ID_SHIFT ) ) > > ( n + IDR_BITS ) ) ) {
return NULL ;
}
2004-10-19 10:39:51 +04:00
/* Mask off upper bits we don't use for the search. */
id & = MAX_ID_MASK ;
2006-03-13 09:58:09 +03:00
while ( n > = IDR_BITS & & p ) {
2004-10-19 10:39:51 +04:00
n - = IDR_BITS ;
p = p - > ary [ ( id > > n ) & IDR_MASK ] ;
}
return ( ( void * ) p ) ;
}
2004-10-22 10:46:04 +04:00
static int _idr_remove ( struct idr_context * idp , int id )
2004-10-19 10:39:51 +04:00
{
struct idr_layer * p ;
/* Mask off upper bits we don't use for the search. */
id & = MAX_ID_MASK ;
2004-10-22 10:46:04 +04:00
if ( sub_remove ( idp , ( idp - > layers - 1 ) * IDR_BITS , id ) = = - 1 ) {
return - 1 ;
}
2014-12-07 16:13:51 +03:00
if ( idp - > top & & idp - > top - > count = = 1 & &
2004-10-19 10:39:51 +04:00
( idp - > layers > 1 ) & &
idp - > top - > ary [ 0 ] ) {
/* We can drop a layer */
p = idp - > top - > ary [ 0 ] ;
idp - > top - > bitmap = idp - > top - > count = 0 ;
free_layer ( idp , idp - > top ) ;
idp - > top = p ;
- - idp - > layers ;
}
while ( idp - > id_free_cnt > = IDR_FREE_MAX ) {
p = alloc_layer ( idp ) ;
talloc_free ( p ) ;
}
2004-10-22 10:46:04 +04:00
return 0 ;
2004-10-19 10:39:51 +04:00
}
/************************************************************************
this is the public interface
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2006-03-05 20:15:19 +03:00
/**
2004-10-19 10:39:51 +04:00
initialise a idr tree . The context return value must be passed to
all subsequent idr calls . To destroy the idr tree use talloc_free ( )
on this context
*/
2006-03-05 20:15:19 +03:00
_PUBLIC_ struct idr_context * idr_init ( TALLOC_CTX * mem_ctx )
2004-10-19 10:39:51 +04:00
{
2005-01-27 10:08:20 +03:00
return talloc_zero ( mem_ctx , struct idr_context ) ;
2004-10-19 10:39:51 +04:00
}
2006-03-05 20:15:19 +03:00
/**
2004-10-19 10:39:51 +04:00
allocate the next available id , and assign ' ptr ' into its slot .
you can retrieve later this pointer using idr_find ( )
*/
2006-03-05 20:15:19 +03:00
_PUBLIC_ int idr_get_new ( struct idr_context * idp , void * ptr , int limit )
2004-10-19 10:39:51 +04:00
{
2004-10-22 10:46:04 +04:00
int ret = idr_get_new_above_int ( idp , ptr , 0 ) ;
2004-10-19 16:06:01 +04:00
if ( ret > limit ) {
2004-10-19 10:39:51 +04:00
idr_remove ( idp , ret ) ;
return - 1 ;
}
return ret ;
}
2006-03-05 20:15:19 +03:00
/**
2004-10-19 10:39:51 +04:00
allocate a new id , giving the first available value greater than or
equal to the given starting id
*/
2006-03-05 20:15:19 +03:00
_PUBLIC_ int idr_get_new_above ( struct idr_context * idp , void * ptr , int starting_id , int limit )
2004-10-19 10:39:51 +04:00
{
2004-10-22 10:46:04 +04:00
int ret = idr_get_new_above_int ( idp , ptr , starting_id ) ;
2004-10-19 16:06:01 +04:00
if ( ret > limit ) {
2004-10-19 10:39:51 +04:00
idr_remove ( idp , ret ) ;
return - 1 ;
}
return ret ;
}
2006-03-05 20:15:19 +03:00
/**
2004-10-19 10:39:51 +04:00
find a pointer value previously set with idr_get_new given an id
*/
2006-03-05 20:15:19 +03:00
_PUBLIC_ void * idr_find ( struct idr_context * idp , int id )
2004-10-19 10:39:51 +04:00
{
2004-10-22 10:46:04 +04:00
return _idr_find ( idp , id ) ;
2004-10-19 10:39:51 +04:00
}
2006-03-05 20:15:19 +03:00
/**
2004-10-19 10:39:51 +04:00
remove an id from the idr tree
*/
2006-03-05 20:15:19 +03:00
_PUBLIC_ int idr_remove ( struct idr_context * idp , int id )
2004-10-19 10:39:51 +04:00
{
2004-10-22 10:46:04 +04:00
int ret ;
ret = _idr_remove ( ( struct idr_context * ) idp , id ) ;
if ( ret ! = 0 ) {
DEBUG ( 0 , ( " WARNING: attempt to remove unset id %d in idtree \n " , id ) ) ;
}
return ret ;
2004-10-19 10:39:51 +04:00
}