diff --git a/doc/Jamfile.v2 b/doc/Jamfile.v2 new file mode 100644 index 00000000..95911f47 --- /dev/null +++ b/doc/Jamfile.v2 @@ -0,0 +1,17 @@ + +# Copyright 2005 Daniel James. +# Distributed under 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) + +using quickbook ; + +xml unordered : unordered.qbk ; +boostbook standalone : unordered : + boost.root=../../../.. + boost.libraries=../../../libraries.htm + html.stylesheet=../../../../doc/html/boostbook.css + chunk.first.sections=1 + chunk.section.depth=2 + generate.section.toc.level=2 + toc.section.depth=1 + toc.max.depth=1 ; diff --git a/doc/buckets.qbk b/doc/buckets.qbk new file mode 100644 index 00000000..3aff9a41 --- /dev/null +++ b/doc/buckets.qbk @@ -0,0 +1,146 @@ +[/ Copyright 2006-2007 Daniel James. + / Distributed under 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) ] + +[section:buckets The Data Structure] + +The containers are made up of a number of 'buckets', each of which can contain +any number of elements. For example, the following diagram shows an [classref +boost::unordered_set unordered_set] with 7 buckets containing 5 elements, `A`, +`B`, `C`, `D` and `E` (this is just for illustration, containers will typically +have more buckets). + +[$../../libs/unordered/doc/diagrams/buckets.png] + +In order to decide which bucket to place an element in, the container applies +the hash function, `Hash`, to the element's key (for `unordered_set` and +`unordered_multiset` the key is the whole element, but is referred to as the key +so that the same terminology can be used for sets and maps). This returns a +value of type `std::size_t`. `std::size_t` has a much greater range of values +then the number of buckets, so that container applies another transformation to +that value to choose a bucket to place the element in. + +Retreiving the elements for a given key is simple. The same process is applied +to the key to find the correct bucket. Then the key is compared with the +elements in the bucket to find any elements that match (using the equality +predicate `Pred`). If the hash function has worked well the elements will be +evenly distributed amongst the buckets so only a small number of elements will +need to be examined. + +There is [link unordered.hash_equality more information on hash functions and +equality predicates in the next section]. + +You can see in the diagram that `A` & `D` have been placed in the same bucket. +When looking for elements in this bucket up to 2 comparisons are made, making +the search slower. This is known as a collision. To keep things fast we try to +keep collisions to a minimum. + +[table Methods for Accessing Buckets + [[Method] [Description]] + + [ + [``size_type bucket_count() const``] + [The number of buckets.] + ] + [ + [``size_type max_bucket_count() const``] + [An upper bound on the number of buckets.] + ] + [ + [``size_type bucket_size(size_type n) const``] + [The number of elements in bucket `n`.] + ] + [ + [``size_type bucket(key_type const& k) const``] + [Returns the index of the bucket which would contain k] + ] + [ + [`` + local_iterator begin(size_type n); + local_iterator end(size_type n); + const_local_iterator begin(size_type n) const; + const_local_iterator end(size_type n) const; + const_local_iterator cbegin(size_type n) const; + const_local_iterator cend(size_type n) const; + ``] + [Return begin and end iterators for bucket `n`.] + ] +] + +[h2 Controlling the number of buckets] + +As more elements are added to an unordered associative container, the number +of elements in the buckets will increase causing performance to degrade. +To combat this the containers increase the bucket count as elements are inserted. +You can also tell the container to change the bucket count (if required) by +calling `rehash`. + +The standard leaves a lot of freedom to the implementor to decide how the +number of buckets are chosen, but it does make some requirements based on the +container's 'load factor', the average number of elements per bucket. +Containers also have a 'maximum load factor' which they should try to keep the +load factor below. + +You can't control the bucket count directly but there are two ways to +influence it: + +* Specify the minimum number of buckets when constructing a container or + when calling `rehash`. +* Suggest a maximum load factor by calling `max_load_factor`. + +`max_load_factor` doesn't let you set the maximum load factor yourself, it just +lets you give a /hint/. And even then, the draft standard doesn't actually +require the container to pay much attention to this value. The only time the +load factor is /required/ to be less than the maximum is following a call to +`rehash`. But most implementations will try to keep the number of elements +below the max load factor, and set the maximum load factor to be the same as +or close to the hint - unless your hint is unreasonably small or large. + +[table Methods for Controlling Bucket Size + [[Method] [Description]] + + [ + [``float load_factor() const``] + [The average number of elements per bucket.] + ] + [ + [``float max_load_factor() const``] + [Returns the current maximum load factor.] + ] + [ + [``float max_load_factor(float z)``] + [Changes the container's maximum load factor, using `z` as a hint.] + ] + [ + [``void rehash(size_type n)``] + [Changes the number of buckets so that there at least n buckets, and + so that the load factor is less than the maximum load factor.] + ] + +] + +[h2 Iterator Invalidation] + +It is not specified how member functions other than `rehash` affect +the bucket count, although `insert` is only allowed to invalidate iterators +when the insertion causes the load factor to be greater than or equal to the +maximum load factor. For most implementations this means that insert will only +change the number of buckets when this happens. While iterators can be +invalidated by calls to `insert` and `rehash`, pointers and references to the +container's elements are never invalidated. + +In a similar manner to using `reserve` for `vector`s, it can be a good idea +to call `rehash` before inserting a large number of elements. This will get +the expensive rehashing out of the way and let you store iterators, safe in +the knowledge that they won't be invalidated. If you are inserting `n` +elements into container `x`, you could first call: + + x.rehash((x.size() + n) / x.max_load_factor() + 1); + +[blurb Note: `rehash`'s argument is the minimum number of buckets, not the +number of elements, which is why the new size is divided by the maximum load factor. The +`+ 1` guarantees there is no invalidation; without it, reallocation could occur +if the number of bucket exactly divides the target size, since the container is +allowed to rehash when the load factor is equal to the maximum load factor.] + +[endsect] diff --git a/doc/comparison.qbk b/doc/comparison.qbk new file mode 100644 index 00000000..689efd0f --- /dev/null +++ b/doc/comparison.qbk @@ -0,0 +1,164 @@ +[/ Copyright 2006-2007 Daniel James. + / Distributed under 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) ] + +[section:comparison Comparison with Associative Containers] + +[table Interface differences. + [[Associative Containers] [Unordered Associative Containers]] + + [ + [Parameterized by an ordering relation `Compare`] + [Parameterized by a function object `Hash` and an equivalence relation + `Pred`] + ] + [ + [Keys can be compared using `key_compare` which is accessed by member function `key_comp()`, + values can be compared using `value_compare` which is accessed by member function `value_comp()`.] + [Keys can be hashed using `hasher` which is accessed by member function `hash_function()`, + and checked for equality using `key_equal` which is accessed by member function `key_eq()`. + There is no function object for compared or hashing values.] + ] + [ + [Constructors have optional extra parameters for the comparison object.] + [Constructors have optional extra parameters for the initial minimum + number of buckets, a hash function and an equality object.] + ] + + [ + [Keys `k1`, `k2` are considered equivalent if + `!Compare(k1, k2) && !Compare(k2, k1)`] + [Keys `k1`, `k2` are considered equivalent if `Pred(k1, k2)`] + ] + [ + [Member function `lower_bound(k)` and `upper_bound(k)`] + [No equivalent. Since the elements aren't ordered `lower_bound` and + `upper_bound` would be meaningless.] + ] + [ + [`equal_range(k)` returns an empty range at the position that k + would be inserted if k isn't present in the container.] + [`equal_range(k)` returns a range at the end of the container if + k isn't present in the container. It can't return a positioned + range as k could be inserted into multiple place. To find out the + bucket that k would be inserted into use `bucket(k)`. But remember + that an insert can cause the container to rehash - meaning that the + element can be inserted into a different bucket.] + ] + [ + [`iterator`, `const_iterator` are of the bidirectional category.] + [`iterator`, `const_iterator` are of at least the forward category.] + ] + [ + [Iterators, pointers and references to the container's elements are + never invalidated.] + [[link unordered.buckets.iterator_invalidation Iterators can + be invalidated by calls to insert or rehash]. Pointers and + references to the container's elements are never invalidated.] + ] + [ + [Iterators iterate through the container in the order defined by + the comparison object.] + [Iterators iterate through the container in an arbitrary order, that + can change as elements are inserted. Although, equivalent elements + are always adjacent.] + ] + [ + [No equivalent] + [Local iterators can be used to iterate through individual buckets. + (I don't think that the order of local iterators and iterators are + required to have any correspondence.)] + ] + [ + [Can be compared using the `==`, `!=`, `<`, `<=`, `>`, `>=` operators.] + [No comparison operators are defined in the standard, although + [link unordered.rationale.equality_operator + implementations might extend the containers to support `==` and + `!=`].] + ] + [ + [] + [When inserting with a hint, implementations are permitted to ignore + the hint.] + ] + [ + [`erase` never throws an exception] + [The containers hash or predicate function can throw exceptions + from `erase`] + ] +] + +[table Complexity Guarantees + [[Operation] [Associative Containers] [Unordered Associative Containers]] + [ + [Construction of empty container] + [constant] + [O(/n/) where /n/ is the minimum number of buckets.] + ] + [ + [Construction of container from a range of /N/ elements] + [O(/N/ log /N/), O(/N/) if the range is sorted with `value_comp()`] + [Average case O(/N/), worst case + O(/N/'''2''')] + ] + [ + [Insert a single element] + [logarithmic] + [Average case constant, worst case linear] + ] + [ + [Insert a single element with a hint] + [Amortized constant if t elements inserted right after hint, + logarithmic otherwise] + [Average case constant, worst case linear (ie. the same as + a normal insert).] + ] + [ + [Inserting a range of /N/ elements] + [ /N/ log(`size()`+/N/) ] + [Average case O(/N/), worst case O(/N/ * `size()`)] + ] + [ + [Erase by key, `k`] + [O(log(`size()`) + `count(k)`)] + [Average case: O(`count(k)`), Worst case: O(`size()`)] + ] + [ + [Erase a single element by iterator] + [Amortized constant] + [Average case: O(1), Worst case: O(`size()`)] + ] + [ + [Erase a range of /N/ elements] + [O(log(`size()`) + /N/)] + [Average case: O(/N/), Worst case: O(`size()`)] + ] + [ + [Clearing the container] + [O(`size()`)] + [O(`size()`)] + ] + [ + [Find] + [logarithmic] + [Average case: O(1), Worst case: O(`size()`)] + ] + [/ TODO: Average case is probably wrong. ] + [ + [Count] + [O(log(`size()`) + `count(k)`)] + [Average case: O(1), Worst case: O(`size()`)] + ] + [ + [`equal_range(k)`] + [logarithmic] + [Average case: O(`count(k)`), Worst case: O(`size()`)] + ] + [ + [`lower_bound`,`upper_bound`] + [logarithmic] + [n/a] + ] +] + +[endsect] diff --git a/doc/diagrams/buckets.dia b/doc/diagrams/buckets.dia new file mode 100644 index 00000000..b28596c9 Binary files /dev/null and b/doc/diagrams/buckets.dia differ diff --git a/doc/diagrams/buckets.png b/doc/diagrams/buckets.png new file mode 100644 index 00000000..971c5c29 Binary files /dev/null and b/doc/diagrams/buckets.png differ diff --git a/doc/hash_equality.qbk b/doc/hash_equality.qbk new file mode 100644 index 00000000..22e83573 --- /dev/null +++ b/doc/hash_equality.qbk @@ -0,0 +1,71 @@ +[/ Copyright 2006-2007 Daniel James. + / Distributed under 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) ] + +[section:hash_equality Equality Predicates and Hash Functions] + +While the associative containers use an ordering relation to specify how the +elements are stored, the unordered associative containers use an equality +predicate and a hash function. For example, [classref boost::unordered_set] +is declared as: + + template, + typename Pred = std::equal_to, + typename Alloc = std::allocator > + class ``[classref boost::unordered_set unordered_set]``; + +The hash function comes first as you might want to change the hash function +but not the equality predicate, while if you were to change the behavior +of the equality predicate you would have to change the hash function to match +it. So, if you wanted to use the +[@http://www.isthe.com/chongo/tech/comp/fnv/ FNV-1 hash] you could write: + + ``[classref boost::unordered_set]`` words; + +An example implementation of FNV-1, and some other hash functions are supplied +in the examples directory. + +Alternatively, you might wish to use a different equality function. If you do +this you will need to use a hash function that matches it. So to implement a +case-insensitive dictionary: + +[import src_code/insensitive.cpp] +[case_insensitive_functions] +[case_insensitive_dictionary] + +This is a simplified version of the example at +[@../../libs/unordered/examples/case_insensitive.hpp /libs/unordered/examples/case_insensitive.hpp] +which supports other locales and string types. + +[h2 Custom Types] + +Similarly, a custom hash function can be used for custom types: + +[import src_code/point1.cpp] +[point_example1] + +Although, [link hash.custom extending boost::hash to support the type] is +probably a better solution: + +[import src_code/point2.cpp] +[point_example2] + +See the [link hash.custom Boost.Hash documentation] for more detail on how to +do this. Remember that it relies on extensions to the draft standard - so it +won't work on other implementations of the unordered associative containers. + +[table Methods for accessing the hash and equality functions. + [[Method] [Description]] + + [ + [``hasher hash_function() const``] + [Returns the container's hash function.] + ] + [ + [``key_equal key_eq() const``] + [Returns the container's key equality function.] + ] +] + +[endsect] diff --git a/doc/intro.qbk b/doc/intro.qbk new file mode 100644 index 00000000..8d4476e0 --- /dev/null +++ b/doc/intro.qbk @@ -0,0 +1,128 @@ +[/ Copyright 2006-2007 Daniel James. + / Distributed under 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) ] + +[def __tr1__ + [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1836.pdf + C++ Standard Library Technical Report]] +[def __boost-tr1__ + [@http://www.boost.org/doc/html/boost_tr1.html + Boost.TR1]] +[def __draft__ + [@http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2007/n2461.pdf + Working Draft of the C++ Standard]] +[def __hash-table__ [@http://en.wikipedia.org/wiki/Hash_table + hash table]] +[def __hash-function__ [@http://en.wikipedia.org/wiki/Hash_function + hash function]] + +[section:intro Introduction] + +For accessing data based on key lookup, the C++ standard library offers `std::set`, +`std::map`, `std::multiset` and `std::multimap`. These are generally +implemented using balanced binary trees so that lookup time has +logarithmic complexity. That is generally okay, but in many cases a +__hash-table__ can perform better, as accessing data has constant complexity, +on average. The worst case complexity is linear, but that occurs rarely and +with some care, can be avoided. + +Also, the existing containers require a 'less than' comparison object +to order their elements. For some data types this is impossible to implement +or isn't practical. In contrast, a hash table only needs an equality function +and a hash function for the key. + +With this in mind, the __tr1__ introduced the unordered associative containers, +which are implemented using hash tables, and they have now been added to the +__draft__. + +This library supplies an almost complete implementation of the specification in +the __draft__, (it doesn't support `emplace` yet, see the [link +unordered.rationale.future_developments Implementation Rationale] section for more +details). If accepted the containers should also be added to __boost-tr1__. + +`unordered_set` and `unordered_multiset` are defined in the header +<[headerref boost/unordered_set.hpp]> + + namespace boost { + template < + class Key, + class Hash = ``[classref boost::hash]``, + class Pred = std::equal_to, + class Alloc = std::allocator > + class ``[classref boost::unordered_set unordered_set]``; + + template< + class Key, + class Hash = ``[classref boost::hash]``, + class Pred = std::equal_to, + class Alloc = std::allocator > + class ``[classref boost::unordered_multiset unordered_multiset]``; + } + +`unordered_map` and `unordered_multimap` are defined in the header +<[headerref boost/unordered_map.hpp]> + + namespace boost { + template < + class Key, class Mapped, + class Hash = ``[classref boost::hash]``, + class Pred = std::equal_to, + class Alloc = std::allocator > + class ``[classref boost::unordered_map unordered_map]``; + + template< + class Key, class Mapped, + class Hash = ``[classref boost::hash]``, + class Pred = std::equal_to, + class Alloc = std::allocator > + class ``[classref boost::unordered_multimap unordered_multimap]``; + } + +When using Boost.TR1, these classes are included from `` and +``, with the classes added to the `std::tr1` namespace. + +The containers are used in a similar manner to the normal associative +containers: + + #include <``[headerref boost/unordered_map.hpp]``> + #include + + int main() + { + boost::unordered_map x; + x["one"] = 1; + x["two"] = 2; + x["three"] = 3; + + assert(x["one"] == 1); + assert(x["missing"] == 0); + } + +But since the elements aren't ordered, the output of: + + BOOST_FOREACH(map::value_type i, x) { + std::cout< + + + + +
+ + + + + + + An unordered associative container that stores unique values. + + + For the normative reference see chapter 23 of + the working draft of the C++ standard [n2461]. + Template Parameters + + + + + Value + Value must be Assignable and CopyConstructible + + Hash + A unary function object type that acts a hash function for a Value. It takes a single argument of type Value and returns a value of type std::size_t. + + Pred + A binary function object that implements an equivalence relation on values of type Value. + A binary function object that induces an equivalence relation on values of type Key. + It takes two arguments of type Key and returns a value of type bool. + + Alloc + An allocator whose value type is the same as the container's value type. + The elements are organized into buckets. Keys with the same hash code are stored in the same bucket. + The number of buckets can be automatically increased by a call to insert, or as the result of calling rehash. + + + Value + + + Value + + + Hash + + + Pred + + + Alloc + + + typename allocator_type::pointer + + + typename allocator_type::const_pointer + + + typename allocator_type::reference + lvalue of value_type. + + + typename allocator_type::const_reference + const lvalue of value_type. + + + implementation-defined + + An unsigned integral type. + size_type can represent any non-negative value of difference_type. + + + + implementation-defined + + A signed integral type. + Is identical to the difference type of iterator and const_iterator. + + + + implementation-defined + + A constant iterator whose value type is value_type. + Any iterator category except output iterator. + Convertible to const_iterator. + + + + implementation-defined + + A constant iterator whose value type is value_type. + Any iterator category except output iterator. + + + + implementation-defined + + An iterator with the same value type, difference type and pointer and reference type as iterator. + A local_iterator object can be used to iterate through a single bucket. + + + + implementation-defined + + A constant iterator with the same value type, difference type and pointer and reference type as const_iterator. + A const_local_iterator object can be used to iterate through a single bucket. + + + + + size_type + implementation-defined + + + hasher const& + hasher() + + + key_equal const& + key_equal() + + + allocator_type const& + allocator_type() + + + size() == 0 + + + Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocator and a maximum load factor of 1.0. + + + + + + InputIterator + + + InputIterator + + + size_type + implementation-defined + + + hasher const& + hasher() + + + key_equal const& + key_equal() + + + allocator_type const& + allocator_type() + + + Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocator and a maximum load factor of 1.0 and inserts the elements from [f, l) into it. + + + + + unordered_set const& + + + The copy constructor. Copies the contained elements, hash function, predicate, maximum load factor and allocator. + + + + + + + unordered_set const& + + unordered_set& + + The assignment operator. Copies the contained elements, hash function, predicate and maximum load factor but not the allocator. + + + + allocator_type + + + + bool + + size() == 0 + + + + size_type + + std::distance(begin(), end()) + + + + size_type + size() of the largest possible container. + + + + + + iterator + const_iterator + An iterator referring to the first element of the container, or if the container is empty the past-the-end value for the container. + + + + + iterator + + + const_iterator + + An iterator which refers to the past-the-end value for the container. + + + + const_iterator + A constant iterator referring to the first element of the container, or if the container is empty the past-the-end value for the container. + + + + const_iterator + A constant iterator which refers to the past-the-end value for the container. + + + + + + + value_type const& + + std::pair<iterator, bool> + + Inserts obj in the container if and only if there is no element in the container with an equivalent value. + + + The bool component of the return type is true if an insert took place. + If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the element with equivalent value. + + + If an exception is thrown by an operation other than a call to hasher the function has no effect. + + + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. + Pointers and references to elements are never invalidated. + + + + + const_iterator + + + value_type const& + + iterator + + Inserts obj in the container if and only if there is no element in the container with an equivalent value. + hint is a suggestion to where the element should be inserted. + + + If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the element with equivalent value. + + + If an exception is thrown by an operation other than a call to hasher the function has no effect. + + + The standard is fairly vague on the meaning of the hint. But the only practical way to use it, and the only way that Boost.Unordered supports is to point to an existing element with the same value. + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. + Pointers and references to elements are never invalidated. + + + + + + InputIterator + + + InputIterator + + void + + Inserts a range of elements into the container. Elements are inserted if and only if there is no element in the container with an equivalent value. + + + When inserting a single element, if an exception is thrown by an operation other than a call to hasher the function has no effect. + + + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. + Pointers and references to elements are never invalidated. + + + + + const_iterator + + iterator + + Erase the element pointed to by position. + + + The iterator following position before the erasure. + + + Only throws an exception if it is thrown by hasher or key_equal. + In this implementation, this overload doesn't call either function object's methods so it is no throw, but this might not be true in other implementations. + + + + + key_type const& + + size_type + + Erase all elements with key equivalent to k. + + + The number of elements erased. + + + Only throws an exception if it is thrown by hasher or key_equal. + + + + + const_iterator + + + const_iterator + + iterator + + Erases the elements in the range from first to last. + + + The iterator following the erased elements - i.e. last. + + + Only throws an exception if it is thrown by hasher or key_equal. + In this implementation, this overload doesn't call either function object's methods so it is no throw, but this might not be true in other implementations. + + + + void + + Erases all elements in the container. + + + size() == 0 + + + Never throws an exception. + + + + + unordered_set& + + void + + If the allocators are equal, doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of key_equal or hasher. + + + For a discussion of the behaviour when allocators aren't equal see + the implementation details. + + + + + + hasher + The container's hash function. + + + + key_equal + The container's key equality predicate. + + + + + + + + key_type const& + + iterator + + + + key_type const& + + iterator + + + An iterator pointing to an element with key equivalent to k, or b.end() if no such element exists. + + + + + key_type const& + + size_type + + The number of elements with key equivalent to k. + + + + + + key_type const& + + std::pair<iterator, iterator> + + + + key_type const& + + std::pair<const_iterator, const_iterator> + + + A range containing all elements with key equivalent to k. + If the container doesn't container any such elements, returns + std::make_pair(b.end(),b.end()). + + + + + + + size_type + + The number of buckets. + + + + size_type + + An upper bound on the number of buckets. + + + + + size_type + + size_type + + n < bucket_count() + + + The number of elements in bucket n. + + + + + key_type const& + + size_type + + The index of the bucket which would contain an element with key k. + + + The return value is less than bucket_count() + + + + + + size_type + + local_iterator + + + + size_type + + const_local_iterator + + + n shall be in the range [0, bucket_count()). + + + A local iterator pointing the first element in the bucket with index n. + + + + + + size_type + + local_iterator + + + + size_type + + const_local_iterator + + + n shall be in the range [0, bucket_count()). + + + A local iterator pointing the 'one past the end' element in the bucket with index n. + + + + + size_type + + const_local_iterator + + n shall be in the range [0, bucket_count()). + + + A constant local iterator pointing the first element in the bucket with index n. + + + + + size_type + + const_local_iterator + + n shall be in the range [0, bucket_count()). + + + A constant local iterator pointing the 'one past the end' element in the bucket with index n. + + + + + + float + + The average number of elements per bucket. + + + + float + + Returns the current maximum load factor. + + + + + float + + float + + Changes the container's maximum load factor, using z as a hint. + + + + + size_type + + void + + Changes the number of buckets so that there at least n buckets, and so that the load factor is less than the maximum load factor. + Invalidates iterators, and changes the order of elements. Pointers and references to elements are not invalidated. + + + The function has no effect if an exception is thrown, unless it is thrown by the container's hash function or comparison function. + + + + + + + + unordered_set<Value, Hash, Pred, Alloc>& + + + unordered_set<Value, Hash, Pred, Alloc>& + + void + + x.swap(y) + + + If the allocators are equal, doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of Hash or Pred. + + + For a discussion of the behaviour when allocators aren't equal see + the implementation details. + + + + + + + + + + An unordered associative container that stores values. The same key can be stored multiple times. + + + For the normative reference see chapter 23 of + the working draft of the C++ standard [n2461]. + Template Parameters + + + + + Value + Value must be Assignable and CopyConstructible + + Hash + A unary function object type that acts a hash function for a Value. It takes a single argument of type Value and returns a value of type std::size_t. + + Pred + A binary function object that implements an equivalence relation on values of type Value. + A binary function object that induces an equivalence relation on values of type Key. + It takes two arguments of type Key and returns a value of type bool. + + Alloc + An allocator whose value type is the same as the container's value type. + The elements are organized into buckets. Keys with the same hash code are stored in the same bucket and elements with equivalent keys are stored next to each other. + The number of buckets can be automatically increased by a call to insert, or as the result of calling rehash. + + + Value + + + Value + + + Hash + + + Pred + + + Alloc + + + typename allocator_type::pointer + + + typename allocator_type::const_pointer + + + typename allocator_type::reference + lvalue of value_type. + + + typename allocator_type::const_reference + const lvalue of value_type. + + + implementation-defined + + An unsigned integral type. + size_type can represent any non-negative value of difference_type. + + + + implementation-defined + + A signed integral type. + Is identical to the difference type of iterator and const_iterator. + + + + implementation-defined + + A constant iterator whose value type is value_type. + Any iterator category except output iterator. + Convertible to const_iterator. + + + + implementation-defined + + A constant iterator whose value type is value_type. + Any iterator category except output iterator. + + + + implementation-defined + + An iterator with the same value type, difference type and pointer and reference type as iterator. + A local_iterator object can be used to iterate through a single bucket. + + + + implementation-defined + + A constant iterator with the same value type, difference type and pointer and reference type as const_iterator. + A const_local_iterator object can be used to iterate through a single bucket. + + + + + size_type + implementation-defined + + + hasher const& + hasher() + + + key_equal const& + key_equal() + + + allocator_type const& + allocator_type() + + + size() == 0 + + + Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocator and a maximum load factor of 1.0. + + + + + + InputIterator + + + InputIterator + + + size_type + implementation-defined + + + hasher const& + hasher() + + + key_equal const& + key_equal() + + + allocator_type const& + allocator_type() + + + Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocator and a maximum load factor of 1.0 and inserts the elements from [f, l) into it. + + + + + unordered_multiset const& + + + The copy constructor. Copies the contained elements, hash function, predicate, maximum load factor and allocator. + + + + + + + unordered_multiset const& + + unordered_multiset& + + The assignment operator. Copies the contained elements, hash function, predicate and maximum load factor but not the allocator. + + + + allocator_type + + + + bool + + size() == 0 + + + + size_type + + std::distance(begin(), end()) + + + + size_type + size() of the largest possible container. + + + + + + iterator + const_iterator + An iterator referring to the first element of the container, or if the container is empty the past-the-end value for the container. + + + + + iterator + + + const_iterator + + An iterator which refers to the past-the-end value for the container. + + + + const_iterator + A constant iterator referring to the first element of the container, or if the container is empty the past-the-end value for the container. + + + + const_iterator + A constant iterator which refers to the past-the-end value for the container. + + + + + + + value_type const& + + iterator + + Inserts obj in the container. + + + An iterator pointing to the inserted element. + + + If an exception is thrown by an operation other than a call to hasher the function has no effect. + + + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. + Pointers and references to elements are never invalidated. + + + + + const_iterator + + + value_type const& + + iterator + + Inserts obj in the container. + hint is a suggestion to where the element should be inserted. + + + An iterator pointing to the inserted element. + + + If an exception is thrown by an operation other than a call to hasher the function has no effect. + + + The standard is fairly vague on the meaning of the hint. But the only practical way to use it, and the only way that Boost.Unordered supports is to point to an existing element with the same value. + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. + Pointers and references to elements are never invalidated. + + + + + + InputIterator + + + InputIterator + + void + + Inserts a range of elements into the container. + + + When inserting a single element, if an exception is thrown by an operation other than a call to hasher the function has no effect. + + + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. + Pointers and references to elements are never invalidated. + + + + + const_iterator + + iterator + + Erase the element pointed to by position. + + + The iterator following position before the erasure. + + + Only throws an exception if it is thrown by hasher or key_equal. + In this implementation, this overload doesn't call either function object's methods so it is no throw, but this might not be true in other implementations. + + + + + key_type const& + + size_type + + Erase all elements with key equivalent to k. + + + The number of elements erased. + + + Only throws an exception if it is thrown by hasher or key_equal. + + + + + const_iterator + + + const_iterator + + iterator + + Erases the elements in the range from first to last. + + + The iterator following the erased elements - i.e. last. + + + Only throws an exception if it is thrown by hasher or key_equal. + In this implementation, this overload doesn't call either function object's methods so it is no throw, but this might not be true in other implementations. + + + + void + + Erases all elements in the container. + + + size() == 0 + + + Never throws an exception. + + + + + unordered_multiset& + + void + + If the allocators are equal, doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of key_equal or hasher. + + + For a discussion of the behaviour when allocators aren't equal see + the implementation details. + + + + + + hasher + The container's hash function. + + + + key_equal + The container's key equality predicate. + + + + + + + + key_type const& + + iterator + + + + key_type const& + + iterator + + + An iterator pointing to an element with key equivalent to k, or b.end() if no such element exists. + + + + + key_type const& + + size_type + + The number of elements with key equivalent to k. + + + + + + key_type const& + + std::pair<iterator, iterator> + + + + key_type const& + + std::pair<const_iterator, const_iterator> + + + A range containing all elements with key equivalent to k. + If the container doesn't container any such elements, returns + std::make_pair(b.end(),b.end()). + + + + + + + size_type + + The number of buckets. + + + + size_type + + An upper bound on the number of buckets. + + + + + size_type + + size_type + + n < bucket_count() + + + The number of elements in bucket n. + + + + + key_type const& + + size_type + + The index of the bucket which would contain an element with key k. + + + The return value is less than bucket_count() + + + + + + size_type + + local_iterator + + + + size_type + + const_local_iterator + + + n shall be in the range [0, bucket_count()). + + + A local iterator pointing the first element in the bucket with index n. + + + + + + size_type + + local_iterator + + + + size_type + + const_local_iterator + + + n shall be in the range [0, bucket_count()). + + + A local iterator pointing the 'one past the end' element in the bucket with index n. + + + + + size_type + + const_local_iterator + + n shall be in the range [0, bucket_count()). + + + A constant local iterator pointing the first element in the bucket with index n. + + + + + size_type + + const_local_iterator + + n shall be in the range [0, bucket_count()). + + + A constant local iterator pointing the 'one past the end' element in the bucket with index n. + + + + + + float + + The average number of elements per bucket. + + + + float + + Returns the current maximum load factor. + + + + + float + + float + + Changes the container's maximum load factor, using z as a hint. + + + + + size_type + + void + + Changes the number of buckets so that there at least n buckets, and so that the load factor is less than the maximum load factor. + Invalidates iterators, and changes the order of elements. Pointers and references to elements are not invalidated. + + + The function has no effect if an exception is thrown, unless it is thrown by the container's hash function or comparison function. + + + + + + + + unordered_multiset<Value, Hash, Pred, Alloc>& + + + unordered_multiset<Value, Hash, Pred, Alloc>& + + void + + x.swap(y) + + + If the allocators are equal, doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of Hash or Pred. + + + For a discussion of the behaviour when allocators aren't equal see + the implementation details. + + + + + +
+ + +
+ + + + + + + An unordered associative container that associates unique keys with another value. + + + For the normative reference see chapter 23 of + the working draft of the C++ standard [n2461]. + Template Parameters + + + + + Key + Key must be Assignable and CopyConstructible. + + Mapped + Mapped must be CopyConstructible + + Hash + A unary function object type that acts a hash function for a Key. It takes a single argument of type Key and returns a value of type std::size_t. + + Pred + A binary function object that implements an equivalence relation on values of type Key. + A binary function object that induces an equivalence relation on values of type Key. + It takes two arguments of type Key and returns a value of type bool. + + Alloc + An allocator whose value type is the same as the container's value type. + The elements are organized into buckets. Keys with the same hash code are stored in the same bucket. + The number of buckets can be automatically increased by a call to insert, or as the result of calling rehash. + + + Key + + + std::pair<Key const, Value> + + + Mapped + + + Hash + + + Pred + + + Alloc + + + typename allocator_type::pointer + + + typename allocator_type::const_pointer + + + typename allocator_type::reference + lvalue of value_type. + + + typename allocator_type::const_reference + const lvalue of value_type. + + + implementation-defined + + An unsigned integral type. + size_type can represent any non-negative value of difference_type. + + + + implementation-defined + + A signed integral type. + Is identical to the difference type of iterator and const_iterator. + + + + implementation-defined + + A iterator whose value type is value_type. + Any iterator category except output iterator. + Convertible to const_iterator. + + + + implementation-defined + + A constant iterator whose value type is value_type. + Any iterator category except output iterator. + + + + implementation-defined + + An iterator with the same value type, difference type and pointer and reference type as iterator. + A local_iterator object can be used to iterate through a single bucket. + + + + implementation-defined + + A constant iterator with the same value type, difference type and pointer and reference type as const_iterator. + A const_local_iterator object can be used to iterate through a single bucket. + + + + + size_type + implementation-defined + + + hasher const& + hasher() + + + key_equal const& + key_equal() + + + allocator_type const& + allocator_type() + + + size() == 0 + + + Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocator and a maximum load factor of 1.0. + + + + + + InputIterator + + + InputIterator + + + size_type + implementation-defined + + + hasher const& + hasher() + + + key_equal const& + key_equal() + + + allocator_type const& + allocator_type() + + + Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocator and a maximum load factor of 1.0 and inserts the elements from [f, l) into it. + + + + + unordered_map const& + + + The copy constructor. Copies the contained elements, hash function, predicate, maximum load factor and allocator. + + + + + + + unordered_map const& + + unordered_map& + + The assignment operator. Copies the contained elements, hash function, predicate and maximum load factor but not the allocator. + + + + allocator_type + + + + bool + + size() == 0 + + + + size_type + + std::distance(begin(), end()) + + + + size_type + size() of the largest possible container. + + + + + + iterator + const_iterator + An iterator referring to the first element of the container, or if the container is empty the past-the-end value for the container. + + + + + iterator + + + const_iterator + + An iterator which refers to the past-the-end value for the container. + + + + const_iterator + A constant iterator referring to the first element of the container, or if the container is empty the past-the-end value for the container. + + + + const_iterator + A constant iterator which refers to the past-the-end value for the container. + + + + + + + value_type const& + + std::pair<iterator, bool> + + Inserts obj in the container if and only if there is no element in the container with an equivalent key. + + + The bool component of the return type is true if an insert took place. + If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the element with equivalent key. + + + If an exception is thrown by an operation other than a call to hasher the function has no effect. + + + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. + Pointers and references to elements are never invalidated. + + + + + const_iterator + + + value_type const& + + iterator + + Inserts obj in the container if and only if there is no element in the container with an equivalent key. + hint is a suggestion to where the element should be inserted. + + + If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the element with equivalent key. + + + If an exception is thrown by an operation other than a call to hasher the function has no effect. + + + The standard is fairly vague on the meaning of the hint. But the only practical way to use it, and the only way that Boost.Unordered supports is to point to an existing element with the same key. + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. + Pointers and references to elements are never invalidated. + + + + + + InputIterator + + + InputIterator + + void + + Inserts a range of elements into the container. Elements are inserted if and only if there is no element in the container with an equivalent key. + + + When inserting a single element, if an exception is thrown by an operation other than a call to hasher the function has no effect. + + + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. + Pointers and references to elements are never invalidated. + + + + + const_iterator + + iterator + + Erase the element pointed to by position. + + + The iterator following position before the erasure. + + + Only throws an exception if it is thrown by hasher or key_equal. + In this implementation, this overload doesn't call either function object's methods so it is no throw, but this might not be true in other implementations. + + + + + key_type const& + + size_type + + Erase all elements with key equivalent to k. + + + The number of elements erased. + + + Only throws an exception if it is thrown by hasher or key_equal. + + + + + const_iterator + + + const_iterator + + iterator + + Erases the elements in the range from first to last. + + + The iterator following the erased elements - i.e. last. + + + Only throws an exception if it is thrown by hasher or key_equal. + In this implementation, this overload doesn't call either function object's methods so it is no throw, but this might not be true in other implementations. + + + + void + + Erases all elements in the container. + + + size() == 0 + + + Never throws an exception. + + + + + unordered_map& + + void + + If the allocators are equal, doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of key_equal or hasher. + + + For a discussion of the behaviour when allocators aren't equal see + the implementation details. + + + + + + hasher + The container's hash function. + + + + key_equal + The container's key equality predicate. + + + + + + + + key_type const& + + iterator + + + + key_type const& + + iterator + + + An iterator pointing to an element with key equivalent to k, or b.end() if no such element exists. + + + + + key_type const& + + size_type + + The number of elements with key equivalent to k. + + + + + + key_type const& + + std::pair<iterator, iterator> + + + + key_type const& + + std::pair<const_iterator, const_iterator> + + + A range containing all elements with key equivalent to k. + If the container doesn't container any such elements, returns + std::make_pair(b.end(),b.end()). + + + + + + key_type const& + + mapped_type& + + If the container does not already contain an elements with a key equivalent to k, inserts the value std::pair<key_type const, mapped_type>(k, mapped_type()) + + + A reference to x.second where x is the element already in the container, or the newly inserted element with a key equivalent to k + + + If an exception is thrown by an operation other than a call to hasher the function has no effect. + + + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. + Pointers and references to elements are never invalidated. + + + + Mapped& + key_type const& + Mapped const& + key_type const& + + A reference to x.second where x is the (unique) element whose key is equivalent to k. + + + An exception object of type std::out_of_range if no such element is present. + + + This is not specified in the draft standard, but that is probably an oversight. The issue has been raised in + comp.std.c++. + + + + + + size_type + + The number of buckets. + + + + size_type + + An upper bound on the number of buckets. + + + + + size_type + + size_type + + n < bucket_count() + + + The number of elements in bucket n. + + + + + key_type const& + + size_type + + The index of the bucket which would contain an element with key k. + + + The return value is less than bucket_count() + + + + + + size_type + + local_iterator + + + + size_type + + const_local_iterator + + + n shall be in the range [0, bucket_count()). + + + A local iterator pointing the first element in the bucket with index n. + + + + + + size_type + + local_iterator + + + + size_type + + const_local_iterator + + + n shall be in the range [0, bucket_count()). + + + A local iterator pointing the 'one past the end' element in the bucket with index n. + + + + + size_type + + const_local_iterator + + n shall be in the range [0, bucket_count()). + + + A constant local iterator pointing the first element in the bucket with index n. + + + + + size_type + + const_local_iterator + + n shall be in the range [0, bucket_count()). + + + A constant local iterator pointing the 'one past the end' element in the bucket with index n. + + + + + + float + + The average number of elements per bucket. + + + + float + + Returns the current maximum load factor. + + + + + float + + float + + Changes the container's maximum load factor, using z as a hint. + + + + + size_type + + void + + Changes the number of buckets so that there at least n buckets, and so that the load factor is less than the maximum load factor. + Invalidates iterators, and changes the order of elements. Pointers and references to elements are not invalidated. + + + The function has no effect if an exception is thrown, unless it is thrown by the container's hash function or comparison function. + + + + + + + + unordered_map<Key, Mapped, Hash, Pred, Alloc>& + + + unordered_map<Key, Mapped, Hash, Pred, Alloc>& + + void + + x.swap(y) + + + If the allocators are equal, doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of Hash or Pred. + + + For a discussion of the behaviour when allocators aren't equal see + the implementation details. + + + + + + + + + + An unordered associative container that associates keys with another value. The same key can be stored multiple times. + + + For the normative reference see chapter 23 of + the working draft of the C++ standard [n2461]. + Template Parameters + + + + + Key + Key must be Assignable and CopyConstructible. + + Mapped + Mapped must be CopyConstructible + + Hash + A unary function object type that acts a hash function for a Key. It takes a single argument of type Key and returns a value of type std::size_t. + + Pred + A binary function object that implements an equivalence relation on values of type Key. + A binary function object that induces an equivalence relation on values of type Key. + It takes two arguments of type Key and returns a value of type bool. + + Alloc + An allocator whose value type is the same as the container's value type. + The elements are organized into buckets. Keys with the same hash code are stored in the same bucket and elements with equivalent keys are stored next to each other. + The number of buckets can be automatically increased by a call to insert, or as the result of calling rehash. + + + Key + + + std::pair<Key const, Value> + + + Mapped + + + Hash + + + Pred + + + Alloc + + + typename allocator_type::pointer + + + typename allocator_type::const_pointer + + + typename allocator_type::reference + lvalue of value_type. + + + typename allocator_type::const_reference + const lvalue of value_type. + + + implementation-defined + + An unsigned integral type. + size_type can represent any non-negative value of difference_type. + + + + implementation-defined + + A signed integral type. + Is identical to the difference type of iterator and const_iterator. + + + + implementation-defined + + A iterator whose value type is value_type. + Any iterator category except output iterator. + Convertible to const_iterator. + + + + implementation-defined + + A constant iterator whose value type is value_type. + Any iterator category except output iterator. + + + + implementation-defined + + An iterator with the same value type, difference type and pointer and reference type as iterator. + A local_iterator object can be used to iterate through a single bucket. + + + + implementation-defined + + A constant iterator with the same value type, difference type and pointer and reference type as const_iterator. + A const_local_iterator object can be used to iterate through a single bucket. + + + + + size_type + implementation-defined + + + hasher const& + hasher() + + + key_equal const& + key_equal() + + + allocator_type const& + allocator_type() + + + size() == 0 + + + Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocator and a maximum load factor of 1.0. + + + + + + InputIterator + + + InputIterator + + + size_type + implementation-defined + + + hasher const& + hasher() + + + key_equal const& + key_equal() + + + allocator_type const& + allocator_type() + + + Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocator and a maximum load factor of 1.0 and inserts the elements from [f, l) into it. + + + + + unordered_multimap const& + + + The copy constructor. Copies the contained elements, hash function, predicate, maximum load factor and allocator. + + + + + + + unordered_multimap const& + + unordered_multimap& + + The assignment operator. Copies the contained elements, hash function, predicate and maximum load factor but not the allocator. + + + + allocator_type + + + + bool + + size() == 0 + + + + size_type + + std::distance(begin(), end()) + + + + size_type + size() of the largest possible container. + + + + + + iterator + const_iterator + An iterator referring to the first element of the container, or if the container is empty the past-the-end value for the container. + + + + + iterator + + + const_iterator + + An iterator which refers to the past-the-end value for the container. + + + + const_iterator + A constant iterator referring to the first element of the container, or if the container is empty the past-the-end value for the container. + + + + const_iterator + A constant iterator which refers to the past-the-end value for the container. + + + + + + + value_type const& + + iterator + + Inserts obj in the container. + + + An iterator pointing to the inserted element. + + + If an exception is thrown by an operation other than a call to hasher the function has no effect. + + + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. + Pointers and references to elements are never invalidated. + + + + + const_iterator + + + value_type const& + + iterator + + Inserts obj in the container. + hint is a suggestion to where the element should be inserted. + + + An iterator pointing to the inserted element. + + + If an exception is thrown by an operation other than a call to hasher the function has no effect. + + + The standard is fairly vague on the meaning of the hint. But the only practical way to use it, and the only way that Boost.Unordered supports is to point to an existing element with the same key. + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. + Pointers and references to elements are never invalidated. + + + + + + InputIterator + + + InputIterator + + void + + Inserts a range of elements into the container. + + + When inserting a single element, if an exception is thrown by an operation other than a call to hasher the function has no effect. + + + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. + Pointers and references to elements are never invalidated. + + + + + const_iterator + + iterator + + Erase the element pointed to by position. + + + The iterator following position before the erasure. + + + Only throws an exception if it is thrown by hasher or key_equal. + In this implementation, this overload doesn't call either function object's methods so it is no throw, but this might not be true in other implementations. + + + + + key_type const& + + size_type + + Erase all elements with key equivalent to k. + + + The number of elements erased. + + + Only throws an exception if it is thrown by hasher or key_equal. + + + + + const_iterator + + + const_iterator + + iterator + + Erases the elements in the range from first to last. + + + The iterator following the erased elements - i.e. last. + + + Only throws an exception if it is thrown by hasher or key_equal. + In this implementation, this overload doesn't call either function object's methods so it is no throw, but this might not be true in other implementations. + + + + void + + Erases all elements in the container. + + + size() == 0 + + + Never throws an exception. + + + + + unordered_multimap& + + void + + If the allocators are equal, doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of key_equal or hasher. + + + For a discussion of the behaviour when allocators aren't equal see + the implementation details. + + + + + + hasher + The container's hash function. + + + + key_equal + The container's key equality predicate. + + + + + + + + key_type const& + + iterator + + + + key_type const& + + iterator + + + An iterator pointing to an element with key equivalent to k, or b.end() if no such element exists. + + + + + key_type const& + + size_type + + The number of elements with key equivalent to k. + + + + + + key_type const& + + std::pair<iterator, iterator> + + + + key_type const& + + std::pair<const_iterator, const_iterator> + + + A range containing all elements with key equivalent to k. + If the container doesn't container any such elements, returns + std::make_pair(b.end(),b.end()). + + + + + + + size_type + + The number of buckets. + + + + size_type + + An upper bound on the number of buckets. + + + + + size_type + + size_type + + n < bucket_count() + + + The number of elements in bucket n. + + + + + key_type const& + + size_type + + The index of the bucket which would contain an element with key k. + + + The return value is less than bucket_count() + + + + + + size_type + + local_iterator + + + + size_type + + const_local_iterator + + + n shall be in the range [0, bucket_count()). + + + A local iterator pointing the first element in the bucket with index n. + + + + + + size_type + + local_iterator + + + + size_type + + const_local_iterator + + + n shall be in the range [0, bucket_count()). + + + A local iterator pointing the 'one past the end' element in the bucket with index n. + + + + + size_type + + const_local_iterator + + n shall be in the range [0, bucket_count()). + + + A constant local iterator pointing the first element in the bucket with index n. + + + + + size_type + + const_local_iterator + + n shall be in the range [0, bucket_count()). + + + A constant local iterator pointing the 'one past the end' element in the bucket with index n. + + + + + + float + + The average number of elements per bucket. + + + + float + + Returns the current maximum load factor. + + + + + float + + float + + Changes the container's maximum load factor, using z as a hint. + + + + + size_type + + void + + Changes the number of buckets so that there at least n buckets, and so that the load factor is less than the maximum load factor. + Invalidates iterators, and changes the order of elements. Pointers and references to elements are not invalidated. + + + The function has no effect if an exception is thrown, unless it is thrown by the container's hash function or comparison function. + + + + + + + + unordered_multimap<Key, Mapped, Hash, Pred, Alloc>& + + + unordered_multimap<Key, Mapped, Hash, Pred, Alloc>& + + void + + x.swap(y) + + + If the allocators are equal, doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of Hash or Pred. + + + For a discussion of the behaviour when allocators aren't equal see + the implementation details. + + + + + +
+
diff --git a/doc/src_code/insensitive.cpp b/doc/src_code/insensitive.cpp new file mode 100644 index 00000000..5e344177 --- /dev/null +++ b/doc/src_code/insensitive.cpp @@ -0,0 +1,79 @@ + +// Copyright 2006-2007 Daniel James. +// Distributed under 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) + +#include +#include +#include + +//[case_insensitive_functions + struct iequal_to + : std::binary_function + { + bool operator()(std::string const& x, + std::string const& y) const + { + return boost::algorithm::iequals(x, y, std::locale()); + } + }; + + struct ihash + : std::unary_function + { + std::size_t operator()(std::string const& x) const + { + std::size_t seed = 0; + std::locale locale; + + for(std::string::const_iterator it = x.begin(); + it != x.end(); ++it) + { + boost::hash_combine(seed, std::toupper(*it, locale)); + } + + return seed; + } + }; + + struct word_info; +//] + + struct word_info { + int tag; + explicit word_info(int t = 0) : tag(t) {} + }; + +int main() { +//[case_insensitive_dictionary + boost::unordered_map + idictionary; +//] + + BOOST_TEST(idictionary.empty()); + + idictionary["one"] = word_info(1); + BOOST_TEST(idictionary.size() == 1); + BOOST_TEST(idictionary.find("ONE") != idictionary.end() && + idictionary.find("ONE") == idictionary.find("one")); + + idictionary.insert(std::make_pair("ONE", word_info(2))); + BOOST_TEST(idictionary.size() == 1); + BOOST_TEST(idictionary.find("ONE") != idictionary.end() && + idictionary.find("ONE")->first == "one" && + idictionary.find("ONE")->second.tag == 1); + + idictionary["One"] = word_info(3); + BOOST_TEST(idictionary.size() == 1); + BOOST_TEST(idictionary.find("ONE") != idictionary.end() && + idictionary.find("ONE")->first == "one" && + idictionary.find("ONE")->second.tag == 3); + + idictionary["two"] = word_info(4); + BOOST_TEST(idictionary.size() == 2); + BOOST_TEST(idictionary.find("two") != idictionary.end() && + idictionary.find("TWO")->first == "two" && + idictionary.find("Two")->second.tag == 4); + + return boost::report_errors(); +} diff --git a/doc/src_code/point1.cpp b/doc/src_code/point1.cpp new file mode 100644 index 00000000..f812dbb9 --- /dev/null +++ b/doc/src_code/point1.cpp @@ -0,0 +1,46 @@ + +// Copyright 2006-2007 Daniel James. +// Distributed under 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) + +#include +#include + +//[point_example1 + struct point { + int x; + int y; + }; + + bool operator==(point const& p1, point const& p2) + { + return p1.x == p2.x && p1.y == p2.y; + } + + struct point_hash + : std::unary_function + { + std::size_t operator()(point const& p) const + { + std::size_t seed = 0; + boost::hash_combine(seed, p.x); + boost::hash_combine(seed, p.y); + return seed; + } + }; + + boost::unordered_multiset > + points; +//] + +int main() { + point x[] = {{1,2}, {3,4}, {1,5}, {1,2}}; + for(int i = 0; i < sizeof(x) / sizeof(point); ++i) + points.insert(x[i]); + BOOST_TEST(points.count(x[0]) == 2); + BOOST_TEST(points.count(x[1]) == 1); + point y = {10, 2}; + BOOST_TEST(points.count(y) == 0); + + return boost::report_errors(); +} diff --git a/doc/src_code/point2.cpp b/doc/src_code/point2.cpp new file mode 100644 index 00000000..c5ca1b52 --- /dev/null +++ b/doc/src_code/point2.cpp @@ -0,0 +1,43 @@ + +// Copyright 2006-2007 Daniel James. +// Distributed under 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) + +#include +#include +#include + +//[point_example2 + struct point { + int x; + int y; + }; + + bool operator==(point const& p1, point const& p2) + { + return p1.x == p2.x && p1.y == p2.y; + } + + std::size_t hash_value(point const& p) { + std::size_t seed = 0; + boost::hash_combine(seed, p.x); + boost::hash_combine(seed, p.y); + return seed; + } + + // Now the default function objects work. + boost::unordered_multiset points; +//] + +int main() { + point x[] = {{1,2}, {3,4}, {1,5}, {1,2}}; + for(int i = 0; i < sizeof(x) / sizeof(point); ++i) + points.insert(x[i]); + BOOST_TEST(points.count(x[0]) == 2); + BOOST_TEST(points.count(x[1]) == 1); + point y = {10, 2}; + BOOST_TEST(points.count(y) == 0); + + return boost::report_errors(); +} + diff --git a/doc/unordered.qbk b/doc/unordered.qbk new file mode 100644 index 00000000..1cef8921 --- /dev/null +++ b/doc/unordered.qbk @@ -0,0 +1,26 @@ +[/ Copyright 2006-2007 Daniel James. + / Distributed under 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) ] + +[library Unordered Associative Containers + [quickbook 1.4] + [authors [James, Daniel]] + [copyright 2003 2004 Jeremy B. Maitin-Shepard] + [copyright 2005 2006 2007 Daniel James] + [purpose std::tr1 compliant hash containers] + [id unordered] + [dirname unordered] + [license + Distributed under 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] + ] +] + +[include:unordered intro.qbk] +[include:unordered buckets.qbk] +[include:unordered hash_equality.qbk] +[include:unordered comparison.qbk] +[include:unordered rationale.qbk] + +[xinclude ref.xml] diff --git a/examples/case_insensitive.hpp b/examples/case_insensitive.hpp new file mode 100644 index 00000000..8cd00224 --- /dev/null +++ b/examples/case_insensitive.hpp @@ -0,0 +1,60 @@ + +// Copyright 2006-2007 Daniel James. +// Distributed under 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) + +// This file implements a locale aware case insenstive equality predicate and +// hash function. Unfortunately it still falls short of full +// internationalization as it only deals with a single character at a time +// (some languages have tricky cases where the characters in an upper case +// string don't have a one-to-one correspondence with the lower case version of +// the text, eg. ) + +#if !defined(BOOST_HASH_EXAMPLES_CASE_INSENSITIVE_HEADER) +#define BOOST_HASH_EXAMPLES_CASE_INSENSITIVE_HEADER + +#include +#include + +namespace hash_examples +{ + struct iequal_to + : std::binary_function + { + iequal_to() {} + explicit iequal_to(std::locale const& l) : locale_(l) {} + + template + bool operator()(String1 const& x1, String2 const& x2) const + { + return boost::algorithm::iequals(x1, x2, locale_); + } + private: + std::locale locale_; + }; + + struct ihash + : std::unary_function + { + ihash() {} + explicit ihash(std::locale const& l) : locale_(l) {} + + template + std::size_t operator()(String const& x) const + { + std::size_t seed = 0; + + for(typename String::const_iterator it = x.begin(); + it != x.end(); ++it) + { + boost::hash_combine(seed, std::toupper(*it, locale_)); + } + + return seed; + } + private: + std::locale locale_; + }; +} + +#endif diff --git a/examples/case_insensitive_test.cpp b/examples/case_insensitive_test.cpp new file mode 100644 index 00000000..259fbda3 --- /dev/null +++ b/examples/case_insensitive_test.cpp @@ -0,0 +1,82 @@ + +// Copyright 2006-2007 Daniel James. +// Distributed under 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) + +#include "./case_insensitive.hpp" +#include +#include + +struct word_info { + int tag; + explicit word_info(int t = 0) : tag(t) {} +}; + +void test1() { + boost::unordered_map idictionary; + + BOOST_TEST(idictionary.empty()); + + idictionary["one"] = word_info(1); + BOOST_TEST(idictionary.size() == 1); + BOOST_TEST(idictionary.find("ONE") != idictionary.end() && + idictionary.find("ONE") == idictionary.find("one")); + + idictionary.insert(std::make_pair("ONE", word_info(2))); + BOOST_TEST(idictionary.size() == 1); + BOOST_TEST(idictionary.find("ONE") != idictionary.end() && + idictionary.find("ONE")->first == "one" && + idictionary.find("ONE")->second.tag == 1); + + idictionary["One"] = word_info(3); + BOOST_TEST(idictionary.size() == 1); + BOOST_TEST(idictionary.find("ONE") != idictionary.end() && + idictionary.find("ONE")->first == "one" && + idictionary.find("ONE")->second.tag == 3); + + idictionary["two"] = word_info(4); + BOOST_TEST(idictionary.size() == 2); + BOOST_TEST(idictionary.find("two") != idictionary.end() && + idictionary.find("TWO")->first == "two" && + idictionary.find("Two")->second.tag == 4); + + +} + +void test2() { + boost::unordered_map idictionary; + + BOOST_TEST(idictionary.empty()); + + idictionary[L"one"] = word_info(1); + BOOST_TEST(idictionary.size() == 1); + BOOST_TEST(idictionary.find(L"ONE") != idictionary.end() && + idictionary.find(L"ONE") == idictionary.find(L"one")); + + idictionary.insert(std::make_pair(L"ONE", word_info(2))); + BOOST_TEST(idictionary.size() == 1); + BOOST_TEST(idictionary.find(L"ONE") != idictionary.end() && + idictionary.find(L"ONE")->first == L"one" && + idictionary.find(L"ONE")->second.tag == 1); + + idictionary[L"One"] = word_info(3); + BOOST_TEST(idictionary.size() == 1); + BOOST_TEST(idictionary.find(L"ONE") != idictionary.end() && + idictionary.find(L"ONE")->first == L"one" && + idictionary.find(L"ONE")->second.tag == 3); + + idictionary[L"two"] = word_info(4); + BOOST_TEST(idictionary.size() == 2); + BOOST_TEST(idictionary.find(L"two") != idictionary.end() && + idictionary.find(L"TWO")->first == L"two" && + idictionary.find(L"Two")->second.tag == 4); +} + +int main() { + test1(); + test2(); + + return boost::report_errors(); +} diff --git a/examples/hash_functions/fnv-1.hpp b/examples/hash_functions/fnv-1.hpp new file mode 100644 index 00000000..fcfd8cdf --- /dev/null +++ b/examples/hash_functions/fnv-1.hpp @@ -0,0 +1,66 @@ +// See: http://www.isthe.com/chongo/tech/comp/fnv/ + +#include + +namespace hash +{ + template + struct basic_fnv_1 + { + std::size_t operator()(std::string const& text) + { + std::size_t hash = OffsetBias; + for(std::string::const_iterator it = text.begin(), end = text.end(); + it != end; ++it) + { + hash *= FnvPrime; + hash ^= *it; + } + + return hash; + } + }; + + template + struct basic_fnv_1a + { + std::size_t operator()(std::string const& text) + { + std::size_t hash = OffsetBias; + for(std::string::const_iterator it = text.begin(), end = text.end(); + it != end; ++it) + { + hash ^= *it; + hash *= FnvPrime; + } + + return hash; + } + }; + + // TODO: Select Bias & Prime base on the size of std::size_t. + // + // 32 bit FNV_prime = 16777619 + // 64 bit FNV_prime = 1099511628211 + // 128 bit FNV_prime = 309485009821345068724781401 + // 256 bit FNV_prime = 374144419156711147060143317175368453031918731002211 + // + // 32 bit offset_basis = 2166136261 + // 64 bit offset_basis = 14695981039346656037 + // 128 bit offset_basis = 275519064689413815358837431229664493455 + // 256 bit offset_basis = 100029257958052580907070968620625704837092796014241193945225284501741471925557 + + const std::size_t fnv_prime = 16777619; + // 64 bit FNV_prime = 1099511628211 + // 128 bit FNV_prime = 309485009821345068724781401 + // 256 bit FNV_prime = 374144419156711147060143317175368453031918731002211 + + const std::size_t fnv_offset_bias = 2166136261; + // 64 bit offset_basis = 14695981039346656037 + // 128 bit offset_basis = 275519064689413815358837431229664493455 + // 256 bit offset_basis = 100029257958052580907070968620625704837092796014241193945225284501741471925557 + + typedef basic_fnv_1 fnv_1; + typedef basic_fnv_1a fnv_1a; + +} diff --git a/include/boost/unordered/detail/allocator.hpp b/include/boost/unordered/detail/allocator.hpp new file mode 100644 index 00000000..e7dd8d11 --- /dev/null +++ b/include/boost/unordered/detail/allocator.hpp @@ -0,0 +1,237 @@ + +// Copyright 2005-2007 Daniel James. +// Distributed under 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 + +#if (defined(BOOST_NO_STD_ALLOCATOR) || defined(BOOST_DINKUMWARE_STDLIB)) \ + && !defined(__BORLANDC__) +# define BOOST_UNORDERED_USE_ALLOCATOR_UTILITIES +#endif + +#if defined(BOOST_UNORDERED_USE_ALLOCATOR_UTILITIES) +# include +#endif + +#include + +namespace boost { + namespace unordered_detail { + +#if defined(BOOST_UNORDERED_USE_ALLOCATOR_UTILITIES) + template + struct rebind_wrap : ::boost::detail::allocator::rebind_to {}; +#else + template + struct rebind_wrap + { + typedef BOOST_DEDUCED_TYPENAME + Alloc::BOOST_NESTED_TEMPLATE rebind::other + type; + }; +#endif + +#if !BOOST_WORKAROUND(BOOST_MSVC, < 1300) + template + inline void reset(T& x) { x = T(); } + + template + inline Ptr null_ptr() { return Ptr(); } +#else + template + inline void reset_impl(T& x, ...) { x = T(); } + template + inline void reset_impl(T*& x, int) { x = 0; } + template + inline void reset(T& x) { reset_impl(x); } + + template + inline Ptr null_ptr() { Ptr x; reset(x); return x; } +#endif + + // Work around for Microsoft's ETI bug. + + template struct allocator_value_type + { + typedef BOOST_DEDUCED_TYPENAME Allocator::value_type type; + }; + + template struct allocator_pointer + { + typedef BOOST_DEDUCED_TYPENAME Allocator::pointer type; + }; + + template struct allocator_const_pointer + { + typedef BOOST_DEDUCED_TYPENAME Allocator::const_pointer type; + }; + + template struct allocator_reference + { + typedef BOOST_DEDUCED_TYPENAME Allocator::reference type; + }; + + template struct allocator_const_reference + { + typedef BOOST_DEDUCED_TYPENAME Allocator::const_reference type; + }; + +#if defined(BOOST_MPL_CFG_MSVC_ETI_BUG) + + template <> + struct allocator_value_type + { + typedef int type; + }; + + template <> + struct allocator_pointer + { + typedef int type; + }; + + template <> + struct allocator_const_pointer + { + typedef int type; + }; + + template <> + struct allocator_reference + { + typedef int type; + }; + + template <> + struct allocator_const_reference + { + typedef int type; + }; + +#endif + + template + struct allocator_constructor + { + typedef BOOST_DEDUCED_TYPENAME allocator_value_type::type value_type; + typedef BOOST_DEDUCED_TYPENAME allocator_pointer::type pointer; + + Allocator& alloc_; + pointer ptr_; + bool constructed_; + + allocator_constructor(Allocator& a) + : alloc_(a), ptr_(), constructed_(false) + { +#if BOOST_WORKAROUND(BOOST_MSVC, < 1300) + unordered_detail::reset(ptr_); +#endif + } + + ~allocator_constructor() { + if(ptr_) { + if(constructed_) alloc_.destroy(ptr_); + alloc_.deallocate(ptr_, 1); + } + } + + template + void construct(V const& v) { + BOOST_ASSERT(!ptr_ && !constructed_); + ptr_ = alloc_.allocate(1); + alloc_.construct(ptr_, value_type(v)); + constructed_ = true; + } + + void construct(value_type const& v) { + BOOST_ASSERT(!ptr_ && !constructed_); + ptr_ = alloc_.allocate(1); + alloc_.construct(ptr_, v); + constructed_ = true; + } + + pointer get() const + { + return ptr_; + } + + // no throw + pointer release() + { + pointer p = ptr_; + constructed_ = false; + unordered_detail::reset(ptr_); + return p; + } + }; + + template + struct allocator_array_constructor + { + typedef BOOST_DEDUCED_TYPENAME allocator_pointer::type pointer; + + Allocator& alloc_; + pointer ptr_; + pointer constructed_; + std::size_t length_; + + allocator_array_constructor(Allocator& a) + : alloc_(a), ptr_(), constructed_(), length_(0) + { +#if BOOST_WORKAROUND(BOOST_MSVC, < 1300) + unordered_detail::reset(constructed_); + unordered_detail::reset(ptr_); +#endif + } + + ~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) + { + BOOST_ASSERT(!ptr_); + 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_); + unordered_detail::reset(ptr_); + return p; + } + private: + allocator_array_constructor(allocator_array_constructor const&); + allocator_array_constructor& operator=(allocator_array_constructor const&); + }; + } +} + +#if defined(BOOST_UNORDERED_USE_ALLOCATOR_UTILITIES) +# undef BOOST_UNORDERED_USE_ALLOCATOR_UTILITIES +#endif + +#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..9f9e4e49 --- /dev/null +++ b/include/boost/unordered/detail/hash_table.hpp @@ -0,0 +1,185 @@ + +// Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard. +// Copyright (C) 2005-2007 Daniel James +// Distributed under 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 + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#if BOOST_WORKAROUND(__BORLANDC__, <= 0x0551) +#define BOOST_UNORDERED_BORLAND_BOOL(x) (bool)(x) +#else +#define BOOST_UNORDERED_BORLAND_BOOL(x) x +#endif + +#if BOOST_WORKAROUND(BOOST_MSVC, < 1300) +#define BOOST_UNORDERED_MSVC_RESET_PTR(x) unordered_detail::reset(x) +#else +#define BOOST_UNORDERED_MSVC_RESET_PTR(x) +#endif + +namespace boost { + namespace unordered_detail { + template struct type_wrapper {}; + + const static std::size_t default_initial_bucket_count = 50; + const static float minimum_max_load_factor = 1e-3f; + inline std::size_t next_prime(std::size_t n); + + template + inline void hash_swap(T& x, T& y) + { +#if defined(BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP) + std::swap(x,y); +#else + using std::swap; + swap(x, y); +#endif + } + + inline std::size_t float_to_size_t(float f) + { + return f > static_cast((std::numeric_limits::max)()) ? + (std::numeric_limits::max)() : + static_cast(f); + } + + // 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* const prime_list_end = prime_list + + sizeof(prime_list) / sizeof(*prime_list); + std::size_t const* bound = + std::lower_bound(prime_list,prime_list_end, n); + if(bound == prime_list_end) + bound--; + return *bound; + } + + // no throw + inline std::size_t prev_prime(std::size_t n) { + std::size_t const* const prime_list_end = prime_list + + sizeof(prime_list) / sizeof(*prime_list); + std::size_t const* bound = + std::upper_bound(prime_list,prime_list_end, n); + if(bound != prime_list) + 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)); + } + } +} + +#define BOOST_UNORDERED_EQUIVALENT_KEYS 1 +#include +#undef BOOST_UNORDERED_EQUIVALENT_KEYS + +#define BOOST_UNORDERED_EQUIVALENT_KEYS 0 +#include +#undef BOOST_UNORDERED_EQUIVALENT_KEYS + +namespace boost { + namespace unordered_detail { + class iterator_access + { + public: + template + static BOOST_DEDUCED_TYPENAME Iterator::base const& get(Iterator const& it) { + return it.base_; + } + }; + + template + class hash_types_unique_keys + { + public: + typedef BOOST_DEDUCED_TYPENAME + boost::unordered_detail::rebind_wrap::type + value_allocator; + + typedef hash_table_unique_keys hash_table; + typedef hash_table_data_unique_keys data; + typedef BOOST_DEDUCED_TYPENAME data::iterator_base iterator_base; + + typedef hash_const_local_iterator_unique_keys const_local_iterator; + typedef hash_local_iterator_unique_keys local_iterator; + typedef hash_const_iterator_unique_keys const_iterator; + typedef hash_iterator_unique_keys iterator; + + typedef BOOST_DEDUCED_TYPENAME data::size_type size_type; + typedef std::ptrdiff_t difference_type; + }; + + template + class hash_types_equivalent_keys + { + public: + typedef BOOST_DEDUCED_TYPENAME + boost::unordered_detail::rebind_wrap::type + value_allocator; + + typedef hash_table_equivalent_keys hash_table; + typedef hash_table_data_equivalent_keys data; + typedef BOOST_DEDUCED_TYPENAME data::iterator_base iterator_base; + + typedef hash_const_local_iterator_equivalent_keys const_local_iterator; + typedef hash_local_iterator_equivalent_keys local_iterator; + typedef hash_const_iterator_equivalent_keys const_iterator; + typedef hash_iterator_equivalent_keys iterator; + + typedef BOOST_DEDUCED_TYPENAME data::size_type size_type; + typedef std::ptrdiff_t difference_type; + }; + } // namespace boost::unordered_detail +} // namespace boost + +#undef BOOST_UNORDERED_BORLAND_BOOL +#undef BOOST_UNORDERED_MSVC_RESET_PTR + +#endif // BOOST_UNORDERED_DETAIL_HASH_TABLE_HPP_INCLUDED diff --git a/include/boost/unordered/detail/hash_table_impl.hpp b/include/boost/unordered/detail/hash_table_impl.hpp new file mode 100644 index 00000000..3fada775 --- /dev/null +++ b/include/boost/unordered/detail/hash_table_impl.hpp @@ -0,0 +1,2035 @@ + +// Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard. +// Copyright (C) 2005-2007 Daniel James +// Distributed under 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) + +#if BOOST_UNORDERED_EQUIVALENT_KEYS +#define BOOST_UNORDERED_TABLE hash_table_equivalent_keys +#define BOOST_UNORDERED_TABLE_DATA hash_table_data_equivalent_keys +#define BOOST_UNORDERED_ITERATOR hash_iterator_equivalent_keys +#define BOOST_UNORDERED_CONST_ITERATOR hash_const_iterator_equivalent_keys +#define BOOST_UNORDERED_LOCAL_ITERATOR hash_local_iterator_equivalent_keys +#define BOOST_UNORDERED_CONST_LOCAL_ITERATOR hash_const_local_iterator_equivalent_keys +#else +#define BOOST_UNORDERED_TABLE hash_table_unique_keys +#define BOOST_UNORDERED_TABLE_DATA hash_table_data_unique_keys +#define BOOST_UNORDERED_ITERATOR hash_iterator_unique_keys +#define BOOST_UNORDERED_CONST_ITERATOR hash_const_iterator_unique_keys +#define BOOST_UNORDERED_LOCAL_ITERATOR hash_local_iterator_unique_keys +#define BOOST_UNORDERED_CONST_LOCAL_ITERATOR hash_const_local_iterator_unique_keys +#endif + +namespace boost { + namespace unordered_detail { + + // + // Hash Table Data + // + // Responsible for managing the hash buckets. + + template + class BOOST_UNORDERED_TABLE_DATA + { + public: + typedef BOOST_UNORDERED_TABLE_DATA data; + + struct node_base; + struct node; + struct bucket; + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + + typedef Alloc value_allocator; + + typedef BOOST_DEDUCED_TYPENAME + boost::unordered_detail::rebind_wrap::type + node_allocator; + typedef BOOST_DEDUCED_TYPENAME + boost::unordered_detail::rebind_wrap::type + node_base_allocator; + typedef BOOST_DEDUCED_TYPENAME + boost::unordered_detail::rebind_wrap::type + bucket_allocator; + + typedef BOOST_DEDUCED_TYPENAME allocator_value_type::type value_type; + typedef BOOST_DEDUCED_TYPENAME allocator_pointer::type node_ptr; + typedef BOOST_DEDUCED_TYPENAME allocator_pointer::type bucket_ptr; + typedef BOOST_DEDUCED_TYPENAME allocator_reference::type reference; + typedef BOOST_DEDUCED_TYPENAME allocator_reference::type bucket_reference; + +#if 1 + typedef bucket_ptr link_ptr; +#else + // This alternative version of link_ptr is used to check that the + // implementation is type safe wrt bucket_ptr and link_ptr. + // + // It's a sort of strict typedef. + + struct link_ptr { + link_ptr() : ptr_() { BOOST_UNORDERED_MSVC_RESET_PTR(ptr_); } + explicit link_ptr(bucket_ptr p) : ptr_(p) {} + bucket_reference operator*() const { return *ptr_; } + bucket* operator->() const { return &*ptr_; } + operator bool() const { return ptr_; } + bool operator==(link_ptr const& x) const { return ptr_ == x.ptr_; } + bool operator!=(link_ptr const& x) const { return ptr_ != x.ptr_; } + private: + bucket_ptr ptr_; + }; +#endif + // Hash Bucket + // + // all no throw + + struct bucket + { + private: + bucket& operator=(bucket const&); + public: + link_ptr next_; + + bucket() : next_() + { + BOOST_UNORDERED_MSVC_RESET_PTR(next_); + } + + bucket(bucket const& x) : next_(x.next_) + { + // Only copy construct when allocating. + BOOST_ASSERT(!x.next_); + } + + bool empty() const + { + return !this->next_; + } + }; + + // Hash Node + // + // all no throw + + struct node_base : bucket + { +#if BOOST_UNORDERED_EQUIVALENT_KEYS + public: + node_base() : group_prev_() + { + BOOST_UNORDERED_MSVC_RESET_PTR(group_prev_); + } + + link_ptr group_prev_; +#endif + }; + + struct node : node_base + { + public: + node(value_type const& v) : node_base(), value_(v) {} + + value_type value_; + }; + + // node_constructor + // + // Used to construct nodes in an exception safe manner. + + struct allocators + { + node_allocator node_alloc_; + bucket_allocator bucket_alloc_; + value_allocator value_alloc_; + node_base_allocator node_base_alloc_; + + allocators(value_allocator const& a) + : node_alloc_(a), bucket_alloc_(a), + value_alloc_(a), node_base_alloc_(a) + {} + + void destroy(link_ptr ptr) + { + node_ptr n(node_alloc_.address(static_cast(*ptr))); + value_alloc_.destroy(value_alloc_.address(n->value_)); + node_base_alloc_.destroy(node_base_alloc_.address(*n)); + node_alloc_.deallocate(n, 1); + } + + void swap(allocators& x) + { + unordered_detail::hash_swap(node_alloc_, x.node_alloc_); + unordered_detail::hash_swap(bucket_alloc_, x.bucket_alloc_); + unordered_detail::hash_swap(value_alloc_, x.value_alloc_); + unordered_detail::hash_swap(node_base_alloc_, x.node_base_alloc_); + } + + bool operator==(allocators const& x) + { + return value_alloc_ == x.value_alloc_; + } + }; + + class node_constructor + { + allocators& allocators_; + + node_ptr node_; + bool value_constructed_; + bool node_base_constructed_; + + public: + + node_constructor(allocators& a) + : allocators_(a), + node_(), value_constructed_(false), node_base_constructed_(false) + { + BOOST_UNORDERED_MSVC_RESET_PTR(node_); + } + + ~node_constructor() + { + if (node_) { + if (value_constructed_) + allocators_.value_alloc_.destroy( + allocators_.value_alloc_.address(node_->value_)); + if (node_base_constructed_) + allocators_.node_base_alloc_.destroy( + allocators_.node_base_alloc_.address(*node_)); + + allocators_.node_alloc_.deallocate(node_, 1); + } + } + + template + void construct(V const& v) + { + BOOST_ASSERT(!node_); + value_constructed_ = false; + node_base_constructed_ = false; + + node_ = allocators_.node_alloc_.allocate(1); + + allocators_.node_base_alloc_.construct( + allocators_.node_base_alloc_.address(*node_), + node_base()); + node_base_constructed_ = true; + + allocators_.value_alloc_.construct( + allocators_.value_alloc_.address(node_->value_), v); + value_constructed_ = true; + } + + node_ptr get() const + { + BOOST_ASSERT(node_); + return node_; + } + + // no throw + link_ptr release() + { + node_ptr p = node_; + unordered_detail::reset(node_); + return link_ptr(allocators_.bucket_alloc_.address(*p)); + } + + private: + node_constructor(node_constructor const&); + node_constructor& operator=(node_constructor const&); + }; + + // Methods for navigating groups of elements with equal keys. + +#if BOOST_UNORDERED_EQUIVALENT_KEYS + static inline link_ptr& prev_in_group(link_ptr n) { + return static_cast(*n).group_prev_; + } + + // pre: Must be pointing to the first node in a group. + static inline link_ptr& next_group(link_ptr n) { + BOOST_ASSERT(BOOST_UNORDERED_BORLAND_BOOL(n) && n != prev_in_group(n)->next_); + return prev_in_group(n)->next_; + } +#else + static inline link_ptr& next_group(link_ptr n) { + BOOST_ASSERT(n); + return n->next_; + } +#endif + + // pre: Must be pointing to a node + static inline node& get_node(link_ptr p) { + BOOST_ASSERT(p); + return static_cast(*p); + } + + // pre: Must be pointing to a node + static inline reference get_value(link_ptr p) { + BOOST_ASSERT(p); + return static_cast(*p).value_; + } + + class iterator_base + { + public: + bucket_ptr bucket_; + link_ptr node_; + + iterator_base() + : bucket_(), node_() + { + BOOST_UNORDERED_MSVC_RESET_PTR(bucket_); + BOOST_UNORDERED_MSVC_RESET_PTR(node_); + } + + explicit iterator_base(bucket_ptr b) + : bucket_(b), node_(b->next_) {} + + iterator_base(bucket_ptr b, link_ptr n) + : bucket_(b), node_(n) {} + + bool operator==(iterator_base const& x) const + { + return node_ == x.node_; + } + + bool operator!=(iterator_base const& x) const + { + return node_ != x.node_; + } + + reference operator*() const + { + return get_value(node_); + } + + void increment() + { + BOOST_ASSERT(bucket_); + node_ = node_->next_; + + while (!node_) { + ++bucket_; + node_ = bucket_->next_; + } + } + + void increment_group() + { + node_ = data::next_group(node_); + + while (!node_) { + ++bucket_; + node_ = bucket_->next_; + } + } + }; + + // Member Variables + + allocators allocators_; + bucket_ptr buckets_; + size_type bucket_count_; + bucket_ptr cached_begin_bucket_; + size_type size_; + + // Constructors/Deconstructor + + BOOST_UNORDERED_TABLE_DATA(size_type n, value_allocator const& a) + : allocators_(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(allocators_.bucket_alloc_); + + // Creates an extra bucket to act as a sentinel. + constructor.construct(bucket(), bucket_count_ + 1); + + cached_begin_bucket_ = constructor.get() + static_cast(bucket_count_); + + // Set up the sentinel. + cached_begin_bucket_->next_ = link_ptr(cached_begin_bucket_); + + // Only release the buckets once everything is successfully + // done. + buckets_ = constructor.release(); + } + + BOOST_UNORDERED_TABLE_DATA(BOOST_UNORDERED_TABLE_DATA const& x, size_type n) + : allocators_(x.allocators_), + 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(allocators_.bucket_alloc_); + + // Creates an extra bucket to act as a sentinel. + constructor.construct(bucket(), bucket_count_ + 1); + + cached_begin_bucket_ = constructor.get() + static_cast(bucket_count_); + + // Set up the sentinel + cached_begin_bucket_->next_ = link_ptr(cached_begin_bucket_); + + // Only release the buckets once everything is successfully + // done. + buckets_ = constructor.release(); + } + + // no throw + ~BOOST_UNORDERED_TABLE_DATA() + { + if(buckets_) { + bucket_ptr begin = cached_begin_bucket_; + bucket_ptr end = buckets_end(); + while(begin != end) { + clear_bucket(begin); + ++begin; + } + + // Destroy an extra bucket for the sentinels. + ++end; + for(begin = buckets_; begin != end; ++begin) + allocators_.bucket_alloc_.destroy(begin); + + allocators_.bucket_alloc_.deallocate(buckets_, bucket_count_ + 1); + } + } + + private: + + BOOST_UNORDERED_TABLE_DATA(BOOST_UNORDERED_TABLE_DATA const&); + BOOST_UNORDERED_TABLE_DATA& operator=(BOOST_UNORDERED_TABLE_DATA const&); + + public: + + // no throw + void swap(BOOST_UNORDERED_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 for a hashed value. + // + // no throw + bucket_ptr bucket_from_hash(size_type hashed) const + { + return buckets_ + static_cast(hashed % bucket_count_); + } + + // Begin & End + // + // no throw + + bucket_ptr buckets_end() const + { + return buckets_ + static_cast(bucket_count_); + } + + iterator_base begin() const + { + return size_ + ? iterator_base(cached_begin_bucket_) + : end(); + } + + iterator_base end() const + { + return iterator_base(buckets_end()); + } + + link_ptr begin(size_type n) const + { + return (buckets_ + static_cast(n))->next_; + } + + link_ptr end(size_type) const + { + return unordered_detail::null_ptr(); + } + + link_ptr begin(bucket_ptr b) const + { + return b->next_; + } + + // Bucket Size + + // no throw + static inline size_type node_count(link_ptr it) + { + size_type count = 0; + while(BOOST_UNORDERED_BORLAND_BOOL(it)) { + ++count; + it = it->next_; + } + return count; + } + + static inline size_type node_count(link_ptr it1, link_ptr it2) + { + size_type count = 0; + while(it1 != it2) { + ++count; + it1 = it1->next_; + } + return count; + } + + size_type bucket_size(size_type n) const + { + return node_count(begin(n)); + } + +#if BOOST_UNORDERED_EQUIVALENT_KEYS + static inline size_type group_count(link_ptr it) + { + return node_count(it, next_group(it)); + } +#else + static inline size_type group_count(link_ptr) + { + return 1; + } +#endif + + // get_for_erase + // + // Find the pointer to a node, for use when erasing. + // + // no throw + +#if BOOST_UNORDERED_EQUIVALENT_KEYS + static link_ptr* get_for_erase(iterator_base r) + { + link_ptr n = r.node_; + + // If the element isn't the first in its group, then + // the link to it will be found in the previous element + // in the group. + link_ptr* it = &prev_in_group(n)->next_; + if(*it == n) return it; + + // The element is the first in its group, so iterate + // throught the groups, checking against the first element. + it = &r.bucket_->next_; + while(*it != n) it = &BOOST_UNORDERED_TABLE_DATA::next_group(*it); + return it; + } +#else + static link_ptr* get_for_erase(iterator_base r) + { + link_ptr n = r.node_; + link_ptr* it = &r.bucket_->next_; + while(*it != n) it = &(*it)->next_; + return it; + } +#endif + + // Link/Unlink/Move Node + // + // For adding nodes to buckets, removing them and moving them to a + // new bucket. + // + // no throw + +#if BOOST_UNORDERED_EQUIVALENT_KEYS + void link_node(link_ptr n, link_ptr pos) + { + node& node_ref = get_node(n); + node& pos_ref = get_node(pos); + node_ref.next_ = pos_ref.group_prev_->next_; + node_ref.group_prev_ = pos_ref.group_prev_; + pos_ref.group_prev_->next_ = n; + pos_ref.group_prev_ = n; + ++size_; + } + + void link_node_in_bucket(link_ptr n, bucket_ptr base) + { + node& node_ref = get_node(n); + node_ref.next_ = base->next_; + node_ref.group_prev_ = n; + base->next_ = n; + ++size_; + if(base < cached_begin_bucket_) cached_begin_bucket_ = base; + } + + void link_group(link_ptr n, bucket_ptr base, size_type count) + { + node& node_ref = get_node(n); + node& last_ref = get_node(node_ref.group_prev_); + last_ref.next_ = base->next_; + base->next_ = n; + size_ += count; + if(base < cached_begin_bucket_) cached_begin_bucket_ = base; + } +#else + void link_node_in_bucket(link_ptr n, bucket_ptr base) + { + n->next_ = base->next_; + base->next_ = n; + ++size_; + if(base < cached_begin_bucket_) cached_begin_bucket_ = base; + } + + void link_group(link_ptr n, bucket_ptr base, size_type) + { + link_node_in_bucket(n, base); + } +#endif + +#if BOOST_UNORDERED_EQUIVALENT_KEYS + void unlink_node(iterator_base it) + { + link_ptr* pos = get_for_erase(it); + node* n = &get_node(it.node_); + link_ptr next = n->next_; + + if(n->group_prev_ == *pos) { + // The deleted node is the sole node in the group, so + // no need to unlink it from a group. + } + else if(BOOST_UNORDERED_BORLAND_BOOL(next) && prev_in_group(next) == *pos) + { + // The deleted node is not at the end of the group, so + // change the link from the next node. + prev_in_group(next) = n->group_prev_; + } + else { + // The deleted node is at the end of the group, so the + // first node in the group is pointing to it. + // Find that to change its pointer. + link_ptr it = n->group_prev_; + while(prev_in_group(it) != *pos) { + it = prev_in_group(it); + } + prev_in_group(it) = n->group_prev_; + } + *pos = next; + --size_; + } + + size_type unlink_group(link_ptr* pos) + { + size_type count = group_count(*pos); + size_ -= count; + *pos = next_group(*pos); + return count; + } +#else + void unlink_node(iterator_base n) + { + link_ptr* pos = get_for_erase(n); + *pos = (*pos)->next_; + --size_; + } + + size_type unlink_group(link_ptr* pos) + { + *pos = (*pos)->next_; + --size_; + return 1; + } +#endif + + void unlink_nodes(iterator_base n) + { + link_ptr* it = get_for_erase(n); + split_group(*it); + unordered_detail::reset(*it); + size_ -= node_count(n.node_); + } + + void unlink_nodes(iterator_base begin, iterator_base end) + { + BOOST_ASSERT(begin.bucket_ == end.bucket_); + size_ -= node_count(begin.node_, end.node_); + link_ptr* it = get_for_erase(begin); + split_group(*it, end.node_); + *it = end.node_; + } + + void unlink_nodes(bucket_ptr base, iterator_base end) + { + BOOST_ASSERT(base == end.bucket_); + + split_group(end.node_); + + link_ptr ptr(base->next_); + base->next_ = end.node_; + + size_ -= node_count(ptr, end.node_); + } + +#if BOOST_UNORDERED_EQUIVALENT_KEYS + // Break a ciruclar list into two, with split as the beginning + // of the second group (if split is at the beginning then don't + // split). + static inline link_ptr split_group(link_ptr split) + { + // If split is at the beginning of the group then there's + // nothing to split. + if(prev_in_group(split)->next_ != split) + return unordered_detail::null_ptr(); + + // Find the start of the group. + link_ptr start = split; + do { + start = prev_in_group(start); + } while(prev_in_group(start)->next_ == start); + + link_ptr last = prev_in_group(start); + prev_in_group(start) = prev_in_group(split); + prev_in_group(split) = last; + + return start; + } + + static inline void split_group(link_ptr split1, link_ptr split2) + { + link_ptr begin1 = split_group(split1); + link_ptr begin2 = split_group(split2); + + if(BOOST_UNORDERED_BORLAND_BOOL(begin1) && split1 == begin2) { + link_ptr end1 = prev_in_group(begin1); + prev_in_group(begin1) = prev_in_group(begin2); + prev_in_group(begin2) = end1; + } + } +#else + static inline void split_group(link_ptr) + { + } + + static inline void split_group(link_ptr, link_ptr) + { + } +#endif + + // throws, strong exception-safety: + link_ptr construct_node(value_type const& v) + { + node_constructor a(allocators_); + a.construct(v); + return a.release(); + } + + // 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 n = construct_node(v); + + // Rest is no throw + link_node_in_bucket(n, base); + return iterator_base(base, n); + } + +#if BOOST_UNORDERED_EQUIVALENT_KEYS + iterator_base create_node(value_type const& v, iterator_base position) + { + // throws, strong exception-safety: + link_ptr n = construct_node(v); + + // Rest is no throw + link_node(n, position.node_); + return iterator_base(position.bucket_, n); + } + + iterator_base create_node(value_type const& v, + bucket_ptr base, link_ptr position) + { + // throws, strong exception-safety: + link_ptr n = construct_node(v); + + // Rest is no throw + if(BOOST_UNORDERED_BORLAND_BOOL(position)) + link_node(n, position); + else + link_node_in_bucket(n, base); + + return iterator_base(base, n); + } +#endif + +#if BOOST_UNORDERED_EQUIVALENT_KEYS + void copy_group(link_ptr it, bucket_ptr dst) + { + link_ptr end = next_group(it); + iterator_base pos = create_node(get_value(it), dst); + for(it = it->next_; it != end; it = it->next_) + create_node(get_value(it), pos); + } +#else + void copy_group(link_ptr it, bucket_ptr dst) + { + create_node(get_value(it), dst); + } +#endif + + // Delete Node + // + // Remove a node, or a range of nodes, from a bucket, and destroy + // them. + // + // no throw + + void delete_to_bucket_end(link_ptr begin) + { + while(begin) { + link_ptr node = begin; + begin = begin->next_; + allocators_.destroy(node); + } + } + + void delete_nodes(link_ptr begin, link_ptr end) + { + while(begin != end) { + link_ptr node = begin; + begin = begin->next_; + allocators_.destroy(node); + } + } + +#if BOOST_UNORDERED_EQUIVALENT_KEYS + void delete_group(link_ptr first_node) + { + delete_nodes(first_node, prev_in_group(first_node)->next_); + } +#else + void delete_group(link_ptr node) + { + allocators_.destroy(node); + } +#endif + + // Clear + // + // Remove all the nodes. + // + // no throw + + void clear_bucket(bucket_ptr b) + { + link_ptr first_node = b->next_; + unordered_detail::reset(b->next_); + delete_to_bucket_end(first_node); + } + + void clear() + { + bucket_ptr begin = cached_begin_bucket_; + bucket_ptr end = buckets_end(); + + size_ = 0; + cached_begin_bucket_ = end; + + while(begin != end) { + clear_bucket(begin); + ++begin; + } + } + + // Erase + // + // no throw + + iterator_base erase(iterator_base r) + { + BOOST_ASSERT(r != end()); + iterator_base next = r; + next.increment(); + unlink_node(r); + allocators_.destroy(r.node_); + // 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_) { + unlink_nodes(r1, r2); + delete_nodes(r1.node_, r2.node_); + + // 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 { + BOOST_ASSERT(r1.bucket_ < r2.bucket_); + + unlink_nodes(r1); + delete_to_bucket_end(r1.node_); + + bucket_ptr i = r1.bucket_; + for(++i; i != r2.bucket_; ++i) { + size_ -= node_count(i->next_); + clear_bucket(i); + } + + if(r2 != end()) { + link_ptr first = r2.bucket_->next_; + unlink_nodes(r2.bucket_, r2); + delete_nodes(first, r2.node_); + } + + // 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 b) + { + BOOST_ASSERT(!(b < cached_begin_bucket_)); + + if(b == cached_begin_bucket_) + { + if (size_ != 0) { + while (cached_begin_bucket_->empty()) + ++cached_begin_bucket_; + } else { + cached_begin_bucket_ = buckets_end(); + } + } + } + + // This is called when a range has been erased + // + // no throw + + void recompute_begin_bucket(bucket_ptr b1, bucket_ptr b2) + { + BOOST_ASSERT(!(b1 < cached_begin_bucket_) && !(b2 < b1)); + BOOST_ASSERT(b2 == buckets_end() || !b2->empty()); + + if(b1 == cached_begin_bucket_ && b1->empty()) + cached_begin_bucket_ = b2; + } + + size_type erase_group(link_ptr* it, bucket_ptr bucket) + { + link_ptr pos = *it; + size_type count = unlink_group(it); + delete_group(pos); + + this->recompute_begin_bucket(bucket); + + return count; + } + }; + +#if defined(BOOST_MPL_CFG_MSVC_ETI_BUG) + template <> + class BOOST_UNORDERED_TABLE_DATA + { + public: + typedef int size_type; + typedef int iterator_base; + }; +#endif + + // + // Hash Table + // + + template + class BOOST_UNORDERED_TABLE + : public BOOST_UNORDERED_TABLE_DATA + { + typedef BOOST_UNORDERED_TABLE_DATA data; + + typedef BOOST_DEDUCED_TYPENAME data::node_constructor node_constructor; + typedef BOOST_DEDUCED_TYPENAME data::bucket_ptr bucket_ptr; + typedef BOOST_DEDUCED_TYPENAME data::link_ptr link_ptr; + + public: + + typedef BOOST_DEDUCED_TYPENAME data::value_allocator value_allocator; + typedef BOOST_DEDUCED_TYPENAME data::node_allocator node_allocator; + + // Type definitions + + typedef KeyType key_type; + typedef Hash hasher; + typedef Pred key_equal; + typedef ValueType value_type; + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + + // iterators + + typedef BOOST_DEDUCED_TYPENAME data::iterator_base iterator_base; + + private: + + class functions + { + std::pair functions_; + + public: + + functions(hasher const& h, key_equal const& k) + : functions_(h, k) {} + + hasher const& hash_function() const + { + return functions_.first; + } + + key_equal const& key_eq() const + { + return functions_.second; + } + }; + + // 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 BOOST_UNORDERED_TABLE::*functions_ptr; + + functions func1_; + functions func2_; + functions_ptr func_; + + float mlf_; + size_type max_load_; + + public: + + // Constructors + // + // In the constructors, if anything throws an exception, + // BOOST_UNORDERED_TABLE_DATA's destructor will clean up. + + BOOST_UNORDERED_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_(&BOOST_UNORDERED_TABLE::func1_), // no throw + mlf_(1.0f) // no throw + { + 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 + size_type initial_size(I i, I j, size_type n, + boost::forward_traversal_tag) + { + // max load factor isn't set yet, but when it is, it'll be 1.0. + return (std::max)(static_cast(std::distance(i, j)) + 1, n); + } + + template + size_type initial_size(I, I, size_type n, + boost::incrementable_traversal_tag) + { + return n; + } + + template + size_type initial_size(I i, I j, size_type n) + { + BOOST_DEDUCED_TYPENAME boost::iterator_traversal::type + iterator_traversal_tag; + return initial_size(i, j, n, iterator_traversal_tag); + } + + template + BOOST_UNORDERED_TABLE(I i, I j, size_type n, + hasher const& hf, key_equal const& eq, + value_allocator const& a) + : data(initial_size(i, j, n), a), // throws, cleans itself up + func1_(hf, eq), // throws " " + func2_(hf, eq), // throws " " + func_(&BOOST_UNORDERED_TABLE::func1_), // no throw + mlf_(1.0f) // no throw + { + calculate_max_load(); // no throw + + // This can throw, but BOOST_UNORDERED_TABLE_DATA's destructor will clean up. + insert(i, j); + } + // Copy Construct + + BOOST_UNORDERED_TABLE(BOOST_UNORDERED_TABLE const& x) + : data(x, x.min_buckets_for_size(x.size())), // throws + func1_(x.current_functions()), // throws + func2_(x.current_functions()), // throws + func_(&BOOST_UNORDERED_TABLE::func1_), // no throw + mlf_(x.mlf_) // no throw + { + calculate_max_load(); // no throw + + // This can throw, but BOOST_UNORDERED_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. + + BOOST_UNORDERED_TABLE& operator=(BOOST_UNORDERED_TABLE const& x) + { + if(this != &x) + { + 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, for + // details see: + // + // http://unordered.nfshost.com/doc/html/unordered/rationale.html#swapping_containers_with_unequal_allocators + // + // ---------------------------------------------------------------- + // + // Strong exception safety (might change unused function objects) + // + // Can throw if hash or predicate object's copy constructor throws + // or if allocators are unequal. + + void swap(BOOST_UNORDERED_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->allocators_ == x.allocators_) { + this->data::swap(x); // no throw + } + else { + // Create new buckets in separate HASH_TABLE_DATA objects + // which will clean up if anything throws an exception. + // (all can throw, but with no effect as these are new objects). + data new_this(*this, x.min_buckets_for_size(x.size_)); + copy_buckets(x, new_this, this->*new_func_this); + + data new_that(x, min_buckets_for_size(this->size_)); + x.copy_buckets(*this, new_that, x.*new_func_that); + + // Start updating the data here, no throw from now on. + this->data::swap(new_this); + x.data::swap(new_that); + } + + // 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(BOOST_UNORDERED_TABLE const& x) + { + // no throw: + functions_ptr ptr = func_ == &BOOST_UNORDERED_TABLE::func1_ + ? &BOOST_UNORDERED_TABLE::func2_ : &BOOST_UNORDERED_TABLE::func1_; + // throws, functions not in use, so strong + this->*ptr = x.current_functions(); + return ptr; + } + + public: + + // accessors + + // no throw + value_allocator get_allocator() const + { + return this->allocators_.value_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 + { + // size < mlf_ * count + return float_to_size_t(ceil( + max_bucket_count() * mlf_)) - 1; + } + + // strong safety + size_type bucket(key_type const& k) const + { + // hash_function can throw: + return hash_function()(k) % this->bucket_count_; + } + + + // strong safety + bucket_ptr get_bucket(key_type const& k) const + { + return this->buckets_ + static_cast(bucket(k)); + } + + // no throw + size_type bucket_count() const + { + return this->bucket_count_; + } + + // no throw + size_type max_bucket_count() const + { + // -1 to account for the end marker. + return prev_prime(this->allocators_.bucket_alloc_.max_size() - 1); + } + + 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_ = float_to_size_t(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)); + BOOST_ASSERT(n < max_load_ || n > max_size()); + return need_to_reserve; + } + + // basic exception safety + // + // This version of reserve is called when inserting a range + // into a container with equivalent keys, it creates more buckets + // if the resulting load factor would be over 80% of the load + // factor. This is to try to avoid excessive rehashes. + bool reserve_extra(size_type n) + { + bool need_to_reserve = n >= max_load_; + // throws - basic: + if (need_to_reserve) { + rehash_impl(static_cast(floor(n / mlf_ * 1.25)) + 1); + } + BOOST_ASSERT(n < max_load_ || n > max_size()); + return need_to_reserve; + } + + public: + + // no throw + float max_load_factor() const + { + return mlf_; + } + + // no throw + void max_load_factor(float z) + { + BOOST_ASSERT(z > 0); + mlf_ = (std::max)(z, minimum_max_load_factor); + 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((float) bucket_count() > (float) 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(*this, n); // throws, seperate + move_buckets(*this, new_buckets, hash_function()); + // basic/no throw + new_buckets.swap(*this); // no throw + calculate_max_load(); // no throw + } + + // move_buckets & copy_buckets + // + // if the hash function throws, basic excpetion safety + // no throw otherwise + + static void move_buckets(data& src, data& dst, hasher const& hf) + { + BOOST_ASSERT(dst.size_ == 0); + //BOOST_ASSERT(src.allocators_.node_alloc_ == dst.allocators_.node_alloc_); + + bucket_ptr end = src.buckets_end(); + + for(; src.cached_begin_bucket_ != end; + ++src.cached_begin_bucket_) { + bucket_ptr src_bucket = src.cached_begin_bucket_; + while(src_bucket->next_) { + // Move the first group of equivalent nodes in + // src_bucket to dst. + + // This next line throws iff the hash function throws. + bucket_ptr dst_bucket = dst.bucket_from_hash( + hf(extract_key(data::get_value(src_bucket->next_)))); + + link_ptr n = src_bucket->next_; + size_type count = src.unlink_group(&src_bucket->next_); + dst.link_group(n, dst_bucket, count); + } + } + } + + // basic excpetion safety. If an exception is thrown this 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_end(); + hasher const& hf = f.hash_function(); + + // no throw: + for(bucket_ptr i = src.cached_begin_bucket_; i != end; ++i) { + // no throw: + for(link_ptr it = src.begin(i); + BOOST_UNORDERED_BORLAND_BOOL(it); it = data::next_group(it)) { + // hash function can throw. + bucket_ptr dst_bucket = dst.bucket_from_hash( + hf(extract_key(data::get_value(it)))); + // throws, strong + dst.copy_group(it, dst_bucket); + } + } + } + + public: + + // Insert functions + // + // basic exception safety, if hash function throws + // strong otherwise. + +#if BOOST_UNORDERED_EQUIVALENT_KEYS + + // Insert (equivalent key containers) + + // if hash function throws, basic exception safety + // strong otherwise + iterator_base insert(value_type const& v) + { + key_type const& k = extract_key(v); + size_type hash_value = hash_function()(k); + bucket_ptr bucket = this->bucket_from_hash(hash_value); + link_ptr position = find_iterator(bucket, k); + + // Create the node before rehashing in case it throws an + // exception (need strong safety in such a case). + node_constructor a(this->allocators_); + a.construct(v); + + // reserve has basic exception safety if the hash function + // throws, strong otherwise. + if(reserve(size() + 1)) + bucket = this->bucket_from_hash(hash_value); + + // Nothing after the point can throw. + + link_ptr n = a.release(); + + // I'm relying on link_ptr not being invalidated by + // the rehash here. + if(BOOST_UNORDERED_BORLAND_BOOL(position)) + this->link_node(n, position); + else + this->link_node_in_bucket(n, bucket); + + return iterator_base(bucket, n); + } + + // Insert (equivalent key containers) + + // if hash function throws, basic exception safety + // strong otherwise + iterator_base insert(iterator_base const& it, value_type const& v) + { + // equal can throw, but with no effects + if (it == this->end() || !equal(extract_key(v), *it)) { + // Use the standard insert if the iterator doesn't point + // to a matching key. + return insert(v); + } + else { + // Find the first node in the group - so that the node + // will be inserted at the end of the group. + + link_ptr start(it.node_); + while(this->prev_in_group(start)->next_ == start) + start = this->prev_in_group(start); + + // Create the node before rehashing in case it throws an + // exception (need strong safety in such a case). + node_constructor a(this->allocators_); + a.construct(v); + + // reserve has basic exception safety if the hash function + // throws, strong otherwise. + bucket_ptr base = reserve(size() + 1) ? + get_bucket(extract_key(v)) : it.bucket_; + + // Nothing after this point can throw + + link_ptr n = a.release(); + this->link_node(n, start); + + return iterator_base(base, n); + } + } + + // Insert from iterator range (equivalent key containers) + + private: + + // if hash function throws, or inserting > 1 element, basic exception safety + // strong otherwise + template + void insert_for_range(I i, I j, forward_traversal_tag) + { + size_type distance = std::distance(i, j); + if(distance == 1) { + insert(*i); + } + else { + // Only require basic exception safety here + reserve_extra(size() + distance); + node_constructor a(this->allocators_); + + for (; i != j; ++i) { + a.construct(*i); + + key_type const& k = extract_key(a.get()->value_); + bucket_ptr bucket = get_bucket(k); + link_ptr position = find_iterator(bucket, k); + + if(BOOST_UNORDERED_BORLAND_BOOL(position)) + this->link_node(a.release(), position); + else + this->link_node_in_bucket(a.release(), bucket); + } + } + } + + // if hash function throws, or inserting > 1 element, basic exception safety + // strong otherwise + template + void insert_for_range(I i, I j, + boost::incrementable_traversal_tag) + { + // If only inserting 1 element, get the required + // safety since insert is only called once. + for (; i != j; ++i) insert(*i); + } + + public: + + // if hash function throws, or inserting > 1 element, basic exception safety + // strong otherwise + template + void insert(I i, I j) + { + BOOST_DEDUCED_TYPENAME boost::iterator_traversal::type + iterator_traversal_tag; + insert_for_range(i, j, iterator_traversal_tag); + } +#else + // if hash function throws, basic exception safety + // strong otherwise + value_type& operator[](key_type const& k) + { + BOOST_STATIC_ASSERT(( + !boost::is_same::value)); + typedef BOOST_DEDUCED_TYPENAME value_type::second_type mapped_type; + + size_type hash_value = hash_function()(k); + bucket_ptr bucket = this->bucket_from_hash(hash_value); + link_ptr pos = find_iterator(bucket, k); + + if (BOOST_UNORDERED_BORLAND_BOOL(pos)) + return data::get_value(pos); + else + { + // Side effects only in this block. + + // Create the node before rehashing in case it throws an + // exception (need strong safety in such a case). + node_constructor a(this->allocators_); + a.construct(value_type(k, mapped_type())); + + // reserve has basic exception safety if the hash function + // throws, strong otherwise. + if(reserve(size() + 1)) + bucket = this->bucket_from_hash(hash_value); + + // Nothing after this point can throw. + + link_ptr n = a.release(); + this->link_node_in_bucket(n, bucket); + + return data::get_value(n); + } + } + + // Insert (unique keys) + + // if hash function throws, basic exception safety + // strong otherwise + std::pair insert(value_type const& v) + { + // No side effects in this initial code + key_type const& k = extract_key(v); + size_type hash_value = hash_function()(k); + bucket_ptr bucket = this->bucket_from_hash(hash_value); + link_ptr pos = find_iterator(bucket, k); + + if (BOOST_UNORDERED_BORLAND_BOOL(pos)) { + // Found an existing key, return it (no throw). + return std::pair( + iterator_base(bucket, pos), false); + + } else { + // Doesn't already exist, add to bucket. + // Side effects only in this block. + + // Create the node before rehashing in case it throws an + // exception (need strong safety in such a case). + node_constructor a(this->allocators_); + a.construct(v); + + // reserve has basic exception safety if the hash function + // throws, strong otherwise. + if(reserve(size() + 1)) + bucket = this->bucket_from_hash(hash_value); + + // Nothing after this point can throw. + + link_ptr n = a.release(); + this->link_node_in_bucket(n, bucket); + + return std::pair( + iterator_base(bucket, n), true); + } + } + + // Insert (unique keys) + + // if hash function throws, basic exception safety + // strong otherwise + iterator_base insert(iterator_base const& it, value_type const& v) + { + if(it != this->end() && equal(extract_key(v), *it)) + return it; + else + return insert(v).first; + } + + // Insert from iterators (unique keys) + + template + size_type insert_size(I i, I j, boost::forward_traversal_tag) + { + return std::distance(i, j); + } + + template + size_type insert_size(I, I, boost::incrementable_traversal_tag) + { + return 1; + } + + template + size_type insert_size(I i, I j) + { + BOOST_DEDUCED_TYPENAME boost::iterator_traversal::type + iterator_traversal_tag; + return insert_size(i, j, iterator_traversal_tag); + } + + // if hash function throws, or inserting > 1 element, basic exception safety + // strong otherwise + template + void insert(InputIterator i, InputIterator j) + { + node_constructor a(this->allocators_); + + for (; i != j; ++i) { + // No side effects in this initial code + size_type hash_value = hash_function()(extract_key(*i)); + bucket_ptr bucket = this->bucket_from_hash(hash_value); + link_ptr pos = find_iterator(bucket, extract_key(*i)); + + if (!BOOST_UNORDERED_BORLAND_BOOL(pos)) { + // Doesn't already exist, add to bucket. + // Side effects only in this block. + + // Create the node before rehashing in case it throws an + // exception (need strong safety in such a case). + value_type const& v = *i; + a.construct(v); + + // reserve has basic exception safety if the hash function + // throws, strong otherwise. + if(size() + 1 >= max_load_) { + reserve(size() + insert_size(i, j)); + bucket = this->bucket_from_hash(hash_value); + } + + // Nothing after this point can throw. + this->link_node_in_bucket(a.release(), bucket); + } + } + } +#endif + 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); + link_ptr* it = find_for_erase(bucket, k); + + // No throw. + return *it ? this->erase_group(it, bucket) : 0; + } + + // 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 + { + link_ptr it = find_iterator(k); // throws, strong + return BOOST_UNORDERED_BORLAND_BOOL(it) ? data::group_count(it) : 0; + } + + // find + // + // strong exception safety, no side effects + iterator_base find(key_type const& k) const + { + bucket_ptr bucket = get_bucket(k); + link_ptr it = find_iterator(bucket, k); + + if (BOOST_UNORDERED_BORLAND_BOOL(it)) + return iterator_base(bucket, it); + else + return this->end(); + } + + value_type& at(key_type const& k) const + { + bucket_ptr bucket = get_bucket(k); + link_ptr it = find_iterator(bucket, k); + + if (BOOST_UNORDERED_BORLAND_BOOL(it)) + return data::get_value(it); + else + throw std::out_of_range("Unable to find key in unordered_map."); + } + + // equal_range + // + // strong exception safety, no side effects + std::pair equal_range(key_type const& k) const + { + bucket_ptr bucket = get_bucket(k); + link_ptr it = find_iterator(bucket, k); + if (BOOST_UNORDERED_BORLAND_BOOL(it)) { + iterator_base first(iterator_base(bucket, it)); + iterator_base second(first); + second.increment_group(); + 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 + link_ptr find_iterator(key_type const& k) const + { + return find_iterator(get_bucket(k), k); + } + + // strong exception safety, no side effects + link_ptr find_iterator(bucket_ptr bucket, + key_type const& k) const + { + link_ptr it = this->begin(bucket); + while (BOOST_UNORDERED_BORLAND_BOOL(it) && !equal(k, data::get_value(it))) + it = data::next_group(it); + + return it; + } + + // strong exception safety, no side effects + link_ptr* find_for_erase(bucket_ptr bucket, key_type const& k) const + { + link_ptr* it = &bucket->next_; + while(BOOST_UNORDERED_BORLAND_BOOL(*it) && !equal(k, data::get_value(*it))) + it = &data::next_group(*it); + + return it; + } + }; + + // Iterators + + template class BOOST_UNORDERED_ITERATOR; + template class BOOST_UNORDERED_CONST_ITERATOR; + template class BOOST_UNORDERED_LOCAL_ITERATOR; + template class BOOST_UNORDERED_CONST_LOCAL_ITERATOR; + class iterator_access; + + // Local Iterators + // + // all no throw + + template + class BOOST_UNORDERED_LOCAL_ITERATOR + : public boost::iterator < + std::forward_iterator_tag, + BOOST_DEDUCED_TYPENAME allocator_value_type::type, + std::ptrdiff_t, + BOOST_DEDUCED_TYPENAME allocator_pointer::type, + BOOST_DEDUCED_TYPENAME allocator_reference::type > + { + public: + typedef BOOST_DEDUCED_TYPENAME allocator_value_type::type value_type; + + private: + typedef BOOST_UNORDERED_TABLE_DATA data; + typedef BOOST_DEDUCED_TYPENAME data::link_ptr ptr; + typedef BOOST_UNORDERED_CONST_LOCAL_ITERATOR const_local_iterator; + + friend class BOOST_UNORDERED_CONST_LOCAL_ITERATOR; + ptr ptr_; + + public: + BOOST_UNORDERED_LOCAL_ITERATOR() : ptr_() { + BOOST_UNORDERED_MSVC_RESET_PTR(ptr_); + } + explicit BOOST_UNORDERED_LOCAL_ITERATOR(ptr x) : ptr_(x) {} + BOOST_DEDUCED_TYPENAME allocator_reference::type operator*() const + { return data::get_value(ptr_); } + value_type* operator->() const { return &data::get_value(ptr_); } + BOOST_UNORDERED_LOCAL_ITERATOR& operator++() { ptr_ = ptr_->next_; return *this; } + BOOST_UNORDERED_LOCAL_ITERATOR operator++(int) { BOOST_UNORDERED_LOCAL_ITERATOR tmp(ptr_); ptr_ = ptr_->next_; return tmp; } + bool operator==(BOOST_UNORDERED_LOCAL_ITERATOR x) const { return ptr_ == x.ptr_; } + bool operator==(const_local_iterator x) const { return ptr_ == x.ptr_; } + bool operator!=(BOOST_UNORDERED_LOCAL_ITERATOR x) const { return ptr_ != x.ptr_; } + bool operator!=(const_local_iterator x) const { return ptr_ != x.ptr_; } + }; + + template + class BOOST_UNORDERED_CONST_LOCAL_ITERATOR + : public boost::iterator < + std::forward_iterator_tag, + BOOST_DEDUCED_TYPENAME allocator_value_type::type, + std::ptrdiff_t, + BOOST_DEDUCED_TYPENAME allocator_const_pointer::type, + BOOST_DEDUCED_TYPENAME allocator_const_reference::type > + { + public: + typedef BOOST_DEDUCED_TYPENAME allocator_value_type::type value_type; + + private: + typedef BOOST_UNORDERED_TABLE_DATA data; + typedef BOOST_DEDUCED_TYPENAME data::link_ptr ptr; + typedef BOOST_UNORDERED_LOCAL_ITERATOR local_iterator; + friend class BOOST_UNORDERED_LOCAL_ITERATOR; + ptr ptr_; + + public: + BOOST_UNORDERED_CONST_LOCAL_ITERATOR() : ptr_() { + BOOST_UNORDERED_MSVC_RESET_PTR(ptr_); + } + explicit BOOST_UNORDERED_CONST_LOCAL_ITERATOR(ptr x) : ptr_(x) {} + BOOST_UNORDERED_CONST_LOCAL_ITERATOR(local_iterator x) : ptr_(x.ptr_) {} + BOOST_DEDUCED_TYPENAME allocator_const_reference::type + operator*() const { return data::get_value(ptr_); } + value_type const* operator->() const { return &data::get_value(ptr_); } + BOOST_UNORDERED_CONST_LOCAL_ITERATOR& operator++() { ptr_ = ptr_->next_; return *this; } + BOOST_UNORDERED_CONST_LOCAL_ITERATOR operator++(int) { BOOST_UNORDERED_CONST_LOCAL_ITERATOR tmp(ptr_); ptr_ = ptr_->next_; return tmp; } + bool operator==(local_iterator x) const { return ptr_ == x.ptr_; } + bool operator==(BOOST_UNORDERED_CONST_LOCAL_ITERATOR x) const { return ptr_ == x.ptr_; } + bool operator!=(local_iterator x) const { return ptr_ != x.ptr_; } + bool operator!=(BOOST_UNORDERED_CONST_LOCAL_ITERATOR x) const { return ptr_ != x.ptr_; } + }; + + // iterators + // + // all no throw + + + template + class BOOST_UNORDERED_ITERATOR + : public boost::iterator < + std::forward_iterator_tag, + BOOST_DEDUCED_TYPENAME allocator_value_type::type, + std::ptrdiff_t, + BOOST_DEDUCED_TYPENAME allocator_pointer::type, + BOOST_DEDUCED_TYPENAME allocator_reference::type > + { + public: + typedef BOOST_DEDUCED_TYPENAME allocator_value_type::type value_type; + + private: + typedef BOOST_DEDUCED_TYPENAME BOOST_UNORDERED_TABLE_DATA::iterator_base base; + typedef BOOST_UNORDERED_CONST_ITERATOR const_iterator; + friend class BOOST_UNORDERED_CONST_ITERATOR; + base base_; + + public: + + BOOST_UNORDERED_ITERATOR() : base_() {} + explicit BOOST_UNORDERED_ITERATOR(base const& x) : base_(x) {} + BOOST_DEDUCED_TYPENAME allocator_reference::type + operator*() const { return *base_; } + value_type* operator->() const { return &*base_; } + BOOST_UNORDERED_ITERATOR& operator++() { base_.increment(); return *this; } + BOOST_UNORDERED_ITERATOR operator++(int) { BOOST_UNORDERED_ITERATOR tmp(base_); base_.increment(); return tmp; } + bool operator==(BOOST_UNORDERED_ITERATOR const& x) const { return base_ == x.base_; } + bool operator==(const_iterator const& x) const { return base_ == x.base_; } + bool operator!=(BOOST_UNORDERED_ITERATOR const& x) const { return base_ != x.base_; } + bool operator!=(const_iterator const& x) const { return base_ != x.base_; } + }; + + template + class BOOST_UNORDERED_CONST_ITERATOR + : public boost::iterator < + std::forward_iterator_tag, + BOOST_DEDUCED_TYPENAME allocator_value_type::type, + std::ptrdiff_t, + BOOST_DEDUCED_TYPENAME allocator_const_pointer::type, + BOOST_DEDUCED_TYPENAME allocator_const_reference::type > + { + public: + typedef BOOST_DEDUCED_TYPENAME allocator_value_type::type value_type; + + private: + typedef BOOST_DEDUCED_TYPENAME BOOST_UNORDERED_TABLE_DATA::iterator_base base; + typedef BOOST_UNORDERED_ITERATOR iterator; + friend class BOOST_UNORDERED_ITERATOR; + friend class iterator_access; + base base_; + + public: + + BOOST_UNORDERED_CONST_ITERATOR() : base_() {} + explicit BOOST_UNORDERED_CONST_ITERATOR(base const& x) : base_(x) {} + BOOST_UNORDERED_CONST_ITERATOR(iterator const& x) : base_(x.base_) {} + BOOST_DEDUCED_TYPENAME allocator_const_reference::type + operator*() const { return *base_; } + value_type const* operator->() const { return &*base_; } + BOOST_UNORDERED_CONST_ITERATOR& operator++() { base_.increment(); return *this; } + BOOST_UNORDERED_CONST_ITERATOR operator++(int) { BOOST_UNORDERED_CONST_ITERATOR tmp(base_); base_.increment(); return tmp; } + bool operator==(iterator const& x) const { return base_ == x.base_; } + bool operator==(BOOST_UNORDERED_CONST_ITERATOR const& x) const { return base_ == x.base_; } + bool operator!=(iterator const& x) const { return base_ != x.base_; } + bool operator!=(BOOST_UNORDERED_CONST_ITERATOR const& x) const { return base_ != x.base_; } + }; + } +} + +#undef BOOST_UNORDERED_TABLE +#undef BOOST_UNORDERED_TABLE_DATA +#undef BOOST_UNORDERED_ITERATOR +#undef BOOST_UNORDERED_CONST_ITERATOR +#undef BOOST_UNORDERED_LOCAL_ITERATOR +#undef BOOST_UNORDERED_CONST_LOCAL_ITERATOR + diff --git a/include/boost/unordered_map.hpp b/include/boost/unordered_map.hpp new file mode 100644 index 00000000..6fface3f --- /dev/null +++ b/include/boost/unordered_map.hpp @@ -0,0 +1,636 @@ + +// Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard. +// Copyright (C) 2005-2007 Daniel James. +// Distributed under 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 +{ + template , + class Pred = std::equal_to, + class Alloc = std::allocator > > + class unordered_map + { + typedef boost::unordered_detail::hash_types_unique_keys< + std::pair, Key, Hash, Pred, Alloc + > implementation; + + BOOST_DEDUCED_TYPENAME implementation::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; + + typedef Alloc allocator_type; + typedef BOOST_DEDUCED_TYPENAME allocator_type::pointer pointer; + typedef BOOST_DEDUCED_TYPENAME allocator_type::const_pointer const_pointer; + typedef BOOST_DEDUCED_TYPENAME allocator_type::reference reference; + typedef BOOST_DEDUCED_TYPENAME allocator_type::const_reference const_reference; + + typedef BOOST_DEDUCED_TYPENAME implementation::size_type size_type; + typedef BOOST_DEDUCED_TYPENAME implementation::difference_type difference_type; + + typedef BOOST_DEDUCED_TYPENAME implementation::iterator iterator; + typedef BOOST_DEDUCED_TYPENAME implementation::const_iterator const_iterator; + typedef BOOST_DEDUCED_TYPENAME implementation::local_iterator local_iterator; + typedef BOOST_DEDUCED_TYPENAME implementation::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) + : base(f, l, boost::unordered_detail::default_initial_bucket_count, + hasher(), key_equal(), allocator_type()) + { + } + + template + unordered_map(InputIterator f, InputIterator l, + size_type n, + const hasher &hf = hasher(), + const key_equal &eql = key_equal(), + const allocator_type &a = allocator_type()) + : base(f, l, n, hf, eql, a) + { + } + + private: + + BOOST_DEDUCED_TYPENAME implementation::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()); + } + + const_iterator cbegin() const + { + return const_iterator(base.begin()); + } + + const_iterator cend() const + { + return const_iterator(base.end()); + } + + // modifiers + + std::pair insert(const value_type& obj) + { + return boost::unordered_detail::pair_cast( + base.insert(obj)); + } + + iterator insert(const_iterator hint, const value_type& obj) + { + return iterator(base.insert(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; + } + + mapped_type& at(const key_type& k) + { + return base.at(k).second; + } + + mapped_type const& at(const key_type& k) const + { + return base.at(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)); + } + + const_local_iterator cbegin(size_type n) const + { + return const_local_iterator(base.begin(n)); + } + + const_local_iterator cend(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); + } + + template , + class Pred = std::equal_to, + class Alloc = std::allocator > > + class unordered_multimap + { + typedef boost::unordered_detail::hash_types_equivalent_keys< + std::pair, Key, Hash, Pred, Alloc + > implementation; + + BOOST_DEDUCED_TYPENAME implementation::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; + + typedef Alloc allocator_type; + typedef BOOST_DEDUCED_TYPENAME allocator_type::pointer pointer; + typedef BOOST_DEDUCED_TYPENAME allocator_type::const_pointer const_pointer; + typedef BOOST_DEDUCED_TYPENAME allocator_type::reference reference; + typedef BOOST_DEDUCED_TYPENAME allocator_type::const_reference const_reference; + + typedef BOOST_DEDUCED_TYPENAME implementation::size_type size_type; + typedef BOOST_DEDUCED_TYPENAME implementation::difference_type difference_type; + + typedef BOOST_DEDUCED_TYPENAME implementation::iterator iterator; + typedef BOOST_DEDUCED_TYPENAME implementation::const_iterator const_iterator; + typedef BOOST_DEDUCED_TYPENAME implementation::local_iterator local_iterator; + typedef BOOST_DEDUCED_TYPENAME implementation::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) + : base(f, l, boost::unordered_detail::default_initial_bucket_count, + hasher(), key_equal(), allocator_type()) + { + } + + template + unordered_multimap(InputIterator f, InputIterator l, + size_type n, + const hasher &hf = hasher(), + const key_equal &eql = key_equal(), + const allocator_type &a = allocator_type()) + : base(f, l, n, hf, eql, a) + { + } + + private: + + BOOST_DEDUCED_TYPENAME implementation::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()); + } + + const_iterator cbegin() const + { + return const_iterator(base.begin()); + } + + const_iterator cend() const + { + return const_iterator(base.end()); + } + + // modifiers + + iterator insert(const value_type& obj) + { + return iterator(base.insert(obj)); + } + + iterator insert(const_iterator hint, const value_type& obj) + { + return iterator(base.insert(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)); + } + + const_local_iterator cbegin(size_type n) const + { + return const_local_iterator(base.begin(n)); + } + + const_local_iterator cend(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..056a9e22 --- /dev/null +++ b/include/boost/unordered_set.hpp @@ -0,0 +1,591 @@ + +// Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard. +// Copyright (C) 2005-2007 Daniel James. +// Distributed under 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 +{ + template , + class Pred = std::equal_to, + class Alloc = std::allocator > + class unordered_set + { + typedef boost::unordered_detail::hash_types_unique_keys< + Value, Value, Hash, Pred, Alloc + > implementation; + + BOOST_DEDUCED_TYPENAME implementation::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 BOOST_DEDUCED_TYPENAME allocator_type::pointer pointer; + typedef BOOST_DEDUCED_TYPENAME allocator_type::const_pointer const_pointer; + typedef BOOST_DEDUCED_TYPENAME allocator_type::reference reference; + typedef BOOST_DEDUCED_TYPENAME allocator_type::const_reference const_reference; + + typedef BOOST_DEDUCED_TYPENAME implementation::size_type size_type; + typedef BOOST_DEDUCED_TYPENAME implementation::difference_type difference_type; + + typedef BOOST_DEDUCED_TYPENAME implementation::const_iterator iterator; + typedef BOOST_DEDUCED_TYPENAME implementation::const_iterator const_iterator; + typedef BOOST_DEDUCED_TYPENAME implementation::const_local_iterator local_iterator; + typedef BOOST_DEDUCED_TYPENAME implementation::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) + : base(f, l, boost::unordered_detail::default_initial_bucket_count, + hasher(), key_equal(), allocator_type()) + { + } + + template + unordered_set(InputIterator f, InputIterator l, size_type n, + const hasher &hf = hasher(), + const key_equal &eql = key_equal(), + const allocator_type &a = allocator_type()) + : base(f, l, n, hf, eql, a) + { + } + + private: + + BOOST_DEDUCED_TYPENAME implementation::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()); + } + + const_iterator cbegin() const + { + return const_iterator(base.begin()); + } + + const_iterator cend() const + { + return const_iterator(base.end()); + } + + // modifiers + + std::pair insert(const value_type& obj) + { + return boost::unordered_detail::pair_cast( + base.insert(obj)); + } + + iterator insert(const_iterator hint, const value_type& obj) + { + return iterator(base.insert(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 + + 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) 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)); + } + + const_local_iterator cbegin(size_type n) const + { + return const_local_iterator(base.begin(n)); + } + + const_local_iterator cend(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); + } + + template , + class Pred = std::equal_to, + class Alloc = std::allocator > + class unordered_multiset + { + typedef boost::unordered_detail::hash_types_equivalent_keys< + Value, Value, Hash, Pred, Alloc + > implementation; + + BOOST_DEDUCED_TYPENAME implementation::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 BOOST_DEDUCED_TYPENAME allocator_type::pointer pointer; + typedef BOOST_DEDUCED_TYPENAME allocator_type::const_pointer const_pointer; + typedef BOOST_DEDUCED_TYPENAME allocator_type::reference reference; + typedef BOOST_DEDUCED_TYPENAME allocator_type::const_reference const_reference; + + typedef BOOST_DEDUCED_TYPENAME implementation::size_type size_type; + typedef BOOST_DEDUCED_TYPENAME implementation::difference_type difference_type; + + typedef BOOST_DEDUCED_TYPENAME implementation::const_iterator iterator; + typedef BOOST_DEDUCED_TYPENAME implementation::const_iterator const_iterator; + typedef BOOST_DEDUCED_TYPENAME implementation::const_local_iterator local_iterator; + typedef BOOST_DEDUCED_TYPENAME implementation::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) + : base(f, l, boost::unordered_detail::default_initial_bucket_count, + hasher(), key_equal(), allocator_type()) + { + } + + template + unordered_multiset(InputIterator f, InputIterator l, size_type n, + const hasher &hf = hasher(), + const key_equal &eql = key_equal(), + const allocator_type &a = allocator_type()) + : base(f, l, n, hf, eql, a) + { + } + + private: + + BOOST_DEDUCED_TYPENAME implementation::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()); + } + + const_iterator cbegin() const + { + return const_iterator(base.begin()); + } + + const_iterator cend() const + { + return const_iterator(base.end()); + } + + // modifiers + + iterator insert(const value_type& obj) + { + return iterator(base.insert(obj)); + } + + iterator insert(const_iterator hint, const value_type& obj) + { + return iterator(base.insert(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 + + 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) 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)); + } + + const_local_iterator cbegin(size_type n) const + { + return const_local_iterator(base.begin(n)); + } + + const_local_iterator cend(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 diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 new file mode 100644 index 00000000..f0f16866 --- /dev/null +++ b/test/Jamfile.v2 @@ -0,0 +1,8 @@ + +# Copyright 2006-2007 Daniel James. +# Distributed under 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) + +build-project container ; +build-project unordered ; +build-project exception ; diff --git a/test/container/Jamfile.v2 b/test/container/Jamfile.v2 new file mode 100644 index 00000000..7d050bd7 --- /dev/null +++ b/test/container/Jamfile.v2 @@ -0,0 +1,21 @@ + +# Copyright 2005 Daniel James. +# Distributed under 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) + +import testing ; + +project unordered-test/container + : requirements + intel-linux:"-strict_ansi -cxxlib-icc" + gcc:-Wsign-promo + msvc:/W4 + ; + +test-suite container-tests + : + [ run set_compile.cpp ] + [ run map_compile.cpp ] + [ run simple_tests.cpp ] + [ run link_test_1.cpp link_test_2.cpp ] + ; diff --git a/test/container/compile_tests.hpp b/test/container/compile_tests.hpp new file mode 100644 index 00000000..b5fe1ab3 --- /dev/null +++ b/test/container/compile_tests.hpp @@ -0,0 +1,130 @@ + +// Copyright 2005-2007 Daniel James. +// Distributed under 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) + +#if defined(BOOST_MSVC) +#pragma warning(push) +#pragma warning(disable:4100) // unreferenced formal parameter +#endif + +#include + +#if defined(BOOST_MSVC) +#pragma warning(pop) +#endif + +#include +#include +#include +#include +#include +#include +#include "../helpers/check_return_type.hpp" + +typedef long double comparison_type; + +template void sink(T const&) {} + +template +void container_test(X& r, T&) +{ + typedef typename X::iterator iterator; + typedef typename X::const_iterator const_iterator; + typedef typename X::difference_type difference_type; + typedef typename X::size_type size_type; + + typedef typename boost::iterator_value::type iterator_value_type; + typedef typename boost::iterator_value::type const_iterator_value_type; + typedef typename boost::iterator_difference::type iterator_difference_type; + typedef typename boost::iterator_difference::type const_iterator_difference_type; + + typedef typename X::value_type value_type; + typedef typename X::reference reference; + typedef typename X::const_reference const_reference; + + // value_type + + BOOST_MPL_ASSERT((boost::is_same)); + boost::function_requires >(); + + // reference_type / const_reference_type + + BOOST_MPL_ASSERT((boost::is_same)); + BOOST_MPL_ASSERT((boost::is_same)); + + // iterator + + boost::function_requires >(); + BOOST_MPL_ASSERT((boost::is_same)); + BOOST_MPL_ASSERT((boost::is_convertible)); + + // const_iterator + + boost::function_requires >(); + BOOST_MPL_ASSERT((boost::is_same)); + + // difference_type + + BOOST_MPL_ASSERT((boost::mpl::bool_< + std::numeric_limits::is_signed>)); + BOOST_MPL_ASSERT((boost::mpl::bool_< + std::numeric_limits::is_integer>)); + BOOST_MPL_ASSERT((boost::is_same)); + BOOST_MPL_ASSERT((boost::is_same)); + + // size_type + + BOOST_MPL_ASSERT_NOT((boost::mpl::bool_< + std::numeric_limits::is_signed>)); + BOOST_MPL_ASSERT((boost::mpl::bool_< + std::numeric_limits::is_integer>)); + + // size_type can represent any non-negative value type of difference_type + // I'm not sure about either of these tests... + size_type max_diff((std::numeric_limits::max)()); + difference_type converted_diff(max_diff); + BOOST_TEST((std::numeric_limits::max)() + == converted_diff); + + BOOST_TEST( + static_cast( + (std::numeric_limits::max)()) > + static_cast( + (std::numeric_limits::max)())); + + // I don't test the runtime post-conditions here. + X u; + BOOST_TEST(u.size() == 0); + BOOST_TEST(X().size() == 0); + + X a,b; + + sink(X(a)); + X u2(a); + X u3 = a; + + X* ptr = new X(); + X& a1 = *ptr; + (&a1)->~X(); + + X const a_const; + test::check_return_type::equals(a.begin()); + test::check_return_type::equals(a_const.begin()); + test::check_return_type::equals(a.cbegin()); + test::check_return_type::equals(a_const.cbegin()); + test::check_return_type::equals(a.end()); + test::check_return_type::equals(a_const.end()); + test::check_return_type::equals(a.cend()); + test::check_return_type::equals(a_const.cend()); + + // No tests for ==, != since they're not required for unordered containers. + + a.swap(b); + test::check_return_type::equals_ref(r = a); + test::check_return_type::equals(a.size()); + test::check_return_type::equals(a.max_size()); + test::check_return_type::convertible(a.empty()); +} diff --git a/test/container/link_test_1.cpp b/test/container/link_test_1.cpp new file mode 100644 index 00000000..94090ffc --- /dev/null +++ b/test/container/link_test_1.cpp @@ -0,0 +1,24 @@ + +// Copyright 2006-2007 Daniel James. +// Distributed under 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) + +#include +#include + +void foo(boost::unordered_set&, + boost::unordered_map&, + boost::unordered_multiset&, + boost::unordered_multimap&); + +int main() +{ + boost::unordered_set x1; + boost::unordered_map x2; + boost::unordered_multiset x3; + boost::unordered_multimap x4; + + foo(x1, x2, x3, x4); + + return 0; +} diff --git a/test/container/link_test_2.cpp b/test/container/link_test_2.cpp new file mode 100644 index 00000000..3a203d10 --- /dev/null +++ b/test/container/link_test_2.cpp @@ -0,0 +1,18 @@ + +// Copyright 2006-2007 Daniel James. +// Distributed under 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) + +#include +#include + +void foo(boost::unordered_set& x1, + boost::unordered_map& x2, + boost::unordered_multiset& x3, + boost::unordered_multimap& x4) +{ + x1.insert(1); + x2[2] = 2; + x3.insert(3); + x4.insert(std::make_pair(4, 5)); +} diff --git a/test/container/map_compile.cpp b/test/container/map_compile.cpp new file mode 100644 index 00000000..e498a934 --- /dev/null +++ b/test/container/map_compile.cpp @@ -0,0 +1,45 @@ + +// Copyright 2006-2007 Daniel James. +// Distributed under 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) + +// This test creates the containers with members that meet their minimum +// requirements. Makes sure everything compiles and is defined correctly. + +#include + +#include +#include +#include "../objects/minimal.hpp" +#include "./compile_tests.hpp" + +int main() +{ + typedef std::pair value_type; + value_type value( + test::minimal::assignable::create(), + test::minimal::copy_constructible::create()); + + std::cout<<"Test unordered_map.\n"; + boost::unordered_map< + test::minimal::assignable, + test::minimal::copy_constructible, + test::minimal::hash, + test::minimal::equal_to, + test::minimal::allocator > map; + + container_test(map, value); + + std::cout<<"Test unordered_multimap.\n"; + boost::unordered_multimap< + test::minimal::assignable, + test::minimal::copy_constructible, + test::minimal::hash, + test::minimal::equal_to, + test::minimal::allocator > multimap; + + container_test(multimap, value); + + return boost::report_errors(); +} diff --git a/test/container/set_compile.cpp b/test/container/set_compile.cpp new file mode 100644 index 00000000..f34de5d7 --- /dev/null +++ b/test/container/set_compile.cpp @@ -0,0 +1,39 @@ + +// Copyright 2006-2007 Daniel James. +// Distributed under 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) + +// This test creates the containers with members that meet their minimum +// requirements. Makes sure everything compiles and is defined correctly. + +#include + +#include +#include +#include "../objects/minimal.hpp" +#include "./compile_tests.hpp" + +int main() +{ + test::minimal::assignable assignable = test::minimal::assignable::create(); + + std::cout<<"Test unordered_set.\n"; + boost::unordered_set< + test::minimal::assignable, + test::minimal::hash, + test::minimal::equal_to, + test::minimal::allocator > set; + + container_test(set, assignable); + + std::cout<<"Test unordered_multiset.\n"; + boost::unordered_multiset< + test::minimal::assignable, + test::minimal::hash, + test::minimal::equal_to, + test::minimal::allocator > multiset; + + container_test(multiset, assignable); + + return boost::report_errors(); +} diff --git a/test/container/simple_tests.cpp b/test/container/simple_tests.cpp new file mode 100644 index 00000000..f614b186 --- /dev/null +++ b/test/container/simple_tests.cpp @@ -0,0 +1,104 @@ + +// Copyright 2006-2007 Daniel James. +// Distributed under 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) + +// This test checks the runtime requirements of containers. + +#include +#include +#include +#include +#include "../helpers/equivalent.hpp" + +template +void simple_test(X const& a) +{ + test::unordered_equivalence_tester equivalent(a); + + { + X u; + BOOST_TEST(u.size() == 0); + BOOST_TEST(X().size() == 0); + } + + { + BOOST_TEST(equivalent(X(a))); + } + + { + X u(a); + BOOST_TEST(equivalent(u)); + } + + { + X u = a; + BOOST_TEST(equivalent(u)); + } + + { + X b(a); + BOOST_TEST(b.begin() == const_cast(b).cbegin()); + BOOST_TEST(b.end() == const_cast(b).cend()); + } + + { + X b(a); + X c; + BOOST_TEST(equivalent(b)); + BOOST_TEST(c.empty()); + b.swap(c); + BOOST_TEST(b.empty()); + BOOST_TEST(equivalent(c)); + b.swap(c); + BOOST_TEST(b.empty()); + BOOST_TEST(equivalent(c)); + } + + { + X u; + X& r = u; + BOOST_TEST(&(r = r) == &r); + BOOST_TEST(r.empty()); + BOOST_TEST(&(r = a) == &r); + BOOST_TEST(equivalent(r)); + BOOST_TEST(&(r = r) == &r); + BOOST_TEST(equivalent(r)); + } + + { + BOOST_TEST(a.size() == + (typename X::size_type) std::distance(a.begin(), a.end())); + } + + { + BOOST_TEST(a.empty() == (a.size() == 0)); + } + + { + BOOST_TEST(a.empty() == (a.begin() == a.end())); + X u; + BOOST_TEST(u.begin() == u.end()); + } +} + +int main() +{ + std::cout<<"Test unordered_set.\n"; + boost::unordered_set set; + simple_test(set); + + std::cout<<"Test unordered_multiset.\n"; + boost::unordered_multiset multiset; + simple_test(multiset); + + std::cout<<"Test unordered_map.\n"; + boost::unordered_map map; + simple_test(map); + + std::cout<<"Test unordered_multimap.\n"; + boost::unordered_multimap multimap; + simple_test(multimap); + + return boost::report_errors(); +} diff --git a/test/exception/Jamfile.v2 b/test/exception/Jamfile.v2 new file mode 100644 index 00000000..7cb28a5e --- /dev/null +++ b/test/exception/Jamfile.v2 @@ -0,0 +1,26 @@ + +# Copyright 2006-2007 Daniel James. +# Distributed under 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) + +import testing ; + +alias framework : /boost/test//boost_unit_test_framework ; + +project unordered-test/exception-tests + : requirements + intel-linux:"-strict_ansi -cxxlib-icc" + gcc:-Wsign-promo + ; + +test-suite unordered-tests + : + [ run constructor_tests.cpp framework ] + [ run copy_tests.cpp framework ] + [ run assign_tests.cpp framework ] + [ run insert_tests.cpp framework ] + [ run erase_tests.cpp framework ] + [ run rehash_tests.cpp framework ] + [ run swap_tests.cpp framework : : : + BOOST_UNORDERED_SWAP_METHOD=2 ] + ; diff --git a/test/exception/assign_tests.cpp b/test/exception/assign_tests.cpp new file mode 100644 index 00000000..dfff9727 --- /dev/null +++ b/test/exception/assign_tests.cpp @@ -0,0 +1,81 @@ + +// Copyright 2006-2007 Daniel James. +// Distributed under 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) + +#include "./containers.hpp" + +#define BOOST_TEST_MAIN +#include +#include +#include "../helpers/random_values.hpp" +#include "../helpers/invariants.hpp" + +test::seed_t seed(12847); + +template +struct self_assign_base : public test::exception_base +{ + test::random_values values; + self_assign_base(int count = 0) : values(count) {} + + typedef T data_type; + T init() const { return T(values.begin(), values.end()); } + void run(T& x) const { x = x; } + void check(T const& x) const { test::check_equivalent_keys(x); } +}; + +template +struct self_assign_test1 : self_assign_base {}; + +template +struct self_assign_test2 : self_assign_base +{ + self_assign_test2() : self_assign_base(100) {} +}; + +template +struct assign_base : public test::exception_base +{ + const test::random_values x_values, y_values; + const T x,y; + + assign_base(unsigned int count1, unsigned int count2, int tag1, int tag2) + : x_values(count1), y_values(count2), + x(x_values.begin(), x_values.end(), 0, typename T::hasher(tag1), typename T::key_equal(tag1), typename T::allocator_type(tag1)), + y(y_values.begin(), y_values.end(), 0, typename T::hasher(tag2), typename T::key_equal(tag2), typename T::allocator_type(tag2)) {} + + typedef T data_type; + T init() const { return T(x); } + void run(T& x1) const { x1 = y; } + void check(T const& x1) const { test::check_equivalent_keys(x1); } +}; + +template +struct assign_test1 : assign_base +{ + assign_test1() : assign_base(0, 0, 0, 0) {} +}; + +template +struct assign_test2 : assign_base +{ + assign_test2() : assign_base(60, 0, 0, 0) {} +}; + +template +struct assign_test3 : assign_base +{ + assign_test3() : assign_base(0, 60, 0, 0) {} +}; + +template +struct assign_test4 : assign_base +{ + assign_test4() : assign_base(10, 10, 1, 2) {} +}; + +RUN_EXCEPTION_TESTS( + (self_assign_test1)(self_assign_test2) + (assign_test1)(assign_test2)(assign_test3)(assign_test4), + CONTAINER_SEQ) diff --git a/test/exception/constructor_tests.cpp b/test/exception/constructor_tests.cpp new file mode 100644 index 00000000..d822e315 --- /dev/null +++ b/test/exception/constructor_tests.cpp @@ -0,0 +1,133 @@ + +// Copyright 2006-2007 Daniel James. +// Distributed under 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) + +#include "./containers.hpp" + +#define BOOST_TEST_MAIN +#include +#include +#include "../helpers/random_values.hpp" +#include "../helpers/input_iterator.hpp" + +test::seed_t seed(91274); + +struct objects +{ + test::exception::object obj; + test::exception::hash hash; + test::exception::equal_to equal_to; + test::exception::allocator allocator; +}; + +template +struct construct_test1 : public objects, test::exception_base +{ + void run() const { + T x; + } +}; + +template +struct construct_test2 : public objects, test::exception_base +{ + void run() const { + T x(300); + } +}; + +template +struct construct_test3 : public objects, test::exception_base +{ + void run() const { + T x(0, hash); + } +}; + +template +struct construct_test4 : public objects, test::exception_base +{ + void run() const { + T x(0, hash, equal_to); + } +}; + +template +struct construct_test5 : public objects, test::exception_base +{ + void run() const { + T x(50, hash, equal_to, allocator); + } +}; + +template +struct range : public test::exception_base +{ + test::random_values values; + + range() : values(5) {} + range(unsigned int count) : values(count) {} +}; + +template +struct range_construct_test1 : public range, objects +{ + void run() const { + T x(this->values.begin(), this->values.end()); + } +}; + +template +struct range_construct_test2 : public range, objects +{ + void run() const { + T x(this->values.begin(), this->values.end(), 0); + } +}; + +template +struct range_construct_test3 : public range, objects +{ + void run() const { + T x(this->values.begin(), this->values.end(), 0, hash); + } +}; + +template +struct range_construct_test4 : public range, objects +{ + void run() const { + T x(this->values.begin(), this->values.end(), 100, hash, equal_to); + } +}; + +// Need to run at least one test with a fairly large number +// of objects in case it triggers a rehash. +template +struct range_construct_test5 : public range, objects +{ + range_construct_test5() : range(60) {} + + void run() const { + T x(this->values.begin(), this->values.end(), 0, hash, equal_to, allocator); + } +}; + +template +struct input_range_construct_test : public range, objects +{ + input_range_construct_test() : range(60) {} + + void run() const { + T x(test::input_iterator(this->values.begin()), + test::input_iterator(this->values.end()), + 0, hash, equal_to, allocator); + } +}; + +RUN_EXCEPTION_TESTS( + (construct_test1)(construct_test2)(construct_test3)(construct_test4)(construct_test5) + (range_construct_test1)(range_construct_test2)(range_construct_test3)(range_construct_test4)(range_construct_test5) + (input_range_construct_test), + CONTAINER_SEQ) diff --git a/test/exception/containers.hpp b/test/exception/containers.hpp new file mode 100644 index 00000000..daed6873 --- /dev/null +++ b/test/exception/containers.hpp @@ -0,0 +1,33 @@ + +// Copyright 2006-2007 Daniel James. +// Distributed under 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) + +#include +#include +#include "../objects/exception.hpp" + +typedef boost::unordered_set< + test::exception::object, + test::exception::hash, + test::exception::equal_to, + test::exception::allocator > test_set; +typedef boost::unordered_multiset< + test::exception::object, + test::exception::hash, + test::exception::equal_to, + test::exception::allocator > test_multiset; +typedef boost::unordered_map< + test::exception::object, + test::exception::object, + test::exception::hash, + test::exception::equal_to, + test::exception::allocator > test_map; +typedef boost::unordered_multimap< + test::exception::object, + test::exception::object, + test::exception::hash, + test::exception::equal_to, + test::exception::allocator > test_multimap; + +#define CONTAINER_SEQ (test_set)(test_multiset)(test_map)(test_multimap) diff --git a/test/exception/copy_tests.cpp b/test/exception/copy_tests.cpp new file mode 100644 index 00000000..53a66514 --- /dev/null +++ b/test/exception/copy_tests.cpp @@ -0,0 +1,53 @@ + +// Copyright 2006-2007 Daniel James. +// Distributed under 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) + +#include "./containers.hpp" + +#define BOOST_TEST_MAIN +#include +#include +#include "../helpers/random_values.hpp" + +test::seed_t seed(73041); + +template +struct copy_test1 : public test::exception_base +{ + T x; + + void run() const { + T y(x); + } +}; + +template +struct copy_test2 : public test::exception_base +{ + test::random_values values; + T x; + + copy_test2() : values(5), x(values.begin(), values.end()) {} + + void run() const { + T y(x); + } +}; + +template +struct copy_test3 : public test::exception_base +{ + test::random_values values; + T x; + + copy_test3() : values(100), x(values.begin(), values.end()) {} + + void run() const { + T y(x); + } +}; + +RUN_EXCEPTION_TESTS( + (copy_test1)(copy_test2)(copy_test3), + CONTAINER_SEQ) diff --git a/test/exception/erase_tests.cpp b/test/exception/erase_tests.cpp new file mode 100644 index 00000000..240e98b0 --- /dev/null +++ b/test/exception/erase_tests.cpp @@ -0,0 +1,57 @@ + +// Copyright 2006-2007 Daniel James. +// Distributed under 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) + +#include "./containers.hpp" + +#define BOOST_TEST_MAIN +#include +#include +#include "../helpers/random_values.hpp" +#include "../helpers/invariants.hpp" +#include "../helpers/helpers.hpp" + +test::seed_t seed(835193); + +template +struct erase_test_base : public test::exception_base +{ + test::random_values values; + erase_test_base(unsigned int count = 5) : values(count) {} + + typedef T data_type; + + data_type init() const { + return T(values.begin(), values.end()); + } + + void check(T const& x) const { + std::string scope(test::scope); + + HASH_CHECK(scope.find("hash::") != std::string::npos || + scope.find("equal_to::") != std::string::npos || + scope == "operator==(object, object)"); + + test::check_equivalent_keys(x); + } +}; + +template +struct erase_by_key_test1 : public erase_test_base +{ + void run(T& x) const + { + typedef typename test::random_values::const_iterator iterator; + + for(iterator it = this->values.begin(), end = this->values.end(); + it != end; ++it) + { + x.erase(test::get_key(*it)); + } + } +}; + +RUN_EXCEPTION_TESTS( + (erase_by_key_test1), + CONTAINER_SEQ) diff --git a/test/exception/insert_tests.cpp b/test/exception/insert_tests.cpp new file mode 100644 index 00000000..1e59f822 --- /dev/null +++ b/test/exception/insert_tests.cpp @@ -0,0 +1,212 @@ + +// Copyright 2006-2007 Daniel James. +// Distributed under 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) + +#include "./containers.hpp" + +#define BOOST_TEST_MAIN +#include +#include +#include +#include "../helpers/random_values.hpp" +#include "../helpers/invariants.hpp" +#include "../helpers/strong.hpp" +#include "../helpers/input_iterator.hpp" + +#include + +test::seed_t seed(747373); + +template +struct insert_test_base : public test::exception_base +{ + test::random_values values; + insert_test_base(unsigned int count = 5) : values(count) {} + + typedef T data_type; + typedef test::strong strong_type; + + data_type init() const { + return T(); + } + + void check(T const& x, strong_type const& strong) const { + std::string scope(test::scope); + + if(scope.find("hash::operator()") == std::string::npos) + strong.test(x); + test::check_equivalent_keys(x); + } +}; + +template +struct insert_test1 : public insert_test_base +{ + typedef typename insert_test_base::strong_type strong_type; + + void run(T& x, strong_type& strong) const { + for(typename test::random_values::const_iterator + it = this->values.begin(), end = this->values.end(); it != end; ++it) + { + strong.store(x); + x.insert(*it); + } + } +}; + +template +struct insert_test2 : public insert_test_base +{ + typedef typename insert_test_base::strong_type strong_type; + + void run(T& x, strong_type& strong) const { + for(typename test::random_values::const_iterator + it = this->values.begin(), end = this->values.end(); it != end; ++it) + { + strong.store(x); + x.insert(x.begin(), *it); + } + } +}; + +template +struct insert_test3 : public insert_test_base +{ + void run(T& x) const { + x.insert(this->values.begin(), this->values.end()); + } + + void check(T const& x) const { + test::check_equivalent_keys(x); + } +}; + +template +struct insert_test4 : public insert_test_base +{ + typedef typename insert_test_base::strong_type strong_type; + + void run(T& x, strong_type& strong) const { + for(typename test::random_values::const_iterator + it = this->values.begin(), end = this->values.end(); it != end; ++it) + { + strong.store(x); + x.insert(it, boost::next(it)); + } + } +}; + +template +struct insert_test_rehash1 : public insert_test_base +{ + typedef typename insert_test_base::strong_type strong_type; + + insert_test_rehash1() : insert_test_base(1000) {} + + T init() const { + typedef typename T::size_type size_type; + + T x; + x.max_load_factor(0.25); + size_type bucket_count = x.bucket_count(); + size_type initial_elements = static_cast( + std::ceil(bucket_count * x.max_load_factor()) - 1); + BOOST_REQUIRE(initial_elements < this->values.size()); + x.insert(this->values.begin(), + boost::next(this->values.begin(), initial_elements)); + BOOST_REQUIRE(bucket_count == x.bucket_count()); + return x; + } + + void run(T& x, strong_type& strong) const { + typename T::size_type bucket_count = x.bucket_count(); + int count = 0; + typename T::const_iterator pos = x.cbegin(); + + for(typename test::random_values::const_iterator + it = boost::next(this->values.begin(), x.size()), end = this->values.end(); + it != end && count < 10; ++it, ++count) + { + strong.store(x); + pos = x.insert(pos, *it); + } + + // This isn't actually a failure, but it means the test isn't doing its + // job. + BOOST_REQUIRE(x.bucket_count() != bucket_count); + } +}; + +template +struct insert_test_rehash2 : public insert_test_rehash1 +{ + typedef typename insert_test_base::strong_type strong_type; + + void run(T& x, strong_type& strong) const { + typename T::size_type bucket_count = x.bucket_count(); + int count = 0; + + for(typename test::random_values::const_iterator + it = boost::next(this->values.begin(), x.size()), end = this->values.end(); + it != end && count < 10; ++it, ++count) + { + strong.store(x); + x.insert(*it); + } + + // This isn't actually a failure, but it means the test isn't doing its + // job. + BOOST_REQUIRE(x.bucket_count() != bucket_count); + } +}; + +template +struct insert_test_rehash3 : public insert_test_base +{ + typename T::size_type mutable rehash_bucket_count, original_bucket_count; + + insert_test_rehash3() : insert_test_base(1000) {} + + T init() const { + typedef typename T::size_type size_type; + + T x; + x.max_load_factor(0.25); + + original_bucket_count = x.bucket_count(); + rehash_bucket_count = static_cast( + std::ceil(original_bucket_count * x.max_load_factor())) - 1; + + size_type initial_elements = rehash_bucket_count - 5; + + BOOST_REQUIRE(initial_elements < this->values.size()); + x.insert(this->values.begin(), + boost::next(this->values.begin(), initial_elements)); + BOOST_REQUIRE(original_bucket_count == x.bucket_count()); + return x; + } + + void run(T& x) const { + typename T::size_type bucket_count = x.bucket_count(); + + x.insert(boost::next(this->values.begin(), x.size()), + boost::next(this->values.begin(), x.size() + 20)); + + // This isn't actually a failure, but it means the test isn't doing its + // job. + BOOST_REQUIRE(x.bucket_count() != bucket_count); + } + + void check(T const& x) const { + if(x.size() < rehash_bucket_count) { + //BOOST_CHECK(x.bucket_count() == original_bucket_count); + } + test::check_equivalent_keys(x); + } +}; + +RUN_EXCEPTION_TESTS( + (insert_test1)(insert_test2)(insert_test3)(insert_test4) + (insert_test_rehash1)(insert_test_rehash2)(insert_test_rehash3), + CONTAINER_SEQ) diff --git a/test/exception/rehash_tests.cpp b/test/exception/rehash_tests.cpp new file mode 100644 index 00000000..1178f4ef --- /dev/null +++ b/test/exception/rehash_tests.cpp @@ -0,0 +1,85 @@ + +// Copyright 2006-2007 Daniel James. +// Distributed under 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) + +#include "./containers.hpp" + +#define BOOST_TEST_MAIN +#include +#include +#include +#include "../helpers/random_values.hpp" +#include "../helpers/invariants.hpp" +#include "../helpers/strong.hpp" + +#include + +test::seed_t seed(3298597); + +template +struct rehash_test_base : public test::exception_base +{ + test::random_values values; + unsigned int n; + rehash_test_base(unsigned int count = 100, unsigned int n = 0) : values(count), n(n) {} + + typedef T data_type; + typedef test::strong strong_type; + + data_type init() const { + T x(values.begin(), values.end(), n); + return x; + } + + void check(T const& x, strong_type const& strong) const { + std::string scope(test::scope); + + if(scope.find("hash::operator()") == std::string::npos && + scope.find("equal_to::operator()") == std::string::npos && + scope != "operator==(object, object)") + strong.test(x); + + test::check_equivalent_keys(x); + } +}; + +template +struct rehash_test0 : rehash_test_base +{ + rehash_test0() : rehash_test_base(0) {} + void run(T& x) const { x.rehash(0); } +}; + +template +struct rehash_test1 : rehash_test_base +{ + rehash_test1() : rehash_test_base(0) {} + void run(T& x) const { x.rehash(200); } +}; + +template +struct rehash_test2 : rehash_test_base +{ + rehash_test2() : rehash_test_base(0, 200) {} + void run(T& x) const { x.rehash(0); } +}; + +template +struct rehash_test3 : rehash_test_base +{ + rehash_test3() : rehash_test_base(10, 0) {} + void run(T& x) const { x.rehash(200); } +}; + +template +struct rehash_test4 : rehash_test_base +{ + rehash_test4() : rehash_test_base(10, 200) {} + void run(T& x) const { x.rehash(0); } +}; + +RUN_EXCEPTION_TESTS( + (rehash_test0)(rehash_test1)(rehash_test2)(rehash_test3)(rehash_test4), + CONTAINER_SEQ) + diff --git a/test/exception/swap_tests.cpp b/test/exception/swap_tests.cpp new file mode 100644 index 00000000..2812e007 --- /dev/null +++ b/test/exception/swap_tests.cpp @@ -0,0 +1,119 @@ + +// Copyright 2006-2007 Daniel James. +// Distributed under 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) + +#include "./containers.hpp" + +#define BOOST_TEST_MAIN +#include +#include +#include "../helpers/random_values.hpp" +#include "../helpers/invariants.hpp" + +test::seed_t seed(9387); + +template +struct self_swap_base : public test::exception_base +{ + test::random_values values; + self_swap_base(int count = 0) : values(count) {} + + typedef T data_type; + T init() const { return T(values.begin(), values.end()); } + void run(T& x) const { x.swap(x); } + void check(T const& x) const { + std::string scope(test::scope); + +#if BOOST_UNORDERED_SWAP_METHOD != 2 + HASH_CHECK( + scope == "hash::operator(hash)" || + scope == "hash::operator=(hash)" || + scope == "equal_to::operator(equal_to)" || + scope == "equal_to::operator=(equal_to)"); +#endif + + test::check_equivalent_keys(x); + } +}; + +template +struct self_swap_test1 : self_swap_base {}; + +template +struct self_swap_test2 : self_swap_base +{ + self_swap_test2() : self_swap_base(100) {} +}; + +template +struct swap_base : public test::exception_base +{ + const test::random_values x_values, y_values; + const T initial_x, initial_y; + + swap_base(unsigned int count1, unsigned int count2, int tag1, int tag2) + : x_values(count1), y_values(count2), + initial_x(x_values.begin(), x_values.end(), 0, typename T::hasher(tag1), + typename T::key_equal(tag1), typename T::allocator_type(tag1)), + initial_y(y_values.begin(), y_values.end(), 0, typename T::hasher(tag2), + typename T::key_equal(tag2), typename T::allocator_type(tag2)) + {} + + struct data_type { + data_type(T const& x, T const& y) + : x(x), y(y) {} + + T x, y; + }; + + data_type init() const { return data_type(initial_x, initial_y); } + void run(data_type& d) const { + try { + d.x.swap(d.y); + } catch (std::runtime_error) {} + } + void check(data_type const& d) const { + std::string scope(test::scope); + +#if BOOST_UNORDERED_SWAP_METHOD != 2 + HASH_CHECK( + scope == "hash::operator(hash)" || + scope == "hash::operator=(hash)" || + scope == "equal_to::operator(equal_to)" || + scope == "equal_to::operator=(equal_to)"); +#endif + + test::check_equivalent_keys(d.x); + test::check_equivalent_keys(d.y); + } +}; + +template +struct swap_test1 : swap_base +{ + swap_test1() : swap_base(0, 0, 0, 0) {} +}; + +template +struct swap_test2 : swap_base +{ + swap_test2() : swap_base(60, 0, 0, 0) {} +}; + +template +struct swap_test3 : swap_base +{ + swap_test3() : swap_base(0, 60, 0, 0) {} +}; + +template +struct swap_test4 : swap_base +{ + swap_test4() : swap_base(10, 10, 1, 2) {} +}; + +RUN_EXCEPTION_TESTS( + (self_swap_test1)(self_swap_test2) + (swap_test1)(swap_test2)(swap_test3)(swap_test4), + CONTAINER_SEQ) diff --git a/test/helpers/allocator.hpp b/test/helpers/allocator.hpp new file mode 100644 index 00000000..0c15f0d9 --- /dev/null +++ b/test/helpers/allocator.hpp @@ -0,0 +1,72 @@ + +// Copyright 2006-2007 Daniel James. +// Distributed under 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) + +#if !defined(BOOST_UNORDERED_TEST_MALLOC_ALLOCATOR_HEADER) +#define BOOST_UNORDERED_TEST_MALLOC_ALLOCATOR_HEADER + +#include +#include +#include + +#if defined(BOOST_MSVC) +#pragma warning(push) +#pragma warning(disable:4100) // unreferenced formal parameter +#endif + +namespace test +{ + template + struct malloc_allocator + { + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + typedef T* pointer; + typedef T const* const_pointer; + typedef T& reference; + typedef T const& const_reference; + typedef T value_type; + + template struct rebind { typedef malloc_allocator other; }; + + malloc_allocator() {} + template malloc_allocator(malloc_allocator const&) {} + malloc_allocator(malloc_allocator const&) {} + + pointer address(reference r) { return &r; } + const_pointer address(const_reference r) { return &r; } + + pointer allocate(size_type n) { + return static_cast(malloc(n * sizeof(T))); + } + + pointer allocate(size_type n, const_pointer u) { return allocate(n); } + void deallocate(pointer p, size_type) { free(p); } + void construct(pointer p, T const& t) { new(p) T(t); } + void destroy(pointer p) { p->~T(); } + + size_type max_size() const { + return (std::numeric_limits::max)(); + } + + bool operator==(malloc_allocator const& x) const { return true; } + bool operator!=(malloc_allocator const& x) const { return false; } + +#if BOOST_WORKAROUND(BOOST_MSVC, < 1300) + template void deallocate(T* p, size_type) { + free(p); + } + char* _Charalloc(size_type n) { + return static_cast(malloc(n * sizeof(char))); + } +#endif + }; +} + +#if defined(BOOST_MSVC) +#pragma warning(pop) +#pragma warning(disable:4100) // unreferenced formal parameter +#endif + +#endif diff --git a/test/helpers/check_return_type.hpp b/test/helpers/check_return_type.hpp new file mode 100644 index 00000000..5938df90 --- /dev/null +++ b/test/helpers/check_return_type.hpp @@ -0,0 +1,38 @@ + +// Copyright 2005-2007 Daniel James. +// Distributed under 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) + +#if !defined(BOOST_UNORDERED_TEST_HELPERS_CHECK_RETURN_TYPE_HEADER) +#define BOOST_UNORDERED_TEST_HELPERS_CHECK_RETURN_TYPE_HEADER + +#include +#include +#include + +namespace test +{ + template + struct check_return_type + { + template + static void equals(T2) + { + BOOST_MPL_ASSERT((boost::is_same)); + } + + template + static void equals_ref(T2&) + { + BOOST_MPL_ASSERT((boost::is_same)); + } + + template + static void convertible(T2) + { + BOOST_MPL_ASSERT((boost::is_convertible)); + } + }; +} + +#endif diff --git a/test/helpers/equivalent.hpp b/test/helpers/equivalent.hpp new file mode 100644 index 00000000..1f2efd4b --- /dev/null +++ b/test/helpers/equivalent.hpp @@ -0,0 +1,94 @@ + +// Copyright 2005-2007 Daniel James. +// Distributed under 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) + +#if !defined(BOOST_UNORDERED_TESTS_EQUIVALENT_HEADER) +#define BOOST_UNORDERED_TESTS_EQUIVALENT_HEADER + +#include +#include +#include +#include +#include "./metafunctions.hpp" +#include "./fwd.hpp" + +namespace test +{ + template + bool equivalent_impl(T1 const& x, T2 const& y, base_type) { + return x == y; + } + + template + bool equivalent_impl(boost::hash const&, boost::hash const&, derived_type) { + return true; + } + + template + bool equivalent_impl(std::equal_to const&, std::equal_to const&, derived_type) { + return true; + } + + template + bool equivalent_impl(std::pair const& x1, + std::pair const& x2, derived_type) { + return equivalent_impl(x1.first, x2.first, derived) && + equivalent_impl(x1.second, x2.second, derived); + } + + struct equivalent_type { + template + bool operator()(T1 const& x, T2 const& y) { + return equivalent_impl(x, y, derived); + } + }; + + namespace { + equivalent_type equivalent; + } + + template + class unordered_equivalence_tester + { + typename Container::size_type size_; + typename Container::hasher hasher_; + typename Container::key_equal key_equal_; + float max_load_factor_; + + typedef typename non_const_value_type::type value_type; + std::vector values_; + public: + unordered_equivalence_tester(Container const &x) + : size_(x.size()), + hasher_(x.hash_function()), key_equal_(x.key_eq()), + max_load_factor_(x.max_load_factor()), + values_() + { + // Can't initialise values_ straight from x because of Visual C++ 6 + values_.reserve(x.size()); + std::copy(x.begin(), x.end(), std::back_inserter(values_)); + + std::sort(values_.begin(), values_.end()); + } + + bool operator()(Container const& x) const + { + if(!((size_ == x.size()) && + (test::equivalent(hasher_, x.hash_function())) && + (test::equivalent(key_equal_, x.key_eq())) && + (max_load_factor_ == x.max_load_factor()) && + (values_.size() == x.size()))) return false; + + std::vector copy; + copy.reserve(x.size()); + std::copy(x.begin(), x.end(), std::back_inserter(copy)); + std::sort(copy.begin(), copy.end()); + return(std::equal(values_.begin(), values_.end(), copy.begin())); + } + private: + unordered_equivalence_tester(); + }; +} + +#endif diff --git a/test/helpers/fwd.hpp b/test/helpers/fwd.hpp new file mode 100644 index 00000000..cc04ad85 --- /dev/null +++ b/test/helpers/fwd.hpp @@ -0,0 +1,26 @@ + +// Copyright 2006-2007 Daniel James. +// Distributed under 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) + +#if !defined(BOOST_UNORDERED_TEST_HELPERS_FWD_HEADER) +#define BOOST_UNORDERED_TEST_HELPERS_FWD_HEADER + +#include + +namespace test +{ + int generate(int const*); + char generate(char const*); + signed char generate(signed char const*); + std::string generate(std::string*); + float generate(float const*); + template + std::pair generate(std::pair*); + + struct base_type {} base; + struct derived_type : base_type {} derived; +} + +#endif + diff --git a/test/helpers/generators.hpp b/test/helpers/generators.hpp new file mode 100644 index 00000000..bdc7ad2c --- /dev/null +++ b/test/helpers/generators.hpp @@ -0,0 +1,91 @@ + +// Copyright 2005-2007 Daniel James. +// Distributed under 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) + +// This uses std::rand to generate random values for tests. +// Which is not good as different platforms will be running different tests. +// It would be much better to use Boost.Random, but it doesn't +// support all the compilers that I want to test on. + +#if !defined(BOOST_UNORDERED_TEST_HELPERS_GENERATORS_HEADER) +#define BOOST_UNORDERED_TEST_HELPERS_GENERATORS_HEADER + +#include +#include +#include +#include + +#include "./fwd.hpp" + +namespace test +{ + struct seed_t { + seed_t(unsigned int x) { + using namespace std; + srand(x); + } + }; + + template + struct generator; + + template std::pair generate( + std::pair const*) + { + static generator g1; + static generator g2; + + return std::pair(g1(), g2()); + } + + template + struct generator + { + typedef T value_type; + value_type operator()() + { + return generate((T const*) 0); + } + }; + + inline int generate(int const*) + { + using namespace std; + return rand(); + } + + inline char generate(char const*) + { + using namespace std; + return static_cast((rand() >> 1) % (128-32) + 32); + } + + inline signed char generate(signed char const*) + { + using namespace std; + return static_cast(rand()); + } + + inline std::string generate(std::string const*) + { + using namespace std; + + static test::generator char_gen; + + std::string result; + + int length = rand() % 10; + for(int i = 0; i < length; ++i) + result += char_gen(); + + return result; + } + + float generate(float const*) + { + return (float) rand() / (float) RAND_MAX; + } +} + +#endif diff --git a/test/helpers/helpers.hpp b/test/helpers/helpers.hpp new file mode 100644 index 00000000..64000d7f --- /dev/null +++ b/test/helpers/helpers.hpp @@ -0,0 +1,41 @@ + +// Copyright 2006-2007 Daniel James. +// Distributed under 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) + +#if !defined(BOOST_UNORDERED_TEST_HELPERS_HEADER) +#define BOOST_UNORDERED_TEST_HELPERS_HEADER + +namespace test +{ + template + struct get_key_impl + { + typedef typename Container::key_type key_type; + + static key_type const& get_key(key_type const& x) + { + return x; + } + + template + static key_type const& get_key(std::pair const& x, char = 0) + { + return x.first; + } + + template + static key_type const& get_key(std::pair const& x, unsigned char = 0) + { + return x.first; + } + }; + + template + inline typename Container::key_type const& get_key(T const& x) + { + return get_key_impl::get_key(x); + } +} + +#endif diff --git a/test/helpers/input_iterator.hpp b/test/helpers/input_iterator.hpp new file mode 100644 index 00000000..c45b1eb3 --- /dev/null +++ b/test/helpers/input_iterator.hpp @@ -0,0 +1,35 @@ + +// Copyright 2005-2007 Daniel James. +// Distributed under 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) + +#if !defined(BOOST_UNORDERED_TEST_HELPERS_INPUT_ITERATOR_HEADER) +#define BOOST_UNORDERED_TEST_HELPERS_INPUT_ITERATOR_HEADER + +#include + +namespace test +{ + template + struct input_iterator_adaptor + : boost::iterator_adaptor< + input_iterator_adaptor, Iterator, + boost::use_default, std::input_iterator_tag> + { + typedef boost::iterator_adaptor< + input_iterator_adaptor, Iterator, + boost::use_default, std::input_iterator_tag> base; + + explicit input_iterator_adaptor(Iterator it = Iterator()) + : base(it) {} + }; + + template + input_iterator_adaptor input_iterator(Iterator it) + { + return input_iterator_adaptor(it); + } +} + +#endif + diff --git a/test/helpers/invariants.hpp b/test/helpers/invariants.hpp new file mode 100644 index 00000000..db64c132 --- /dev/null +++ b/test/helpers/invariants.hpp @@ -0,0 +1,105 @@ + +// Copyright 2006-2007 Daniel James. +// Distributed under 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) + +// This header contains metafunctions/functions to get the equivalent +// associative container for an unordered container, and compare the contents. + +#if !defined(BOOST_UNORDERED_TEST_HELPERS_INVARIANT_HEADER) +#define BOOST_UNORDERED_TEST_HELPERS_INVARIANT_HEADER + +#include +#include +#include "./metafunctions.hpp" +#include "./helpers.hpp" +#include "./allocator.hpp" + +#if defined(BOOST_MSVC) +#pragma warning(push) +#pragma warning(disable:4127) // conditional expression is constant +#endif + +namespace test +{ + template + void check_equivalent_keys(X const& x1) + { + typename X::key_equal eq = x1.key_eq(); + typedef typename X::key_type key_type; + // Boost.Test was reporting memory leaks for std::set on g++-3.3. + // So I work around it by using malloc. + std::set, test::malloc_allocator > found_; + + typename X::const_iterator it = x1.begin(), end = x1.end(); + typename X::size_type size = 0; + while(it != end) { + // First test that the current key has not occured before, required + // to test either that keys are unique or that equivalent keys are + // adjacent. (6.3.1/6) + key_type key = get_key(*it); + if(!found_.insert(key).second) + BOOST_ERROR("Elements with equivalent keys aren't adjacent."); + + // Iterate over equivalent keys, counting them. + unsigned int count = 0; + do { + ++it; + ++count; + ++size; + } while(it != end && eq(get_key(*it), key)); + + // If the container has unique keys, test that there's only one. + // Since the previous test makes sure that all equivalent keys are + // adjacent, this is all the equivalent keys - so the test is + // sufficient. (6.3.1/6 again). + if(test::has_unique_keys::value && count != 1) + BOOST_ERROR("Non-unique key."); + + if(x1.count(key) != count) { + BOOST_ERROR("Incorrect output of count."); + std::cerr<(*lit), key); ++lit) continue; + // if(lit == lend) + // BOOST_ERROR("Unable to find element with a local_iterator"); + // unsigned int count2 = 0; + // for(; lit != lend && eq(get_key(*lit), key); ++lit) ++count2; + // if(count != count2) + // BOOST_ERROR("Element count doesn't match local_iterator."); + // for(; lit != lend; ++lit) { + // if(eq(get_key(*lit), key)) { + // BOOST_ERROR("Non-adjacent element with equivalent key in bucket."); + // break; + // } + // } + }; + + // Finally, check that size matches up. + if(x1.size() != size) + BOOST_ERROR("x1.size() doesn't match actual size."); + float load_factor = static_cast(size) / static_cast(x1.bucket_count()); + using namespace std; + if(fabs(x1.load_factor() - load_factor) > x1.load_factor() / 64) + BOOST_ERROR("x1.load_factor() doesn't match actual load_factor."); + } +} + +#if defined(BOOST_MSVC) +#pragma warning(pop) +#endif + +#endif + diff --git a/test/helpers/metafunctions.hpp b/test/helpers/metafunctions.hpp new file mode 100644 index 00000000..75d4d5ad --- /dev/null +++ b/test/helpers/metafunctions.hpp @@ -0,0 +1,103 @@ + +// Copyright 2005-2007 Daniel James. +// Distributed under 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) + +#if !defined(BOOST_UNORDERED_TEST_HELPERS_METAFUNCTIONS_HEADER) +#define BOOST_UNORDERED_TEST_HELPERS_METAFUNCTIONS_HEADER + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace test +{ + /* + struct unordered_set_type { char x[100]; }; + struct unordered_multiset_type { char x[200]; }; + struct unordered_map_type { char x[300]; }; + struct unordered_multimap_type { char x[400]; }; + + template + unordered_set_type container_type( + boost::unordered_set const*); + template + unordered_multiset_type container_type( + boost::unordered_multiset const*); + template + unordered_map_type container_type( + boost::unordered_map const*); + template + unordered_multimap_type container_type( + boost::unordered_multimap const*); + */ + + template + struct is_set + : public boost::is_same< + typename Container::key_type, + typename Container::value_type> {}; + + template + struct is_map + : public boost::mpl::not_ > {}; + + struct yes_type { char x[100]; }; + struct no_type { char x[200]; }; + + template + yes_type has_unique_key_impl( + boost::unordered_set const*); + template + no_type has_unique_key_impl( + boost::unordered_multiset const*); + template + yes_type has_unique_key_impl( + boost::unordered_map const*); + template + no_type has_unique_key_impl( + boost::unordered_multimap const*); + + template + struct has_unique_keys + { + BOOST_STATIC_CONSTANT(bool, value = + sizeof(has_unique_key_impl((Container const*)0)) + == sizeof(yes_type)); + }; + + template + struct has_equivalent_keys + { + BOOST_STATIC_CONSTANT(bool, value = + sizeof(has_unique_key_impl((Container const*)0)) + == sizeof(no_type)); + }; + + // Non Const Value Type + + template + struct map_non_const_value_type + { + typedef std::pair< + typename Container::key_type, + typename Container::mapped_type> type; + }; + + + template + struct non_const_value_type + : boost::mpl::eval_if, + map_non_const_value_type, + boost::mpl::identity > + { + }; +} + +#endif + diff --git a/test/helpers/random_values.hpp b/test/helpers/random_values.hpp new file mode 100644 index 00000000..31abf134 --- /dev/null +++ b/test/helpers/random_values.hpp @@ -0,0 +1,27 @@ + +// Copyright 2005-2007 Daniel James. +// Distributed under 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) + +#if !defined(BOOST_UNORDERED_TEST_HELPERS_RANDOM_VALUES_HEADER) +#define BOOST_UNORDERED_TEST_HELPERS_RANDOM_VALUES_HEADER + +#include +#include +#include "./generators.hpp" + +namespace test +{ + template + struct random_values + : public std::list + { + random_values(int count) { + typedef typename X::value_type value_type; + static test::generator gen; + std::generate_n(std::back_inserter(*this), count, gen); + } + }; +} + +#endif diff --git a/test/helpers/strong.hpp b/test/helpers/strong.hpp new file mode 100644 index 00000000..5054e170 --- /dev/null +++ b/test/helpers/strong.hpp @@ -0,0 +1,39 @@ + +// Copyright 2005-2007 Daniel James. +// Distributed under 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) + +#if !defined(BOOST_UNORDERED_TEST_HELPERS_STRONG_HEADER) +#define BOOST_UNORDERED_TEST_HELPERS_STRONG_HEADER + +#include +#include +#include +#include "./metafunctions.hpp" +#include "./equivalent.hpp" +#include "../objects/exception.hpp" + +namespace test +{ + template + class strong + { + typedef std::vector::type> values_type; + values_type values_; + public: + void store(X const& x) { + DISABLE_EXCEPTIONS; + values_.clear(); + values_.reserve(x.size()); + std::copy(x.cbegin(), x.cend(), std::back_inserter(values_)); + } + + void test(X const& x) const { + if(!(x.size() == values_.size() && + std::equal(x.cbegin(), x.cend(), values_.begin(), test::equivalent))) + BOOST_ERROR("Strong exception safety failure."); + } + }; +} + +#endif diff --git a/test/helpers/tracker.hpp b/test/helpers/tracker.hpp new file mode 100644 index 00000000..91220e3f --- /dev/null +++ b/test/helpers/tracker.hpp @@ -0,0 +1,163 @@ + +// Copyright 2006-2007 Daniel James. +// Distributed under 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) + +// This header contains metafunctions/functions to get the equivalent +// associative container for an unordered container, and compare the contents. + +#if !defined(BOOST_UNORDERED_TEST_HELPERS_TRACKER_HEADER) +#define BOOST_UNORDERED_TEST_HELPERS_TRACKER_HEADER + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../objects/fwd.hpp" +#include "./metafunctions.hpp" +#include "./helpers.hpp" +#include "./equivalent.hpp" + +namespace test +{ + template + struct equals_to_compare2 + : public boost::mpl::identity > + { + }; + + template + struct equals_to_compare + : public boost::mpl::eval_if< + boost::is_same, + boost::mpl::identity, + equals_to_compare2 + > + { + }; + + template + void compare_range(X1 const& x1, X2 const& x2) + { + typedef typename non_const_value_type::type value_type; + std::vector values1, values2; + values1.reserve(x1.size()); + values2.reserve(x2.size()); + std::copy(x1.begin(), x1.end(), std::back_inserter(values1)); + std::copy(x2.begin(), x2.end(), std::back_inserter(values2)); + std::sort(values1.begin(), values1.end()); + std::sort(values2.begin(), values2.end()); + BOOST_TEST(values1.size() == values2.size() && + std::equal(values1.begin(), values1.end(), values2.begin(), test::equivalent)); + } + + template + void compare_pairs(X1 const& x1, X2 const& x2, T*) + { + std::vector values1, values2; + values1.reserve(std::distance(x1.first, x1.second)); + values2.reserve(std::distance(x2.first, x2.second)); + std::copy(x1.first, x1.second, std::back_inserter(values1)); + std::copy(x2.first, x2.second, std::back_inserter(values2)); + std::sort(values1.begin(), values1.end()); + std::sort(values2.begin(), values2.end()); + BOOST_TEST(values1.size() == values2.size() && + std::equal(values1.begin(), values1.end(), values2.begin(), test::equivalent)); + } + + template + struct ordered_set + : public boost::mpl::if_< + test::has_unique_keys, + std::set::type>, + std::multiset::type> + > {}; + + template + struct ordered_map + : public boost::mpl::if_< + test::has_unique_keys, + std::map::type>, + std::multimap::type> + > {}; + + template + struct ordered_base + : public boost::mpl::eval_if< + test::is_set, + test::ordered_set, + test::ordered_map > + { + }; + + template + class ordered : public ordered_base::type + { + typedef typename ordered_base::type base; + public: + typedef typename base::key_compare key_compare; + + ordered() + : base() + {} + + explicit ordered(key_compare const& compare) + : base(compare) + {} + + void compare(X const& x) + { + compare_range(x, *this); + } + + void compare_key(X const& x, typename X::value_type const& val) + { + compare_pairs( + x.equal_range(get_key(val)), + this->equal_range(get_key(val)), + (typename non_const_value_type::type*) 0 + ); + } + + template + void insert_range(It begin, It end) { + while(begin != end) { + this->insert(*begin); + ++begin; + } + } + }; + + template + typename equals_to_compare::type create_compare(Equals const&) + { + typename equals_to_compare::type x; + return x; + } + + template + ordered create_ordered(X const& container) + { + return ordered(create_compare(container.key_eq())); + } + + template + void check_container(X1 const& container, X2 const& values) + { + ordered tracker = create_ordered(container); + tracker.insert_range(values.begin(), values.end()); + tracker.compare(container); + } +} + +#endif + diff --git a/test/objects/exception.hpp b/test/objects/exception.hpp new file mode 100644 index 00000000..8bb50a7b --- /dev/null +++ b/test/objects/exception.hpp @@ -0,0 +1,659 @@ + +// Copyright 2006-2007 Daniel James. +// Distributed under 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) + +#if !defined(BOOST_UNORDERED_TEST_OBJECTS_HEADER) +#define BOOST_UNORDERED_TEST_OBJECTS_HEADER + +#include +#include +#include +#include +#include +#include +#include +#include +#include "../helpers/fwd.hpp" +#include "../helpers/allocator.hpp" +#include + +#define RUN_EXCEPTION_TESTS(test_seq, param_seq) \ + BOOST_PP_SEQ_FOR_EACH_PRODUCT(RUN_EXCEPTION_TESTS_OP, (test_seq)(param_seq)) + +#define RUN_EXCEPTION_TESTS_OP(r, product) \ + RUN_EXCEPTION_TESTS_OP2( \ + BOOST_PP_CAT(BOOST_PP_SEQ_ELEM(0, product), \ + BOOST_PP_CAT(_, BOOST_PP_SEQ_ELEM(1, product)) \ + ), \ + BOOST_PP_SEQ_ELEM(0, product), \ + BOOST_PP_SEQ_ELEM(1, product) \ + ) + +#define RUN_EXCEPTION_TESTS_OP2(name, test_func, type) \ + BOOST_AUTO_TEST_CASE(name) \ + { \ + test_func< type > fixture; \ + ::test::exception_safety(fixture, BOOST_STRINGIZE(test_func)); \ + } + +#define SCOPE(scope_name) \ + for(::test::scope_guard unordered_test_guard( \ + BOOST_STRINGIZE(scope_name)); \ + !unordered_test_guard.dismissed(); \ + unordered_test_guard.dismiss()) + +#define EPOINT(name) \ + if(::test::exceptions_enabled) { \ + BOOST_ITEST_EPOINT(name); \ + } + +#define ENABLE_EXCEPTIONS \ + ::test::exceptions_enable BOOST_PP_CAT(ENABLE_EXCEPTIONS_, __LINE__)(true) +#define DISABLE_EXCEPTIONS \ + ::test::exceptions_enable BOOST_PP_CAT(ENABLE_EXCEPTIONS_, __LINE__)(false) + +#define HASH_CHECK(test) if(!(test)) BOOST_ERROR(BOOST_STRINGIZE(test)) + +namespace test { + static char const* scope = ""; + bool exceptions_enabled = false; + + class scope_guard { + scope_guard& operator=(scope_guard const&); + scope_guard(scope_guard const&); + + char const* old_scope_; + char const* scope_; + bool dismissed_; + public: + scope_guard(char const* name) + : old_scope_(scope), + scope_(name), + dismissed_(false) + { + scope = scope_; + } + + ~scope_guard() { + if(dismissed_) scope = old_scope_; + } + + void dismiss() { + dismissed_ = true; + } + + bool dismissed() const { + return dismissed_; + } + }; + + class exceptions_enable + { + exceptions_enable& operator=(exceptions_enable const&); + exceptions_enable(exceptions_enable const&); + + bool old_value_; + public: + exceptions_enable(bool enable) + : old_value_(exceptions_enabled) + { + exceptions_enabled = enable; + } + + ~exceptions_enable() + { + exceptions_enabled = old_value_; + } + }; + + struct exception_base { + struct data_type {}; + struct strong_type { + template void store(T const&) {} + template void test(T const&) const {} + }; + data_type init() const { return data_type(); } + void check() const {} + }; + + template + inline void call_ignore_extra_parameters(void (T::*fn)() const, T2 const& obj, + P1&, P2&) + { + (obj.*fn)(); + } + + template + inline void call_ignore_extra_parameters(void (T::*fn)(P1&) const, T2 const& obj, + P1& p1, P2&) + { + (obj.*fn)(p1); + } + + template + inline void call_ignore_extra_parameters(void (T::*fn)(P1&, P2&) const, T2 const& obj, + P1& p1, P2& p2) + { + (obj.*fn)(p1, p2); + } + + template + T const& constant(T const& x) { + return x; + } + + template + class test_runner + { + Test const& test_; + public: + test_runner(Test const& t) : test_(t) {} + void operator()() const { + DISABLE_EXCEPTIONS; + typename Test::data_type x(test_.init()); + typename Test::strong_type strong; + strong.store(x); + try { + ENABLE_EXCEPTIONS; + call_ignore_extra_parameters(&Test::run, test_, x, strong); + } + catch(...) { + call_ignore_extra_parameters(&Test::check, test_, + constant(x), constant(strong)); + throw; + } + } + }; + + template + void exception_safety(Test const& f, char const* name) { + test_runner runner(f); + ::boost::itest::exception_safety(runner, name); + } +} + +namespace test +{ +namespace exception +{ + namespace detail + { + // This annoymous namespace won't cause ODR violations as I won't + // be linking multiple translation units together. I'll probably + // move this into a cpp file before a full release, but for now it's + // the most convenient way. + namespace + { + struct memory_area { + void const* start; + void const* end; + + memory_area(void const* s, void const* e) + : start(s), end(e) + { + } + + // This is a bit dodgy as it defines overlapping + // areas as 'equal', so this isn't a total ordering. + // But it is for non-overlapping memory regions - which + // is what'll be stored. + // + // All searches will be for areas entirely contained by + // a member of the set - so it should find the area that contains + // the region that is searched for. + bool operator<(memory_area const& other) const { + return end < other.start; + } + }; + + struct memory_track { + explicit memory_track(int tag = -1) : + tag_(tag) {} + + int tag_; + }; + + typedef std::map, + test::malloc_allocator > > + allocated_memory_type; + allocated_memory_type allocated_memory; + unsigned int count_allocators = 0; + unsigned int count_allocations = 0; + unsigned int count_constructions = 0; + } + + void allocator_ref() + { + if(count_allocators == 0) { + count_allocations = 0; + count_constructions = 0; + allocated_memory.clear(); + } + ++count_allocators; + } + + void allocator_unref() + { + HASH_CHECK(count_allocators > 0); + if(count_allocators > 0) { + --count_allocators; + if(count_allocators == 0) { + bool no_allocations_left = (count_allocations == 0); + bool no_constructions_left = (count_constructions == 0); + bool allocated_memory_empty = allocated_memory.empty(); + + // Clearing the data before the checks terminate the tests. + count_allocations = 0; + count_constructions = 0; + allocated_memory.clear(); + + HASH_CHECK(no_allocations_left); + HASH_CHECK(no_constructions_left); + HASH_CHECK(allocated_memory_empty); + } + } + } + + void track_allocate(void *ptr, std::size_t n, std::size_t size, int tag) + { + if(n == 0) { + BOOST_ERROR("Allocating 0 length array."); + } + else { + ++count_allocations; + allocated_memory[memory_area(ptr, (char*) ptr + n * size)] = + memory_track(tag); + } + } + + void track_deallocate(void* ptr, std::size_t n, std::size_t size, int tag) + { + allocated_memory_type::iterator pos + = allocated_memory.find(memory_area(ptr, ptr)); + if(pos == allocated_memory.end()) { + BOOST_ERROR("Deallocating unknown pointer."); + } else { + HASH_CHECK(pos->first.start == ptr); + HASH_CHECK(pos->first.end == (char*) ptr + n * size); + HASH_CHECK(pos->second.tag_ == tag); + allocated_memory.erase(pos); + } + HASH_CHECK(count_allocations > 0); + if(count_allocations > 0) --count_allocations; + } + + void track_construct(void* ptr, std::size_t /*size*/, int tag) + { + ++count_constructions; + } + + void track_destroy(void* ptr, std::size_t /*size*/, int tag) + { + HASH_CHECK(count_constructions > 0); + if(count_constructions > 0) --count_constructions; + } + } + + class object; + class hash; + class equal_to; + template class allocator; + + class object + { + public: + int tag1_, tag2_; + + explicit object() : tag1_(0), tag2_(0) + { + SCOPE(object::object()) { + EPOINT("Mock object default constructor."); + } + } + + explicit object(int t1, int t2 = 0) : tag1_(t1), tag2_(t2) + { + SCOPE(object::object(int)) { + EPOINT("Mock object constructor by value."); + } + } + + object(object const& x) + : tag1_(x.tag1_), tag2_(x.tag2_) + { + SCOPE(object::object(object)) { + EPOINT("Mock object copy constructor."); + } + } + + ~object() { + tag1_ = -1; + tag2_ = -1; + } + + object& operator=(object const& x) + { + SCOPE(object::operator=(object)) { + tag1_ = x.tag1_; + EPOINT("Mock object assign operator 1."); + tag2_ = x.tag2_; + //EPOINT("Mock object assign operator 2."); + } + return *this; + } + + friend bool operator==(object const& x1, object const& x2) { + SCOPE(operator==(object, object)) { + EPOINT("Mock object equality operator."); + } + + return x1.tag1_ == x2.tag1_ && x1.tag2_ == x2.tag2_; + } + + friend bool operator!=(object const& x1, object const& x2) { + SCOPE(operator!=(object, object)) { + EPOINT("Mock object inequality operator."); + } + + return !(x1.tag1_ == x2.tag1_ && x1.tag2_ == x2.tag2_); + } + + // None of the last few functions are used by the unordered associative + // containers - so there aren't any exception points. + friend bool operator<(object const& x1, object const& x2) { + return x1.tag1_ < x2.tag1_ || + (x1.tag1_ == x2.tag1_ && x1.tag2_ < x2.tag2_); + } + + friend object generate(object const*) { + int* x = 0; + return object(::test::generate(x), ::test::generate(x)); + } + + friend std::ostream& operator<<(std::ostream& out, object const& o) + { + return out<<"("< + class allocator + { + public: + int tag_; + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + typedef T* pointer; + typedef T const* const_pointer; + typedef T& reference; + typedef T const& const_reference; + typedef T value_type; + + template struct rebind { typedef allocator other; }; + + explicit allocator(int t = 0) : tag_(t) + { + SCOPE(allocator::allocator()) { + EPOINT("Mock allocator default constructor."); + } + detail::allocator_ref(); + } + + template allocator(allocator const& x) : tag_(x.tag_) + { + SCOPE(allocator::allocator()) { + EPOINT("Mock allocator template copy constructor."); + } + detail::allocator_ref(); + } + + allocator(allocator const& x) : tag_(x.tag_) + { + SCOPE(allocator::allocator()) { + EPOINT("Mock allocator copy constructor."); + } + detail::allocator_ref(); + } + + ~allocator() { + detail::allocator_unref(); + } + + allocator& operator=(allocator const& x) { + SCOPE(allocator::allocator()) { + EPOINT("Mock allocator assignment operator."); + tag_ = x.tag_; + } + return *this; + } + + // If address throws, then it can't be used in erase or the + // destructor, which is very limiting. I need to check up on + // this. + + pointer address(reference r) { + //SCOPE(allocator::address(reference)) { + // EPOINT("Mock allocator address function."); + //} + return pointer(&r); + } + + const_pointer address(const_reference r) { + //SCOPE(allocator::address(const_reference)) { + // EPOINT("Mock allocator const address function."); + //} + return const_pointer(&r); + } + + pointer allocate(size_type n) { + T* ptr = 0; + SCOPE(allocator::allocate(size_type)) { + EPOINT("Mock allocator allocate function."); + + using namespace std; + ptr = (T*) malloc(n * sizeof(T)); + if(!ptr) throw std::bad_alloc(); + } + detail::track_allocate((void*) ptr, n, sizeof(T), tag_); + return pointer(ptr); + + //return pointer(static_cast(::operator new(n * sizeof(T)))); + } + + pointer allocate(size_type n, const_pointer u) + { + T* ptr = 0; + SCOPE(allocator::allocate(size_type, const_pointer)) { + EPOINT("Mock allocator allocate function."); + + using namespace std; + ptr = (T*) malloc(n * sizeof(T)); + if(!ptr) throw std::bad_alloc(); + } + detail::track_allocate((void*) ptr, n, sizeof(T), tag_); + return pointer(ptr); + + //return pointer(static_cast(::operator new(n * sizeof(T)))); + } + + void deallocate(pointer p, size_type n) + { + //::operator delete((void*) p); + if(p) { + detail::track_deallocate((void*) p, n, sizeof(T), tag_); + using namespace std; + free(p); + } + } + + void construct(pointer p, T const& t) { + SCOPE(allocator::construct(pointer, T)) { + EPOINT("Mock allocator construct function."); + new(p) T(t); + } + detail::track_construct((void*) p, sizeof(T), tag_); + } + + void destroy(pointer p) { + detail::track_destroy((void*) p, sizeof(T), tag_); + p->~T(); + } + + size_type max_size() const { + SCOPE(allocator::construct(pointer, T)) { + EPOINT("Mock allocator max_size function."); + } + return (std::numeric_limits::max)(); + } + }; + + template + void swap(allocator& x, allocator& y) + { + std::swap(x.tag_, y.tag_); + } + + // It's pretty much impossible to write a compliant swap when these + // two can throw. So they don't. + + template + inline bool operator==(allocator const& x, allocator const& y) + { + //SCOPE(operator==(allocator, allocator)) { + // EPOINT("Mock allocator equality operator."); + //} + return x.tag_ == y.tag_; + } + + template + inline bool operator!=(allocator const& x, allocator const& y) + { + //SCOPE(operator!=(allocator, allocator)) { + // EPOINT("Mock allocator inequality operator."); + //} + return x.tag_ != y.tag_; + } +} +} + +#endif + diff --git a/test/objects/fwd.hpp b/test/objects/fwd.hpp new file mode 100644 index 00000000..30b74f1b --- /dev/null +++ b/test/objects/fwd.hpp @@ -0,0 +1,18 @@ + +// Copyright 2006-2007 Daniel James. +// Distributed under 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) + +#if !defined(BOOST_UNORDERED_TEST_OBJECTS_FWD_HEADER) +#define BOOST_UNORDERED_TEST_OBJECTS_FWD_HEADER + +namespace test +{ + class object; + class hash; + class less; + class equal_to; + template class allocator; +} + +#endif diff --git a/test/objects/minimal.hpp b/test/objects/minimal.hpp new file mode 100644 index 00000000..2932b387 --- /dev/null +++ b/test/objects/minimal.hpp @@ -0,0 +1,251 @@ + +// Copyright 2006-2007 Daniel James. +// Distributed under 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) + +#if !defined(BOOST_UNORDERED_OBJECTS_MINIMAL_HEADER) +#define BOOST_UNORDERED_OBJECTS_MINIMAL_HEADER + +#include + +#if defined(BOOST_MSVC) +#pragma warning(push) +#pragma warning(disable:4100) // unreferenced formal parameter +#endif + +namespace test +{ +namespace minimal +{ + class copy_constructible; + class default_copy_constructible; + class assignable; + template class hash; + template class equal_to; + template class ptr; + template class const_ptr; + template class allocator; + + class copy_constructible + { + public: + static copy_constructible create() { return copy_constructible(); } + copy_constructible(copy_constructible const&) {} + ~copy_constructible() {} + private: + copy_constructible& operator=(copy_constructible const&); + copy_constructible() {} + }; + + class default_copy_constructible + { + public: + static default_copy_constructible create() { return default_copy_constructible(); } + default_copy_constructible() {} + default_copy_constructible(default_copy_constructible const&) {} + ~default_copy_constructible() {} + private: + default_copy_constructible& operator=(default_copy_constructible const&); + }; + + class assignable + { + public: + static assignable create() { return assignable(); } + assignable(assignable const&) {} + assignable& operator=(assignable const&) { return *this; } + ~assignable() {} + private: + assignable() {} + }; + + template + class hash + { + public: + static hash create() { return hash(); } + hash() {} + hash(hash const&) {} + hash& operator=(hash const&) { return *this; } + ~hash() {} + + std::size_t operator()(T const&) const { return 0; } + }; + + template + class equal_to + { + public: + static equal_to create() { return equal_to(); } + equal_to() {} + equal_to(equal_to const&) {} + equal_to& operator=(equal_to const&) { return *this; } + ~equal_to() {} + + bool operator()(T const&, T const&) const { return true; } + }; + + template class ptr; + template class const_ptr; + + template + class ptr + { + friend class allocator; + friend class const_ptr; + + T* ptr_; + + ptr(T* x) : ptr_(x) {} + public: + ptr() : ptr_(0) {} + + typedef void (ptr::*bool_type)() const; + void this_type_does_not_support_comparisons() const {} + + T& operator*() const { return *ptr_; } + T* operator->() const { return ptr_; } + ptr& operator++() { ++ptr_; return *this; } + ptr operator++(int) { ptr tmp(*this); ++ptr_; return tmp; } + ptr operator+(int s) const { return ptr(ptr_ + s); } + T& operator[](int s) const { return ptr_[s]; } + bool operator!() const { return !ptr_; } + + operator bool_type() const { + return ptr_ ? + &ptr::this_type_does_not_support_comparisons + : 0; + } + + bool operator==(ptr const& x) const { return ptr_ == x.ptr_; } + bool operator!=(ptr const& x) const { return ptr_ != x.ptr_; } + bool operator<(ptr const& x) const { return ptr_ < x.ptr_; } + bool operator>(ptr const& x) const { return ptr_ > x.ptr_; } + bool operator<=(ptr const& x) const { return ptr_ <= x.ptr_; } + bool operator>=(ptr const& x) const { return ptr_ >= x.ptr_; } + + bool operator==(const_ptr const& x) const { return ptr_ == x.ptr_; } + bool operator!=(const_ptr const& x) const { return ptr_ != x.ptr_; } + bool operator<(const_ptr const& x) const { return ptr_ < x.ptr_; } + bool operator>(const_ptr const& x) const { return ptr_ > x.ptr_; } + bool operator<=(const_ptr const& x) const { return ptr_ <= x.ptr_; } + bool operator>=(const_ptr const& x) const { return ptr_ >= x.ptr_; } + }; + + template + class const_ptr + { + friend class allocator; + + T const* ptr_; + + const_ptr(T const* ptr) : ptr_(ptr) {} + public: + const_ptr() : ptr_(0) {} + const_ptr(ptr const& x) : ptr_(x.ptr_) {} + + typedef void (const_ptr::*bool_type)() const; + void this_type_does_not_support_comparisons() const {} + + T const& operator*() const { return *ptr_; } + T const* operator->() const { return ptr_; } + const_ptr& operator++() { ++ptr_; return *this; } + const_ptr operator++(int) { const_ptr tmp(*this); ++ptr_; return tmp; } + const_ptr operator+(int s) const { return const_ptr(ptr_ + s); } + T const& operator[](int s) const { return ptr_[s]; } + bool operator!() const { return !ptr_; } + + operator bool_type() const { + return ptr_ ? + &const_ptr::this_type_does_not_support_comparisons + : 0; + } + + bool operator==(ptr const& x) const { return ptr_ == x.ptr_; } + bool operator!=(ptr const& x) const { return ptr_ != x.ptr_; } + bool operator<(ptr const& x) const { return ptr_ < x.ptr_; } + bool operator>(ptr const& x) const { return ptr_ > x.ptr_; } + bool operator<=(ptr const& x) const { return ptr_ <= x.ptr_; } + bool operator>=(ptr const& x) const { return ptr_ >= x.ptr_; } + + bool operator==(const_ptr const& x) const { return ptr_ == x.ptr_; } + bool operator!=(const_ptr const& x) const { return ptr_ != x.ptr_; } + bool operator<(const_ptr const& x) const { return ptr_ < x.ptr_; } + bool operator>(const_ptr const& x) const { return ptr_ > x.ptr_; } + bool operator<=(const_ptr const& x) const { return ptr_ <= x.ptr_; } + bool operator>=(const_ptr const& x) const { return ptr_ >= x.ptr_; } + }; + + template + class allocator + { + public: + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + typedef ptr pointer; + typedef const_ptr const_pointer; + typedef T& reference; + typedef T const& const_reference; + typedef T value_type; + + template struct rebind { typedef allocator other; }; + + allocator() {} + template allocator(allocator const&) {} + allocator(allocator const&) {} + ~allocator() {} + + pointer address(reference r) { return pointer(&r); } + const_pointer address(const_reference r) { return const_pointer(&r); } + + pointer allocate(size_type n) { + return pointer(static_cast(::operator new(n * sizeof(T)))); + } + + pointer allocate(size_type n, const_pointer u) + { + return pointer(static_cast(::operator new(n * sizeof(T)))); + } + + void deallocate(pointer p, size_type) + { + ::operator delete((void*) p.ptr_); + } + + void construct(pointer p, T const& t) { new((void*)p.ptr_) T(t); } + void destroy(pointer p) { ((T*)p.ptr_)->~T(); } + + size_type max_size() const { return 1000; } + +#if defined(BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP) || \ + BOOST_WORKAROUND(MSVC, <= 1300) + public: allocator& operator=(allocator const&) { return *this;} +#else + private: allocator& operator=(allocator const&); +#endif + }; + + template + inline bool operator==(allocator const&, allocator const&) + { + return true; + } + + template + inline bool operator!=(allocator const&, allocator const&) + { + return false; + } + + template + void swap(allocator&, allocator&) + { + } +} +} + +#if defined(BOOST_MSVC) +#pragma warning(pop) +#endif + +#endif diff --git a/test/objects/test.hpp b/test/objects/test.hpp new file mode 100644 index 00000000..5f473070 --- /dev/null +++ b/test/objects/test.hpp @@ -0,0 +1,458 @@ + +// Copyright 2006-2007 Daniel James. +// Distributed under 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) + +#if !defined(BOOST_UNORDERED_TEST_OBJECTS_HEADER) +#define BOOST_UNORDERED_TEST_OBJECTS_HEADER + +#include +#include +#include +#include "../helpers/fwd.hpp" +#include +#include + +namespace test +{ + // Note that the default hash function will work for any equal_to (but not + // very well). + class object; + class hash; + class less; + class equal_to; + template class allocator; + + class object + { + friend class hash; + friend class equal_to; + friend class less; + int tag1_, tag2_; + public: + explicit object(int t1 = 0, int t2 = 0) : tag1_(t1), tag2_(t2) {} + + ~object() { + tag1_ = -1; + tag2_ = -1; + } + + friend bool operator==(object const& x1, object const& x2) { + return x1.tag1_ == x2.tag1_ && x1.tag2_ == x2.tag2_; + } + + friend bool operator!=(object const& x1, object const& x2) { + return x1.tag1_ != x2.tag1_ || x1.tag2_ != x2.tag2_; + } + + friend bool operator<(object const& x1, object const& x2) { + return x1.tag1_ < x2.tag1_ || + (x1.tag1_ == x2.tag1_ && x1.tag2_ < x2.tag2_); + } + + friend object generate(object const*) { + int* x = 0; + return object(generate(x), generate(x)); + } + + friend std::ostream& operator<<(std::ostream& out, object const& o) + { + return out<<"("< allocated_memory_type; + allocated_memory_type allocated_memory; + unsigned int count_allocators = 0; + unsigned int count_allocations = 0; + unsigned int count_constructions = 0; + } + + void allocator_ref() + { + if(count_allocators == 0) { + count_allocations = 0; + count_constructions = 0; + allocated_memory.clear(); + } + ++count_allocators; + } + + void allocator_unref() + { + BOOST_TEST(count_allocators > 0); + if(count_allocators > 0) { + --count_allocators; + if(count_allocators == 0) { + bool no_allocations_left = (count_allocations == 0); + bool no_constructions_left = (count_constructions == 0); + bool allocated_memory_empty = allocated_memory.empty(); + + // Clearing the data before the checks terminate the tests. + count_allocations = 0; + count_constructions = 0; + allocated_memory.clear(); + + BOOST_TEST(no_allocations_left); + BOOST_TEST(no_constructions_left); + BOOST_TEST(allocated_memory_empty); + } + } + } + + void track_allocate(void *ptr, std::size_t n, std::size_t size, int tag) + { + if(n == 0) { + BOOST_ERROR("Allocating 0 length array."); + } + else { + ++count_allocations; + allocated_memory[memory_area(ptr, (char*) ptr + n * size)] = + memory_track(tag); + } + } + + void track_deallocate(void* ptr, std::size_t n, std::size_t size, int tag) + { + allocated_memory_type::iterator pos + = allocated_memory.find(memory_area(ptr, ptr)); + if(pos == allocated_memory.end()) { + BOOST_ERROR("Deallocating unknown pointer."); + } else { + BOOST_TEST(pos->first.start == ptr); + BOOST_TEST(pos->first.end == (char*) ptr + n * size); + BOOST_TEST(pos->second.tag_ == tag); + BOOST_TEST(pos->second.constructed_ == 0); + allocated_memory.erase(pos); + } + BOOST_TEST(count_allocations > 0); + if(count_allocations > 0) --count_allocations; + } + + void track_construct(void* ptr, std::size_t /*size*/, int tag) + { + allocated_memory_type::iterator pos + = allocated_memory.find(memory_area(ptr, ptr)); + if(pos == allocated_memory.end()) + BOOST_ERROR("Constructing unknown pointer."); + BOOST_TEST(pos->second.tag_ == tag); + ++count_constructions; + ++pos->second.constructed_; + } + + void track_destroy(void* ptr, std::size_t /*size*/, int tag) + { + allocated_memory_type::iterator pos + = allocated_memory.find(memory_area(ptr, ptr)); + if(pos == allocated_memory.end()) + BOOST_ERROR("Destroying unknown pointer."); + BOOST_TEST(count_constructions > 0); + BOOST_TEST(pos->second.tag_ == tag); + BOOST_TEST(pos->second.constructed_ > 0); + if(count_constructions > 0) --count_constructions; + if(pos->second.constructed_ > 0) --pos->second.constructed_; + } + } + + template + class allocator + { +# ifdef BOOST_NO_MEMBER_TEMPLATE_FRIENDS + public: +# else + template friend class allocator; +# endif + int tag_; + public: + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + typedef T* pointer; + typedef T const* const_pointer; + typedef T& reference; + typedef T const& const_reference; + typedef T value_type; + + template struct rebind { typedef allocator other; }; + + explicit allocator(int t = 0) : tag_(t) { detail::allocator_ref(); } + template allocator(allocator const& x) : tag_(x.tag_) { detail::allocator_ref(); } + allocator(allocator const& x) : tag_(x.tag_) { detail::allocator_ref(); } + ~allocator() { detail::allocator_unref(); } + + pointer address(reference r) { return pointer(&r); } + const_pointer address(const_reference r) { return const_pointer(&r); } + + pointer allocate(size_type n) { + pointer ptr(static_cast(::operator new(n * sizeof(T)))); + detail::track_allocate((void*) ptr, n, sizeof(T), tag_); + return ptr; + } + + pointer allocate(size_type n, const_pointer u) + { + pointer ptr(static_cast(::operator new(n * sizeof(T)))); + detail::track_allocate((void*) ptr, n, sizeof(T), tag_); + return ptr; + } + + void deallocate(pointer p, size_type n) + { + detail::track_deallocate((void*) p, n, sizeof(T), tag_); + ::operator delete((void*) p); + } + + void construct(pointer p, T const& t) { + detail::track_construct((void*) p, sizeof(T), tag_); + new(p) T(t); + } + + void destroy(pointer p) { + detail::track_destroy((void*) p, sizeof(T), tag_); + p->~T(); + } + + size_type max_size() const { + return (std::numeric_limits::max)(); + } + + bool operator==(allocator const& x) const + { + return tag_ == x.tag_; + } + + bool operator!=(allocator const& x) const + { + return tag_ != x.tag_; + } + }; + + template + bool equivalent_impl(allocator const& x, allocator const& y, test::derived_type) { + return x == y; + } + +#if BOOST_WORKAROUND(__GNUC__, < 3) + void swap(test::object& x, test::object& y) { + test::object tmp; + tmp = x; + x = y; + y = tmp; + } + + void swap(test::equivalent_object& x, test::equivalent_object& y) { + test::equivalent_object tmp; + tmp = x; + x = y; + y = tmp; + } + + void swap(test::hash& x, test::hash& y) { + test::hash tmp; + tmp = x; + x = y; + y = tmp; + } + + void swap(test::less& x, test::less& y) { + test::less tmp; + tmp = x; + x = y; + y = tmp; + } + + void swap(test::equal_to& x, test::equal_to& y) { + test::equal_to tmp; + tmp = x; + x = y; + y = tmp; + } + + template + void swap(test::allocator& x, test::allocator& y) { + test::allocator tmp; + tmp = x; + x = y; + y = tmp; + } +#endif +} + +#endif diff --git a/test/unordered/Jamfile.v2 b/test/unordered/Jamfile.v2 new file mode 100644 index 00000000..5390ec46 --- /dev/null +++ b/test/unordered/Jamfile.v2 @@ -0,0 +1,33 @@ + +# Copyright 2006-2007 Daniel James. +# Distributed under 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) + +import testing ; + +project unordered-test/unordered + : requirements + intel-linux:"-strict_ansi -cxxlib-icc" + gcc:-Wsign-promo + msvc:/W4 + ; + +test-suite unordered-tests + : + [ run equivalent_keys_tests.cpp ] + [ run compile_tests.cpp ] + [ run constructor_tests.cpp ] + [ run copy_tests.cpp ] + [ run assign_tests.cpp ] + [ run insert_tests.cpp ] + [ run insert_stable_tests.cpp ] + [ run unnecessary_copy_tests.cpp ] + [ run erase_tests.cpp ] + [ run erase_equiv_tests.cpp ] + [ run find_tests.cpp ] + [ run at_tests.cpp ] + [ run bucket_tests.cpp ] + [ run load_factor_tests.cpp ] + [ run rehash_tests.cpp ] + [ run swap_tests.cpp : : : BOOST_UNORDERED_SWAP_METHOD=2 ] + ; diff --git a/test/unordered/assign_tests.cpp b/test/unordered/assign_tests.cpp new file mode 100644 index 00000000..5b5f0d85 --- /dev/null +++ b/test/unordered/assign_tests.cpp @@ -0,0 +1,109 @@ + +// Copyright 2006-2007 Daniel James. +// Distributed under 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) + +#include +#include +#include +#include "../objects/test.hpp" +#include "../helpers/random_values.hpp" +#include "../helpers/tracker.hpp" +#include "../helpers/equivalent.hpp" + +#include + +test::seed_t seed(96785); + +template +void assign_tests1(T* = 0) +{ + typename T::hasher hf; + typename T::key_equal eq; + + std::cerr<<"assign_tests1.1\n"; + { + T x; + x = x; + BOOST_TEST(x.empty()); + BOOST_TEST(test::equivalent(x.hash_function(), hf)); + BOOST_TEST(test::equivalent(x.key_eq(), eq)); + } + + std::cerr<<"assign_tests1.2\n"; + { + test::random_values v(1000); + T x(v.begin(), v.end()); + + test::ordered tracker = test::create_ordered(x); + tracker.insert_range(v.begin(), v.end()); + + x = x; + tracker.compare(x); + + T y; + y.max_load_factor(x.max_load_factor() / 20); + y = x; + tracker.compare(y); + BOOST_TEST(x.max_load_factor() == y.max_load_factor()); + } +} + +template +void assign_tests2(T* = 0) +{ + typename T::hasher hf1(1); + typename T::hasher hf2(2); + typename T::key_equal eq1(1); + typename T::key_equal eq2(2); + typename T::allocator_type al1(1); + typename T::allocator_type al2(2); + + std::cerr<<"assign_tests2.1\n"; + { + test::random_values v(1000); + T x1(v.begin(), v.end(), 0, hf1, eq1); + T x2(0, hf2, eq2); + x2 = x1; + BOOST_TEST(test::equivalent(x2.hash_function(), hf1)); + BOOST_TEST(test::equivalent(x2.key_eq(), eq1)); + test::check_container(x2, v); + } + + std::cerr<<"assign_tests2.2\n"; + { + test::random_values v1(100), v2(100); + T x1(v1.begin(), v1.end(), 0, hf1, eq1, al1); + T x2(v2.begin(), v2.end(), 0, hf2, eq2, al2); + x2 = x1; + BOOST_TEST(test::equivalent(x2.hash_function(), hf1)); + BOOST_TEST(test::equivalent(x2.key_eq(), eq1)); + BOOST_TEST(test::equivalent(x2.get_allocator(), al2)); + test::check_container(x2, v1); + } +} + +int main() +{ + assign_tests1((boost::unordered_set*) 0); + assign_tests1((boost::unordered_multiset*) 0); + assign_tests1((boost::unordered_map*) 0); + assign_tests1((boost::unordered_multimap*) 0); + + assign_tests1((boost::unordered_set >*) 0); + assign_tests1((boost::unordered_multiset >*) 0); + assign_tests1((boost::unordered_map >*) 0); + assign_tests1((boost::unordered_multimap >*) 0); + + assign_tests1((boost::unordered_set >*) 0); + assign_tests1((boost::unordered_multiset >*) 0); + assign_tests1((boost::unordered_map >*) 0); + assign_tests1((boost::unordered_multimap >*) 0); + + assign_tests2((boost::unordered_set >*) 0); + assign_tests2((boost::unordered_multiset >*) 0); + assign_tests2((boost::unordered_map >*) 0); + assign_tests2((boost::unordered_multimap >*) 0); + + return boost::report_errors(); +} diff --git a/test/unordered/at_tests.cpp b/test/unordered/at_tests.cpp new file mode 100644 index 00000000..149a890c --- /dev/null +++ b/test/unordered/at_tests.cpp @@ -0,0 +1,28 @@ + +// Copyright 2007 Daniel James. +// Distributed under 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) + +#include +#include +#include + +int main() { + boost::unordered_map x; + typedef boost::unordered_map::iterator iterator; + + x["one"] = 1; + x["two"] = 2; + + BOOST_TEST(x.at("one") == 1); + BOOST_TEST(x.at("two") == 2); + + try { + x.at("three"); + BOOST_ERROR("Should have thrown."); + } + catch(std::out_of_range) { + } + + return boost::report_errors(); +} diff --git a/test/unordered/bucket_tests.cpp b/test/unordered/bucket_tests.cpp new file mode 100644 index 00000000..620e16f0 --- /dev/null +++ b/test/unordered/bucket_tests.cpp @@ -0,0 +1,64 @@ + +// Copyright 2006-2007 Daniel James. +// Distributed under 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) + +#include +#include +#include +#include +#include "../objects/test.hpp" +#include "../helpers/random_values.hpp" +#include "../helpers/helpers.hpp" + +test::seed_t seed(54635); + +template +void bucket_tests(X* = 0) +{ + typedef typename X::size_type size_type; + typedef typename X::const_local_iterator const_local_iterator; + test::random_values v(1000); + + X x(v.begin(), v.end()); + + BOOST_TEST(x.bucket_count() < x.max_bucket_count()); + std::cerr<::const_iterator + it = v.begin(), end = v.end(); it != end; ++it) + { + size_type bucket = x.bucket(test::get_key(*it)); + + BOOST_TEST(bucket < x.bucket_count()); + if(bucket < x.max_bucket_count()) { + // lit? lend?? I need a new naming scheme. + const_local_iterator lit = x.begin(bucket), lend = x.end(bucket); + while(lit != lend && test::get_key(*it) != test::get_key(*lit)) ++lit; + BOOST_TEST(lit != lend); + } + } + + for(size_type i = 0; i < x.bucket_count(); ++i) { + BOOST_TEST(x.bucket_size(i) == (size_type) std::distance(x.begin(i), x.end(i))); + BOOST_TEST(x.bucket_size(i) == (size_type) std::distance(x.cbegin(i), x.cend(i))); + X const& x_ref = x; + BOOST_TEST(x.bucket_size(i) == (size_type) std::distance(x_ref.begin(i), x_ref.end(i))); + BOOST_TEST(x.bucket_size(i) == (size_type) std::distance(x_ref.cbegin(i), x_ref.cend(i))); + } +} + +int main() +{ + bucket_tests((boost::unordered_set*) 0); + bucket_tests((boost::unordered_multiset*) 0); + bucket_tests((boost::unordered_map*) 0); + bucket_tests((boost::unordered_multimap*) 0); + + bucket_tests((boost::unordered_set >*) 0); + bucket_tests((boost::unordered_multiset >*) 0); + bucket_tests((boost::unordered_map >*) 0); + bucket_tests((boost::unordered_multimap >*) 0); + + return boost::report_errors(); +} diff --git a/test/unordered/compile_tests.cpp b/test/unordered/compile_tests.cpp new file mode 100644 index 00000000..331bdad8 --- /dev/null +++ b/test/unordered/compile_tests.cpp @@ -0,0 +1,328 @@ + +// Copyright 2006-2007 Daniel James. +// Distributed under 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) + +#include +#include + +#if defined(BOOST_MSVC) +#pragma warning(push) +#pragma warning(disable:4100) // unreferenced formal parameter +#endif + +#include + +#if defined(BOOST_MSVC) +#pragma warning(pop) +#endif + +#include +#include +#include "../helpers/check_return_type.hpp" + +#include +#include +#include "../objects/minimal.hpp" + +template void sink(T const&) {} + +template +void unordered_set_test(X&, Key const&) +{ + typedef typename X::value_type value_type; + typedef typename X::key_type key_type; + + BOOST_MPL_ASSERT((boost::is_same)); +} + +template +void unordered_map_test(X&, Key const&, T const&) +{ + typedef typename X::value_type value_type; + typedef typename X::key_type key_type; + BOOST_MPL_ASSERT((boost::is_same >)); +} + +template +void unordered_unique_test(X& r, T const& t) +{ + typedef typename X::iterator iterator; + test::check_return_type >::equals(r.insert(t)); +} + +template +void unordered_equivalent_test(X& r, T const& t) +{ + typedef typename X::iterator iterator; + test::check_return_type::equals(r.insert(t)); +} + +template +void unordered_map_functions(X&, Key const& k, T const&) +{ + typedef typename X::mapped_type mapped_type; + + X a; + test::check_return_type::equals_ref(a[k]); + test::check_return_type::equals_ref(a.at(k)); + + X const b = a; + test::check_return_type::equals_ref(b.at(k)); +} + +template +void unordered_test(X&, Key& k, T& t, Hash& hf, Pred& eq) +{ + typedef typename X::key_type key_type; + typedef typename X::hasher hasher; + typedef typename X::key_equal key_equal; + typedef typename X::size_type size_type; + + typedef typename X::iterator iterator; + typedef typename X::const_iterator const_iterator; + typedef typename X::local_iterator local_iterator; + typedef typename X::const_local_iterator const_local_iterator; + + typedef typename boost::BOOST_ITERATOR_CATEGORY::type iterator_category; + typedef typename boost::iterator_difference::type iterator_difference; + typedef typename boost::iterator_pointer::type iterator_pointer; + typedef typename boost::iterator_reference::type iterator_reference; + + typedef typename boost::BOOST_ITERATOR_CATEGORY::type local_iterator_category; + typedef typename boost::iterator_difference::type local_iterator_difference; + typedef typename boost::iterator_pointer::type local_iterator_pointer; + typedef typename boost::iterator_reference::type local_iterator_reference; + + typedef typename boost::BOOST_ITERATOR_CATEGORY::type const_iterator_category; + typedef typename boost::iterator_difference::type const_iterator_difference; + typedef typename boost::iterator_pointer::type const_iterator_pointer; + typedef typename boost::iterator_reference::type const_iterator_reference; + + typedef typename boost::BOOST_ITERATOR_CATEGORY::type const_local_iterator_category; + typedef typename boost::iterator_difference::type const_local_iterator_difference; + typedef typename boost::iterator_pointer::type const_local_iterator_pointer; + typedef typename boost::iterator_reference::type const_local_iterator_reference; + + BOOST_MPL_ASSERT((boost::is_same)); + boost::function_requires >(); + boost::function_requires >(); + + BOOST_MPL_ASSERT((boost::is_same)); + test::check_return_type::equals(hf(k)); + + BOOST_MPL_ASSERT((boost::is_same)); + test::check_return_type::convertible(eq(k, k)); + + boost::function_requires >(); + BOOST_MPL_ASSERT((boost::is_same)); + BOOST_MPL_ASSERT((boost::is_same)); + BOOST_MPL_ASSERT((boost::is_same)); + BOOST_MPL_ASSERT((boost::is_same)); + + boost::function_requires >(); + BOOST_MPL_ASSERT((boost::is_same)); + BOOST_MPL_ASSERT((boost::is_same)); + BOOST_MPL_ASSERT((boost::is_same)); + BOOST_MPL_ASSERT((boost::is_same)); + + X(10, hf, eq); + X a(10, hf, eq); + X(10, hf); + X a2(10, hf); + X(10); + X a3(10); + X(); + X a4; + + typename X::value_type* i = 0; + typename X::value_type* j = 0; + + X(i, j, 10, hf, eq); + X a5(i, j, 10, hf, eq); + X(i, j, 10, hf); + X a6(i, j, 10, hf); + X(i, j, 10); + X a7(i, j, 10); + X(i, j); + X a8(i, j); + + X const b; + sink(X(b)); + X a9(b); + a = b; + + test::check_return_type::equals(b.hash_function()); + test::check_return_type::equals(b.key_eq()); + + const_iterator q = a.cbegin(); + test::check_return_type::equals(a.insert(q, t)); + + a.insert(i, j); + test::check_return_type::equals(a.erase(k)); + + BOOST_TEST(a.empty()); + if(a.empty()) { + a.insert(t); + q = a.cbegin(); + test::check_return_type::equals(a.erase(q)); + } + + const_iterator q1 = a.cbegin(), q2 = a.cend(); + test::check_return_type::equals(a.erase(q1, q2)); + + a.clear(); + + test::check_return_type::equals(a.find(k)); + test::check_return_type::equals(b.find(k)); + test::check_return_type::equals(b.count(k)); + test::check_return_type >::equals( + a.equal_range(k)); + test::check_return_type >::equals( + b.equal_range(k)); + test::check_return_type::equals(b.bucket_count()); + test::check_return_type::equals(b.max_bucket_count()); + test::check_return_type::equals(b.bucket(k)); + test::check_return_type::equals(b.bucket_size(0)); + + test::check_return_type::equals(a.begin(0)); + test::check_return_type::equals(b.begin(0)); + test::check_return_type::equals(a.end(0)); + test::check_return_type::equals(b.end(0)); + + test::check_return_type::equals(a.cbegin(0)); + test::check_return_type::equals(b.cbegin(0)); + test::check_return_type::equals(a.cend(0)); + test::check_return_type::equals(b.cend(0)); + + test::check_return_type::equals(b.load_factor()); + test::check_return_type::equals(b.max_load_factor()); + a.max_load_factor((float) 2.0); + a.rehash(100); +} + +void test1() +{ + boost::hash hash; + std::equal_to equal_to; + int value = 0; + std::pair map_value(0, 0); + + std::cout<<"Test unordered_set.\n"; + + boost::unordered_set set; + + unordered_unique_test(set, value); + unordered_set_test(set, value); + unordered_test(set, value, value, hash, equal_to); + + std::cout<<"Test unordered_multiset.\n"; + + boost::unordered_multiset multiset; + + unordered_equivalent_test(multiset, value); + unordered_set_test(multiset, value); + unordered_test(multiset, value, value, hash, equal_to); + + std::cout<<"Test unordered_map.\n"; + + boost::unordered_map map; + + unordered_unique_test(map, map_value); + unordered_map_test(map, value, value); + unordered_test(map, value, map_value, hash, equal_to); + unordered_map_functions(map, value, value); + + std::cout<<"Test unordered_multimap.\n"; + + boost::unordered_multimap multimap; + + unordered_equivalent_test(multimap, map_value); + unordered_map_test(multimap, value, value); + unordered_test(multimap, value, map_value, hash, equal_to); +} + +void test2() +{ + test::minimal::assignable assignable + = test::minimal::assignable::create(); + test::minimal::copy_constructible copy_constructible + = test::minimal::copy_constructible::create(); + test::minimal::hash hash + = test::minimal::hash::create(); + test::minimal::equal_to equal_to + = test::minimal::equal_to::create(); + + typedef std::pair map_value_type; + map_value_type map_value(assignable, copy_constructible); + + std::cout<<"Test unordered_set.\n"; + + boost::unordered_set< + test::minimal::assignable, + test::minimal::hash, + test::minimal::equal_to, + test::minimal::allocator > set; + + unordered_unique_test(set, assignable); + unordered_set_test(set, assignable); + unordered_test(set, assignable, assignable, hash, equal_to); + + std::cout<<"Test unordered_multiset.\n"; + + boost::unordered_multiset< + test::minimal::assignable, + test::minimal::hash, + test::minimal::equal_to, + test::minimal::allocator > multiset; + + unordered_equivalent_test(multiset, assignable); + unordered_set_test(multiset, assignable); + unordered_test(multiset, assignable, assignable, hash, equal_to); + + std::cout<<"Test unordered_map.\n"; + + boost::unordered_map< + test::minimal::assignable, + test::minimal::copy_constructible, + test::minimal::hash, + test::minimal::equal_to, + test::minimal::allocator > map; + + unordered_unique_test(map, map_value); + unordered_map_test(map, assignable, copy_constructible); + unordered_test(map, assignable, map_value, hash, equal_to); + + + boost::unordered_map< + test::minimal::assignable, + test::minimal::default_copy_constructible, + test::minimal::hash, + test::minimal::equal_to, + test::minimal::allocator > map2; + + test::minimal::default_copy_constructible default_copy_constructible; + + unordered_map_functions(map2, assignable, default_copy_constructible); + + std::cout<<"Test unordered_multimap.\n"; + + boost::unordered_multimap< + test::minimal::assignable, + test::minimal::copy_constructible, + test::minimal::hash, + test::minimal::equal_to, + test::minimal::allocator > multimap; + + unordered_equivalent_test(multimap, map_value); + unordered_map_test(multimap, assignable, copy_constructible); + unordered_test(multimap, assignable, map_value, hash, equal_to); +} + +int main() { + test1(); + test2(); + + return boost::report_errors(); +} diff --git a/test/unordered/constructor_tests.cpp b/test/unordered/constructor_tests.cpp new file mode 100644 index 00000000..f2109410 --- /dev/null +++ b/test/unordered/constructor_tests.cpp @@ -0,0 +1,311 @@ + +// Copyright 2006-2007 Daniel James. +// Distributed under 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) + +#include +#include +#include +#include "../objects/test.hpp" +#include "../helpers/random_values.hpp" +#include "../helpers/tracker.hpp" +#include "../helpers/equivalent.hpp" +#include "../helpers/input_iterator.hpp" +#include "../helpers/invariants.hpp" + +#include + +test::seed_t seed(356730); + +template +void constructor_tests1(T* = 0) +{ + typename T::hasher hf; + typename T::key_equal eq; + typename T::allocator_type al; + + std::cerr<<"Construct 1\n"; + { + T x(0, hf, eq); + BOOST_TEST(x.empty()); + BOOST_TEST(test::equivalent(x.hash_function(), hf)); + BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); + test::check_equivalent_keys(x); + } + + std::cerr<<"Construct 2\n"; + { + T x(100, hf); + BOOST_TEST(x.empty()); + BOOST_TEST(x.bucket_count() >= 100); + BOOST_TEST(test::equivalent(x.hash_function(), hf)); + BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); + test::check_equivalent_keys(x); + } + + std::cerr<<"Construct 3\n"; + { + T x(2000); + BOOST_TEST(x.empty()); + BOOST_TEST(x.bucket_count() >= 2000); + BOOST_TEST(test::equivalent(x.hash_function(), hf)); + BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); + test::check_equivalent_keys(x); + } + + std::cerr<<"Construct 4\n"; + { + T x; + BOOST_TEST(x.empty()); + BOOST_TEST(test::equivalent(x.hash_function(), hf)); + BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); + test::check_equivalent_keys(x); + } + + std::cerr<<"Construct 5\n"; + { + test::random_values v(1000); + T x(v.begin(), v.end(), 10000, hf, eq); + BOOST_TEST(x.bucket_count() >= 10000); + BOOST_TEST(test::equivalent(x.hash_function(), hf)); + BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); + test::check_container(x, v); + test::check_equivalent_keys(x); + } + + std::cerr<<"Construct 6\n"; + { + test::random_values v(10); + T x(v.begin(), v.end(), 10000, hf); + BOOST_TEST(x.bucket_count() >= 10000); + BOOST_TEST(test::equivalent(x.hash_function(), hf)); + BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); + test::check_container(x, v); + test::check_equivalent_keys(x); + } + + std::cerr<<"Construct 7\n"; + { + test::random_values v(100); + T x(v.begin(), v.end(), 100); + BOOST_TEST(x.bucket_count() >= 100); + BOOST_TEST(test::equivalent(x.hash_function(), hf)); + BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); + test::check_container(x, v); + test::check_equivalent_keys(x); + } + + std::cerr<<"Construct 8\n"; + { + test::random_values v(1); + T x(v.begin(), v.end()); + BOOST_TEST(test::equivalent(x.hash_function(), hf)); + BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); + test::check_container(x, v); + test::check_equivalent_keys(x); + } + + std::cerr<<"Construct 9\n"; + { + T x(0, hf, eq, al); + BOOST_TEST(x.empty()); + BOOST_TEST(test::equivalent(x.hash_function(), hf)); + BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); + test::check_equivalent_keys(x); + } + + std::cerr<<"Construct 10\n"; + { + test::random_values v(1000); + T x(v.begin(), v.end(), 10000, hf, eq, al); + BOOST_TEST(x.bucket_count() >= 10000); + BOOST_TEST(test::equivalent(x.hash_function(), hf)); + BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); + test::check_container(x, v); + test::check_equivalent_keys(x); + } +} + +template +void constructor_tests2(T* = 0) +{ + typename T::hasher hf; + typename T::hasher hf1(1); + typename T::hasher hf2(2); + typename T::key_equal eq; + typename T::key_equal eq1(1); + typename T::key_equal eq2(2); + typename T::allocator_type al; + typename T::allocator_type al1(1); + typename T::allocator_type al2(2); + + std::cerr<<"Construct 1\n"; + { + T x(10000, hf1, eq1); + BOOST_TEST(x.bucket_count() >= 10000); + BOOST_TEST(test::equivalent(x.hash_function(), hf1)); + BOOST_TEST(test::equivalent(x.key_eq(), eq1)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); + test::check_equivalent_keys(x); + } + + std::cerr<<"Construct 2\n"; + { + T x(100, hf1); + BOOST_TEST(x.empty()); + BOOST_TEST(x.bucket_count() >= 100); + BOOST_TEST(test::equivalent(x.hash_function(), hf1)); + BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); + test::check_equivalent_keys(x); + } + + std::cerr<<"Construct 3\n"; + { + test::random_values v(100); + T x(v.begin(), v.end(), 0, hf1, eq1); + BOOST_TEST(test::equivalent(x.hash_function(), hf1)); + BOOST_TEST(test::equivalent(x.key_eq(), eq1)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); + test::check_container(x, v); + test::check_equivalent_keys(x); + } + + std::cerr<<"Construct 4\n"; + { + test::random_values v(5); + T x(v.begin(), v.end(), 1000, hf1); + BOOST_TEST(x.bucket_count() >= 1000); + BOOST_TEST(test::equivalent(x.hash_function(), hf1)); + BOOST_TEST(test::equivalent(x.key_eq(), eq)); + BOOST_TEST(test::equivalent(x.get_allocator(), al)); + test::check_container(x, v); + test::check_equivalent_keys(x); + } + + + std::cerr<<"Construct 5\n"; + { + test::random_values v(100); + T x(v.begin(), v.end(), 0, hf, eq, al1); + T y(x.begin(), x.end(), 0, hf1, eq1, al2); + test::check_container(x, v); + test::check_container(y, x); + test::check_equivalent_keys(x); + test::check_equivalent_keys(y); + } + + std::cerr<<"Construct 6\n"; + { + test::random_values v(100); + T x(v.begin(), v.end(), 0, hf1, eq1); + T y(x.begin(), x.end(), 0, hf, eq); + test::check_container(x, v); + test::check_container(y, x); + test::check_equivalent_keys(x); + test::check_equivalent_keys(y); + } + + std::cerr<<"Construct 7\n"; + { + test::random_values v(100); + T x(v.begin(), v.end(), 0, hf1, eq1); + T y(x.begin(), x.end(), 0, hf2, eq2); + test::check_container(x, v); + test::check_container(y, x); + test::check_equivalent_keys(x); + test::check_equivalent_keys(y); + } + + std::cerr<<"Construct 8 - from input iterator\n"; + { + test::random_values v(100); + T x(test::input_iterator(v.begin()), test::input_iterator(v.end()), 0, hf1, eq1); + T y(test::input_iterator(x.begin()), test::input_iterator(x.end()), 0, hf2, eq2); + test::check_container(x, v); + test::check_container(y, x); + test::check_equivalent_keys(x); + test::check_equivalent_keys(y); + } +} + +template +void map_constructor_test(T* = 0) +{ + std::cerr<<"map_constructor_test\n"; + + typedef std::list > list; + test::random_values v(1000); + list l; + std::copy(v.begin(), v.end(), std::back_inserter(l)); + + T x(l.begin(), l.end()); + + test::check_container(x, v); + test::check_equivalent_keys(x); +} + +int main() +{ + std::cerr<<"Test1 unordered_set\n"; + constructor_tests1((boost::unordered_set*) 0); + std::cerr<<"Test1 unordered_multiset\n"; + constructor_tests1((boost::unordered_multiset*) 0); + std::cerr<<"Test1 unordered_map\n"; + constructor_tests1((boost::unordered_map*) 0); + std::cerr<<"Test1 unordered_multimap\n"; + constructor_tests1((boost::unordered_multimap*) 0); + + std::cerr<<"Test1 unordered_set\n"; + constructor_tests1((boost::unordered_set >*) 0); + std::cerr<<"Test1 unordered_multiset\n"; + constructor_tests1((boost::unordered_multiset >*) 0); + std::cerr<<"Test1 unordered_map\n"; + constructor_tests1((boost::unordered_map >*) 0); + std::cerr<<"Test1 unordered_multimap\n"; + constructor_tests1((boost::unordered_multimap >*) 0); + + std::cerr<<"Test1 unordered_set\n"; + constructor_tests1((boost::unordered_set >*) 0); + std::cerr<<"Test1 unordered_multiset\n"; + constructor_tests1((boost::unordered_multiset >*) 0); + std::cerr<<"Test1 unordered_map\n"; + constructor_tests1((boost::unordered_map >*) 0); + std::cerr<<"Test1 unordered_multimap\n"; + constructor_tests1((boost::unordered_multimap >*) 0); + + std::cerr<<"Test2 unordered_set\n"; + constructor_tests2((boost::unordered_set >*) 0); + std::cerr<<"Test2 unordered_multiset\n"; + constructor_tests2((boost::unordered_multiset >*) 0); + std::cerr<<"Test2 unordered_map\n"; + constructor_tests2((boost::unordered_map >*) 0); + std::cerr<<"Test2 unordered_multimap\n"; + constructor_tests2((boost::unordered_multimap >*) 0); + + std::cerr<<"Test2 unordered_set\n"; + constructor_tests2((boost::unordered_set >*) 0); + std::cerr<<"Test2 unordered_multiset\n"; + constructor_tests2((boost::unordered_multiset >*) 0); + std::cerr<<"Test2 unordered_map\n"; + constructor_tests2((boost::unordered_map >*) 0); + std::cerr<<"Test2 unordered_multimap\n"; + constructor_tests2((boost::unordered_multimap >*) 0); + + std::cerr<<"Map Test unordered_map\n"; + map_constructor_test((boost::unordered_map >*) 0); + std::cerr<<"Map Test unordered_multimap\n"; + map_constructor_test((boost::unordered_multimap >*) 0); + + return boost::report_errors(); +} diff --git a/test/unordered/copy_tests.cpp b/test/unordered/copy_tests.cpp new file mode 100644 index 00000000..5dde9782 --- /dev/null +++ b/test/unordered/copy_tests.cpp @@ -0,0 +1,111 @@ + +// Copyright 2006-2007 Daniel James. +// Distributed under 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) + +#include +#include +#include +#include "../objects/test.hpp" +#include "../helpers/random_values.hpp" +#include "../helpers/tracker.hpp" +#include "../helpers/equivalent.hpp" +#include "../helpers/invariants.hpp" + +test::seed_t seed(9063); + +template +void copy_construct_tests1(T* = 0) +{ + typename T::hasher hf; + typename T::key_equal eq; + typename T::allocator_type al; + + { + T x; + T y(x); + BOOST_TEST(y.empty()); + BOOST_TEST(test::equivalent(y.hash_function(), hf)); + BOOST_TEST(test::equivalent(y.key_eq(), eq)); + BOOST_TEST(test::equivalent(y.get_allocator(), al)); + BOOST_TEST(x.max_load_factor() == y.max_load_factor()); + test::check_equivalent_keys(y); + } + + { + test::random_values v(1000); + + T x(v.begin(), v.end()); + T y(x); + test::unordered_equivalence_tester equivalent(x); + equivalent(y); + test::check_equivalent_keys(y); + } + + { + // In this test I drop the original containers max load factor, so it + // is much lower than the load factor. The hash table is not allowed + // to rehash, but the destination container should probably allocate + // enough buckets to decrease the load factor appropriately. + test::random_values v(1000); + T x(v.begin(), v.end()); + x.max_load_factor(x.load_factor() / 4); + T y(x); + test::unordered_equivalence_tester equivalent(x); + equivalent(y); + // This isn't guaranteed: + BOOST_TEST(y.load_factor() < y.max_load_factor()); + test::check_equivalent_keys(y); + } +} + +template +void copy_construct_tests2(T* ptr = 0) +{ + copy_construct_tests1(ptr); + + typename T::hasher hf(1); + typename T::key_equal eq(1); + typename T::allocator_type al(1); + + { + T x(10000, hf, eq, al); + T y(x); + BOOST_TEST(y.empty()); + BOOST_TEST(test::equivalent(y.hash_function(), hf)); + BOOST_TEST(test::equivalent(y.key_eq(), eq)); + BOOST_TEST(test::equivalent(y.get_allocator(), al)); + BOOST_TEST(x.max_load_factor() == y.max_load_factor()); + test::check_equivalent_keys(y); + } + + { + test::random_values v(1000); + + T x(v.begin(), v.end(), 0, hf, eq, al); + T y(x); + test::unordered_equivalence_tester equivalent(x); + equivalent(y); + test::check_equivalent_keys(y); + } +} + +int main() +{ + copy_construct_tests1((boost::unordered_set*) 0); + copy_construct_tests1((boost::unordered_multiset*) 0); + copy_construct_tests1((boost::unordered_map*) 0); + copy_construct_tests1((boost::unordered_multimap*) 0); + + copy_construct_tests2((boost::unordered_set >*) 0); + copy_construct_tests2((boost::unordered_multiset >*) 0); + copy_construct_tests2((boost::unordered_map >*) 0); + copy_construct_tests2((boost::unordered_multimap >*) 0); + + copy_construct_tests2((boost::unordered_set >*) 0); + copy_construct_tests2((boost::unordered_multiset >*) 0); + copy_construct_tests2((boost::unordered_map >*) 0); + copy_construct_tests2((boost::unordered_multimap >*) 0); + + return boost::report_errors(); +} diff --git a/test/unordered/equivalent_keys_tests.cpp b/test/unordered/equivalent_keys_tests.cpp new file mode 100644 index 00000000..e457bba2 --- /dev/null +++ b/test/unordered/equivalent_keys_tests.cpp @@ -0,0 +1,85 @@ + +// Copyright 2006-2007 Daniel James. +// Distributed under 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) + +#include +#include +#include +#include +#include +#include +#include "../helpers/tracker.hpp" +#include "../helpers/invariants.hpp" + +#include + +template +void test_equal_insertion(Iterator begin, Iterator end) +{ + typedef test::ordered tracker; + + Container x1; + tracker x2 = test::create_ordered(x1); + + for(Iterator it = begin; it != end; ++it) { + x1.insert(*it); + x2.insert(*it); + x2.compare_key(x1, *it); + } + + x2.compare(x1); + test::check_equivalent_keys(x1); +} + +void set_tests() +{ + int values[][5] = { + {1}, + {54, 23}, + {-13, 65}, + {77, 77}, + {986, 25, 986} + }; + + test_equal_insertion >(values[0], values[0] + 1); + test_equal_insertion >(values[1], values[1] + 2); + test_equal_insertion >(values[2], values[2] + 2); + test_equal_insertion >(values[3], values[3] + 2); + test_equal_insertion >(values[4], values[4] + 3); + + test_equal_insertion >(values[0], values[0] + 1); + test_equal_insertion >(values[1], values[1] + 2); + test_equal_insertion >(values[2], values[2] + 2); + test_equal_insertion >(values[3], values[3] + 2); + test_equal_insertion >(values[4], values[4] + 3); +} + +void map_tests() +{ + typedef std::deque > values_type; + values_type v[5]; + v[0].push_back(std::pair(1,1)); + v[1].push_back(std::pair(28,34)); + v[1].push_back(std::pair(16,58)); + v[1].push_back(std::pair(-124, 62)); + v[2].push_back(std::pair(432,12)); + v[2].push_back(std::pair(9,13)); + v[2].push_back(std::pair(432,24)); + + for(int i = 0; i < 5; ++i) + test_equal_insertion >( + v[i].begin(), v[i].end()); + + for(int i2 = 0; i2 < 5; ++i2) + test_equal_insertion >( + v[i2].begin(), v[i2].end()); +} + +int main() +{ + set_tests(); + map_tests(); + + return boost::report_errors(); +} diff --git a/test/unordered/erase_equiv_tests.cpp b/test/unordered/erase_equiv_tests.cpp new file mode 100644 index 00000000..29f4756e --- /dev/null +++ b/test/unordered/erase_equiv_tests.cpp @@ -0,0 +1,199 @@ + +// Copyright 2006-2007 Daniel James. +// Distributed under 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) + +// The code for erasing elements from containers with equivalent keys is very +// hairy with several tricky edge cases - so explicitly test each one. + +#include +#include +#include +#include +#include +#include +#include +#include "../objects/test.hpp" + +struct write_pair_type +{ + template + void operator()(std::pair const& x) const + { + std::cout<<"("< +void write_container(Container const& x) +{ + std::for_each(x.begin(), x.end(), write_pair); + std::cout<<"\n"; +} + +// Make everything collide - for testing erase in a single bucket. +struct collision_hash +{ + int operator()(int) const { return 0; } +}; + +// For testing erase in 2 buckets. +struct collision2_hash +{ + int operator()(int x) const { return x & 1; } +}; + +typedef boost::unordered_multimap, + test::allocator > > collide_map; +typedef boost::unordered_multimap, + test::allocator > > collide_map2; +typedef collide_map::value_type collide_value; +typedef std::list collide_list; + + +void empty_range_tests() +{ + collide_map x; + x.erase(x.begin(), x.end()); + x.erase(x.begin(), x.begin()); + x.erase(x.end(), x.end()); +} + +void single_item_tests() +{ + collide_list init; + init.push_back(collide_value(1,1)); + + collide_map x(init.begin(), init.end()); + x.erase(x.begin(), x.begin()); + BOOST_TEST(x.count(1) == 1 && x.size() == 1); + x.erase(x.end(), x.end()); + BOOST_TEST(x.count(1) == 1 && x.size() == 1); + x.erase(x.begin(), x.end()); + BOOST_TEST(x.count(1) == 0 && x.size() == 0); +} + +void two_equivalent_item_tests() +{ + collide_list init; + init.push_back(collide_value(1,1)); + init.push_back(collide_value(1,2)); + + { + collide_map x(init.begin(), init.end()); + x.erase(x.begin(), x.end()); + BOOST_TEST(x.count(1) == 0 && x.size() == 0); + } + + { + collide_map x(init.begin(), init.end()); + int value = boost::next(x.begin())->second; + x.erase(x.begin(), boost::next(x.begin())); + BOOST_TEST(x.count(1) == 1 && x.size() == 1 && + x.begin()->first == 1 && x.begin()->second == value); + } + + { + collide_map x(init.begin(), init.end()); + int value = x.begin()->second; + x.erase(boost::next(x.begin()), x.end()); + BOOST_TEST(x.count(1) == 1 && x.size() == 1 && + x.begin()->first == 1 && x.begin()->second == value); + } +} + +// More automated tests... + +template +bool compare(Range1 const& x, Range2 const& y) +{ + collide_list a; + collide_list b; + std::copy(x.begin(), x.end(), std::back_inserter(a)); + std::copy(y.begin(), y.end(), std::back_inserter(b)); + a.sort(); + b.sort(); + return a == b; +} + +template +bool general_erase_range_test(Container& x, int start, int end) +{ + collide_list l; + std::copy(x.begin(), x.end(), std::back_inserter(l)); + l.erase(boost::next(l.begin(), start), boost::next(l.begin(), end)); + x.erase(boost::next(x.begin(), start), boost::next(x.begin(), end)); + return compare(l, x); +} + +template +void erase_subrange_tests(Container const& x) +{ + for(std::size_t length = 0; length < x.size(); ++length) { + for(std::size_t position = 0; position < x.size() - length; ++position) { + Container y(x); + collide_list init; + std::copy(y.begin(), y.end(), std::back_inserter(init)); + if(!general_erase_range_test(y, position, position + length)) { + BOOST_ERROR("general_erase_range_test failed."); + std::cout<<"Erase: ["< +void x_by_y_erase_range_tests(Container*, int values, int duplicates) +{ + Container y; + + for(int i = 0; i < values; ++i) { + for(int j = 0; j < duplicates; ++j) { + y.insert(collide_value(i, j)); + } + } + + std::cout<<"Values: "< +void exhaustive_erase_tests(Container* x, int num_values, + int num_duplicated) +{ + for(int i = 0; i < num_values; ++i) { + for(int j = 0; j < num_duplicated; ++j) { + x_by_y_erase_range_tests(x, i, j); + } + } +} + +void exhaustive_collide_tests() +{ + std::cout<<"exhaustive_collide_tests:\n"; + collide_map m; + exhaustive_erase_tests((collide_map*) 0, 4, 4); + std::cout<<"\n"; +} + +void exhaustive_collide2_tests() +{ + std::cout<<"exhaustive_collide2_tests:\n"; + exhaustive_erase_tests((collide_map2*) 0, 8, 4); + std::cout<<"\n"; +} + +int main() +{ + empty_range_tests(); + single_item_tests(); + two_equivalent_item_tests(); + exhaustive_collide_tests(); + exhaustive_collide2_tests(); + + return boost::report_errors(); +} diff --git a/test/unordered/erase_tests.cpp b/test/unordered/erase_tests.cpp new file mode 100644 index 00000000..bea51013 --- /dev/null +++ b/test/unordered/erase_tests.cpp @@ -0,0 +1,150 @@ + +// Copyright 2006-2007 Daniel James. +// Distributed under 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) + +#include +#include +#include +#include +#include "../objects/test.hpp" +#include "../helpers/random_values.hpp" +#include "../helpers/tracker.hpp" +#include "../helpers/equivalent.hpp" +#include "../helpers/helpers.hpp" + +#include + +test::seed_t seed(85638); + +template +void erase_tests1(Container* = 0) +{ + std::cerr<<"Erase by key.\n"; + { + test::random_values v(1000); + Container x(v.begin(), v.end()); + for(typename test::random_values::iterator it = v.begin(); + it != v.end(); ++it) + { + std::size_t count = x.count(test::get_key(*it)); + std::size_t old_size = x.size(); + BOOST_TEST(count == x.erase(test::get_key(*it))); + BOOST_TEST(x.size() == old_size - count); + BOOST_TEST(x.count(test::get_key(*it)) == 0); + BOOST_TEST(x.find(test::get_key(*it)) == x.end()); + } + } + + std::cerr<<"erase(begin()).\n"; + { + test::random_values v(1000); + Container x(v.begin(), v.end()); + std::size_t size = x.size(); + while(size > 0 && !x.empty()) + { + typename Container::key_type key = test::get_key(*x.begin()); + std::size_t count = x.count(key); + typename Container::iterator pos = x.erase(x.begin()); + --size; + BOOST_TEST(pos == x.begin()); + BOOST_TEST(x.count(key) == count - 1); + BOOST_TEST(x.size() == size); + } + BOOST_TEST(x.empty()); + } + + std::cerr<<"erase(random position).\n"; + { + test::random_values v(1000); + Container x(v.begin(), v.end()); + std::size_t size = x.size(); + while(size > 0 && !x.empty()) + { + using namespace std; + int index = rand() % x.size(); + typename Container::const_iterator prev, pos, next; + if(index == 0) { + prev = pos = x.begin(); + } + else { + prev = boost::next(x.begin(), index - 1); + pos = boost::next(prev); + } + next = boost::next(pos); + typename Container::key_type key = test::get_key(*pos); + std::size_t count = x.count(key); + BOOST_TEST(next == x.erase(pos)); + --size; + if(size > 0) + BOOST_TEST(next == + (index == 0 ? x.begin() : boost::next(prev))); + BOOST_TEST(x.count(key) == count - 1); + BOOST_TEST(x.size() == size); + } + BOOST_TEST(x.empty()); + } + + std::cerr<<"erase(ranges).\n"; + { + test::random_values v(500); + Container x(v.begin(), v.end()); + + std::size_t size = x.size(); + + // I'm actually stretching it a little here, as the standard says it + // returns 'the iterator immediately following the erase elements' + // and if nothing is erased, then there's nothing to follow. But I + // think this is the only sensible option... + BOOST_TEST(x.erase(x.end(), x.end()) == x.end()); + BOOST_TEST(x.erase(x.begin(), x.begin()) == x.begin()); + BOOST_TEST(x.size() == size); + + BOOST_TEST(x.erase(x.begin(), x.end()) == x.end()); + BOOST_TEST(x.empty()); + BOOST_TEST(x.begin() == x.end()); + + BOOST_TEST(x.erase(x.begin(), x.end()) == x.begin()); + } + + std::cerr<<"clear().\n"; + { + test::random_values v(500); + Container x(v.begin(), v.end()); + x.clear(); + BOOST_TEST(x.empty()); + BOOST_TEST(x.begin() == x.end()); + } +} + +int main() +{ + std::cerr<<"Erase unordered_set.\n"; + erase_tests1((boost::unordered_set*) 0); + std::cerr<<"\nErase unordered_multiset.\n"; + erase_tests1((boost::unordered_multiset*) 0); + std::cerr<<"\nErase unordered_map.\n"; + erase_tests1((boost::unordered_map*) 0); + std::cerr<<"\nErase unordered_multimap.\n"; + erase_tests1((boost::unordered_multimap*) 0); + + std::cerr<<"\nErase unordered_set.\n"; + erase_tests1((boost::unordered_set >*) 0); + std::cerr<<"\nErase unordered_multiset.\n"; + erase_tests1((boost::unordered_multiset >*) 0); + std::cerr<<"\nErase unordered_map.\n"; + erase_tests1((boost::unordered_map >*) 0); + std::cerr<<"\nErase unordered_multimap.\n"; + erase_tests1((boost::unordered_multimap >*) 0); + + std::cerr<<"\nErase unordered_set.\n"; + erase_tests1((boost::unordered_set >*) 0); + std::cerr<<"\nErase unordered_multiset.\n"; + erase_tests1((boost::unordered_multiset >*) 0); + std::cerr<<"\nErase unordered_map.\n"; + erase_tests1((boost::unordered_map >*) 0); + std::cerr<<"\nErase unordered_multimap.\n"; + erase_tests1((boost::unordered_multimap >*) 0); + + return boost::report_errors(); +} diff --git a/test/unordered/find_tests.cpp b/test/unordered/find_tests.cpp new file mode 100644 index 00000000..a402df91 --- /dev/null +++ b/test/unordered/find_tests.cpp @@ -0,0 +1,99 @@ + +// Copyright 2006-2007 Daniel James. +// Distributed under 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) + +#include +#include +#include +#include "../objects/test.hpp" +#include "../helpers/random_values.hpp" +#include "../helpers/tracker.hpp" +#include "../helpers/helpers.hpp" + +test::seed_t seed(78937); + +template +void find_tests1(X*) +{ + typedef typename X::iterator iterator; + + { + test::random_values v(500); + X x(v.begin(), v.end()); + X const& x_const = x; + test::ordered tracker = test::create_ordered(x); + tracker.insert_range(v.begin(), v.end()); + + for(typename test::ordered::const_iterator it1 = + tracker.begin(); it1 != tracker.end(); ++it1) + { + typename X::key_type key = test::get_key(*it1); + iterator pos = x.find(key); + typename X::const_iterator const_pos = x_const.find(key); + BOOST_TEST(pos != x.end() && + x.key_eq()(key, test::get_key(*pos))); + BOOST_TEST(const_pos != x_const.end() && + x_const.key_eq()(key, test::get_key(*const_pos))); + + BOOST_TEST(x.count(key) == tracker.count(key)); + + test::compare_pairs(x.equal_range(key), + tracker.equal_range(key), + (typename test::non_const_value_type::type*) 0); + test::compare_pairs(x_const.equal_range(key), + tracker.equal_range(key), + (typename test::non_const_value_type::type*) 0); + } + + test::random_values v2(500); + for(typename test::random_values::const_iterator it2 = + v2.begin(); it2 != v2.end(); ++it2) + { + typename X::key_type key = test::get_key(*it2); + if(tracker.find(test::get_key(key)) == tracker.end()) + { + BOOST_TEST(x.find(key) == x.end()); + BOOST_TEST(x_const.find(key) == x_const.end()); + BOOST_TEST(x.count(key) == 0); + std::pair range = x.equal_range(key); + BOOST_TEST(range.first == range.second); + } + } + } + + { + X x; + + test::random_values v2(5); + for(typename test::random_values::const_iterator it3 = + v2.begin(); it3 != v2.end(); ++it3) + { + typename X::key_type key = test::get_key(*it3); + BOOST_TEST(x.find(key) == x.end()); + BOOST_TEST(x.count(key) == 0); + std::pair range = x.equal_range(key); + BOOST_TEST(range.first == range.second); + } + } +} + +int main() +{ + find_tests1((boost::unordered_set*) 0); + find_tests1((boost::unordered_multiset*) 0); + find_tests1((boost::unordered_map*) 0); + find_tests1((boost::unordered_multimap*) 0); + + find_tests1((boost::unordered_set >*) 0); + find_tests1((boost::unordered_multiset >*) 0); + find_tests1((boost::unordered_map >*) 0); + find_tests1((boost::unordered_multimap >*) 0); + + find_tests1((boost::unordered_set >*) 0); + find_tests1((boost::unordered_multiset >*) 0); + find_tests1((boost::unordered_map >*) 0); + find_tests1((boost::unordered_multimap >*) 0); + + return 0; +} diff --git a/test/unordered/insert_stable_tests.cpp b/test/unordered/insert_stable_tests.cpp new file mode 100644 index 00000000..aad31dd8 --- /dev/null +++ b/test/unordered/insert_stable_tests.cpp @@ -0,0 +1,84 @@ + +// Copyright 2007 Daniel James. +// Distributed under 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) + +#include +#include +#include + +#include + +namespace insert_stable +{ + struct member { + int tag1_; + int tag2_; + + member() : tag1_(0), tag2_(0) {} + member(int t1, int t2) : tag1_(t1), tag2_(t2) {} + + friend bool operator==(member const& x, member const& y) { + return x.tag1_ == y.tag1_; + } + + friend bool operator!=(member const& x, member const& y) { + return x.tag1_ != y.tag1_; + } + }; +} + +#ifdef BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP +namespace boost +#else +namespace insert_stable +#endif +{ + std::size_t hash_value(insert_stable::member const& x) { + return static_cast(x.tag1_); + } +} + +void stable_insert_test1() { + boost::unordered_multiset x; + + x.insert(insert_stable::member(1,1)); + x.insert(insert_stable::member(1,2)); + x.insert(insert_stable::member(1,3)); + + boost::unordered_multiset::const_iterator it = x.begin(), end = x.end(); + BOOST_TEST(it != end); + if(it != end) { BOOST_TEST(it->tag2_ == 1); ++it; } + BOOST_TEST(it != end); + if(it != end) { BOOST_TEST(it->tag2_ == 2); ++it; } + BOOST_TEST(it != end); + if(it != end) { BOOST_TEST(it->tag2_ == 3); ++it; } + BOOST_TEST(it == end); +} + +void stable_insert_test2() { + boost::unordered_multimap x; + typedef boost::unordered_multimap::const_iterator iterator; + + iterator it = x.insert(x.end(), std::make_pair(insert_stable::member(1,1), 1)); + it = x.insert(it, std::make_pair(insert_stable::member(1,2), 2)); + it = x.insert(it, std::make_pair(insert_stable::member(1,3), 3)); + + it = x.begin(); + iterator end = x.end(); + BOOST_TEST(it != end); + if(it != end) { BOOST_TEST(it->first.tag2_ == 1 && it->second == 1); ++it; } + BOOST_TEST(it != end); + if(it != end) { BOOST_TEST(it->first.tag2_ == 2 && it->second == 2); ++it; } + BOOST_TEST(it != end); + if(it != end) { BOOST_TEST(it->first.tag2_ == 3 && it->second == 3); ++it; } + BOOST_TEST(it == end); +} + +int main() +{ + stable_insert_test1(); + stable_insert_test2(); + + return boost::report_errors(); +} diff --git a/test/unordered/insert_tests.cpp b/test/unordered/insert_tests.cpp new file mode 100644 index 00000000..06606d5e --- /dev/null +++ b/test/unordered/insert_tests.cpp @@ -0,0 +1,301 @@ + +// Copyright 2006-2007 Daniel James. +// Distributed under 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) + +#include +#include +#include +#include +#include "../objects/test.hpp" +#include "../helpers/random_values.hpp" +#include "../helpers/tracker.hpp" +#include "../helpers/equivalent.hpp" +#include "../helpers/invariants.hpp" +#include "../helpers/input_iterator.hpp" + +#include + +test::seed_t seed(243432); + +template +void unique_insert_tests1(X* = 0) +{ + typedef typename X::iterator iterator; + typedef test::ordered ordered; + typedef typename test::ordered::iterator ordered_iterator; + + std::cerr<<"insert(value) tests for containers with unique keys.\n"; + + X x; + test::ordered tracker = test::create_ordered(x); + + test::random_values v(1000); + for(typename test::random_values::iterator it = v.begin(); + it != v.end(); ++it) + { + typename X::size_type old_bucket_count = x.bucket_count(); + float b = x.max_load_factor(); + + std::pair r1 = x.insert(*it); + std::pair r2 = tracker.insert(*it); + + BOOST_TEST(r1.second == r2.second); + BOOST_TEST(*r1.first == *r2.first); + + tracker.compare_key(x, *it); + + if(x.size() < b * old_bucket_count) + BOOST_TEST(x.bucket_count() == old_bucket_count); + } + + test::check_equivalent_keys(x); +} + +template +void equivalent_insert_tests1(X* = 0) +{ + std::cerr<<"insert(value) tests for containers with equivalent keys.\n"; + + X x; + test::ordered tracker = test::create_ordered(x); + + test::random_values v(1000); + for(typename test::random_values::iterator it = v.begin(); + it != v.end(); ++it) + { + typename X::size_type old_bucket_count = x.bucket_count(); + float b = x.max_load_factor(); + + typename X::iterator r1 = x.insert(*it); + typename test::ordered::iterator r2 = tracker.insert(*it); + + BOOST_TEST(*r1 == *r2); + + tracker.compare_key(x, *it); + + if(x.size() < b * old_bucket_count) + BOOST_TEST(x.bucket_count() == old_bucket_count); + } + + test::check_equivalent_keys(x); +} + +template +void insert_tests2(X* = 0) +{ + typedef typename test::ordered tracker_type; + typedef typename X::iterator iterator; + typedef typename X::const_iterator const_iterator; + typedef typename tracker_type::iterator tracker_iterator; + + std::cerr<<"insert(begin(), value) tests.\n"; + + { + X x; + tracker_type tracker = test::create_ordered(x); + + test::random_values v(1000); + for(typename test::random_values::iterator it = v.begin(); + it != v.end(); ++it) + { + typename X::size_type old_bucket_count = x.bucket_count(); + float b = x.max_load_factor(); + + iterator r1 = x.insert(x.begin(), *it); + tracker_iterator r2 = tracker.insert(tracker.begin(), *it); + BOOST_TEST(*r1 == *r2); + tracker.compare_key(x, *it); + + if(x.size() < b * old_bucket_count) + BOOST_TEST(x.bucket_count() == old_bucket_count); + } + + test::check_equivalent_keys(x); + } + + std::cerr<<"insert(end(), value) tests.\n"; + + { + X x; + X const& x_const = x; + tracker_type tracker = test::create_ordered(x); + + test::random_values v(100); + for(typename test::random_values::iterator it = v.begin(); + it != v.end(); ++it) + { + typename X::size_type old_bucket_count = x.bucket_count(); + float b = x.max_load_factor(); + + const_iterator r1 = x.insert(x_const.end(), *it); + tracker_iterator r2 = tracker.insert(tracker.end(), *it); + BOOST_TEST(*r1 == *r2); + tracker.compare_key(x, *it); + + if(x.size() < b * old_bucket_count) + BOOST_TEST(x.bucket_count() == old_bucket_count); + } + + test::check_equivalent_keys(x); + } + + std::cerr<<"insert(pos, value) tests.\n"; + + { + X x; + const_iterator pos = x.begin(); + tracker_type tracker = test::create_ordered(x); + + test::random_values v(1000); + for(typename test::random_values::iterator it = v.begin(); + it != v.end(); ++it) + { + typename X::size_type old_bucket_count = x.bucket_count(); + float b = x.max_load_factor(); + + pos = x.insert(pos, *it); + tracker_iterator r2 = tracker.insert(tracker.begin(), *it); + BOOST_TEST(*pos == *r2); + tracker.compare_key(x, *it); + + if(x.size() < b * old_bucket_count) + BOOST_TEST(x.bucket_count() == old_bucket_count); + } + + test::check_equivalent_keys(x); + } + + std::cerr<<"insert single item range tests.\n"; + + { + X x; + tracker_type tracker = test::create_ordered(x); + + test::random_values v(1000); + for(typename test::random_values::iterator it = v.begin(); + it != v.end(); ++it) + { + typename X::size_type old_bucket_count = x.bucket_count(); + float b = x.max_load_factor(); + + x.insert(it, boost::next(it)); + tracker.insert(*it); + tracker.compare_key(x, *it); + + if(x.size() < b * old_bucket_count) + BOOST_TEST(x.bucket_count() == old_bucket_count); + } + + test::check_equivalent_keys(x); + } + + std::cerr<<"insert range tests.\n"; + + { + X x; + + test::random_values v(1000); + x.insert(v.begin(), v.end()); + + test::check_container(x, v); + test::check_equivalent_keys(x); + } + + std::cerr<<"insert input iterator range tests.\n"; + + { + X x; + + test::random_values v(1000); + x.insert(test::input_iterator(v.begin()), test::input_iterator(v.end())); + test::check_container(x, v); + + test::check_equivalent_keys(x); + } +} + +template +void map_tests(X* = 0) +{ + std::cerr<<"map tests.\n"; + + X x; + test::ordered tracker = test::create_ordered(x); + + test::random_values v(1000); + for(typename test::random_values::iterator it = v.begin(); + it != v.end(); ++it) + { + typename X::size_type old_bucket_count = x.bucket_count(); + float b = x.max_load_factor(); + + x[it->first] = it->second; + tracker[it->first] = it->second; + + tracker.compare_key(x, *it); + + if(x.size() < b * old_bucket_count) + BOOST_TEST(x.bucket_count() == old_bucket_count); + } + + test::check_equivalent_keys(x); +} + +template +void associative_insert_range_test(X* = 0) +{ + std::cerr<<"associative_insert_range_test\n"; + + typedef std::list > list; + test::random_values v(1000); + list l; + std::copy(v.begin(), v.end(), std::back_inserter(l)); + + X x; x.insert(l.begin(), l.end()); + + test::check_equivalent_keys(x); +} + +int main() +{ + unique_insert_tests1((boost::unordered_set*) 0); + equivalent_insert_tests1((boost::unordered_multiset*) 0); + unique_insert_tests1((boost::unordered_map*) 0); + equivalent_insert_tests1((boost::unordered_multimap*) 0); + + unique_insert_tests1((boost::unordered_set >*) 0); + equivalent_insert_tests1((boost::unordered_multiset >*) 0); + unique_insert_tests1((boost::unordered_map >*) 0); + equivalent_insert_tests1((boost::unordered_multimap >*) 0); + + unique_insert_tests1((boost::unordered_set >*) 0); + equivalent_insert_tests1((boost::unordered_multiset >*) 0); + unique_insert_tests1((boost::unordered_map >*) 0); + equivalent_insert_tests1((boost::unordered_multimap >*) 0); + + insert_tests2((boost::unordered_set*) 0); + insert_tests2((boost::unordered_multiset*) 0); + insert_tests2((boost::unordered_map*) 0); + insert_tests2((boost::unordered_multimap*) 0); + + insert_tests2((boost::unordered_set >*) 0); + insert_tests2((boost::unordered_multiset >*) 0); + insert_tests2((boost::unordered_map >*) 0); + insert_tests2((boost::unordered_multimap >*) 0); + + insert_tests2((boost::unordered_set >*) 0); + insert_tests2((boost::unordered_multiset >*) 0); + insert_tests2((boost::unordered_map >*) 0); + insert_tests2((boost::unordered_multimap >*) 0); + + map_tests((boost::unordered_map*) 0); + map_tests((boost::unordered_map >*) 0); + map_tests((boost::unordered_map >*) 0); + + associative_insert_range_test((boost::unordered_map >*) 0); + associative_insert_range_test((boost::unordered_multimap >*) 0); + associative_insert_range_test((boost::unordered_multimap >*) 0); + + return boost::report_errors(); +} diff --git a/test/unordered/load_factor_tests.cpp b/test/unordered/load_factor_tests.cpp new file mode 100644 index 00000000..93a078ec --- /dev/null +++ b/test/unordered/load_factor_tests.cpp @@ -0,0 +1,84 @@ + +// Copyright 2006-2007 Daniel James. +// Distributed under 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) + +#include +#include +#include +#include +#include "../helpers/random_values.hpp" + +#if defined(BOOST_MSVC) +#pragma warning(push) +#pragma warning(disable:4127) // conditional expression is constant +#endif + +test::seed_t seed(783656); + +template +void load_factor_tests(X* = 0) +{ + X x; + + BOOST_TEST(x.max_load_factor() == 1.0); + BOOST_TEST(x.load_factor() == 0); + + // A valid implementation could fail these tests, but I think they're + // reasonable. + x.max_load_factor(2.0); BOOST_TEST(x.max_load_factor() == 2.0); + x.max_load_factor(0.5); BOOST_TEST(x.max_load_factor() == 0.5); +} + +template +void insert_test(X*, float mlf) +{ + X x; + x.max_load_factor(mlf); + float b = x.max_load_factor(); + + test::random_values values(1000); + + for(typename test::random_values::const_iterator + it = values.begin(), end = values.end(); it != end; ++it) + { + typename X::size_type old_size = x.size(), + old_bucket_count = x.bucket_count(); + x.insert(*it); + if(old_size + 1 < b * old_bucket_count) + BOOST_TEST(x.bucket_count() == old_bucket_count); + } +} + +template +void load_factor_insert_tests(X* ptr = 0) +{ + insert_test(ptr, 1.0f); + insert_test(ptr, 0.1f); + insert_test(ptr, 100.0f); + + insert_test(ptr, (std::numeric_limits::min)()); + + if(std::numeric_limits::has_infinity) + insert_test(ptr, std::numeric_limits::infinity()); +} + +int main() +{ + load_factor_tests((boost::unordered_set*) 0); + load_factor_tests((boost::unordered_multiset*) 0); + load_factor_tests((boost::unordered_map*) 0); + load_factor_tests((boost::unordered_multimap*) 0); + + load_factor_insert_tests((boost::unordered_set*) 0); + load_factor_insert_tests((boost::unordered_multiset*) 0); + load_factor_insert_tests((boost::unordered_map*) 0); + load_factor_insert_tests((boost::unordered_multimap*) 0); + + return boost::report_errors(); +} + +#if defined(BOOST_MSVC) +#pragma warning(pop) +#pragma warning(disable:4127) // conditional expression is constant +#endif diff --git a/test/unordered/rehash_tests.cpp b/test/unordered/rehash_tests.cpp new file mode 100644 index 00000000..b364ffa2 --- /dev/null +++ b/test/unordered/rehash_tests.cpp @@ -0,0 +1,69 @@ + +// Copyright 2006-2007 Daniel James. +// Distributed under 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) + +#include +#include +#include +#include "../helpers/random_values.hpp" +#include "../helpers/tracker.hpp" + +test::seed_t seed(2974); + +template +bool postcondition(X const& x, typename X::size_type n) +{ + return x.bucket_count() > x.size() / x.max_load_factor() && x.bucket_count() >= n; +} + +template +void rehash_empty_test1(X* = 0) +{ + X x; + + x.rehash(10000); + BOOST_TEST(postcondition(x, 10000)); + + x.rehash(0); + BOOST_TEST(postcondition(x, 0)); +} + +template +void rehash_test1(X* = 0) +{ + test::random_values v(1000); + test::ordered tracker; + tracker.insert_range(v.begin(), v.end()); + X x(v.begin(), v.end()); + + x.rehash(0); BOOST_TEST(postcondition(x, 0)); + tracker.compare(x); + + x.max_load_factor(0.25); + x.rehash(0); BOOST_TEST(postcondition(x, 0)); + tracker.compare(x); + + x.max_load_factor(50.0); + x.rehash(0); BOOST_TEST(postcondition(x, 0)); + tracker.compare(x); + + x.rehash(1000); BOOST_TEST(postcondition(x, 1000)); + tracker.compare(x); +} + +template +void rehash_tests(X* ptr = 0) +{ + rehash_empty_test1(ptr); + rehash_test1(ptr); +} + +int main() { + rehash_tests((boost::unordered_set*) 0); + rehash_tests((boost::unordered_multiset*) 0); + rehash_tests((boost::unordered_map*) 0); + rehash_tests((boost::unordered_multimap*) 0); + + return boost::report_errors(); +} diff --git a/test/unordered/swap_tests.cpp b/test/unordered/swap_tests.cpp new file mode 100644 index 00000000..d62c6e54 --- /dev/null +++ b/test/unordered/swap_tests.cpp @@ -0,0 +1,138 @@ + +// Copyright 2006-2007 Daniel James. +// Distributed under 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) + +#include +#include +#include +#include +#include +#include +#include "../objects/test.hpp" +#include "../helpers/random_values.hpp" +#include "../helpers/tracker.hpp" +#include "../helpers/invariants.hpp" + +test::seed_t seed(783472); + +template +void swap_test_impl(X& x1, X& x2) +{ + test::ordered tracker1 = test::create_ordered(x1); + test::ordered tracker2 = test::create_ordered(x2); + tracker1.insert_range(x1.begin(), x1.end()); + tracker2.insert_range(x2.begin(), x2.end()); + x1.swap(x2); + tracker1.compare(x2); + tracker2.compare(x1); +} + +template +void swap_tests1(X* = 0) +{ + { + X x; + swap_test_impl(x, x); + } + + { + X x,y; + swap_test_impl(x, y); + } + + { + test::random_values v(1000); + X x, y(v.begin(), v.end()); + swap_test_impl(x, y); + swap_test_impl(x, y); + } + + { + test::random_values vx(1000), vy(1000); + X x(vx.begin(), vx.end()), y(vy.begin(), vy.end()); + swap_test_impl(x, y); + swap_test_impl(x, y); + } +} + +template +void swap_tests2(X* ptr = 0) +{ + swap_tests1(ptr); + + typedef typename X::hasher hasher; + typedef typename X::key_equal key_equal; + typedef typename X::allocator_type allocator_type; + + { + X x(0, hasher(1), key_equal(1)); + X y(0, hasher(2), key_equal(2)); + swap_test_impl(x, y); + } + + { + test::random_values v(1000); + X x(v.begin(), v.end(), 0, hasher(1), key_equal(1)); + X y(0, hasher(2), key_equal(2)); + swap_test_impl(x, y); + } + + { + test::random_values vx(100), vy(50); + X x(vx.begin(), vx.end(), 0, hasher(1), key_equal(1)); + X y(vy.begin(), vy.end(), 0, hasher(2), key_equal(2)); + swap_test_impl(x, y); + swap_test_impl(x, y); + } + +#if BOOST_UNORDERED_SWAP_METHOD == 1 + { + test::random_values vx(100), vy(50); + X x(vx.begin(), vx.end(), 0, hasher(), key_equal(), allocator_type(1)); + X y(vy.begin(), vy.end(), 0, hasher(), key_equal(), allocator_type(2)); + try { + swap_test_impl(x, y); + BOOST_ERROR("Using swap method 1, swapping with unequal allocators didn't throw."); + } catch (std::runtime_error) {} + } +#else + { + test::random_values vx(50), vy(100); + X x(vx.begin(), vx.end(), 0, hasher(), key_equal(), allocator_type(1)); + X y(vy.begin(), vy.end(), 0, hasher(), key_equal(), allocator_type(2)); + swap_test_impl(x, y); + } + + { + test::random_values vx(100), vy(100); + X x(vx.begin(), vx.end(), 0, hasher(1), key_equal(1), allocator_type(1)); + X y(vy.begin(), vy.end(), 0, hasher(2), key_equal(2), allocator_type(2)); + swap_test_impl(x, y); + swap_test_impl(x, y); + } +#endif +} + +int main() +{ + std::cerr<<"Erase unordered_set.\n"; + swap_tests1((boost::unordered_set*) 0); + std::cerr<<"\nErase unordered_multiset.\n"; + swap_tests1((boost::unordered_multiset*) 0); + std::cerr<<"\nErase unordered_map.\n"; + swap_tests1((boost::unordered_map*) 0); + std::cerr<<"\nErase unordered_multimap.\n"; + swap_tests1((boost::unordered_multimap*) 0); + + std::cerr<<"\nErase unordered_set.\n"; + swap_tests2((boost::unordered_set >*) 0); + std::cerr<<"\nErase unordered_multiset.\n"; + swap_tests2((boost::unordered_multiset >*) 0); + std::cerr<<"\nErase unordered_map.\n"; + swap_tests2((boost::unordered_map >*) 0); + std::cerr<<"\nErase unordered_multimap.\n"; + swap_tests2((boost::unordered_multimap >*) 0); + + return boost::report_errors(); +} diff --git a/test/unordered/unnecessary_copy_tests.cpp b/test/unordered/unnecessary_copy_tests.cpp new file mode 100644 index 00000000..0968d8d4 --- /dev/null +++ b/test/unordered/unnecessary_copy_tests.cpp @@ -0,0 +1,57 @@ + +// Copyright 2006-2007 Daniel James. +// Distributed under 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) + +#include +#include +#include + +struct count_copies +{ + static int count; + count_copies() { ++count; } + count_copies(count_copies const&) { ++count; } +private: + count_copies& operator=(count_copies const&); +}; + +#if defined(BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP) +namespace boost { +#endif + +std::size_t hash_value(count_copies const&) { + return 0; +} + +#if defined(BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP) +} +#endif + +bool operator==(count_copies const&, count_copies const&) { + return true; +} + +int count_copies::count; + +template +void unnecessary_copy_test(T*) +{ + count_copies::count = 0; + T x; + typename T::value_type a; + BOOST_TEST(count_copies::count == 1); + x.insert(a); + BOOST_TEST(count_copies::count == 2); +} + + +int main() +{ + unnecessary_copy_test((boost::unordered_set*) 0); + unnecessary_copy_test((boost::unordered_multiset*) 0); + unnecessary_copy_test((boost::unordered_map*) 0); + unnecessary_copy_test((boost::unordered_multimap*) 0); + + return boost::report_errors(); +}