381 lines
10 KiB
C
381 lines
10 KiB
C
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// Copyright (c) 2000, Microsoft Corp. All rights reserved.
|
||
|
//
|
||
|
// FILE
|
||
|
//
|
||
|
// iascache.h
|
||
|
//
|
||
|
// SYNOPSIS
|
||
|
//
|
||
|
// Declares the classes for creating hash tables and caches.
|
||
|
//
|
||
|
// MODIFICATION HISTORY
|
||
|
//
|
||
|
// 02/07/2000 Original version.
|
||
|
//
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
#ifndef IASCACHE_H
|
||
|
#define IASCACHE_H
|
||
|
#if _MSC_VER >= 1000
|
||
|
#pragma once
|
||
|
#endif
|
||
|
|
||
|
#include <iasobj.h>
|
||
|
#include <iaswin32.h>
|
||
|
|
||
|
class HashTableBase;
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// CLASS
|
||
|
//
|
||
|
// HashTableEntry
|
||
|
//
|
||
|
// DESCRIPTION
|
||
|
//
|
||
|
// Abstract base class for objects that will be stored in a hash table.
|
||
|
//
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
class HashTableEntry
|
||
|
{
|
||
|
public:
|
||
|
virtual void AddRef() throw () = 0;
|
||
|
virtual void Release() throw () = 0;
|
||
|
|
||
|
virtual const void* getKey() const throw () = 0;
|
||
|
|
||
|
virtual bool matches(const void* key) const throw () = 0;
|
||
|
|
||
|
friend class HashTableBase;
|
||
|
|
||
|
protected:
|
||
|
HashTableEntry* next;
|
||
|
|
||
|
virtual ~HashTableEntry() throw ();
|
||
|
};
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// CLASS
|
||
|
//
|
||
|
// HashTableBase
|
||
|
//
|
||
|
// DESCRIPTION
|
||
|
//
|
||
|
// Implements a simple hash table.
|
||
|
//
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
class HashTableBase
|
||
|
{
|
||
|
public:
|
||
|
// Entry type.
|
||
|
typedef HashTableEntry Entry;
|
||
|
|
||
|
typedef ULONG (WINAPI *HashKey)(const void*) throw ();
|
||
|
|
||
|
|
||
|
HashTableBase(
|
||
|
HashKey hashFunction,
|
||
|
ULONG initialSize
|
||
|
);
|
||
|
~HashTableBase() throw ();
|
||
|
|
||
|
// Erases all entries from the table.
|
||
|
void clear() throw ();
|
||
|
|
||
|
// Removes and releases the entry matching key. Returns true if successful.
|
||
|
bool erase(const void* key) throw ();
|
||
|
|
||
|
// Returns the entry with the given key or null if no such entry exists. The
|
||
|
// caller is responsible for releasing the returned entry.
|
||
|
HashTableEntry* find(const void* key) throw ();
|
||
|
|
||
|
// Insert a new entry in the cache. Returns 'true' if the entry was
|
||
|
// successfully inserted. Note: this operation can only fail if
|
||
|
// checkForDuplicates is true and the entry already exists.
|
||
|
bool insert(
|
||
|
HashTableEntry& entry,
|
||
|
bool checkForDuplicates = true
|
||
|
) throw ();
|
||
|
|
||
|
// Removes and returns the entry with the given key or null if no such entry
|
||
|
// exists. The caller is responsible for releasing the returned entry.
|
||
|
HashTableEntry* remove(const void* key) throw ();
|
||
|
|
||
|
// Resize the hash table to have newSize buckets. Returns true if
|
||
|
// successful.
|
||
|
bool resize(ULONG newSize) throw ();
|
||
|
|
||
|
protected:
|
||
|
void lock() throw ()
|
||
|
{ monitor.lock(); }
|
||
|
void unlock() throw ()
|
||
|
{ monitor.unlock(); }
|
||
|
|
||
|
HashTableEntry* bucketAsEntry(size_t bucket) throw ()
|
||
|
{ return (HashTableEntry*)(table + bucket); }
|
||
|
|
||
|
typedef HashTableEntry* Bucket;
|
||
|
|
||
|
HashKey hash; // Hash function for hashing keys.
|
||
|
Bucket* table; // Hash table of entries.
|
||
|
ULONG buckets; // Number of buckets in the table.
|
||
|
ULONG entries; // Number of entries in the table.
|
||
|
CriticalSection monitor; // Synchronizes access to the table.
|
||
|
|
||
|
// Not implemented.
|
||
|
HashTableBase(const HashTableBase&) throw ();
|
||
|
HashTableBase& operator=(const HashTableBase&) throw ();
|
||
|
};
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// CLASS
|
||
|
//
|
||
|
// HashTable<T>
|
||
|
//
|
||
|
// DESCRIPTION
|
||
|
//
|
||
|
// Provides a type safe wrapper around HashTableBase.
|
||
|
//
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
template <class K, class T>
|
||
|
class HashTable : public HashTableBase
|
||
|
{
|
||
|
public:
|
||
|
|
||
|
HashTable(
|
||
|
HashKey hashFunction,
|
||
|
ULONG initialSize
|
||
|
) throw ()
|
||
|
: HashTableBase(hashFunction, initialSize)
|
||
|
{ }
|
||
|
|
||
|
bool erase(const K& key) throw ()
|
||
|
{ return HashTableBase::erase(&key); }
|
||
|
|
||
|
bool erase(const T& value) throw ()
|
||
|
{ return HashTableBase::erase(value.getKey()); }
|
||
|
|
||
|
ObjectPointer<T> find(const K& key) throw ()
|
||
|
{ return ObjectPointer<T>(narrow(HashTableBase::find(&key)), false); }
|
||
|
|
||
|
bool insert(
|
||
|
T& entry,
|
||
|
bool checkForDuplicates = true
|
||
|
) throw ()
|
||
|
{ return HashTableBase::insert(entry, checkForDuplicates); }
|
||
|
|
||
|
ObjectPointer<T> remove(const K& key) throw ()
|
||
|
{ return ObjectPointer<T>(narrow(HashTableBase::remove(&key)), false); }
|
||
|
|
||
|
protected:
|
||
|
static T* narrow(HashTableBase::Entry* entry) throw ()
|
||
|
{ return entry ? static_cast<T*>(entry) : NULL; }
|
||
|
};
|
||
|
class CacheBase;
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// CLASS
|
||
|
//
|
||
|
// CacheEntry
|
||
|
//
|
||
|
// DESCRIPTION
|
||
|
//
|
||
|
// Abstract base class for objects that will be stored in a cache.
|
||
|
//
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
class CacheEntry : public HashTableEntry
|
||
|
{
|
||
|
public:
|
||
|
// Methods for traversing the doubly-linked list.
|
||
|
CacheEntry* prevInList() const throw ();
|
||
|
CacheEntry* nextInList() const throw ();
|
||
|
|
||
|
protected:
|
||
|
friend class CacheBase;
|
||
|
|
||
|
// Removes the node from the list.
|
||
|
void removeFromList() throw ();
|
||
|
|
||
|
// Methods for manipulating the expiration time.
|
||
|
bool isExpired(const ULONG64& now) const throw ();
|
||
|
bool isExpired() const throw ();
|
||
|
void setExpiry(ULONG64 ttl) throw ();
|
||
|
|
||
|
CacheEntry* flink; // Allows this to be an entry in a doubly-linked list.
|
||
|
CacheEntry* blink;
|
||
|
ULONG64 expiry; // Expiration time.
|
||
|
};
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// CLASS
|
||
|
//
|
||
|
// CacheBase
|
||
|
//
|
||
|
// DESCRIPTION
|
||
|
//
|
||
|
// Extends HashTableBase to add support for LRU and TTL based eviction.
|
||
|
//
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
class CacheBase : protected HashTableBase
|
||
|
{
|
||
|
public:
|
||
|
// Entry type
|
||
|
typedef CacheEntry Entry;
|
||
|
|
||
|
CacheBase(
|
||
|
HashKey hashFunction,
|
||
|
ULONG initialSize,
|
||
|
ULONG maxCapacity,
|
||
|
ULONG timeToLive
|
||
|
);
|
||
|
~CacheBase() throw ();
|
||
|
|
||
|
// Erases all entries from the cache.
|
||
|
void clear() throw ();
|
||
|
|
||
|
// Removes and releases the entry matching key. Returns true if successful.
|
||
|
bool erase(const void* key) throw ();
|
||
|
|
||
|
// Evict any eligible entries. Returns the number of entries evicted.
|
||
|
ULONG evict() throw ();
|
||
|
|
||
|
// Returns the entry with the given key or null if no such entry exists. The
|
||
|
// caller is responsible for releasing the returned entry.
|
||
|
CacheEntry* find(const void* key) throw ();
|
||
|
|
||
|
// Insert a new entry in the cache. Returns 'true' if the entry was
|
||
|
// successfully inserted. Note: this operation can only fail if
|
||
|
// checkForDuplicates is true and the entry already exists.
|
||
|
bool insert(
|
||
|
CacheEntry& entry,
|
||
|
bool checkForDuplicates = true
|
||
|
) throw ();
|
||
|
|
||
|
// Removes and returns the entry with the given key or null if no such entry
|
||
|
// exists. The caller is responsible for releasing the returned entry.
|
||
|
CacheEntry* remove(const void* key) throw ();
|
||
|
|
||
|
//////////
|
||
|
// iterator for traversing the cache entries.
|
||
|
//////////
|
||
|
class iterator
|
||
|
{
|
||
|
public:
|
||
|
iterator() throw () {}
|
||
|
|
||
|
iterator(CacheEntry* entry) throw () : p(entry) { }
|
||
|
|
||
|
CacheEntry& operator*() const throw ()
|
||
|
{ return *p; }
|
||
|
|
||
|
CacheEntry* operator->() const throw ()
|
||
|
{ return p; }
|
||
|
|
||
|
iterator& operator++() throw ()
|
||
|
{ p = p->nextInList(); return *this; }
|
||
|
|
||
|
iterator operator++(int) throw ()
|
||
|
{ iterator tmp = *this; ++*this; return tmp; }
|
||
|
|
||
|
iterator& operator--() throw ()
|
||
|
{ p = p->prevInList(); return *this; }
|
||
|
|
||
|
iterator operator--(int) throw ()
|
||
|
{ iterator tmp = *this; --*this; return tmp; }
|
||
|
|
||
|
bool operator==(const iterator& i) const throw ()
|
||
|
{ return p == i.p; }
|
||
|
|
||
|
bool operator!=(const iterator& i) const throw ()
|
||
|
{ return p != i.p; }
|
||
|
|
||
|
protected:
|
||
|
CacheEntry* p;
|
||
|
};
|
||
|
|
||
|
// Iterators for traversing the cache from most to least recently used.
|
||
|
iterator begin() const throw ()
|
||
|
{ return flink; }
|
||
|
iterator end() const throw ()
|
||
|
{ return listAsEntry(); }
|
||
|
|
||
|
protected:
|
||
|
CacheEntry* flink; // Doubly-linked list of entries.
|
||
|
CacheEntry* blink;
|
||
|
ULONG64 ttl; // Time-to-live of cache entries.
|
||
|
ULONG maxEntries; // Max number of entries in the cache.
|
||
|
|
||
|
// Evict an eligible entries without grabbing the lock.
|
||
|
void unsafe_evict() throw ();
|
||
|
|
||
|
// Remove an entry without grabbing the lock.
|
||
|
CacheEntry* unsafe_remove(const void* key) throw ();
|
||
|
|
||
|
// Returns the list as if it were a CacheEntry.
|
||
|
CacheEntry* listAsEntry() const throw ()
|
||
|
{
|
||
|
return (CacheEntry*)
|
||
|
((ULONG_PTR)&flink - FIELD_OFFSET(CacheEntry, flink));
|
||
|
}
|
||
|
|
||
|
// Add an entry to the front of the LRU list (i.e., it is the most recently
|
||
|
// used entry.
|
||
|
void push_front(CacheEntry* entry) throw ();
|
||
|
};
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// CLASS
|
||
|
//
|
||
|
// Cache<T>
|
||
|
//
|
||
|
// DESCRIPTION
|
||
|
//
|
||
|
// Provides a type safe wrapper around CacheBase.
|
||
|
//
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
template <class K, class T>
|
||
|
class Cache : public CacheBase
|
||
|
{
|
||
|
public:
|
||
|
|
||
|
Cache(
|
||
|
HashKey hashFunction,
|
||
|
ULONG initialSize,
|
||
|
ULONG maxCapacity,
|
||
|
ULONG timeToLive
|
||
|
) throw ()
|
||
|
: CacheBase(hashFunction, initialSize, maxCapacity, timeToLive)
|
||
|
{ }
|
||
|
|
||
|
bool erase(const K& key) throw ()
|
||
|
{ return CacheBase::erase(&key); }
|
||
|
|
||
|
bool erase(const T& value) throw ()
|
||
|
{ return CacheBase::erase(value.getKey()); }
|
||
|
|
||
|
ObjectPointer<T> find(const K& key) throw ()
|
||
|
{ return ObjectPointer<T>(narrow(CacheBase::find(&key)), false); }
|
||
|
|
||
|
bool insert(
|
||
|
T& entry,
|
||
|
bool checkForDuplicates = true
|
||
|
) throw ()
|
||
|
{ return CacheBase::insert(entry, checkForDuplicates); }
|
||
|
|
||
|
ObjectPointer<T> remove(const K& key) throw ()
|
||
|
{ return ObjectPointer<T>(narrow(CacheBase::remove(&key)), false); }
|
||
|
|
||
|
protected:
|
||
|
static T* narrow(CacheBase::Entry* entry) throw ()
|
||
|
{ return entry ? static_cast<T*>(entry) : NULL; }
|
||
|
};
|
||
|
|
||
|
#endif // IASCACHE_H
|