blob: 6c25ab527aaa3ff78375a3be7c0f296b5b222030 [file] [log] [blame]
/*
* Copyright 1999-2004 The Apache Software Foundation.
*
* Licensed 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.
*/
#if !defined(XALANMAP_HEADER_GUARD_1357924680)
#define XALANMAP_HEADER_GUARD_1357924680
// Base include file. Must be first.
#include <xalanc/Include/PlatformDefinitions.hpp>
#include <cstddef>
#include <algorithm>
#include <functional>
#include <utility>
#include <xalanc/Include/XalanMemoryManagement.hpp>
#include <xalanc/Include/XalanVector.hpp>
#include <xalanc/Include/XalanList.hpp>
XALAN_CPP_NAMESPACE_BEGIN
typedef size_t size_type;
template <class Key>
class XalanHasher : public XALAN_STD_QUALIFIER unary_function<Key, size_type>
{
public:
size_type operator()(const Key& key) const
{
const char *byteArray = reinterpret_cast<const char*>(&key);
size_type result = 0;
for (size_type i = 0; i < sizeof(Key); ++i)
{
result = (result << 1) ^ byteArray[i];
}
return result;
}
};
template <class Key>
struct XalanMapKeyTraits
{
typedef XalanHasher<Key> Hasher;
typedef XALAN_STD_QUALIFIER equal_to<Key> Comparator;
};
template <class Key>
struct XalanHashMemberPointer
{
size_type operator() (const Key * key) const
{
assert (key != 0);
return key->hash();
}
};
template <class Key>
struct XalanHashMemberReference
{
size_type operator() (const Key& key) const
{
return key.hash();
}
};
/**
* Xalan map entry pair of a key and data
*/
template <class KeyType, class DataType>
struct XalanMapEntry
{
typedef KeyType first_type;
typedef DataType second_type;
typedef size_t size_type;
XalanMapEntry(
const first_type & theFirst,
const second_type& theSecond,
size_type index = 0) :
first(theFirst),
second(theSecond),
bucketIndex(index)
{
}
XalanMapEntry() :
first(),
second(),
bucketIndex(size_type())
{
}
first_type first;
second_type second;
size_type bucketIndex;
};
template <class Value>
struct XalanMapIteratorTraits
{
typedef Value value_type;
typedef Value& reference;
typedef Value* pointer;
};
template <class Value>
struct XalanMapConstIteratorTraits
{
typedef Value value_type;
typedef const Value& reference;
typedef const Value* pointer;
};
template <class XalanMapTraits, class BaseIterator>
struct XalanMapIterator : public BaseIterator
{
typedef typename XalanMapTraits::value_type value_type;
typedef typename XalanMapTraits::reference reference;
typedef typename XalanMapTraits::pointer pointer;
typedef ptrdiff_t difference_type;
typedef XALAN_STD_QUALIFIER bidirectional_iterator_tag iterator_category;
typedef XalanMapIterator<
XalanMapIteratorTraits<value_type>,
typename BaseIterator::iterator> Iterator;
XalanMapIterator() :
BaseIterator()
{
}
XalanMapIterator(const Iterator & theRhs) :
BaseIterator(theRhs)
{
}
XalanMapIterator(const BaseIterator& theRhs) :
BaseIterator(theRhs)
{
}
XalanMapIterator operator++(int)
{
XalanMapIterator temp(*this);
BaseIterator::operator++();
return temp;
}
XalanMapIterator& operator++()
{
BaseIterator::operator++();
return *this;
}
reference operator*() const
{
return *(BaseIterator::operator*());
}
pointer operator->() const
{
return (BaseIterator::operator*());
}
};
/**
* Xalan implementation of a hashtable.
*
*/
template <
class Key,
class Value,
class KeyTraits = XalanMapKeyTraits<Key> >
class XalanMap
{
public:
/**
* Each map entry is stored in a linked list.
* The hash buckets are a vector of pointers into the entry list
* An empty bucket will point to the end of the list,
* A non-empty bucket will point to its first entry. Remaining
* entries in the chain follow the first and have the same index value.
*/
typedef Key key_type;
typedef Value data_type;
typedef size_t size_type;
typedef XalanMapEntry<const key_type, data_type> value_type;
typedef value_type Entry;
typedef XalanList<Entry*> EntryListType;
typedef XalanMapIterator<XalanMapIteratorTraits<Entry>, typename EntryListType::iterator> iterator;
typedef XalanMapIterator<XalanMapConstIteratorTraits<Entry>, typename EntryListType::const_iterator> const_iterator;
typedef XalanVector<iterator> EntryPosVectorType;
typedef typename MemoryManagedConstructionTraits<key_type>::Constructor FirstConstructor;
typedef typename MemoryManagedConstructionTraits<data_type>::Constructor SecondConstructor;
XalanMap(
MemoryManagerType* theMemoryManager = 0,
float loadFactor = 0.75,
size_type minBuckets = 10) :
m_memoryManager(theMemoryManager),
m_loadFactor(loadFactor),
m_minBuckets(minBuckets),
m_size(0),
m_entries(m_memoryManager),
m_buckets(m_memoryManager)
{
}
XalanMap(
const XalanMap &theRhs,
MemoryManagerType* theManager = 0) :
m_memoryManager(theManager != 0 ? theManager : theRhs.m_memoryManager),
m_loadFactor(theRhs.m_loadFactor),
m_minBuckets(10),
m_size(0),
m_entries(m_memoryManager),
m_buckets(size_type(m_loadFactor * theRhs.size())+ 1, m_entries.end(), m_memoryManager)
{
const_iterator entry = theRhs.begin();
while(entry != theRhs.end())
{
insert(*entry);
++entry;
}
assert(m_size == theRhs.m_size);
}
~XalanMap()
{
doRemoveEntries();
}
XalanMap & operator=(const XalanMap& theRhs)
{
XalanMap theTemp(theRhs);
swap(theTemp);
return *this;
}
size_type size() const
{
return m_size;
}
bool empty() const
{
return m_size == 0;
}
iterator begin()
{
return m_entries.begin();
}
const_iterator begin() const
{
return m_entries.begin();
}
iterator end()
{
return m_entries.end();
}
const_iterator end() const
{
return m_entries.end();
}
iterator find(const key_type& key)
{
if (!m_buckets.empty())
{
const size_type index = doHash(key);
assert(index < m_buckets.size());
iterator bucketPos = m_buckets[index];
while (bucketPos != m_entries.end() &&
bucketPos->bucketIndex == index)
{
if (m_equals(key,bucketPos->first))
{
return bucketPos;
}
++bucketPos;
}
}
return end();
}
const_iterator find(const key_type& key) const
{
return const_cast<XalanMap *>(this)->find(key);
}
data_type & operator[](const key_type& key)
{
iterator pos = find(key);
if (pos == end())
{
pos = doCreateEntry(key);
}
return (*pos).second;
}
void insert(const value_type& value)
{
const key_type& key = value.first;
const data_type& data = value.second;
insert(key, data);
}
void insert(const key_type& key, const data_type& data)
{
const const_iterator pos = find(key);
if (pos == end())
{
doCreateEntry(key, &data);
}
}
void erase(iterator pos)
{
if (pos != end())
{
doRemoveEntry(pos);
}
}
size_type erase(const key_type& key)
{
const iterator pos = find(key);
if (pos != end())
{
erase(pos);
return 1;
}
else
{
return 0;
}
}
void clear()
{
doRemoveEntries();
m_size = 0;
XALAN_STD_QUALIFIER fill(
m_buckets.begin(),
m_buckets.end(),
m_entries.end());
m_entries.clear();
}
void swap(XalanMap& theRhs)
{
size_type tempSize = m_size;
m_size = theRhs.m_size;
theRhs.m_size = tempSize;
MemoryManagerType* tempMemoryManager = m_memoryManager;
m_memoryManager = theRhs.m_memoryManager;
theRhs.m_memoryManager = tempMemoryManager;
m_entries.swap(theRhs.m_entries);
m_buckets.swap(theRhs.m_buckets);
}
protected:
iterator doCreateEntry(const key_type & key, const data_type* data = 0)
{
// if there are no buckets, create initial minimum set of buckets
if (m_buckets.empty())
{
m_buckets.insert(m_buckets.begin(), m_minBuckets, m_entries.end());
}
// if the load factor has been reached, rehash
if (size_type(m_loadFactor * size()) > m_buckets.size())
{
rehash();
}
size_type index = doHash(key);
iterator & bucketStartPos = m_buckets[index];
// insert a new entry as the first position in the bucket
Entry * newEntry = allocate(1);
FirstConstructor::construct(const_cast<key_type*>(&newEntry->first), key, *m_memoryManager);
if (data != 0)
{
SecondConstructor::construct(&newEntry->second, *data, *m_memoryManager);
}
else
{
SecondConstructor::construct(&newEntry->second, *m_memoryManager);
}
new (&newEntry->bucketIndex) size_type(index);
bucketStartPos = m_entries.insert(bucketStartPos, newEntry);
++m_size;
return bucketStartPos;
}
void doRemoveEntry(const iterator & toRemovePos)
{
size_type index = toRemovePos->bucketIndex;
iterator nextPos = ++(iterator(toRemovePos));
// if the entry to remove is the first in the bucket
// set the next entry as the first or,
// if there are no more entries set it to the end
if (m_buckets[index] == toRemovePos)
{
if (nextPos != m_entries.end() &&
nextPos->bucketIndex == index)
{
m_buckets[index] = nextPos;
}
else
{
m_buckets[index] = m_entries.end();
}
}
value_type& toRemove = *toRemovePos;
#if defined(_MSC_VER) && _MSC_VER <= 1300
toRemove.value_type::~value_type();
#else
toRemove.~value_type();
#endif
deallocate(&toRemove);
m_entries.erase(toRemovePos);
--m_size;
}
void
doRemoveEntries()
{
while(size() > 0)
{
doRemoveEntry(begin());
}
}
size_type doHash(const Key & key) const
{
return m_hash(key) % m_buckets.size();
}
void rehash()
{
// grow the number of buckets by 60%
EntryPosVectorType temp(size_type(1.6 * size()), m_entries.end(), m_memoryManager);
m_buckets.swap(temp);
// move current entries into a temporary list
EntryListType tempEntryList;
tempEntryList.splice(tempEntryList.begin(),m_entries, m_entries.begin(), m_entries.end());
// rehash each entry assign to bucket and insert into list
while (tempEntryList.begin() != tempEntryList.end())
{
iterator entry = tempEntryList.begin();
entry->bucketIndex = doHash(entry->first);
iterator & bucketStartPos = m_buckets[entry->bucketIndex];
// if the bucket is empty assign to the entry and insert
// into the list, otherwise insert into front of the
// current first entry
if (bucketStartPos == m_entries.end())
{
bucketStartPos = entry;
m_entries.splice(m_entries.begin(), tempEntryList, entry);
}
else
{
m_entries.splice(bucketStartPos, tempEntryList, entry);
--bucketStartPos;
}
}
}
value_type*
allocate(size_type size)
{
const size_type theBytesNeeded = size * sizeof(value_type);
void* pointer = m_memoryManager == 0 ?
::operator new (theBytesNeeded) :
m_memoryManager->allocate(theBytesNeeded);
assert(pointer != 0);
return (value_type*) pointer;
}
void
deallocate(value_type* pointer)
{
if (m_memoryManager == 0)
{
::operator delete(pointer);
}
else
{
m_memoryManager->deallocate(pointer);
}
}
// Data members...
typename KeyTraits::Hasher m_hash;
typename KeyTraits::Comparator m_equals;
MemoryManagerType* m_memoryManager;
float m_loadFactor;
size_type m_minBuckets;
size_type m_size;
EntryListType m_entries;
EntryPosVectorType m_buckets;
};
XALAN_CPP_NAMESPACE_END
#endif // XALANMAP_HEADER_GUARD_1357924680