/* Hash table for checking keyword links. Implemented using double hashing. Copyright (C) 1989-1998, 2000, 2002 Free Software Foundation, Inc. Written by Douglas C. Schmidt and Bruno Haible . This file is part of GNU GPERF. GNU GPERF is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GNU GPERF 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. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* Specification. */ #include "hash-table.h" #include #include /* declares memset(), strcmp() */ #include #include "options.h" /* We use a hash table with double hashing. This is the simplest kind of hash table, given that we always only insert and never remove entries from the hash table. */ /* To make double hashing efficient, there need to be enough spare entries. */ static const int size_factor = 10; /* We make the size of the hash table a power of 2. This allows for two optimizations: It eliminates the modulo instruction, and allows for an easy secondary hashing function. */ /* Constructor. */ Hash_Table::Hash_Table (unsigned int size, bool ignore_length) : _ignore_length (ignore_length), _collisions (0) { /* There need to be enough spare entries. */ size = size * size_factor; /* Find smallest power of 2 that is >= size. */ unsigned int shift = 0; if ((size >> 16) > 0) { size = size >> 16; shift += 16; } if ((size >> 8) > 0) { size = size >> 8; shift += 8; } if ((size >> 4) > 0) { size = size >> 4; shift += 4; } if ((size >> 2) > 0) { size = size >> 2; shift += 2; } if ((size >> 1) > 0) { size = size >> 1; shift += 1; } _log_size = shift; _size = 1 << shift; /* Allocate table. */ _table = new KeywordExt*[_size]; memset (_table, 0, _size * sizeof (*_table)); } /* Destructor. */ Hash_Table::~Hash_Table () { delete[] _table; } /* Print the table's contents. */ void Hash_Table::dump () const { int field_width; field_width = 0; { for (int i = _size - 1; i >= 0; i--) if (_table[i]) if (field_width < _table[i]->_selchars_length) field_width = _table[i]->_selchars_length; } fprintf (stderr, "\ndumping the hash table\n" "total available table slots = %d, total bytes = %d, total collisions = %d\n" "location, %*s, keyword\n", _size, _size * static_cast(sizeof (*_table)), _collisions, field_width, "keysig"); for (int i = _size - 1; i >= 0; i--) if (_table[i]) { fprintf (stderr, "%8d, ", i); if (field_width > _table[i]->_selchars_length) fprintf (stderr, "%*s", field_width - _table[i]->_selchars_length, ""); for (int j = 0; j < _table[i]->_selchars_length; j++) putc (_table[i]->_selchars[j], stderr); fprintf (stderr, ", %.*s\n", _table[i]->_allchars_length, _table[i]->_allchars); } fprintf (stderr, "\nend dumping hash table\n\n"); } /* Compares two items. */ inline bool Hash_Table::equal (KeywordExt *item1, KeywordExt *item2) const { return item1->_selchars_length == item2->_selchars_length && memcmp (item1->_selchars, item2->_selchars, item2->_selchars_length * sizeof (unsigned int)) == 0 && (_ignore_length || item1->_allchars_length == item2->_allchars_length); } /* Attempts to insert ITEM in the table. If there is already an equal entry in it, returns it. Otherwise inserts ITEM and returns NULL. */ KeywordExt * Hash_Table::insert (KeywordExt *item) { unsigned hash_val = hashpjw (reinterpret_cast(item->_selchars), item->_selchars_length * sizeof (unsigned int)); unsigned int probe = hash_val & (_size - 1); unsigned int increment = (((hash_val >> _log_size) ^ (_ignore_length ? 0 : item->_allchars_length)) << 1) + 1; /* Note that because _size is a power of 2 and increment is odd, we have gcd(increment,_size) = 1, which guarantees that we'll find an empty entry during the loop. */ while (_table[probe] != NULL) { if (equal (_table[probe], item)) return _table[probe]; _collisions++; probe = (probe + increment) & (_size - 1); } _table[probe] = item; return NULL; }