/*
* Copyright (c) 2004, 2005 Sendmail, Inc. and its suppliers.
* All rights reserved.
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the sendmail distribution.
*/
#include "sm/generic.h"
SM_RCSID("@(#)$Id: servid.c,v 1.4 2005/06/02 19:00:36 ca Exp $")
#include "sm/error.h"
#include "sm/assert.h"
#include "sm/memops.h"
#include "sm/limits.h"
#include "sm/heap.h"
#include "sm/queue.h"
#include "sm/servid.h"
/*
** ID context
** Note: this module doesn't give out 0 as id because 0 is used as
** "free" marker in id_used[].
**
** id_min is currently "fixed" here in the code, it could be made an
** option or slightly modified if necessary.
**
** Optimization: by default id_max is large and the application (mcp?)
** is expected not to use too many ids, hence it should be possible
** to sequentially return id_min ... id_max without checking whether
** the id has already been issued; this is recorded in id_wrapped.
*/
struct id_ctx_S
{
uint id_size; /* currently used size */
uint id_alloc; /* allocated size */
uint id_next; /* next id to use */
uint id_min; /* min id to return */
uint id_max; /* max id to return */
bool id_wrapped; /* exceeded max once? */
uint id_free_idx; /* a free index in array */
uint *id_used; /* array of ids (0: free) */
};
/*
** SM_NEW_ID_CTX -- Create ID context
**
** Parameters:
** size -- number of ids to maintain (> 1)
** maxid -- maximum value of id to issue (0: use default)
** pid_ctx -- (pointer to) ID context (output)
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sm_new_id_ctx(uint size, uint maxid, id_ctx_P *pid_ctx)
{
sm_ret_T ret;
size_t len;
id_ctx_P id_ctx;
SM_REQUIRE(pid_ctx != NULL);
/* SM_REQUIRE(size > 1); */
SM_REQUIRE(maxid == 0 || maxid > size);
id_ctx = (id_ctx_P) sm_zalloc(sizeof(*id_ctx));
if (id_ctx == NULL)
goto enomem;
len = sizeof(*id_ctx->id_used) * size;
SM_ASSERT(len >= size);
SM_ASSERT(len >= sizeof(*id_ctx->id_used));
id_ctx->id_used = (uint *) sm_zalloc(len);
if (id_ctx->id_used == NULL)
goto enomem;
id_ctx->id_next = 1;
id_ctx->id_min = 1;
id_ctx->id_max = maxid == 0 ? INT_MAX : maxid;
id_ctx->id_wrapped = false;
id_ctx->id_free_idx = 0;
id_ctx->id_size = size;
id_ctx->id_alloc = size;
*pid_ctx = id_ctx;
return SM_SUCCESS;
enomem:
ret = sm_err_temp(ENOMEM);
if (id_ctx != NULL)
sm_free_size(id_ctx, sizeof(*id_ctx));
return ret;
}
/*
** SM_CHG_ID_CTX -- Change size for ID context
**
** Parameters:
** id_ctx -- ID context
** size -- new number of ids to maintain
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sm_chg_id_ctx(id_ctx_P id_ctx, uint size)
{
sm_ret_T ret;
size_t len;
uint *used;
SM_REQUIRE(id_ctx != NULL);
if (size <= id_ctx->id_size || size <= id_ctx->id_alloc)
{
id_ctx->id_size = size;
return SM_SUCCESS;
}
len = sizeof(*used) * size;
SM_ASSERT(len >= size);
SM_ASSERT(len >= sizeof(*id_ctx->id_used));
used = (uint *) sm_zalloc(len);
if (used == NULL)
goto enomem;
len = sizeof(*id_ctx->id_used) * id_ctx->id_size;
sm_memcpy(used, id_ctx->id_used, len);
len = sizeof(*id_ctx->id_used) * id_ctx->id_alloc;
sm_free_size(id_ctx->id_used, len);
id_ctx->id_used = used;
id_ctx->id_size = size;
id_ctx->id_alloc = size;
if (id_ctx->id_max != INT_MAX || id_ctx->id_max <= size)
id_ctx->id_max = size + 1;
return SM_SUCCESS;
enomem:
ret = sm_err_temp(ENOMEM);
return ret;
}
/*
** SM_DESTROY_ID_CTX -- Destroy ID context
**
** Parameters:
** id_ctx -- ID context
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sm_destroy_id_ctx(id_ctx_P id_ctx)
{
if (id_ctx == NULL)
return SM_SUCCESS;
if (id_ctx->id_used != NULL)
{
size_t len;
len = sizeof(*id_ctx->id_used) * id_ctx->id_alloc;
sm_free_size(id_ctx->id_used, len);
}
sm_free_size(id_ctx, sizeof(*id_ctx));
return SM_SUCCESS;
}
/*
** SM_FIND_ID -- Find ID in array
**
** Parameters:
** id_ctx -- ID context
** id -- ID to check
**
** Returns:
** <0: ID not found
** >=0: index where ID was found
*/
static int
sm_find_id(id_ctx_P id_ctx, uint id)
{
uint i;
SM_REQUIRE(id_ctx != NULL);
for (i = 0; i < id_ctx->id_size; i++)
{
if (id == id_ctx->id_used[i])
return i;
}
return -1;
}
/*
** SM_NEW_ID -- Get new ID
**
** Parameters:
** id_ctx -- ID context
** pid -- (pointer to) ID (output)
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sm_new_id(id_ctx_P id_ctx, uint *pid)
{
uint id;
int idx;
SM_REQUIRE(id_ctx != NULL);
SM_REQUIRE(pid != NULL);
id = id_ctx->id_next;
if (!id_ctx->id_wrapped)
goto found;
do
{
idx = sm_find_id(id_ctx, id);
if (idx < 0)
goto found;
++id;
} while (id <= id_ctx->id_max);
id_ctx->id_wrapped = true;
id = id_ctx->id_min;
do
{
idx = sm_find_id(id_ctx, id);
if (idx < 0)
goto found;
++id;
} while (id < id_ctx->id_next);
return sm_err_perm(EINVAL);
found:
*pid = id;
id_ctx->id_next = id + 1;
if (id_ctx->id_next > id_ctx->id_max)
{
id_ctx->id_next = id_ctx->id_min;
id_ctx->id_wrapped = true;
}
if (id_ctx->id_free_idx >= id_ctx->id_size
/* || id_ctx->id_free_idx < 0 ||*/)
{
idx = sm_find_id(id_ctx, 0);
if (idx < 0 || (uint)idx >= id_ctx->id_size)
return sm_err_perm(EINVAL);
id_ctx->id_free_idx = idx;
}
SM_ASSERT(/*id_ctx->id_free_idx >= 0 &&*/
id_ctx->id_free_idx < id_ctx->id_size);
id_ctx->id_used[id_ctx->id_free_idx] = id;
id_ctx->id_free_idx = sm_find_id(id_ctx, 0);
return SM_SUCCESS;
}
/*
** SM_FREE_ID -- Release ID
**
** Parameters:
** id_ctx -- ID context
** id -- ID to release
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sm_free_id(id_ctx_P id_ctx, uint id)
{
int idx;
SM_REQUIRE(id_ctx != NULL);
idx = sm_find_id(id_ctx, id);
if (idx < 0)
return sm_err_perm(EINVAL);
id_ctx->id_used[idx] = 0;
id_ctx->id_free_idx = idx;
return SM_SUCCESS;
}
syntax highlighted by Code2HTML, v. 0.9.1