gecko-dev/security/psm/server/hashtbl.c

419 строки
11 KiB
C

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is the Netscape security libraries.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License Version 2 or later (the
* "GPL"), in which case the provisions of the GPL are applicable
* instead of those above. If you wish to allow use of your
* version of this file only under the terms of the GPL and not to
* allow others to use your version of this file under the MPL,
* indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by
* the GPL. If you do not delete the provisions above, a recipient
* may use your version of this file under either the MPL or the
* GPL.
*/
/*
hashtbl.c -- hash tables for Cartman, specifically for use with
resources (32-bit integer keys).
*/
#include "hashtbl.h"
#define BUCKETNUM(key) ((key) & (HASH_NUM_BUCKETS-1))
/* SSMHashBucket support routines */
void ssmbucket_invariant(SSMHashBucket *bucket)
{
PR_ASSERT(bucket->m_size >= 0);
PR_ASSERT(bucket->m_allocSize > 0);
PR_ASSERT(bucket->m_size <= bucket->m_allocSize);
PR_ASSERT(bucket->m_keys != NULL);
PR_ASSERT(bucket->m_values != NULL);
}
SSMStatus ssmbucket_remove(SSMHashBucket *bucket, PRIntn whichElem)
{
SSMHashKey *keyptr;
void **valptr;
PRIntn numToMove;
ssmbucket_invariant(bucket);
PR_ASSERT(whichElem < bucket->m_size);
if (whichElem >= bucket->m_size) return PR_FAILURE;
keyptr = bucket->m_keys + whichElem;
valptr = bucket->m_values + whichElem;
numToMove = bucket->m_size - (whichElem + 1);
memmove(keyptr, keyptr + 1, numToMove * sizeof(SSMHashKey));
memmove(valptr, valptr + 1, numToMove * sizeof(void *));
bucket->m_size--;
ssmbucket_invariant(bucket);
return PR_SUCCESS;
}
PRIntn ssmbucket_find(SSMHashBucket *bucket, SSMHashKey key)
{
PRIntn i;
for(i=0;i<bucket->m_size; i++)
{
if (bucket->m_keys[i] == key)
return i;
}
return -1;
}
SSMStatus ssmbucket_grow(SSMHashBucket *bucket)
{
size_t newSize;
SSMHashKey *newKeys = NULL;
void **newValues = NULL;
ssmbucket_invariant(bucket);
/* If we're growing, double the size */
newSize = bucket->m_allocSize * 2;
/* realloc the memory. is this broken? */
newKeys = (SSMHashKey *) PR_REALLOC(bucket->m_keys, newSize * sizeof(SSMHashKey));
if (!newKeys) goto loser;
newValues = (void **) PR_REALLOC(bucket->m_values, newSize * sizeof(void *));
if (!newValues) goto loser;
bucket->m_keys = newKeys;
bucket->m_values = newValues;
bucket->m_allocSize = newSize;
ssmbucket_invariant(bucket);
return PR_SUCCESS;
loser:
if (newKeys) PR_Free(newKeys);
if (newValues) PR_Free(newValues);
return PR_FAILURE;
}
SSMStatus ssmbucket_add(SSMHashBucket *bucket, SSMHashKey key, void *value)
{
PRIntn newSize = bucket->m_size + 1;
SSMStatus rv = PR_SUCCESS;
ssmbucket_invariant(bucket);
if (bucket->m_size >= bucket->m_allocSize)
rv = ssmbucket_grow(bucket);
if (rv == PR_SUCCESS)
{
bucket->m_keys[newSize-1] = key;
bucket->m_values[newSize-1] = value;
bucket->m_size = newSize;
}
ssmbucket_invariant(bucket);
return rv;
}
SSMStatus ssmbucket_free(SSMHashBucket *bucket)
{
if (bucket)
{
if (bucket->m_keys)
{
PR_Free(bucket->m_keys);
bucket->m_keys = NULL;
}
if (bucket->m_values)
{
PR_Free(bucket->m_values);
bucket->m_values = NULL;
}
bucket->m_size = bucket->m_allocSize = 0;
}
return PR_SUCCESS;
}
SSMStatus ssmbucket_init(SSMHashBucket *bucket)
{
bucket->m_keys = NULL;
bucket->m_values = NULL;
bucket->m_keys = (SSMHashKey *) PR_CALLOC(2 * sizeof(SSMHashKey));
if (!bucket->m_keys) goto loser;
bucket->m_values = (void **) PR_CALLOC(2 * sizeof(void *));
if (!bucket->m_values) goto loser;
bucket->m_size = 0;
bucket->m_allocSize = 2;
return PR_SUCCESS;
loser:
ssmbucket_free(bucket);
return PR_FAILURE;
}
/************************************************************
** FUNCTION: SSM_HashInsert
** DESCRIPTION: Insert a new data item keyed by (key) into (hash).
** INPUTS:
** hash
** The hash into which the item is to be inserted.
** key
** The key used to store the item.
** value
** The item to be inserted.
** RETURNS:
** PR_SUCCESS on success.
** PR_FAILURE on failure.
**
*************************************************************/
SSMStatus SSM_HashInsert(SSMHashTable *hash, SSMHashKey key, void *value)
{
PRIntn whichElem;
if ((hash == NULL) || (value == NULL))
{
PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
return PR_FAILURE;
}
PR_EnterMonitor(hash->m_hashLock);
/* If the key already exists, replace the value.
Otherwise, add the new key/value. */
whichElem = ssmbucket_find(&(hash->m_buckets[BUCKETNUM(key)]), key);
if (whichElem >= 0)
hash->m_buckets[BUCKETNUM(key)].m_values[whichElem] = value;
else
ssmbucket_add(&(hash->m_buckets[BUCKETNUM(key)]), key, value);
PR_ExitMonitor(hash->m_hashLock);
return PR_SUCCESS;
}
SSMStatus ssm_HashFindValue(SSMHashTable *hash, SSMHashKey key, void **value,
PRBool removeElem)
{
PRIntn whichElem;
SSMStatus rv = PR_SUCCESS;
if ((hash == NULL) || (value == NULL))
{
PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
return PR_FAILURE;
}
PR_EnterMonitor(hash->m_hashLock);
*value = NULL; /* in case we fail */
whichElem = ssmbucket_find(&(hash->m_buckets[BUCKETNUM(key)]), key);
if (whichElem >= 0)
{
*value = hash->m_buckets[BUCKETNUM(key)].m_values[whichElem];
if (removeElem)
rv = ssmbucket_remove(&(hash->m_buckets[BUCKETNUM(key)]),
whichElem);
}
else
rv = PR_FAILURE;
PR_ExitMonitor(hash->m_hashLock);
return rv;
}
/************************************************************
** FUNCTION: SSM_HashFind
** DESCRIPTION: Get the item whose key is (key).
** INPUTS:
** hash
** The hash from which the item is to be retrieved.
** key
** The key used to store the item.
** value
** The item to be returned. Set to NULL if no suitable
** item is found.
** RETURNS:
** PR_SUCCESS on success.
** PR_FAILURE on failure.
**
*************************************************************/
SSMStatus SSM_HashFind(SSMHashTable *hash, SSMHashKey key, void **value)
{
return ssm_HashFindValue(hash, key, value, PR_FALSE);
}
/************************************************************
** FUNCTION: SSM_HashRemove
** DESCRIPTION: Remove from a hash the item whose key is (key).
** INPUTS:
** hash
** The hash from which the item is to be removed.
** key
** The key used to store the item.
** value
** The value removed from the hash.
** RETURNS:
** PR_SUCCESS on success.
** PR_FAILURE on failure.
**
*************************************************************/
SSMStatus SSM_HashRemove(SSMHashTable *hash, SSMHashKey key, void **value)
{
return ssm_HashFindValue(hash, key, value, PR_TRUE);
}
/************************************************************
** FUNCTION: SSM_HashKeys
** DESCRIPTION: Get keys for all items stored in the hash.
** INPUTS:
** hash
** The hash from which the keys are to be returned.
** keys
** Returns an array of keys. The array is allocated using PR_Malloc.
** numKeys
** Returns the number of keys in the array.
** RETURNS:
** PR_SUCCESS on success.
** PR_FAILURE on failure.
**
*************************************************************/
SSMStatus SSM_HashKeys(SSMHashTable *hash, SSMHashKey **keys, PRIntn *numKeys)
{
SSMHashKey *rkeys = NULL, *walk;
PRIntn i, howMany;
/* in case we fail */
*keys = NULL;
*numKeys = 0;
if (hash == NULL)
{
PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
return PR_FAILURE;
}
PR_EnterMonitor(hash->m_hashLock);
rkeys = (SSMHashKey *) PR_CALLOC(hash->m_size * sizeof(SSMHashKey));
if (!rkeys)
return PR_FAILURE;
/* Walk through all the buckets, grabbing their keys. */
walk = rkeys;
for(i=0;i<HASH_NUM_BUCKETS;i++)
{
howMany = hash->m_buckets[i].m_size;
(void) memcpy(walk, hash->m_buckets[i].m_keys,
hash->m_buckets[i].m_size * sizeof(SSMHashKey));
walk += howMany;
}
PR_ExitMonitor(hash->m_hashLock);
*keys = rkeys;
*numKeys = hash->m_size;
return PR_SUCCESS;
}
/************************************************************
** FUNCTION: SSM_HashDestroy
** DESCRIPTION: Destroy a hash. Items in the hash are not freed.
** INPUTS:
** hash
** The hash to be destroyed.
** RETURNS:
** PR_SUCCESS on success.
** PR_FAILURE on failure.
**
*************************************************************/
SSMStatus SSM_HashDestroy(SSMHashTable *hash)
{
if (hash)
{
PRIntn i;
for (i=0;i<HASH_NUM_BUCKETS;i++)
ssmbucket_free(&(hash->m_buckets[i]));
PR_DestroyMonitor(hash->m_hashLock);
PR_Free(hash);
}
return PR_SUCCESS;
}
/************************************************************
** FUNCTION: SSM_HashCreate
** DESCRIPTION: Create and initialize a hash.
** INPUTS:
** hash
** Returns the newly created hash.
** RETURNS:
** PR_SUCCESS on success.
** PR_FAILURE on failure.
**
*************************************************************/
SSMStatus SSM_HashCreate(SSMHashTable **hash)
{
PRIntn i;
SSMHashTable *rhash;
*hash = NULL; /* in case we fail */
rhash = (SSMHashTable *) PR_CALLOC(sizeof(SSMHashTable));
if (!rhash) goto loser;
for(i=0;i<HASH_NUM_BUCKETS;i++)
{
if (ssmbucket_init(&(rhash->m_buckets[i])))
goto loser;
}
rhash->m_hashLock = PR_NewMonitor();
if (!rhash->m_hashLock)
goto loser;
*hash = rhash;
return PR_SUCCESS;
loser:
if (rhash)
SSM_HashDestroy(rhash);
return PR_FAILURE;
}
SSMStatus SSM_HashCount(SSMHashTable *hash, PRIntn *numItems)
{
*numItems = 0; /* in case we fail */
if (hash == NULL)
{
PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
return PR_FAILURE;
}
*numItems = hash->m_size;
return PR_SUCCESS;
}