From f6a60033e388dba82ba28b1c02c7d1a4a36e2450 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Sun, 24 Apr 2005 14:23:24 +0000 Subject: [PATCH] Check in unordered associative containers. I haven't looked at these for ages but they pass my unit tests so they should be okay. [SVN r2585] --- include/boost/unordered/detail/allocator.hpp | 152 ++ include/boost/unordered/detail/hash_table.hpp | 1835 +++++++++++++++++ include/boost/unordered_map.hpp | 593 ++++++ include/boost/unordered_set.hpp | 581 ++++++ 4 files changed, 3161 insertions(+) create mode 100644 include/boost/unordered/detail/allocator.hpp create mode 100644 include/boost/unordered/detail/hash_table.hpp create mode 100644 include/boost/unordered_map.hpp create mode 100644 include/boost/unordered_set.hpp diff --git a/include/boost/unordered/detail/allocator.hpp b/include/boost/unordered/detail/allocator.hpp new file mode 100644 index 00000000..aaa414a5 --- /dev/null +++ b/include/boost/unordered/detail/allocator.hpp @@ -0,0 +1,152 @@ +// (C) Copyright Daniel James 2005. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_UNORDERED_DETAIL_ALLOCATOR_UTILITIES_HPP_INCLUDED +#define BOOST_UNORDERED_DETAIL_ALLOCATOR_UTILITIES_HPP_INCLUDED + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +#include +#include + +namespace boost { + namespace unordered_detail { + + // Work around for Microsoft's ETI bug. + + template struct get_value_type + { + typedef typename Allocator::value_type type; + }; + + template struct get_pointer + { + typedef typename Allocator::pointer type; + }; + + template struct get_const_pointer + { + typedef typename Allocator::const_pointer type; + }; + + template struct get_reference + { + typedef typename Allocator::reference type; + }; + + template struct get_const_reference + { + typedef typename Allocator::const_reference type; + }; + + #if defined(BOOST_MPL_CFG_MSVC_ETI_BUG) + + template <> + struct get_value_type + { + typedef int type; + }; + + template <> + struct get_pointer + { + typedef int type; + }; + + template <> + struct get_const_pointer + { + typedef int type; + }; + + template <> + struct get_reference + { + typedef int type; + }; + + template <> + struct get_const_reference + { + typedef int type; + }; + + #endif + + template + struct allocator_constructor + { + typedef typename get_pointer::type pointer; + + Allocator& alloc_; + pointer ptr_; + + allocator_constructor(Allocator& a) + : alloc_(a), ptr_() {} + + ~allocator_constructor() { + if (ptr_) alloc_.deallocate(ptr_, 1); + } + + template + pointer construct(V const& v) { + pointer p = alloc_.allocate(1); + ptr_ = p; + alloc_.construct(p, v); + ptr_ = pointer(); + return p; + } + }; + + template + struct allocator_array_constructor + { + typedef typename get_pointer::type pointer; + + Allocator& alloc_; + pointer ptr_; + pointer constructed_; + std::size_t length_; + + allocator_array_constructor(Allocator& a) + : alloc_(a), ptr_(), constructed_(), length_(0) {} + + ~allocator_array_constructor() { + if (ptr_) { + for(pointer p = ptr_; p != constructed_; ++p) + alloc_.destroy(p); + + alloc_.deallocate(ptr_, length_); + } + } + + template + void construct(V const& v, std::size_t l) + { + length_ = l; + ptr_ = alloc_.allocate(length_); + pointer end = ptr_ + length_; + for(constructed_ = ptr_; constructed_ != end; ++constructed_) + alloc_.construct(constructed_, v); + } + + pointer get() const + { + return ptr_; + } + + pointer release() + { + pointer p(ptr_); + ptr_ = pointer(); + return p; + } + }; + } +} + +#endif diff --git a/include/boost/unordered/detail/hash_table.hpp b/include/boost/unordered/detail/hash_table.hpp new file mode 100644 index 00000000..1ab62f52 --- /dev/null +++ b/include/boost/unordered/detail/hash_table.hpp @@ -0,0 +1,1835 @@ +// JTC1/SC22/WG21 N1456 Hash table implementation +// http://std.dkuug.dk/jtc1/sc22/wg21/docs/papers/2003/n1456.html + +// boost/detail/hash_table.hpp + +// Copyright © 2003-2004 Jeremy B. Maitin-Shepard. +// Copyright © 2005 Daniel James + +// Use, modification, and distribution is subject to the Boost Software +// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy +// at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_UNORDERED_DETAIL_HASH_TABLE_HPP_INCLUDED +#define BOOST_UNORDERED_DETAIL_HASH_TABLE_HPP_INCLUDED + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#if !defined(BOOST_MSVC) || BOOST_MSVC > 1200 +#include +#endif + +// See hash_table::swap() for details about this. +#if !defined(BOOST_UNORDERED_SWAP_METHOD) +#define BOOST_UNORDERED_SWAP_METHOD 3 +#endif + +#if BOOST_UNORDERED_SWAP_METHOD == 1 +#include +#endif + +namespace boost { + namespace unordered_detail { + template struct type_wrapper {}; + + const static std::size_t default_initial_bucket_count = 50; + inline std::size_t next_prime(std::size_t n); + + // I bet this is already in boost somewhere. + + template + void hash_swap(T& x, T& y) + { + using namespace std; + swap(x, y); + } + + // prime number list, accessor + + static const std::size_t prime_list[] = { + 53ul, 97ul, 193ul, 389ul, 769ul, + 1543ul, 3079ul, 6151ul, 12289ul, 24593ul, + 49157ul, 98317ul, 196613ul, 393241ul, 786433ul, + 1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul, + 50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul, + 1610612741ul, 3221225473ul, 4294967291ul }; + + // no throw + inline std::size_t next_prime(std::size_t n) { + std::size_t const* bound = + std::lower_bound(prime_list,prime_list + 28, n); + if(bound == prime_list + 28) + bound--; + return *bound; + } + + // pair_cast - used to convert between pair types. + + template + inline std::pair pair_cast(std::pair const& x) + { + return std::pair(Dst1(x.first), Dst2(x.second)); + } + + // Hash Table Data + // + // Responsible for managing the hash buckets. Has no knowledge of hash + // functions or uniqueness. + + template + class hash_table_data + { + public: + class node; + class bucket; + + typedef std::size_t size_type; + + typedef Alloc value_allocator; + typedef typename boost::detail::allocator::rebind_to::type node_allocator; + typedef typename boost::detail::allocator::rebind_to::type bucket_allocator; + typedef typename get_value_type::type value_type; + typedef typename get_pointer::type node_ptr; + typedef typename get_pointer::type bucket_ptr; + typedef typename get_reference::type reference; + +#if defined(BOOST_UNORDERED_PARANOID) + // If the allocator has the expected pointer types I take some liberties. + BOOST_STATIC_CONSTANT(bool, is_pointer_allocator = + (boost::mpl::and_< + boost::is_same, + boost::is_same + >::value)); + + typedef typename boost::mpl::if_c< + is_pointer_allocator, bucket_ptr, node_ptr>::type link_ptr; +#else + typedef bucket_ptr link_ptr; +#endif + + // Hash Bucket + // + // all no throw (memory management is performed by hash_table_data). + + class bucket + { + bucket& operator=(bucket const&); + public: + link_ptr next_; + + bucket() : next_() + { + } + + bucket(bucket const& x) : next_() + { + // Only copy construct when allocating. + BOOST_ASSERT(!x.next_); + } + + bool empty() const + { + return !this->next_; + } + }; + + // Hash Node + // + // all no throw + + class node : public bucket + { + public: + node(value_type const& v) : bucket(), value_(v) {} + + value_type value_; + }; + +#if !defined(BOOST_UNORDERED_PARANOID) + class node_constructor + { + node_allocator& node_alloc_; + bucket_allocator& bucket_alloc_; + value_allocator value_alloc_; + + node_ptr ptr_; + bool value_allocated_; + bool bucket_allocated_; + + public: + + node_constructor(node_allocator& n, bucket_allocator& b) + : node_alloc_(n), bucket_alloc_(b), value_alloc_(n), + ptr_(), value_allocated_(false), bucket_allocated_(false) + { + } + + ~node_constructor() + { + if (ptr_) { + if (value_allocated_) + value_alloc_.destroy( + value_alloc_.address(ptr_->value_)); + if (bucket_allocated_) + bucket_alloc_.destroy( + bucket_alloc_.address(*ptr_)); + + node_alloc_.deallocate(ptr_, 1); + } + } + + template + node_ptr construct(V const& v) + { + assert(!ptr_); + value_allocated_ = bucket_allocated_ = false; + + ptr_ = node_alloc_.allocate(1); + + bucket_alloc_.construct(bucket_alloc_.address( + *ptr_), bucket()); + bucket_allocated_ = true; + + value_alloc_.construct(value_alloc_.address( + ptr_->value_), v); + value_allocated_ = true; + + node_ptr p = ptr_; + ptr_ = node_ptr(); + return p; + } + }; +#endif + + class local_iterator_base + { + public: + link_ptr node_pointer_; + + local_iterator_base() + : node_pointer_() {} + + explicit local_iterator_base(link_ptr n) + : node_pointer_(n) {} + + bool operator==(local_iterator_base const& x) const + { + return node_pointer_ == x.node_pointer_; + } + + bool operator!=(local_iterator_base const& x) const + { + return node_pointer_ != x.node_pointer_; + } + + reference operator*() const + { + BOOST_ASSERT(node_pointer_); + return static_cast(*node_pointer_).value_; + } + + void increment() + { + BOOST_ASSERT(node_pointer_); + node_pointer_ = node_pointer_->next_; + } + }; + + // Erase Iterator + // + // This is an internal 'iterator' (not an STL iterator) which stores a + // pointer to the pointer to the current node. This is needed to remove + // a node from a bucket. + // + // all no throw. + + class erase_iterator + { + link_ptr* prev_ptr; + + public: + + explicit erase_iterator(bucket_ptr b) + : prev_ptr(&b->next_) {} + + void next() + { + prev_ptr = &(*prev_ptr)->next_; + } + + operator bool() const + { + return *prev_ptr; + } + + value_type& operator*() const + { + return static_cast(**prev_ptr).value_; + } + + link_ptr& get_node() const + { + return *prev_ptr; + } + + bool operator!=(local_iterator_base const& y) + { + return *prev_ptr != y.node_pointer_; + } + }; + + class iterator_base + { + public: + bucket_ptr bucket_; + local_iterator_base local_; + + iterator_base() + : bucket_(), local_() {} + + iterator_base(bucket_ptr b) + : bucket_(b), local_(b->next_) {} + + iterator_base(bucket_ptr b, link_ptr n) + : bucket_(b), local_(n) {} + + iterator_base(bucket_ptr b, local_iterator_base const& it) + : bucket_(b), local_(it) {} + + local_iterator_base local() const + { + return local_iterator_base(local_); + } + + bool operator==(iterator_base const& x) const + { + return local_ == x.local_; + } + + bool operator!=(iterator_base const& x) const + { + return local_ != x.local_; + } + + reference operator*() const + { + return *local_; + } + + void increment() + { + BOOST_ASSERT(bucket_); + local_.increment(); + + while (!local_.node_pointer_) { + ++bucket_; + local_ = local_iterator_base(bucket_->next_); + } + } + }; + + // Member Variables + + node_allocator node_alloc_; + bucket_allocator bucket_alloc_; + + bucket_ptr buckets_; + size_type bucket_count_; + bucket_ptr cached_begin_bucket_; + size_type size_; + + // Constructor + + hash_table_data(size_type n, node_allocator const& a) + : node_alloc_(a), bucket_alloc_(a), + buckets_(), bucket_count_(next_prime(n)), + cached_begin_bucket_(), size_(0) + { + // The array constructor will clean up in the event of an + // exception. + allocator_array_constructor + constructor(bucket_alloc_); + constructor.construct(bucket(), bucket_count_ + 1); + + cached_begin_bucket_ = constructor.get() + bucket_count_; + + // Only release the buckets once everything is successfully + // done. + buckets_ = constructor.release(); + } + + // no throw + ~hash_table_data() + { + if(buckets_) { + if(buckets_[bucket_count_].next_) remove_end_marker(); + + for(size_type i = 0; i < bucket_count_; ++i) + delete_nodes(erase_iterator(buckets_ + i), + end(buckets_ + i)); + + for(size_type i2 = 0; i2 < bucket_count_ + 1; ++i2) + bucket_alloc_.destroy(buckets_ + i2); + + bucket_alloc_.deallocate(buckets_, bucket_count_ + 1); + } + } + + void add_end_marker() + { + BOOST_ASSERT(buckets_ && !buckets_[bucket_count_].next_); +#if !defined(BOOST_UNORDERED_PARANOID) + buckets_[bucket_count_].next_ = buckets_ + bucket_count_; +#else + if(is_pointer_allocator) { + buckets_[bucket_count_].next_ = buckets_ + bucket_count_; + } + else { + // This seems very wasteful, but I can't think of a better way + // to create an end node and work with all allocators. Although, + // I might change it to do something different when + // typename node_allocator::pointer == node*. + buckets_[bucket_count_].next_ = node_alloc_.allocate(1); + } +#endif + } + + void move_end_marker(hash_table_data& src) + { + BOOST_ASSERT(buckets_ && !buckets_[bucket_count_].next_); + BOOST_ASSERT(src.buckets_ && src.buckets_[src.bucket_count_].next_); + +#if !defined(BOOST_UNORDERED_PARANOID) + buckets_[bucket_count_].next_ = buckets_ + bucket_count_; +#else + if(is_pointer_allocator) { + buckets_[bucket_count_].next_ = buckets_ + bucket_count_; + } + else { + buckets_[bucket_count_].next_ + = src.buckets_[src.bucket_count_].next_; + } +#endif + + src.buckets_[src.bucket_count_].next_ = link_ptr(); + } + + void remove_end_marker() + { + BOOST_ASSERT(buckets_ && buckets_[bucket_count_].next_); + +#if defined(BOOST_UNORDERED_PARANOID) + if(!is_pointer_allocator) + node_alloc_.deallocate(static_cast( + buckets_[bucket_count_].next_), 1); +#endif + + buckets_[bucket_count_].next_ = link_ptr(); + } + + private: + + hash_table_data(hash_table_data const&); + hash_table_data& operator=(hash_table_data const&); + + public: + + // no throw + void swap(hash_table_data& other) + { + std::swap(buckets_, other.buckets_); + std::swap(bucket_count_, other.bucket_count_); + std::swap(cached_begin_bucket_, other.cached_begin_bucket_); + std::swap(size_, other.size_); + } + + // Return the bucket index for a hashed value. + // + // no throw + size_type index_from_hash(size_type hashed) const + { + return hashed % bucket_count_; + } + + // Begin & End + // + // no throw + + iterator_base begin() const + { + return size_ + ? iterator_base(cached_begin_bucket_) + : end(); + } + + iterator_base end() const + { + return iterator_base(buckets_ + bucket_count_); + } + + local_iterator_base begin(size_type n) const + { + return local_iterator_base(buckets_[n].next_); + } + + local_iterator_base end(size_type n) const + { + return local_iterator_base(); + } + + local_iterator_base begin(bucket_ptr b) const + { + return local_iterator_base(b->next_); + } + + local_iterator_base end(bucket_ptr b) const + { + return local_iterator_base(); + } + + // Bucket Size + + // no throw + size_type bucket_size(size_type n) const + { + std::size_t count = 0; + local_iterator_base it1 = begin(n); + local_iterator_base it2 = end(n); + while(it1 != it2) { + ++count; + it1.increment(); + } + return count; + } + + // Erase iterator + // + // Find the pointer to a node, for use when erasing. + // + // no throw + + erase_iterator get_for_erase(iterator_base r) const + { + erase_iterator it(r.bucket_); + local_iterator_base end(r.local()); + while(it != end) it.next(); + return it; + } + + // Link/Unlink/Move Node + // + // For adding nodes to buckets, removing them and moving them to a + // new bucket. + // + // no throw + + void link_node(link_ptr node, local_iterator_base pos) + { + node->next_ = pos.node_pointer_->next_; + pos.node_pointer_->next_ = node; + ++size_; + } + + void link_node(link_ptr node, bucket_ptr base) + { + node->next_ = base->next_; + base->next_ = node; + ++size_; + if(base < cached_begin_bucket_) cached_begin_bucket_ = base; + } + + void unlink_node(erase_iterator it) + { + it.get_node() = it.get_node()->next_; + --size_; + } + + void move_node(hash_table_data& src, erase_iterator it, + bucket_ptr dst) + { + link_ptr n = it.get_node(); + src.unlink_node(it); + link_node(n, dst); + } + + // throws, strong exception-safety: + link_ptr construct_node(value_type const& v) + { +#if defined(BOOST_UNORDERED_PARANOID) + allocator_constructor a(node_alloc_); + return a.construct(v); +#else + node_constructor a(node_alloc_, bucket_alloc_); + return bucket_alloc_.address(*a.construct(v)); +#endif + } + + // Create Node + // + // Create a node and add it to the buckets in the given position. + // + // strong exception safety. + + iterator_base create_node(value_type const& v, bucket_ptr base) + { + // throws, strong exception-safety: + link_ptr node = construct_node(v); + + // Rest is no throw + link_node(node, base); + return iterator_base(base, node); + } + + iterator_base create_node(value_type const& v, iterator_base position) + { + // throws, strong exception-safety: + link_ptr node = construct_node(v); + + // Rest is no throw + link_node(node, position.local()); + return iterator_base(position.bucket_, node); + } + + iterator_base create_node(value_type const& v, + bucket_ptr base, local_iterator_base position) + { + // throws, strong exception-safety: + link_ptr node = construct_node(v); + + // Rest is no throw + if(position != end(base)) + link_node(node, position); + else + link_node(node, base); + + return iterator_base(base, node); + } + + // Delete Node + // + // Remove a node, or a range of nodes, from a bucket, and destory + // them. + // + // no throw + + void delete_node(erase_iterator it) + { + node_ptr n = node_alloc_.address( + static_cast(*it.get_node())); + unlink_node(it); + + node_alloc_.destroy(n); + node_alloc_.deallocate(n, 1); + } + + void delete_nodes(erase_iterator begin, local_iterator_base end) + { + while(begin != end) delete_node(begin); + } + + // Clear + // + // Remove all the nodes. + // + // no throw + // + // TODO: If delete_nodes did throw (it shouldn't but just for + // argument's sake), could this leave cached_begin_bucket_ pointing + // at an empty bucket? + + void clear() + { + bucket_ptr end = buckets_ + bucket_count_; + while(cached_begin_bucket_ != end) { + delete_nodes(erase_iterator(cached_begin_bucket_), + this->end(cached_begin_bucket_)); + ++cached_begin_bucket_; + } + BOOST_ASSERT(!size_); + } + + // Erase + // + // Return type of erase(const_iterator): + // http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1753.html#130 + // + // no throw + + iterator_base erase(iterator_base r) + { + BOOST_ASSERT(r != end()); + iterator_base next = r; + next.increment(); + delete_node(get_for_erase(r)); + // r has been invalidated but its bucket is still valid + recompute_begin_bucket(r.bucket_, next.bucket_); + return next; + } + + iterator_base erase(iterator_base r1, iterator_base r2) + { + if(r1 != r2) + { + BOOST_ASSERT(r1 != end()); + + if (r1.bucket_ == r2.bucket_) { + delete_nodes(get_for_erase(r1), r2.local()); + + // No need to call recompute_begin_bucket because + // the nodes are only deleted from one bucket, which + // still contains r2 after the erase. + BOOST_ASSERT(!r1.bucket_->empty()); + } + else { + delete_nodes(get_for_erase(r1), end(r1.bucket_)); + + for(bucket_ptr i = r1.bucket_ + 1; i != r2.bucket_; ++i) + delete_nodes(erase_iterator(i), end(i)); + + delete_nodes(erase_iterator(r2.bucket_), r2.local()); + + // r1 has been invalidated but its bucket is still + // valid. + recompute_begin_bucket(r1.bucket_, r2.bucket_); + } + } + + return r2; + } + + // recompute_begin_bucket + // + // After an erase cached_begin_bucket_ might be left pointing to + // an empty bucket, so this is called to update it + // + // no throw + + void recompute_begin_bucket(bucket_ptr ptr) + { + BOOST_ASSERT(!(ptr < cached_begin_bucket_)); + + if(ptr == cached_begin_bucket_) + { + if (size_ != 0) { + while (cached_begin_bucket_->empty()) + ++cached_begin_bucket_; + } else { + cached_begin_bucket_ = buckets_ + bucket_count_; + } + } + } + + // This is called when a range has been erased + // + // no throw + + void recompute_begin_bucket(bucket_ptr i, bucket_ptr j) + { + BOOST_ASSERT(!(i < cached_begin_bucket_) && !(j < i)); + BOOST_ASSERT(j == buckets_ + bucket_count_ || !j->empty()); + + if(i == cached_begin_bucket_ && i->empty()) + cached_begin_bucket_ = j; + } + }; + + template + struct hash_table_data_type + { + typedef typename boost::detail::allocator::rebind_to::type + value_allocator; + typedef hash_table_data type; + }; + + template + class hash_table + : public hash_table_data_type::type + { + typedef typename hash_table_data_type::type data; + + public: + + typedef typename data::value_allocator value_allocator; + typedef typename data::node_allocator node_allocator; + typedef typename data::bucket_ptr bucket_ptr; + typedef typename data::erase_iterator erase_iterator; + + // Type definitions + + typedef KeyType key_type; + typedef Hash hasher; + typedef Pred key_equal; + typedef ValueType value_type; + typedef std::size_t size_type; + + // iterators + + typedef typename data::local_iterator_base local_iterator_base; + typedef typename data::iterator_base iterator_base; + + private: + + // From the compressed functions docs: + // + // "Finally, a word of caution for Visual C++ 6 users: if either + // argument is an empty type, then assigning to that member will + // produce memory corruption, unless the empty type has a "do + // nothing" assignment operator defined. This is due to a bug in + // the way VC6 generates implicit assignment operators." + // + // Nice. + // + // So use std::pair for Visual C++. + + class functions + { +#if !defined(BOOST_MSVC) || BOOST_MSVC > 1200 + boost::compressed_pair functions_; +#else + typedef std::pair functions; +#endif + + public: + + functions(hasher const& h, key_equal const& k) + : functions_(h, k) {} + + hasher const& hash_function() const + { +#if !defined(BOOST_MSVC) || BOOST_MSVC > 1200 + return functions_.first(); +#else + return functions_.first; +#endif + } + + key_equal const& key_eq() const + { +#if !defined(BOOST_MSVC) || BOOST_MSVC > 1200 + return functions_.second(); +#else + return functions_.second; +#endif + } + }; + + // Both hasher and key_equal's copy/assign can throw so double + // buffering is used to copy them. func_ points to the currently + // active function objects. + + typedef functions hash_table::*functions_ptr; + + functions func1_; + functions func2_; + functions_ptr func_; + + float mlf_; + size_type max_load_; + + public: + + // Constructors + + hash_table(size_type n, + hasher const& hf, key_equal const& eq, + value_allocator const& a) + : data(n, a), // throws, cleans itself up + func1_(hf, eq), // throws " " + func2_(hf, eq), // throws " " + func_(&hash_table::func1_), // no throw + mlf_(1.0f) // no throw + { + this->add_end_marker(); + calculate_max_load(); // no throw + } + + // Construct from iterators + + // initial_size + // + // A helper function for the copy constructor to calculate how many + // nodes will be created if the iterator's support it. Might get it + // totally wrong for containers with unique keys. + // + // no throw + + template + std::size_t initial_size(I i, I j, size_type x, + boost::random_access_traversal_tag) + { + // max load factor isn't set yet, but when it is, it'll be 1.0. + size_type n = j - i + 1; + return n > x ? n : x; + }; + + template + std::size_t initial_size(I i, I j, size_type n, + boost::incrementable_traversal_tag) + { + return n; + }; + + template + std::size_t initial_size(I i, I j, size_type x) + { + typedef typename boost::iterator_traversal::type + iterator_traversal_tag; + return initial_size(i, j, x, iterator_traversal_tag()); + }; + + template + hash_table(I i, I j, size_type n, + hasher const& hf, key_equal const& eq, + node_allocator const& a) + : data(initial_size(i, j, n), a), // throws, cleans itself up + func1_(hf, eq), // throws " " + func2_(hf, eq), // throws " " + func_(&hash_table::func1_), // no throw + mlf_(1.0f) // no throw + { + this->add_end_marker(); + calculate_max_load(); // no throw + + // This can throw, but hash_table_data's destructor will clean + // up. + insert(i, j); + } + + // Copy Construct + + hash_table(hash_table const& x) + : data(x.min_buckets_for_size(x.size()), x.node_alloc_), // throws + func1_(x.current_functions()), // throws + func2_(x.current_functions()), // throws + func_(&hash_table::func1_), // no throw + mlf_(x.mlf_) // no throw + { + this->add_end_marker(); + calculate_max_load(); // no throw + + // This can throw, but hash_table_data's destructor will clean + // up. + copy_buckets(x, *this, current_functions()); + } + + // Assign + // + // basic exception safety, if copy_functions of reserver throws + // the container is left in a sane, empty state. If copy_buckets + // throws the container is left with whatever was successfully + // copied. + + hash_table& operator=(hash_table const& x) + { + if(this != &x) + { + // TODO: I could rewrite this to use the existing nodes. + this->clear(); // no throw + func_ = copy_functions(x); // throws, strong + mlf_ = x.mlf_; // no throw + calculate_max_load(); // no throw + reserve(x.size()); // throws + copy_buckets(x, *this, current_functions()); // throws + } + + return *this; + } + + // Swap + // + // Swap's behaviour when allocators aren't equal is in dispute, see + // this paper for full details: + // + // http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2004/n1599.html + // + // It lists 3 possible behaviours: + // + // 1 - If the allocators aren't equal then throw an exception. + // 2 - Reallocate the elements in the containers with the + // appropriate allocators - messing up exception safety in + // the process. + // 3 - Swap the allocators, hoping that the allocators have a + // no-throw swap. + // + // The paper recommends #3. + // + // I've implemented all three, but actived #3 by default, to change + // it '#define BOOST_UNORDERED_SWAP_METHOD n' where n is the option. + // + // ---------------------------------------------------------------- + // + // Strong exception safety (might change unused function objects) + // + // Can throw if hash or predicate object's copy constructor throws. + // If allocators are unequal: + // Method 1: always throws. + // Method 2: can throw if copying throws + // (memory allocation/hash function object) + // Method 3: Can throw if allocator's swap throws + // (TODO: This one is broken right now, double buffering?) + + void swap(hash_table& x) + { + // This only effects the function objects that aren't in use + // so it is strongly exception safe, via. double buffering. + functions_ptr new_func_this = copy_functions(x); // throws + functions_ptr new_func_that = x.copy_functions(*this); // throws + + if(this->node_alloc_ == x.node_alloc_) { + this->data::swap(x); // no throw + } + else { +#if BOOST_UNORDERED_SWAP_METHOD == 1 + throw std::runtime_error( + "Swapping containers with different allocators.");; +#elif BOOST_UNORDERED_SWAP_METHOD == 2 + // Create new buckets in separate hash_table_data objects + // which will clean up if any of this throws. + data new_this(x.min_buckets_for_size(x.size_), + this->node_alloc_); // throws + copy_buckets(x, new_this, this->*new_func_this); // throws + + data new_that(min_buckets_for_size(this->size_), + x.node_alloc_); // throws + x.copy_buckets(*this, new_that, x.*new_func_that); // throws + + // Start updating the data here, no throw from now on. + new_this.move_end_marker(*this); // no throw + new_that.move_end_marker(x); // no throw + this->data::swap(new_this); // no throw + x.data::swap(new_that); // no throw +#elif BOOST_UNORDERED_SWAP_METHOD == 3 + hash_swap(this->node_alloc_, + x.node_alloc_); // no throw, or is it? + hash_swap(this->bucket_alloc_, + x.bucket_alloc_); // no throw, or is it? + this->data::swap(x); // no throw +#else +#error "Invalid swap method" +#endif + } + + // We've made it, the rest is no throw. + std::swap(mlf_, x.mlf_); + + func_ = new_func_this; + x.func_ = new_func_that; + + calculate_max_load(); + x.calculate_max_load(); + } + + private: + + functions const& current_functions() const + { + return this->*func_; + } + + // This copies the given function objects into the currently unused + // function objects and returns a pointer, that func_ can later be + // set to, to commit the change. + // + // Strong exception safety (since only usued function objects are + // changed). + functions_ptr copy_functions(hash_table const& x) + { + // no throw: + functions_ptr ptr = func_ == &hash_table::func1_ + ? &hash_table::func2_ : &hash_table::func1_; + // throws, functions not in use, so strong + this->*ptr = x.current_functions(); + return ptr; + } + + public: + + // accessors + + // no throw + node_allocator const& get_allocator() const + { + return this->node_alloc_; + } + + // no throw + hasher const& hash_function() const + { + return current_functions().hash_function(); + } + + // no throw + key_equal const& key_eq() const + { + return current_functions().key_eq(); + } + + // no throw + size_type size() const + { + return this->size_; + } + + // no throw + bool empty() const + { + return this->size_ == 0; + } + + // no throw + size_type max_size() const + { + return this->node_alloc_.max_size(); + } + + // strong safety + size_type bucket(key_type const& k) const + { + // hash_function can throw: + return this->index_from_hash(hash_function()(k)); + } + + // strong safety + bucket_ptr get_bucket(key_type const& k) const + { + return this->buckets_ + bucket(k); + } + + // no throw + size_type bucket_count() const + { + return this->bucket_count_; + } + + // no throw + size_type max_bucket_count() const + { + return this->bucket_alloc_.max_size(); + } + + private: + + // no throw + size_type min_buckets_for_size(size_type n) const + { + BOOST_ASSERT(mlf_ != 0); + + using namespace std; + + // From 6.3.1/13: + // size < mlf_ * count + // => count > size / mlf_ + // + // Or from rehash post-condition: + // count > size / mlf_ + return static_cast(floor(n / mlf_)) + 1; + } + + // no throw + void calculate_max_load() + { + using namespace std; + + // From 6.3.1/13: + // Only resize when size >= mlf_ * count + max_load_ = static_cast( + ceil(mlf_ * this->bucket_count_)); + } + + // basic exception safety + bool reserve(size_type n) + { + bool need_to_reserve = n >= max_load_; + // throws - basic: + if (need_to_reserve) rehash_impl(min_buckets_for_size(n)); + return need_to_reserve; + } + + public: + + // no throw + float max_load_factor() const + { + return mlf_; + } + + // no throw + void max_load_factor(float z) + { + mlf_ = z; + calculate_max_load(); + } + + // no throw + float load_factor() const + { + BOOST_ASSERT(this->bucket_count_ != 0); + return static_cast(this->size_) + / static_cast(this->bucket_count_); + } + + private: + + // key extractors + + // no throw + static key_type const& extract_key(value_type const& v) + { + return extract(v, (type_wrapper*)0); + } + + static key_type const& extract(value_type const& v, + type_wrapper*) + { + return v; + } + + static key_type const& extract(value_type const& v, + void*) + { + return v.first; + } + + public: + + // if hash function throws, basic exception safety + // strong otherwise. + void rehash(size_type n) + { + using namespace std; + + // no throw: + size_type min_size = min_buckets_for_size(size()); + // basic/strong: + rehash_impl(min_size > n ? min_size : n); + + BOOST_ASSERT(bucket_count() > size() / max_load_factor() + && bucket_count() >= n); + } + + private: + + // if hash function throws, basic exception safety + // strong otherwise + void rehash_impl(size_type n) + { + n = next_prime(n); // no throw + + if (n == bucket_count()) // no throw + return; + + data new_buckets(n, this->node_alloc_); // throws, seperate + move_buckets(*this, new_buckets); // basic/no throw + new_buckets.swap(*this); // no throw + calculate_max_load(); // no throw + } + + // move_buckets & copy_buckets + // + // Note: Because equivalent elements are already + // adjacent to each other in the existing buckets, this + // simple rehashing technique is sufficient to ensure + // that they remain adjacent to each other in the new + // buckets (but in reverse order). + // + // if the hash function throws, basic excpetion safety + // no throw otherwise + + void move_buckets(data& src, data& dst) + { + BOOST_ASSERT(dst.size_ == 0); + BOOST_ASSERT(src.node_alloc_ == dst.node_alloc_); + + bucket_ptr end = src.buckets_ + src.bucket_count_; // no throw + for(; src.cached_begin_bucket_ != end; // no throw + ++src.cached_begin_bucket_) { + erase_iterator it(src.cached_begin_bucket_); // no throw + while(it) { + // This next line throws iff the hash function throws. + bucket_ptr dst_bucket = dst.buckets_ + + dst.index_from_hash( + hash_function()(extract_key(*it))); + + dst.move_node(src, it, dst_bucket); // no throw + } + } + + // Move the end marker from the source to destination. + // Now destination is valid, source is not. + dst.move_end_marker(src); + } + + // basic excpetion safety, will leave dst partially filled. + + static void copy_buckets(data const& src, data& dst, functions const& f) + { + BOOST_ASSERT(dst.size_ == 0); + + // no throw: + bucket_ptr end = src.buckets_ + src.bucket_count_; + + hasher const& hf = f.hash_function(); + + // no throw: + for(bucket_ptr i = src.cached_begin_bucket_; i != end; ++i) { + // no throw: + for(local_iterator_base it = src.begin(i); + it != src.end(i); it.increment()) { + // hash function can throw. + bucket_ptr dst_bucket = dst.buckets_ + + dst.index_from_hash(hf(extract_key(*it))); + // throws, strong + dst.create_node(*it, dst_bucket); + } + } + } + + public: + + // Insert functions + // + // basic exception safety, if hash function throws + // strong otherwise. + + // if hash function throws, basic exception safety + // strong otherwise + value_type& operator[](key_type const& k) + { + BOOST_STATIC_ASSERT(!EquivalentKeys); + BOOST_STATIC_ASSERT(( + !boost::is_same::value)); + typedef typename value_type::second_type mapped_type; + + bucket_ptr bucket = get_bucket(k); + local_iterator_base node = find_iterator(bucket, k); + + if (node != this->end(bucket)) + return *node; + else + { + // Effects only in this block: + + if (reserve(size() + 1)) // basic/strong + bucket = get_bucket(k); // throws, strong + return *this->create_node( // throws, strong + value_type(k, mapped_type()), + bucket); + } + } + + // Insert for containers with equivalent keys + + private: + + // Insert node without checking if a resize is necessary. + + // strong exception safety. + iterator_base unchecked_insert_equivalent(value_type const& v) + { + key_type const& k = extract_key(v); + bucket_ptr bucket = get_bucket(k); + local_iterator_base position = find_iterator(bucket, k); + + // No effects until here. + return this->create_node(v, bucket, position); // throws, strong + } + + // strong exception safety + iterator_base unchecked_insert_equivalent(iterator_base const& it, + value_type const& v) + { + // Condition throws, no side effects. + if(it != this->end() && equal(extract_key(v), *it)) { + return this->create_node(v, it); // throws, strong + } + else { + return unchecked_insert_equivalent(v); // throws, strong + } + } + + public: + + // if hash function throws, basic exception safety + // strong otherwise + iterator_base insert_equivalent(value_type const& v) + { + reserve(size() + 1); // basic/strong + return unchecked_insert_equivalent(v); // throws, strong + } + + // if hash function throws, basic exception safety + // strong otherwise + iterator_base insert_equivalent(iterator_base const& it, value_type const& v) + { + if (it != this->end() && equal(extract_key(v), *it)) { // throws, no side effects + // Create new node immediately as rehashing will invalidate 'it' + iterator_base new_node = this->create_node(v, it); // throws, strong + + // If reserve rehashes, new_node is invalidated. + if (reserve(size() + 1)) // basic/strong + return iterator_base( // no throw + get_bucket(extract_key(v)), // throws, no effects + new_node.local()); + else + return new_node; + } + else { + return insert_equivalent(v); // basic/strong + } + } + + public: + + // Insert for containers with unique keys + + // if hash function throws, basic exception safety + // strong otherwise + std::pair insert_unique(value_type const& v) + { + // Throws, but no side effects in this initial code + key_type const& k = extract_key(v); + bucket_ptr bucket = get_bucket(k); + local_iterator_base pos = find_iterator(bucket, k); + + if (pos != this->end(bucket)) { // no throw + // Found existing key, return it. + return std::pair( + iterator_base(bucket, pos), false); // no throw + + } else { + // Doesn't already exist, add to bucket. + // Data is only changed in this block. + + // If we resize, then need to recalculate bucket. + if(reserve(size() + 1)) // throws, basic/strong + bucket = get_bucket(k); // throws, strong + + return std::pair( + this->create_node(v, bucket), true); // throws, strong + } + } + + // if hash function throws, basic exception safety + // strong otherwise + iterator_base insert_unique(iterator_base const& it, value_type const& v) + { + // If we are given an iterator pointer to the given key, + // then just return it. + if(it != this->end() && equal(extract_key(v), *it)) // throws, strong + return it; + else + return insert_unique(v).first; // basic, if hash + } + + private: + + // if hash function throws, basic exception safety + // strong otherwise + void insert(value_type const& v) + { + if(EquivalentKeys) + insert_equivalent(v); + else + insert_unique(v); + } + + // if hash function throws, basic exception safety + // strong otherwise + void unchecked_insert(value_type const& v) + { + if(EquivalentKeys) + unchecked_insert_equivalent(v); + else + insert_unique(v); + } + + // Insert from iterators + + private: + + // basic exception safety + template + void insert_for_range(I i, I j, + boost::random_access_traversal_tag) + { + reserve(size() + (j - i)); // basic/strong + for (; i != j; ++i) unchecked_insert(*i); // strong + } + + // basic exception safety + template + void insert_for_range(I i, I j, + boost::incrementable_traversal_tag) + { + for (; i != j; ++i) insert(*i); // basic/strong + } + + public: + + // basic exception safety + template + void insert(InputIterator i, InputIterator j) + { + typedef typename boost::iterator_traversal::type + iterator_traversal_tag; + insert_for_range(i, j, iterator_traversal_tag()); + } + + public: + + // erase + + // no throw + iterator_base erase(iterator_base const& r) + { + return this->data::erase(r); + } + + // strong exception safety + size_type erase(key_type const& k) + { + // No side effects in initial section + bucket_ptr bucket = get_bucket(k); + size_type count = 0; + erase_iterator it(find_for_erase(bucket, k)); + + // Rest is no throw, side effects only after this point. + if (it) { + if (EquivalentKeys) { + do { + ++count; + this->delete_node(it); + } while(it && equal(k, *it)); + } + else { + count = 1; + this->delete_node(it); + } + + this->recompute_begin_bucket(bucket); + } + + return count; + } + + // no throw + iterator_base erase(iterator_base const& r1, iterator_base const& r2) + { + return this->data::erase(r1, r2); + } + + // count + // + // strong exception safety, no side effects + size_type count(key_type const& k) const + { + local_iterator_base it = find_iterator(k); // throws, strong + local_iterator_base end; + size_type count = 0; + + if(it != end) { + if(EquivalentKeys) { + do { + ++count; + it.increment(); + } while (it != end && equal(k, *it)); // throws, strong + } + else { + count = 1; + } + } + + return count; + } + + // find + // + // strong exception safety, no side effects + iterator_base find(key_type const& k) const + { + bucket_ptr bucket = get_bucket(k); + local_iterator_base it = find_iterator(bucket, k); + + if (it != this->end(bucket)) + return iterator_base(bucket, it); + else + return this->end(); + } + + // equal_range + // + // strong exception safety, no side effects + std::pair equal_range(key_type const& k) const + { + bucket_ptr bucket = get_bucket(k); + local_iterator_base it = find_iterator(bucket, k); + if (it != this->end(bucket)) { + local_iterator_base last = it; + + if(EquivalentKeys) { + local_iterator_base end = this->end(bucket); + local_iterator_base next = last; + next.increment(); + + while(next != end && equal(k, *next)) { + last = next; + next.increment(); + } + } + + iterator_base first(iterator_base(bucket, it)); + iterator_base second(iterator_base(bucket, last)); + second.increment(); + return std::pair(first, second); + } + else { + return std::pair( + this->end(), this->end()); + } + } + + private: + + // strong exception safety, no side effects + bool equal(key_type const& k, value_type const& v) const + { + return key_eq()(k, extract_key(v)); + } + + // strong exception safety, no side effects + local_iterator_base find_iterator(key_type const& k) const + { + return find_iterator(get_bucket(k), k); + } + + // strong exception safety, no side effects + local_iterator_base find_iterator(bucket_ptr bucket, + key_type const& k) const + { + local_iterator_base it = this->begin(bucket); + local_iterator_base end = this->end(bucket); + while (it != end && !equal(k, *it)) + it.increment(); + + return it; + } + + // strong exception safety, no side effects + erase_iterator find_for_erase(bucket_ptr bucket, key_type const& k) + const + { + erase_iterator it(bucket); + while(it && !equal(k, *it)) + it.next(); + + return it; + } + }; + + // Iterators + + template class hash_iterator; + template class hash_const_iterator; + template class hash_local_iterator; + template class hash_const_local_iterator; + class iterator_access; + + // Local Iterators + // + // all no throw + + template + class hash_local_iterator + : public boost::iterator < + std::forward_iterator_tag, + typename get_value_type::type, + std::ptrdiff_t, + typename get_pointer::type, + typename get_reference::type > + { + public: + typedef typename hash_local_iterator::pointer pointer; + typedef typename hash_local_iterator::reference reference; + typedef typename get_value_type::type value_type; + + private: + typedef typename hash_table_data::local_iterator_base base; + typedef hash_const_local_iterator const_local_iterator; + + friend class hash_const_local_iterator; + base base_; + + public: + hash_local_iterator() : base_() {} + explicit hash_local_iterator(base x) : base_(x) {} + reference operator*() const { return *base_; } + value_type* operator->() const { return &*base_; } + hash_local_iterator& operator++() { base_.increment(); return *this; } + hash_local_iterator operator++(int) { hash_local_iterator tmp(base_); base_.increment(); return tmp; } + bool operator==(hash_local_iterator x) const { return base_ == x.base_; } + bool operator==(const_local_iterator x) const { return base_ == x.base_; } + bool operator!=(hash_local_iterator x) const { return base_ != x.base_; } + bool operator!=(const_local_iterator x) const { return base_ != x.base_; } + }; + + template + class hash_const_local_iterator + : public boost::iterator < + std::forward_iterator_tag, + typename get_value_type::type, + std::ptrdiff_t, + typename get_const_pointer::type, + typename get_const_reference::type > + { + public: + typedef typename hash_const_local_iterator::pointer pointer; + typedef typename hash_const_local_iterator::reference reference; + typedef typename get_value_type::type value_type; + + private: + typedef typename hash_table_data::local_iterator_base base; + typedef hash_local_iterator local_iterator; + friend class hash_local_iterator; + base base_; + + public: + hash_const_local_iterator() : base_() {} + explicit hash_const_local_iterator(base x) : base_(x) {} + hash_const_local_iterator(local_iterator x) : base_(x.base_) {} + reference operator*() const { return *base_; } + value_type const* operator->() const { return &*base_; } + hash_const_local_iterator& operator++() { base_.increment(); return *this; } + hash_const_local_iterator operator++(int) { hash_const_local_iterator tmp(base_); base_.increment(); return tmp; } + bool operator==(local_iterator x) const { return base_ == x.base_; } + bool operator==(hash_const_local_iterator x) const { return base_ == x.base_; } + bool operator!=(local_iterator x) const { return base_ != x.base_; } + bool operator!=(hash_const_local_iterator x) const { return base_ != x.base_; } + }; + + // iterators + // + // all no throw + + + template + class hash_iterator + : public boost::iterator < + std::forward_iterator_tag, + typename get_value_type::type, + std::ptrdiff_t, + typename get_pointer::type, + typename get_reference::type > + { + public: + typedef typename hash_iterator::pointer pointer; + typedef typename hash_iterator::reference reference; + typedef typename get_value_type::type value_type; + + private: + typedef typename hash_table_data::iterator_base base; + typedef hash_const_iterator const_iterator; + friend class hash_const_iterator; + base base_; + + public: + + hash_iterator() : base_() {} + explicit hash_iterator(base const& x) : base_(x) {} + reference operator*() const { return *base_; } + value_type* operator->() const { return &*base_; } + hash_iterator& operator++() { base_.increment(); return *this; } + hash_iterator operator++(int) { hash_iterator tmp(base_); base_.increment(); return tmp; } + bool operator==(hash_iterator const& x) const { return base_ == x.base_; } + bool operator==(const_iterator const& x) const { return base_ == x.base_; } + bool operator!=(hash_iterator const& x) const { return base_ != x.base_; } + bool operator!=(const_iterator const& x) const { return base_ != x.base_; } + }; + + template + class hash_const_iterator + : public boost::iterator < + std::forward_iterator_tag, + typename get_value_type::type, + std::ptrdiff_t, + typename get_const_pointer::type, + typename get_const_reference::type > + { + public: + typedef typename hash_const_iterator::pointer pointer; + typedef typename hash_const_iterator::reference reference; + typedef typename get_value_type::type value_type; + + private: + typedef typename hash_table_data::iterator_base base; + typedef hash_iterator iterator; + friend class hash_iterator; + friend class iterator_access; + base base_; + + public: + + hash_const_iterator() : base_() {} + explicit hash_const_iterator(base const& x) : base_(x) {} + hash_const_iterator(iterator const& x) : base_(x.base_) {} + reference operator*() const { return *base_; } + value_type const* operator->() const { return &*base_; } + hash_const_iterator& operator++() { base_.increment(); return *this; } + hash_const_iterator operator++(int) { hash_const_iterator tmp(base_); base_.increment(); return tmp; } + bool operator==(iterator const& x) const { return base_ == x.base_; } + bool operator==(hash_const_iterator const& x) const { return base_ == x.base_; } + bool operator!=(iterator const& x) const { return base_ != x.base_; } + bool operator!=(hash_const_iterator const& x) const { return base_ != x.base_; } + }; + + class iterator_access + { + public: + template + static typename Iterator::base const& get(Iterator const& it) { + return it.base_; + } + }; + + template + class hash_types + { + public: + typedef hash_table hash_table; + + typedef typename hash_table::value_allocator value_allocator; + typedef typename hash_table::iterator_base iterator_base; + + typedef hash_const_local_iterator const_local_iterator; + typedef hash_local_iterator local_iterator; + typedef hash_const_iterator const_iterator; + typedef hash_iterator iterator; + + typedef typename hash_table::size_type size_type; + typedef std::ptrdiff_t difference_type; + }; + } // namespace boost::unordered_detail +} // namespace boost + +#endif // BOOST_UNORDERED_DETAIL_HASH_TABLE_HPP_INCLUDED + diff --git a/include/boost/unordered_map.hpp b/include/boost/unordered_map.hpp new file mode 100644 index 00000000..c8a351b0 --- /dev/null +++ b/include/boost/unordered_map.hpp @@ -0,0 +1,593 @@ + +// JTC1/SC22/WG21 N1456 Hash table implementation +// http://std.dkuug.dk/jtc1/sc22/wg21/docs/papers/2003/n1456.html + +// boost/unordered_map.hpp + +// Copyright © 2003-2004 Jeremy B. Maitin-Shepard. +// Copyright © 2005 Daniel James. + +// Use, modification, and distribution is subject to the Boost Software +// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy +// at http://www.boost.org/LICENSE_1_0.txt) + + +#ifndef BOOST_UNORDERED_MAP_HPP_INCLUDED +#define BOOST_UNORDERED_MAP_HPP_INCLUDED + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +#include + +#include +#include + +#include +#include + +namespace boost +{ + //! An unordered associative container that associates unique keys with another value. + /*! For full details see section 6.3.4.4 of the Technical report. + * http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1745.pdf + */ + + template , + class Pred = std::equal_to, + class Alloc = std::allocator > > + class unordered_map + { + // Named for the benefit of Doxygen. + typedef boost::unordered_detail::hash_types< + std::pair, Key, Hash, + Pred, Alloc, false + > implementation_defined; + + typename implementation_defined::hash_table base; + + public: + // types + typedef Key key_type; + typedef std::pair value_type; + typedef T mapped_type; + typedef Hash hasher; + typedef Pred key_equal; + + // This version can be used when compiling against CVS. + typedef Alloc allocator_type; + typedef typename allocator_type::pointer pointer; + typedef typename allocator_type::const_pointer const_pointer; + typedef typename allocator_type::reference reference; + typedef typename allocator_type::const_reference const_reference; + + typedef typename implementation_defined::size_type size_type; + typedef typename implementation_defined::difference_type difference_type; + + typedef typename implementation_defined::iterator iterator; + typedef typename implementation_defined::const_iterator const_iterator; + typedef typename implementation_defined::local_iterator local_iterator; + typedef typename implementation_defined::const_local_iterator const_local_iterator; + + // construct/destroy/copy + + explicit unordered_map( + size_type n = boost::unordered_detail::default_initial_bucket_count, + const hasher &hf = hasher(), + const key_equal &eql = key_equal(), + const allocator_type &a = allocator_type()) + : base(n, hf, eql, a) + { + } + + template + unordered_map(InputIterator f, InputIterator l, + size_type n = boost::unordered_detail::default_initial_bucket_count, + const hasher &hf = hasher(), + const key_equal &eql = key_equal(), + const allocator_type &a = allocator_type()) + : base(f, l, n, hf, eql, a) + { + } + + private: + + typename implementation_defined::iterator_base const& + get(const_iterator const& it) + { + return boost::unordered_detail::iterator_access::get(it); + } + + public: + + allocator_type get_allocator() const + { + return base.get_allocator(); + } + + // size and capacity + + bool empty() const + { + return base.empty(); + } + + size_type size() const + { + return base.size(); + } + + size_type max_size() const + { + return base.max_size(); + } + + // iterators + + iterator begin() + { + return iterator(base.begin()); + } + + const_iterator begin() const + { + return const_iterator(base.begin()); + } + + iterator end() + { + return iterator(base.end()); + } + + const_iterator end() const + { + return const_iterator(base.end()); + } + + // modifiers + + std::pair insert(const value_type& obj) + { + return boost::unordered_detail::pair_cast( + base.insert_unique(obj)); + } + + iterator insert(const_iterator hint, const value_type& obj) + { + return iterator(base.insert_unique(get(hint), obj)); + } + + template + void insert(InputIterator first, InputIterator last) + { + base.insert(first, last); + } + + iterator erase(const_iterator position) + { + return iterator(base.erase(get(position))); + } + + size_type erase(const key_type& k) + { + return base.erase(k); + } + + iterator erase(const_iterator first, const_iterator last) + { + return iterator(base.erase(get(first), get(last))); + } + + void clear() + { + base.clear(); + } + + void swap(unordered_map& other) + { + base.swap(other.base); + } + + // observers + + hasher hash_function() const + { + return base.hash_function(); + } + + key_equal key_eq() const + { + return base.key_eq(); + } + + mapped_type& operator[](const key_type &k) + { + return base[k].second; + } + + // lookup + + iterator find(const key_type& k) + { + return iterator(base.find(k)); + } + + const_iterator find(const key_type& k) const + { + return const_iterator(base.find(k)); + } + + size_type count(const key_type& k) const + { + return base.count(k); + } + + std::pair + equal_range(const key_type& k) + { + return boost::unordered_detail::pair_cast( + base.equal_range(k)); + } + + std::pair + equal_range(const key_type& k) const + { + return boost::unordered_detail::pair_cast( + base.equal_range(k)); + } + + // bucket interface + + size_type bucket_count() const + { + return base.bucket_count(); + } + + size_type max_bucket_count() const + { + return base.max_bucket_count(); + } + + size_type bucket_size(size_type n) const + { + return base.bucket_size(n); + } + + size_type bucket(const key_type& k) const + { + return base.bucket(k); + } + + local_iterator begin(size_type n) + { + return local_iterator(base.begin(n)); + } + + const_local_iterator begin(size_type n) const + { + return const_local_iterator(base.begin(n)); + } + + local_iterator end(size_type n) + { + return local_iterator(base.end(n)); + } + + const_local_iterator end(size_type n) const + { + return const_local_iterator(base.end(n)); + } + + // hash policy + + float load_factor() const + { + return base.load_factor(); + } + + float max_load_factor() const + { + return base.max_load_factor(); + } + + void max_load_factor(float m) + { + base.max_load_factor(m); + } + + void rehash(size_type n) + { + base.rehash(n); + } + }; // class template unordered_map + + template + void swap(unordered_map &m1, + unordered_map &m2) + { + m1.swap(m2); + } + + //! An unordered associative container that associates equivalent keys with another value. + /*! For full details see section 6.3.4.6 of the Technical report. + * http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1745.pdf + */ + + template , + class Pred = std::equal_to, + class Alloc = std::allocator > > + class unordered_multimap + { + // Named for the benefit of Doxygen. + typedef boost::unordered_detail::hash_types< + std::pair, Key, Hash, + Pred, Alloc, true + > implementation_defined; + + typename implementation_defined::hash_table base; + + public: + // types + typedef Key key_type; + typedef std::pair value_type; + typedef T mapped_type; + typedef Hash hasher; + typedef Pred key_equal; + + // This version can be used when compiling against CVS. + + typedef Alloc allocator_type; + typedef typename allocator_type::pointer pointer; + typedef typename allocator_type::const_pointer const_pointer; + typedef typename allocator_type::reference reference; + typedef typename allocator_type::const_reference const_reference; + + typedef typename implementation_defined::size_type size_type; + typedef typename implementation_defined::difference_type difference_type; + + typedef typename implementation_defined::iterator iterator; + typedef typename implementation_defined::const_iterator const_iterator; + typedef typename implementation_defined::local_iterator local_iterator; + typedef typename implementation_defined::const_local_iterator const_local_iterator; + + // construct/destroy/copy + + explicit unordered_multimap( + size_type n = boost::unordered_detail::default_initial_bucket_count, + const hasher &hf = hasher(), + const key_equal &eql = key_equal(), + const allocator_type &a = allocator_type()) + : base(n, hf, eql, a) + { + } + + template + unordered_multimap(InputIterator f, InputIterator l, + size_type n = boost::unordered_detail::default_initial_bucket_count, + const hasher &hf = hasher(), + const key_equal &eql = key_equal(), + const allocator_type &a = allocator_type()) + : base(f, l, n, hf, eql, a) + { + } + + private: + + typename implementation_defined::iterator_base const& + get(const_iterator const& it) + { + return boost::unordered_detail::iterator_access::get(it); + } + + public: + + allocator_type get_allocator() const + { + return base.get_allocator(); + } + + // size and capacity + + bool empty() const + { + return base.empty(); + } + + size_type size() const + { + return base.size(); + } + + size_type max_size() const + { + return base.max_size(); + } + + // iterators + + iterator begin() + { + return iterator(base.begin()); + } + + const_iterator begin() const + { + return const_iterator(base.begin()); + } + + iterator end() + { + return iterator(base.end()); + } + + const_iterator end() const + { + return const_iterator(base.end()); + } + + // modifiers + + iterator insert(const value_type& obj) + { + return iterator(base.insert_equivalent(obj)); + } + + iterator insert(const_iterator hint, const value_type& obj) + { + return iterator(base.insert_equivalent(get(hint), obj)); + } + + template + void insert(InputIterator first, InputIterator last) + { + base.insert(first, last); + } + + iterator erase(const_iterator position) + { + return iterator(base.erase(get(position))); + } + + size_type erase(const key_type& k) + { + return base.erase(k); + } + + iterator erase(const_iterator first, const_iterator last) + { + return iterator(base.erase(get(first), get(last))); + } + + void clear() + { + base.clear(); + } + + void swap(unordered_multimap& other) + { + base.swap(other.base); + } + + // observers + + hasher hash_function() const + { + return base.hash_function(); + } + + key_equal key_eq() const + { + return base.key_eq(); + } + + // lookup + + iterator find(const key_type& k) + { + return iterator(base.find(k)); + } + + const_iterator find(const key_type& k) const + { + return const_iterator(base.find(k)); + } + + size_type count(const key_type& k) const + { + return base.count(k); + } + + std::pair + equal_range(const key_type& k) + { + return boost::unordered_detail::pair_cast( + base.equal_range(k)); + } + + std::pair + equal_range(const key_type& k) const + { + return boost::unordered_detail::pair_cast( + base.equal_range(k)); + } + + // bucket interface + + size_type bucket_count() const + { + return base.bucket_count(); + } + + size_type max_bucket_count() const + { + return base.max_bucket_count(); + } + + size_type bucket_size(size_type n) const + { + return base.bucket_size(n); + } + + size_type bucket(const key_type& k) const + { + return base.bucket(k); + } + + local_iterator begin(size_type n) + { + return local_iterator(base.begin(n)); + } + + const_local_iterator begin(size_type n) const + { + return const_local_iterator(base.begin(n)); + } + + local_iterator end(size_type n) + { + return local_iterator(base.end(n)); + } + + const_local_iterator end(size_type n) const + { + return const_local_iterator(base.end(n)); + } + + // hash policy + + float load_factor() const + { + return base.load_factor(); + } + + float max_load_factor() const + { + return base.max_load_factor(); + } + + void max_load_factor(float m) + { + base.max_load_factor(m); + } + + void rehash(size_type n) + { + base.rehash(n); + } + }; // class template unordered_multimap + + template + void swap(unordered_multimap &m1, + unordered_multimap &m2) + { + m1.swap(m2); + } + +} // namespace boost + +#endif // BOOST_UNORDERED_MAP_HPP_INCLUDED diff --git a/include/boost/unordered_set.hpp b/include/boost/unordered_set.hpp new file mode 100644 index 00000000..ad3ed669 --- /dev/null +++ b/include/boost/unordered_set.hpp @@ -0,0 +1,581 @@ + +// JTC1/SC22/WG21 N1456 Hash table implementation +// http://std.dkuug.dk/jtc1/sc22/wg21/docs/papers/2003/n1456.html + +// boost/unordered_set.hpp + +// Copyright © 2003-2004 Jeremy B. Maitin-Shepard. +// Copyright © 2005 Daniel James. + +// Use, modification, and distribution is subject to the Boost Software +// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy +// at http://www.boost.org/LICENSE_1_0.txt) + + +#ifndef BOOST_UNORDERED_SET_HPP_INCLUDED +#define BOOST_UNORDERED_SET_HPP_INCLUDED + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +#include + +#include +#include + +#include +#include + +namespace boost +{ + //! An unordered associative container that stores unique values. + /*! For full details see section 6.3.4.3 of the Technical report. + * http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1745.pdf + */ + + template , + class Pred = std::equal_to, + class Alloc = std::allocator > + class unordered_set + { + // Named for the benefit of Doxygen. + typedef boost::unordered_detail::hash_types< + Value, Value, Hash, + Pred, Alloc, false + > implementation_defined; + + typename implementation_defined::hash_table base; + + public: + // types + typedef Value key_type; + typedef Value value_type; + typedef Hash hasher; + typedef Pred key_equal; + + typedef Alloc allocator_type; + typedef typename allocator_type::pointer pointer; + typedef typename allocator_type::const_pointer const_pointer; + typedef typename allocator_type::reference reference; + typedef typename allocator_type::const_reference const_reference; + + typedef typename implementation_defined::size_type size_type; + typedef typename implementation_defined::difference_type difference_type; + + typedef typename implementation_defined::const_iterator iterator; + typedef typename implementation_defined::const_iterator const_iterator; + typedef typename implementation_defined::const_local_iterator local_iterator; + typedef typename implementation_defined::const_local_iterator const_local_iterator; + + // construct/destroy/copy + + explicit unordered_set( + size_type n = boost::unordered_detail::default_initial_bucket_count, + const hasher &hf = hasher(), + const key_equal &eql = key_equal(), + const allocator_type &a = allocator_type()) + : base(n, hf, eql, a) + { + } + + template + unordered_set(InputIterator f, InputIterator l, + size_type n = boost::unordered_detail::default_initial_bucket_count, + const hasher &hf = hasher(), + const key_equal &eql = key_equal(), + const allocator_type &a = allocator_type()) + : base(f, l, n, hf, eql, a) + { + } + + private: + + typename implementation_defined::iterator_base const& + get(const_iterator const& it) + { + return boost::unordered_detail::iterator_access::get(it); + } + + public: + + allocator_type get_allocator() const + { + return base.get_allocator(); + } + + // size and capacity + + bool empty() const + { + return base.empty(); + } + + size_type size() const + { + return base.size(); + } + + size_type max_size() const + { + return base.max_size(); + } + + // iterators + + iterator begin() + { + return iterator(base.begin()); + } + + const_iterator begin() const + { + return const_iterator(base.begin()); + } + + iterator end() + { + return iterator(base.end()); + } + + const_iterator end() const + { + return const_iterator(base.end()); + } + + // modifiers + + std::pair insert(const value_type& obj) + { + return boost::unordered_detail::pair_cast( + base.insert_unique(obj)); + } + + iterator insert(const_iterator hint, const value_type& obj) + { + return iterator(base.insert_unique(get(hint), obj)); + } + + template + void insert(InputIterator first, InputIterator last) + { + base.insert(first, last); + } + + iterator erase(const_iterator position) + { + return iterator(base.erase(get(position))); + } + + size_type erase(const key_type& k) + { + return base.erase(k); + } + + iterator erase(const_iterator first, const_iterator last) + { + return iterator(base.erase(get(first), get(last))); + } + + void clear() + { + base.clear(); + } + + void swap(unordered_set& other) + { + base.swap(other.base); + } + + // observers + + hasher hash_function() const + { + return base.hash_function(); + } + + key_equal key_eq() const + { + return base.key_eq(); + } + + // lookup + + iterator find(const key_type& k) + { + return iterator(base.find(k)); + } + + const_iterator find(const key_type& k) const + { + return const_iterator(base.find(k)); + } + + size_type count(const key_type& k) const + { + return base.count(k); + } + + std::pair + equal_range(const key_type& k) + { + return boost::unordered_detail::pair_cast( + base.equal_range(k)); + } + + std::pair + equal_range(const key_type& k) const + { + return boost::unordered_detail::pair_cast( + base.equal_range(k)); + } + + // bucket interface + + size_type bucket_count() const + { + return base.bucket_count(); + } + + size_type max_bucket_count() const + { + return base.max_bucket_count(); + } + + size_type bucket_size(size_type n) const + { + return base.bucket_size(n); + } + + size_type bucket(const key_type& k) const + { + return base.bucket(k); + } + + local_iterator begin(size_type n) + { + return local_iterator(base.begin(n)); + } + + const_local_iterator begin(size_type n) const + { + return local_iterator(base.begin(n)); + } + + local_iterator end(size_type n) + { + return local_iterator(base.end(n)); + } + + const_local_iterator end(size_type n) const + { + return const_local_iterator(base.end(n)); + } + + // hash policy + + float load_factor() const + { + return base.load_factor(); + } + + float max_load_factor() const + { + return base.max_load_factor(); + } + + void max_load_factor(float m) + { + base.max_load_factor(m); + } + + void rehash(size_type n) + { + base.rehash(n); + } + }; // class template unordered_set + + template + void swap(unordered_set &m1, + unordered_set &m2) + { + m1.swap(m2); + } + + //! An unordered associative container that stores equivalent values. + /*! For full details see section 6.3.4.5 of the Technical report. + * http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1745.pdf + */ + + template , + class Pred = std::equal_to, + class Alloc = std::allocator > + class unordered_multiset + { + // Named for the benefit of Doxygen. + typedef boost::unordered_detail::hash_types< + Value, Value, Hash, + Pred, Alloc, true + > implementation_defined; + + typename implementation_defined::hash_table base; + + public: + //types + typedef Value key_type; + typedef Value value_type; + typedef Hash hasher; + typedef Pred key_equal; + + typedef Alloc allocator_type; + typedef typename allocator_type::pointer pointer; + typedef typename allocator_type::const_pointer const_pointer; + typedef typename allocator_type::reference reference; + typedef typename allocator_type::const_reference const_reference; + + typedef typename implementation_defined::size_type size_type; + typedef typename implementation_defined::difference_type difference_type; + + typedef typename implementation_defined::const_iterator iterator; + typedef typename implementation_defined::const_iterator const_iterator; + typedef typename implementation_defined::const_local_iterator local_iterator; + typedef typename implementation_defined::const_local_iterator const_local_iterator; + + // construct/destroy/copy + + explicit unordered_multiset( + size_type n = boost::unordered_detail::default_initial_bucket_count, + const hasher &hf = hasher(), + const key_equal &eql = key_equal(), + const allocator_type &a = allocator_type()) + : base(n, hf, eql, a) + { + } + + template + unordered_multiset(InputIterator f, InputIterator l, + size_type n = boost::unordered_detail::default_initial_bucket_count, + const hasher &hf = hasher(), + const key_equal &eql = key_equal(), + const allocator_type &a = allocator_type()) + : base(f, l, n, hf, eql, a) + { + } + + private: + + typename implementation_defined::iterator_base const& + get(const_iterator const& it) + { + return boost::unordered_detail::iterator_access::get(it); + } + + public: + + allocator_type get_allocator() const + { + return base.get_allocator(); + } + + // size and capacity + + bool empty() const + { + return base.empty(); + } + + size_type size() const + { + return base.size(); + } + + size_type max_size() const + { + return base.max_size(); + } + + // iterators + + iterator begin() + { + return iterator(base.begin()); + } + + const_iterator begin() const + { + return const_iterator(base.begin()); + } + + iterator end() + { + return iterator(base.end()); + } + + const_iterator end() const + { + return const_iterator(base.end()); + } + + // modifiers + + iterator insert(const value_type& obj) + { + return iterator(base.insert_equivalent(obj)); + } + + iterator insert(const_iterator hint, const value_type& obj) + { + return iterator(base.insert_equivalent(get(hint), obj)); + } + + template + void insert(InputIterator first, InputIterator last) + { + base.insert(first, last); + } + + iterator erase(const_iterator position) + { + return iterator(base.erase(get(position))); + } + + size_type erase(const key_type& k) + { + return base.erase(k); + } + + iterator erase(const_iterator first, const_iterator last) + { + return iterator(base.erase(get(first), get(last))); + } + + void clear() + { + base.clear(); + } + + void swap(unordered_multiset& other) + { + base.swap(other.base); + } + + // observers + + hasher hash_function() const + { + return base.hash_function(); + } + + key_equal key_eq() const + { + return base.key_eq(); + } + + // lookup + + iterator find(const key_type& k) + { + return iterator(base.find(k)); + } + + const_iterator find(const key_type& k) const + { + return const_iterator(base.find(k)); + } + + size_type count(const key_type& k) const + { + return base.count(k); + } + + std::pair + equal_range(const key_type& k) + { + return boost::unordered_detail::pair_cast( + base.equal_range(k)); + } + + std::pair + equal_range(const key_type& k) const + { + return boost::unordered_detail::pair_cast( + base.equal_range(k)); + } + + // bucket interface + + size_type bucket_count() const + { + return base.bucket_count(); + } + + size_type max_bucket_count() const + { + return base.max_bucket_count(); + } + + size_type bucket_size(size_type n) const + { + return base.bucket_size(n); + } + + size_type bucket(const key_type& k) const + { + return base.bucket(k); + } + + local_iterator begin(size_type n) + { + return local_iterator(base.begin(n)); + } + + const_local_iterator begin(size_type n) const + { + return const_local_iterator(base.begin(n)); + } + + local_iterator end(size_type n) + { + return local_iterator(base.end(n)); + } + + const_local_iterator end(size_type n) const + { + return const_local_iterator(base.end(n)); + } + + // hash policy + + float load_factor() const + { + return base.load_factor(); + } + + float max_load_factor() const + { + return base.max_load_factor(); + } + + void max_load_factor(float m) + { + base.max_load_factor(m); + } + + void rehash(size_type n) + { + base.rehash(n); + } + }; // class template unordered_multiset + + template + void swap(unordered_multiset &m1, + unordered_multiset &m2) + { + m1.swap(m2); + } + +} // namespace boost + +#endif // BOOST_UNORDERED_SET_HPP_INCLUDED