/*
* Copyright 2011 Steven Gribble
*
* This file is part of the UW CSE 333 course project sequence
* (333proj).
*
* 333proj 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 3 of the License, or
* (at your option) any later version.
*
* 333proj 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 333proj. If not, see .
*/
#ifndef _HW1_HASHTABLE_H_
#define _HW1_HASHTABLE_H_
#include // for bool, true, false
#include // so we can use uint64_t, etc.
// A HashTable is a simple chained hash table with a static number of buckets.
// We provide the interface; your job is to provide the implementation.
//
// To hide the implementation of HashTable, we declare the "struct htrec"
// structure here, but we define the structure in the internal header
// HashTable_priv.h. This lets us define a pointer to the HashTableRecord as
// a new "HashTable" type, but leave the implementation details of HashTable
// opaque to the customer.
struct htrec;
typedef struct htrec *HashTable;
// When freeing a HashTable, customers need to pass a pointer to a function
// that frees the payload. The pointed-to function is invoked once for each
// value in the HashTable.
typedef void(*ValueFreeFnPtr)(void *value);
// Allocate and return a new HashTable.
//
// Arguments:
//
// - num_buckets: the number of buckets the hash table should
// initially contain. Remember that this is a chained hash
// table, so as the load factor approaches 1, linked lists hanging
// off of each bucket will start to grow. This implementation
// will dynamically resize the hashtable when the load factor
// exceeds 3. It will multiple the number of buckets in the
// hashtable by 3, so that post-resize load factor is 1/3.
//
// Returns NULL on error, non-NULL on success.
HashTable AllocateHashTable(uint32_t num_buckets);
// Free a HashTable.
//
// Arguments:
//
// - table: the HashTable to free. It is unsafe to use table
// after this function returns.
//
// - value_free_function: this argument is a pointer to a value
// freeing function; see above for details.
void FreeHashTable(HashTable table, ValueFreeFnPtr value_free_function);
// Figure out the number of elements in the hash table.
//
// Arguments:
//
// - table: the table to query
//
// Returns:
//
// - table size (>=0); note that this is an unsigned 64-bit integer.
uint64_t NumElementsInHashTable(HashTable table);
// HashTables store key/value pairs. We'll define a key to be an
// unsigned 64-bit integer; it's up to the customer to figure out how
// to produce an appropriate hash key, but below we provide an
// implementation of FNV hashing to help them out. We'll define
// a value to be a (void *), so that customers can pass in pointers to
// arbitrary structs as values, but of course we have to worry about
// memory management as a side-effect.
typedef struct {
uint64_t key; // the key in the key/value pair
void *value; // the value in the key/value pair
} HTKeyValue, *HTKeyValuePtr;
// This is an implementation of FNV hashing. Customers
// can use it to hash an arbitrary sequence of bytes into
// a 64-bit key suitable for using as a hash key.
//
// If you're curious, you can read about FNV hashing here:
//
// http://en.wikipedia.org/wiki/Fowler–Noll–Vo_hash_function
//
// Arguments:
//
// - buffer: a pointer to a len-size buffer of unsigned chars
//
// - len: how many bytes are in the buffer
//
// Returns:
//
// - a nicely distributed 64-bit hash value suitable for
// use in a HTKeyValue
uint64_t FNVHash64(unsigned char *buffer, unsigned int len);
// This is a convenience routine to produce a nice, evenly
// distributed 64-bit hash key from a potentially poorly
// distributed 64 bit number. It uses FNVHash64 to get its
// job done.
//
// Arguments:
//
// - hashme: a 64-bit integer to hash
//
// Returns:
//
// - a nicely distributed 64-bit hash value suitable for
// use in a HTKeyValue
uint64_t FNVHashInt64(uint64_t hashme);
// Inserts a key,value pair into the HashTable.
//
// Arguments:
//
// - table: the HashTable to insert into
//
// - newkeyvalue: the HTKeyValue to insert into the table
//
// - oldkeyval: if the key in newkeyvalue is already present
// in the HashTable, that old key/value is replaced with
// newkeyvalue. In that case, the old key/value is returned via
// this return parameter to the caller. It's up to the caller
// to free any allocated memory associated with oldkeyvalue->value.
//
// Returns:
//
// - 0 on failure (e.g., out of memory)
//
// - +1 if the newkeyvalue was inserted and there was no
// existing key/value with that key
//
// - +2 if the newkeyvalue was inserted and an old key/value
// with the same key was replaced and returned through
// the oldkeyval return parameter. In this case, the caller assumes
// ownership of oldkeyvalue.
int InsertHashTable(HashTable table,
HTKeyValue newkeyvalue,
HTKeyValue *oldkeyvalue);
// Looks up a key in the HashTable, and if it is
// present, returns the key/value associated with it.
//
// Arguments:
//
// - table: the HashTable to look in
//
// - key: the key to look up
//
// - keyvalue: if the key is present, a copy of the key/value is
// returned to the caller via this return parameter. Note that the
// key/value is left in the HashTable, so it is not safe for the
// caller to free keyvalue->value.
//
// Returns:
//
// - -1 if there was an error (e.g., out of memory)
//
// - 0 if the key wasn't found in the HashTable
//
// - +1 if the key was found, and therefore the associated key/value
// was returned to the caller via that keyvalue return parameter.
int LookupHashTable(HashTable table,
uint64_t key,
HTKeyValue *keyvalue);
// Removes a key/value from the HashTable and returns it to the
// caller.
//
// Arguments:
//
// - table: the HashTable to look in
//
// - key: the key to look up
//
// - keyvalue: if the key is present, a copy of key/value is returned
// to the caller via this return parameter and the key/value is
// removed from the HashTable. Note that the caller is responsible
// for managing the memory associated with keyvalue->value from
// this point on.
//
// Returns:
// - -1 on error (e.g., out of memory)
//
// - 0 if the key wasn't found in the HashTable
//
// - +1 if the key was found, and therefore (a) the associated
// key/value was returned to the caller via that keyvalue return
// parameter, and (b) that key/value was removed from the
// HashTable.
int RemoveFromHashTable(HashTable table,
uint64_t key,
HTKeyValue *keyvalue);
// HashTables support the notion of an iterator, similar to
// Java iterators. You use an iterator to iterate forward
// through the HashTable. The order in which the iterator
// goes through the HashTable is undefined, and not necessarily
// deterministic; all that is promised is that each key/value
// is visited exactly once. Also, if the customer uses a
// HashTable function to mutate the hash table, any existing
// iterators become dangerous to use and should be freed.
struct ht_itrec;
typedef struct ht_itrec *HTIter; // same trick to hide implementation.
// Manufacture an iterator for the table. If there are
// elements in the hash table, the iterator is initialized
// to point at the "first" one. If there are no elements
// in the hash table, the iterator is unusable and
//
// Arguments:
//
// - table: the table from which to return an iterator
//
// Returns NULL on failure, non-NULL on success.
HTIter HashTableMakeIterator(HashTable table);
// When you're done with a hash table iterator, you need to free it
// by calling this function.
//
// Arguments:
//
// - iter: the iterator to free. It is not safe to use
// the iterator after freeing it.
void HTIteratorFree(HTIter iter);
// Move the iterator to the next element of the table.
//
// Arguments:
//
// - iter: the iterator to move
//
// Returns:
//
// - +1 on success
//
// - 0 if the iterator cannot be moved forward to the next
// valid element, either because the table is empty, or
// because the iterator (after moving it forward) is past the
// end of the table. In either case, the iterator is no
// longer usable.
int HTIteratorNext(HTIter iter);
// Tests to see whether the iterator is past the end
// of the table.
//
// Arguments:
//
// - iter: the iterator to test
//
// Returns:
//
// - +1 if iter is past the end of the table or the table is empty.
//
// - 0 if iter is not at the end of the table (and therefore
// the table is non-empty).
int HTIteratorPastEnd(HTIter iter);
// Returns a copy of the key/value that the iterator is currently
// pointing at.
//
// Arguments:
//
// - iter: the iterator to fetch the key/value from
//
// - keyvalue: a return parameter through which the key/value
// is returned.
//
// Returns:
//
// - 0 if the iterator is not valid or the table is empty
//
// - +1 on success.
int HTIteratorGet(HTIter iter, HTKeyValue *keyvalue);
// Returns a copy of key/value that the iterator is currently
// pointing at, and removes that key/value from the
// hashtable. The caller assumes ownership of any memory
// pointed to by the value. As well, this advances
// the iterator to the next element in the hashtable.
//
// Arguments:
//
// - iter: the iterator to fetch the key/value from
//
// - keyvalue: a return parameter through which the key/value
// is returned.
//
// Returns:
//
// - 0 if the iterator is not valid or the table is empty
//
// - +1 on success, and the iterator is still valid.
//
// - +2 on success, but the iterator is now invalid because
// it has advanced past the end of the hash table.
int HTIteratorDelete(HTIter iter, HTKeyValue *keyvalue);
#endif // _HW1_HASHTABLE_H_