| /***************************************************************************** | |
| Licensed to Accellera Systems Initiative Inc. (Accellera) under one or | |
| more contributor license agreements. See the NOTICE file distributed | |
| with this work for additional information regarding copyright ownership. | |
| Accellera licenses this file to you under the Apache License, Version 2.0 | |
| (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.apache.org/licenses/LICENSE-2.0 | |
| Unless required by applicable law or agreed to in writing, software | |
| distributed under the License is distributed on an "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
| implied. See the License for the specific language governing | |
| permissions and limitations under the License. | |
| *****************************************************************************/ | |
| /***************************************************************************** | |
| sc_hash.cpp -- Implementation of a chained hash table with MTF | |
| (move-to-front). | |
| Original Author: Stan Y. Liao, Synopsys, Inc. | |
| CHANGE LOG AT END OF FILE | |
| *****************************************************************************/ | |
| #include <assert.h> | |
| #include <stdlib.h> // duplicate (c)stdlib.h headers for Solaris | |
| #include <cstdlib> | |
| #include <cstddef> | |
| #include <string.h> | |
| #include "sysc/kernel/sc_cmnhdr.h" | |
| #include "sysc/utils/sc_hash.h" | |
| #include "sysc/utils/sc_mempool.h" | |
| namespace sc_core { | |
| // we can't assume global availability of uintptr_t, | |
| // approximate it by size_t | |
| typedef std::size_t uintptr_t; | |
| const double PHASH_DEFAULT_GROW_FACTOR = 2.0; | |
| class sc_phash_elem { | |
| friend class sc_phash_base; | |
| friend class sc_phash_base_iter; | |
| private: | |
| void* key; | |
| void* contents; | |
| sc_phash_elem* next; | |
| sc_phash_elem( void* k, void* c, sc_phash_elem* n ) | |
| : key(k), contents(c), next(n) { } | |
| sc_phash_elem() : key(0), contents(0), next(0) { } | |
| ~sc_phash_elem() { } | |
| static void* operator new(std::size_t sz) | |
| { return sc_mempool::allocate(sz); } | |
| static void operator delete(void* p, std::size_t sz) | |
| { sc_mempool::release(p, sz); } | |
| }; | |
| sc_phash_base::sc_phash_base( | |
| void* def, | |
| int size, | |
| int density, | |
| double grow, | |
| bool reorder, | |
| unsigned (*hash_fn)(const void*), | |
| int (*cmp_fn)(const void*, const void*) | |
| ) : | |
| default_value(def), num_bins(0), num_entries(0), max_density(density), | |
| reorder_flag(reorder), grow_factor(grow), bins(0), hash(hash_fn), | |
| cmpr(cmp_fn) | |
| { | |
| if (size <= 0) | |
| size = PHASH_DEFAULT_INIT_TABLE_SIZE; | |
| else if ((size % 2) == 0) | |
| size += 1; | |
| num_bins = size; | |
| bins = new sc_phash_elem*[size]; | |
| for (int i = 0; i < size; ++i) | |
| bins[i] = 0; | |
| } | |
| void | |
| sc_phash_base::set_cmpr_fn(cmpr_fn_t c) | |
| { | |
| cmpr = c; | |
| } | |
| void | |
| sc_phash_base::set_hash_fn(hash_fn_t h) | |
| { | |
| hash = h; | |
| } | |
| sc_phash_base::~sc_phash_base() | |
| { | |
| sc_phash_elem* ptr; | |
| sc_phash_elem* next; | |
| for (int i = 0; i < num_bins; ++i) { | |
| ptr = bins[i]; | |
| while (ptr != 0) { | |
| next = ptr->next; | |
| delete ptr; | |
| ptr = next; | |
| } | |
| } | |
| delete[] bins; | |
| } | |
| void | |
| sc_phash_base::rehash() | |
| { | |
| sc_phash_elem* ptr; | |
| sc_phash_elem* next; | |
| sc_phash_elem** old_bins = bins; | |
| int old_num_bins = num_bins; | |
| unsigned hash_val; | |
| num_bins = (int) (grow_factor * old_num_bins); | |
| if (num_bins % 2 == 0) | |
| ++num_bins; | |
| num_entries = 0; | |
| bins = new sc_phash_elem*[num_bins]; | |
| memset( bins, 0, sizeof(sc_phash_elem*) * num_bins ); | |
| for (int i = 0; i < old_num_bins; ++i) { | |
| ptr = old_bins[i]; | |
| while (ptr != 0) { | |
| next = ptr->next; | |
| hash_val = do_hash(ptr->key); | |
| ptr->next = bins[hash_val]; | |
| bins[hash_val] = ptr; | |
| ++num_entries; | |
| ptr = next; | |
| } | |
| } | |
| delete[] old_bins; | |
| } | |
| sc_phash_elem* | |
| sc_phash_base::find_entry_q( unsigned hash_val, const void* key, sc_phash_elem*** plast ) | |
| { | |
| sc_phash_elem** last = &(bins[hash_val]); | |
| sc_phash_elem* ptr = *last; | |
| /* The (ptr->key != key) here is meant by the "q" */ | |
| while ((ptr != 0) && (ptr->key != key)) { | |
| /* ^^ right here */ | |
| last = &(ptr->next); | |
| ptr = *last; | |
| } | |
| if ((ptr != 0) && reorder_flag) { | |
| *last = ptr->next; | |
| ptr->next = bins[hash_val]; | |
| bins[hash_val] = ptr; | |
| last = &(bins[hash_val]); | |
| } | |
| if (plast) *plast = last; | |
| return ptr; | |
| } | |
| sc_phash_elem* | |
| sc_phash_base::find_entry_c( unsigned hash_val, const void* key, sc_phash_elem*** plast ) | |
| { | |
| sc_phash_elem** last = &(bins[hash_val]); | |
| sc_phash_elem* ptr = *last; | |
| while ((ptr != 0) && ((*cmpr)(ptr->key, key) != 0)) { | |
| last = &(ptr->next); | |
| ptr = *last; | |
| } | |
| /* Bring to front */ | |
| if ((ptr != 0) && reorder_flag) { | |
| *last = ptr->next; | |
| ptr->next = bins[hash_val]; | |
| bins[hash_val] = ptr; | |
| last = &(bins[hash_val]); | |
| } | |
| if (plast) *plast = last; | |
| return ptr; | |
| } | |
| sc_phash_elem* | |
| sc_phash_base::add_direct( void* key, void* contents, unsigned hash_val ) | |
| { | |
| if (num_entries / num_bins >= max_density) { | |
| rehash(); | |
| hash_val = do_hash(key); | |
| } | |
| sc_phash_elem* new_entry = new sc_phash_elem(key, contents, bins[hash_val]); | |
| bins[hash_val] = new_entry; | |
| ++num_entries; | |
| return new_entry; | |
| } | |
| void | |
| sc_phash_base::erase() | |
| { | |
| for (int i = 0; i < num_bins; ++i) { | |
| sc_phash_elem* ptr = bins[i]; | |
| while (ptr != 0) { | |
| sc_phash_elem* next = ptr->next; | |
| delete ptr; | |
| ptr = next; | |
| --num_entries; | |
| } | |
| bins[i] = 0; | |
| } | |
| assert(num_entries == 0); | |
| } | |
| void | |
| sc_phash_base::erase(void (*kfree)(void*)) | |
| { | |
| for (int i = 0; i < num_bins; ++i) { | |
| sc_phash_elem* ptr = bins[i]; | |
| while (ptr != 0) { | |
| sc_phash_elem* next = ptr->next; | |
| (*kfree)(ptr->key); | |
| delete ptr; | |
| ptr = next; | |
| --num_entries; | |
| } | |
| bins[i] = 0; | |
| } | |
| assert(num_entries == 0); | |
| } | |
| void | |
| sc_phash_base::copy( const sc_phash_base* b ) | |
| { | |
| erase(); | |
| iterator iter((sc_phash_base*) b); /* cast away the const */ | |
| for ( ; ! iter.empty(); iter++) | |
| insert( iter.key(), iter.contents() ); | |
| } | |
| void | |
| sc_phash_base::copy(const sc_phash_base& b, void* (*kdup)(const void*), void (*kfree)(void*)) | |
| { | |
| erase(kfree); | |
| iterator iter((sc_phash_base&) b); | |
| for ( ; ! iter.empty(); iter++) | |
| insert( (*kdup)(iter.key()), iter.contents() ); | |
| } | |
| int | |
| sc_phash_base::insert( void* k, void* c ) | |
| { | |
| unsigned hash_val = do_hash(k); | |
| sc_phash_elem* ptr = find_entry( hash_val, k ); | |
| if (ptr == 0) { | |
| (void) add_direct(k, c, hash_val); | |
| return 0; | |
| } | |
| else { | |
| ptr->contents = c; | |
| return 1; | |
| } | |
| } | |
| int | |
| sc_phash_base::insert( void* k, void* c, void* (*kdup)(const void*) ) | |
| { | |
| unsigned hash_val = do_hash(k); | |
| sc_phash_elem* ptr = find_entry( hash_val, k ); | |
| if (ptr == 0) { | |
| (void) add_direct((*kdup)(k), c, hash_val); | |
| return 0; | |
| } | |
| else { | |
| ptr->contents = c; | |
| return 1; | |
| } | |
| } | |
| int | |
| sc_phash_base::insert_if_not_exists( void* k, void* c ) | |
| { | |
| unsigned hash_val = do_hash(k); | |
| sc_phash_elem* ptr = find_entry( hash_val, k ); | |
| if (ptr == 0) { | |
| (void) add_direct( k, c, hash_val ); | |
| return 0; | |
| } | |
| else | |
| return 1; | |
| } | |
| int | |
| sc_phash_base::insert_if_not_exists( void* k, void* c, void* (*kdup)(const void*) ) | |
| { | |
| unsigned hash_val = do_hash(k); | |
| sc_phash_elem* ptr = find_entry( hash_val, k ); | |
| if (ptr == 0) { | |
| (void) add_direct( (*kdup)(k), c, hash_val ); | |
| return 0; | |
| } | |
| else | |
| return 1; | |
| } | |
| int | |
| sc_phash_base::remove( const void* k ) | |
| { | |
| unsigned hash_val = do_hash(k); | |
| sc_phash_elem** last; | |
| sc_phash_elem* ptr = find_entry( hash_val, k, &last ); | |
| if (ptr == 0) | |
| return 0; | |
| assert(*last == ptr); | |
| *last = ptr->next; | |
| delete ptr; | |
| --num_entries; | |
| return 1; | |
| } | |
| int | |
| sc_phash_base::remove( const void* k, void** pk, void** pc ) | |
| { | |
| unsigned hash_val = do_hash(k); | |
| sc_phash_elem** last; | |
| sc_phash_elem* ptr = find_entry( hash_val, k, &last ); | |
| if (ptr == 0) { | |
| *pk = 0; | |
| *pc = 0; | |
| return 0; | |
| } | |
| else { | |
| *pk = ptr->key; | |
| *pc = ptr->contents; | |
| } | |
| assert(*last == ptr); | |
| *last = ptr->next; | |
| delete ptr; | |
| --num_entries; | |
| return 1; | |
| } | |
| int | |
| sc_phash_base::remove(const void* k, void (*kfree)(void*)) | |
| { | |
| void* rk; | |
| void* rc; | |
| if (remove(k, &rk, &rc)) { | |
| (*kfree)(rk); | |
| return 1; | |
| } | |
| else | |
| return 0; | |
| } | |
| int | |
| sc_phash_base::remove_by_contents( const void* c ) | |
| { | |
| sc_phash_elem** last; | |
| sc_phash_elem* ptr; | |
| int num_removed = 0; | |
| for (int i = 0; i < num_bins; ++i) { | |
| last = &(bins[i]); | |
| ptr = *last; | |
| while (ptr != 0) { | |
| if (ptr->contents != c) { | |
| last = &(ptr->next); | |
| ptr = *last; | |
| } | |
| else { | |
| *last = ptr->next; | |
| delete ptr; | |
| ptr = *last; | |
| --num_entries; | |
| ++num_removed; | |
| } | |
| } | |
| } | |
| return num_removed; | |
| } | |
| int | |
| sc_phash_base::remove_by_contents( bool (*predicate)(const void* c, void* arg), void* arg ) | |
| { | |
| sc_phash_elem** last; | |
| sc_phash_elem* ptr; | |
| int num_removed = 0; | |
| for (int i = 0; i < num_bins; ++i) { | |
| last = &(bins[i]); | |
| ptr = *last; | |
| while (ptr != 0) { | |
| if (! (*predicate)(ptr->contents, arg)) { | |
| last = &(ptr->next); | |
| ptr = *last; | |
| } | |
| else { | |
| *last = ptr->next; | |
| delete ptr; | |
| ptr = *last; | |
| --num_entries; | |
| ++num_removed; | |
| } | |
| } | |
| } | |
| return num_removed; | |
| } | |
| int | |
| sc_phash_base::remove_by_contents( const void* c, void (*kfree)(void*) ) | |
| { | |
| sc_phash_elem** last; | |
| sc_phash_elem* ptr; | |
| int num_removed = 0; | |
| for (int i = 0; i < num_bins; ++i) { | |
| last = &(bins[i]); | |
| ptr = *last; | |
| while (ptr != 0) { | |
| if (ptr->contents != c) { | |
| last = &(ptr->next); | |
| ptr = *last; | |
| } | |
| else { | |
| *last = ptr->next; | |
| (*kfree)(ptr->key); | |
| delete ptr; | |
| ptr = *last; | |
| --num_entries; | |
| ++num_removed; | |
| } | |
| } | |
| } | |
| return num_removed; | |
| } | |
| int | |
| sc_phash_base::remove_by_contents( bool (*predicate)(const void*, void*), void* arg, void (*kfree)(void*)) | |
| { | |
| sc_phash_elem** last; | |
| sc_phash_elem* ptr; | |
| int num_removed = 0; | |
| for (int i = 0; i < num_bins; ++i) { | |
| last = &(bins[i]); | |
| ptr = *last; | |
| while (ptr != 0) { | |
| if (! (*predicate)(ptr->contents, arg)) { | |
| last = &(ptr->next); | |
| ptr = *last; | |
| } | |
| else { | |
| *last = ptr->next; | |
| (*kfree)(ptr->key); | |
| delete ptr; | |
| ptr = *last; | |
| --num_entries; | |
| ++num_removed; | |
| } | |
| } | |
| } | |
| return num_removed; | |
| } | |
| int | |
| sc_phash_base::lookup( const void* k, void** c_ptr ) const | |
| { | |
| unsigned hash_val = do_hash(k); | |
| sc_phash_elem* ptr = find_entry( hash_val, k ); | |
| if (ptr == 0) { | |
| if (c_ptr != 0) *c_ptr = default_value; | |
| return 0; | |
| } | |
| else { | |
| if (c_ptr != 0) *c_ptr = ptr->contents; | |
| return 1; | |
| } | |
| } | |
| void* | |
| sc_phash_base::operator[]( const void* key ) const | |
| { | |
| void* contents; | |
| lookup( key, &contents ); | |
| return contents; | |
| } | |
| /***************************************************************************/ | |
| void | |
| sc_phash_base_iter::reset( sc_phash_base* t ) | |
| { | |
| table = t; | |
| index = 0; | |
| entry = 0; | |
| next = 0; | |
| for (int i = index; i < table->num_bins; ++i) { | |
| if (table->bins[i] != 0) { | |
| index = i + 1; | |
| last = &(table->bins[i]); | |
| entry = *last; | |
| next = entry->next; | |
| break; | |
| } | |
| } | |
| } | |
| bool | |
| sc_phash_base_iter::empty() const | |
| { | |
| return (entry == 0); | |
| } | |
| void | |
| sc_phash_base_iter::step() | |
| { | |
| if (entry) { | |
| last = &(entry->next); | |
| } | |
| entry = next; | |
| if (! entry) { | |
| for (int i = index; i < table->num_bins; ++i) { | |
| if (table->bins[i] != 0) { | |
| index = i + 1; | |
| last = &(table->bins[i]); | |
| entry = *last; | |
| next = entry->next; | |
| break; | |
| } | |
| } | |
| } | |
| else { | |
| next = entry->next; | |
| } | |
| } | |
| void | |
| sc_phash_base_iter::remove() | |
| { | |
| delete entry; | |
| *last = next; | |
| entry = 0; | |
| --table->num_entries; | |
| step(); | |
| } | |
| void | |
| sc_phash_base_iter::remove(void (*kfree)(void*)) | |
| { | |
| (*kfree)(entry->key); | |
| delete entry; | |
| *last = next; | |
| entry = 0; | |
| --table->num_entries; | |
| step(); | |
| } | |
| void* | |
| sc_phash_base_iter::key() const | |
| { | |
| return entry->key; | |
| } | |
| void* | |
| sc_phash_base_iter::contents() const | |
| { | |
| return entry->contents; | |
| } | |
| void* | |
| sc_phash_base_iter::set_contents( void* c ) | |
| { | |
| return entry->contents = c; | |
| } | |
| /****************************************************************************/ | |
| unsigned | |
| default_ptr_hash_fn(const void* p) | |
| { | |
| return static_cast<unsigned>(((uintptr_t)(p) >> 2) * 2654435789U); | |
| } | |
| unsigned | |
| default_int_hash_fn(const void* p) | |
| { | |
| return static_cast<unsigned>((uintptr_t)(p) * 3141592661U); | |
| } | |
| unsigned | |
| default_str_hash_fn(const void* p) | |
| { | |
| if (!p) return 0; | |
| const char* x = (const char*) p; | |
| unsigned int h = 0; | |
| unsigned int g; | |
| while (*x != 0) { | |
| h = (h << 4) + *x++; | |
| if ((g = h & 0xf0000000) != 0) | |
| h = (h ^ (g >> 24)) ^ g; | |
| } | |
| return h; | |
| } | |
| int | |
| sc_strhash_cmp( const void* a, const void* b ) | |
| { | |
| return strcmp( (const char*) a, (const char*) b ); | |
| } | |
| void* | |
| sc_strhash_kdup(const void* k) | |
| { | |
| char* result = (char*) malloc( strlen((const char*)k)+1 ); | |
| strcpy(result, (const char*) k); | |
| return result; | |
| } | |
| void | |
| sc_strhash_kfree(void* k) | |
| { | |
| if (k) free((char*) k); | |
| } | |
| } // namespace sc_core | |
| // $Log: sc_hash.cpp,v $ | |
| // Revision 1.5 2011/08/26 20:42:30 acg | |
| // Andy Goodrich: | |
| // (1) Replaced strdup with new and strcpy to eliminate issue with the | |
| // Greenhills compiler. | |
| // (2) Moved modification log to the end of the file to eliminate line | |
| // skew when check-ins are done. | |
| // | |
| // Revision 1.4 2011/08/24 22:05:56 acg | |
| // Torsten Maehne: initialization changes to remove warnings. | |
| // | |
| // Revision 1.3 2011/05/05 17:46:04 acg | |
| // Philip A. Hartmann: changes in "swap" support. | |
| // | |
| // Revision 1.2 2011/02/18 20:38:43 acg | |
| // Andy Goodrich: Updated Copyright notice. | |
| // | |
| // Revision 1.1.1.1 2006/12/15 20:20:06 acg | |
| // SystemC 2.3 | |
| // | |
| // Revision 1.3 2006/01/13 18:53:10 acg | |
| // Andy Goodrich: Added $Log command so that CVS comments are reproduced in | |
| // the source. | |
| // taf |