📄 hashtbl.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 PSM, 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;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -