// bslalg_hashtableanchor.h -*-C++-*- #ifndef INCLUDED_BSLALG_HASHTABLEANCHOR #define INCLUDED_BSLALG_HASHTABLEANCHOR #include <bsls_ident.h> BSLS_IDENT("$Id: $") //@PURPOSE: Provide a type holding the constituent parts of a hash table. // //@CLASSES: // bslalg::HashTableAnchor: (in-core) bucket-array and node list // //@SEE_ALSO: bslstl_hashtable, bslalg_hashtableimputil // //@DESCRIPTION: This component provides a single, complex-constrained // *in*-*core* (value-semantic) attribute class, 'bslalg::HashTableAnchor', // that is used to hold (not own) the array of buckets and the list of nodes // that form the key data elements of a hash-table. This class is typically // used with the utilities provided in 'bslstl_hashtableimputil'. Note that // the decision to store nodes in a linked list (i.e., resolving collisions // through chaining) is intended to facilitate a hash-table implementation // meeting the requirements of a C++11 standard unordered container. // ///Attributes ///---------- //.. // Name Type Simple Constraints // ------------------ ------------------- ------------------ // bucketArrayAddress HashTableBucket * none // // bucketArraySize size_t none // // listRootAddress BidirectionalLink * none // // // Complex Constraint // ------------------------------------------------------------------------- // 'bucketArrayAddress' must refer to a contiguous sequence of valid // 'bslalg::HashTableBucket' objects of at least the specified // 'bucketArraySize' or both 'bucketArrayAddress' and 'bucketArraySize' must // be 0. //.. // //: o 'listRootAddress': address of the head of the linked list of nodes //: holding the elements contained in a hash table //: //: o 'bucketArrayAddress': address of the first element of the sequence of //: 'HashTableBucket' objects, each of which refers to the first and last //: node (from 'listRootAddress') in that bucket //: //: o 'bucketArraySize': the number of (contiguous) buckets in the array of //: buckets at 'bucketArrayAddress' // ///Usage ///----- // This section illustrates intended use of this component. // // Suppose we want to create a hash table that keeps track of pointers. // Pointers can be added ('insert'ed) or removed ('erase'd) from the table, and // the table will keep track, at any time, of whether a pointer is currently // stored in the table using the 'count' method. It will also be able to // return the total number of objects stored in the table (the 'size' method). // Redundant 'insert's have no effect -- a given pointer may only be stored in // the table once. // // First, we create our class: //.. // class PtrHashSet : public bslalg::HashTableAnchor { // // PRIVATE TYPES // typedef bsls::Types::UintPtr UintPtr; // typedef bslalg::BidirectionalNode<void *> Node; // typedef bslalg::HashTableBucket Bucket; // typedef bslalg::BidirectionalLinkListUtil Util; // // // DATA // double d_maxLoadFactor; // unsigned d_numNodes; // bslma::Allocator *d_allocator_p; // // // PRIVATE MANIPULATORS // void grow(); // // Roughly double the number of buckets, such that the number of // // buckets shall always be '2^N - 1'. // // // PRIVATE ACCESSORS // bool checkInvariants() const; // // Perform sanity checks on this table, returning 'true' if all the // // tests pass and 'false' otherwise. Note that many of the checks // // are done with the 'ASSERTV' macro and will cause messages to be // // written to the console. // // bool find(Node **node, Bucket **bucket, const void *ptr) const; // // If the specified value 'ptr' is stored in this table, return // // pointers to its node and bucket in the specified 'node' and // // 'bucket'. If it is not in this table, return the bucket it // // should be in, and a pointer to the first node, if any, in that // // bucket. If the bucket is empty, return with // // '*node == listRootAddress()'. Return 'true' if 'ptr' was found // // in the table and 'false' otherwise. Note that it is permissible // // to pass 0 to 'node' and / or 'bucket', in which case these // // arguments are ignored. // // private: // // NOT IMPLEMENTED // PtrHashSet(const PtrHashSet&, bslma::Allocator *); // PtrHashSet& operator=(const PtrHashSet&); // // public: // // CREATORS // explicit // PtrHashSet(bslma::Allocator *allocator = 0); // // Create a 'PtrHashSet', using the specified 'allocator'. If no // // allocator is specified, use the default allocator. // // ~PtrHashSet(); // // Destroy this 'PtrHashSet', freeing all its memory. // // // MANIPULATORS // bool insert(void *ptr); // // If the specfied 'ptr' is not in this hash table, add it, // // returning 'true'. If it is already in the table, return 'false' // // with no action taken. // // bool erase(void *ptr); // // If the specfied 'ptr' is in this hash table, remove it, // // returning 'true'. If it is not found in the table, return // // 'false' with no action taken. // // // ACCESSORS // std::size_t count(void *ptr) const; // // Return 1 if the specified value 'ptr' is in this table and 0 // // otherwise. // // std::size_t size() const; // // Return the number of discrete values that are stored in this // // table. // }; // // // PRIVATE MANIPULATORS // void PtrHashSet::grow() // { // // 'bucketArraySize' will always be '2^N - 1', so that when pointers // // are aligned by some 2^N they're likely to be relatively prime. // // std::size_t newBucketArraySize = bucketArraySize() * 2 + 1; // std::size_t newBucketArraySizeInBytes = // newBucketArraySize * sizeof(Bucket); // memset(bucketArrayAddress(), 0x5a, size() * sizeof(Bucket)); // d_allocator_p->deallocate(bucketArrayAddress()); // setBucketArrayAddressAndSize( // (Bucket *) d_allocator_p->allocate(newBucketArraySizeInBytes), // newBucketArraySize); // memset(bucketArrayAddress(), 0, newBucketArraySizeInBytes); // Node *newListRootAddress = 0; // // unsigned numNodes = 0; // for (Node *node = (Node *) listRootAddress(); node; ++numNodes) { // Node *rippedOut = node; // node = (Node *) node->nextLink(); // // std::size_t index = // (UintPtr) rippedOut->value() % bucketArraySize(); // Bucket& bucket = bucketArrayAddress()[index]; // if (bucket.first()) { // if (0 == bucket.first()->previousLink()) { // newListRootAddress = rippedOut; // } // Util::insertLinkBeforeTarget(rippedOut, bucket.first()); // bucket.setFirst(rippedOut); // } // else { // Util::insertLinkBeforeTarget(rippedOut, // newListRootAddress); // newListRootAddress = rippedOut; // bucket.setFirstAndLast(rippedOut, rippedOut); // } // } // assert(size() == numNodes); // // setListRootAddress(newListRootAddress); // // checkInvariants(); // } // // // PRIVATE ACCESSORS // bool PtrHashSet::checkInvariants() const // { // bool ok; // // unsigned numNodes = 0; // Node *prev = 0; // for (Node *node = (Node *) listRootAddress(); node; // prev = node, node = (Node *) node->nextLink()) { // ok = node->previousLink() == prev; // assert(ok && "node->previousLink() == prev"); // if (!ok) return false; // RETURN // ++numNodes; // } // ok = size() == numNodes; // assert(ok && "size() == numNodes"); // if (!ok) return false; // RETURN // // numNodes = 0; // for (unsigned i = 0; i < bucketArraySize(); ++i) { // Bucket& bucket = bucketArrayAddress()[i]; // if (bucket.first()) { // ++numNodes; // for (Node *node = (Node *) bucket.first(); // bucket.last() != node; // node = (Node *) node->nextLink()) { // ++numNodes; // } // } // else { // ok = !bucket.last(); // assert(ok && "!bucket.last()"); // if (!ok) return false; // RETURN // } // } // ok = size() == numNodes; // assert(ok && "size() == numNodes"); // // return ok; // } // // bool PtrHashSet::find(Node **node, Bucket **bucket, const void *ptr) const // { // Node *dummyNodePtr; // Bucket *dummyBucketPtr; // if (!node ) node = &dummyNodePtr; // if (!bucket) bucket = &dummyBucketPtr; // // Node *& nodePtrRef = *node; // unsigned index = (UintPtr) ptr % bucketArraySize(); // Bucket& bucketRef = bucketArrayAddress()[index]; // *bucket = &bucketRef; // if (bucketRef.first()) { // Node *begin = (Node *) bucketRef.first(); // Node * const end = (Node *) bucketRef.last()->nextLink(); // for (Node *n = begin; end != n; n = (Node *) n->nextLink()) { // if (n->value() == ptr) { // // found // // nodePtrRef = n; // return true; // RETURN // } // } // // not found // // nodePtrRef = begin; // return false; // RETURN // } // // empty bucket // // nodePtrRef = (Node *) listRootAddress(); // return false; // } // // // CREATORS // PtrHashSet::PtrHashSet(bslma::Allocator *allocator) // : HashTableAnchor(0, 0, 0) // , d_maxLoadFactor(0.4) // , d_numNodes(0) // { // enum { NUM_BUCKETS = 3 }; // // d_allocator_p = bslma::Default::allocator(allocator); // std::size_t bucketArraySizeInBytes = NUM_BUCKETS * sizeof(Bucket); // // setBucketArrayAddressAndSize( // (Bucket *) d_allocator_p->allocate(bucketArraySizeInBytes), // NUM_BUCKETS); // memset(bucketArrayAddress(), 0, bucketArraySizeInBytes); // } // // PtrHashSet::~PtrHashSet() // { // BSLS_ASSERT_SAFE(checkInvariants()); // // for (Node *node = (Node *) listRootAddress(); node; ) { // Node *toDelete = node; // node = (Node *) node->nextLink(); // // memset(toDelete, 0x5a, sizeof(*toDelete)); // d_allocator_p->deallocate(toDelete); // } // // d_allocator_p->deallocate(bucketArrayAddress()); // } // // // MANIPULATORS // bool PtrHashSet::erase(void *ptr) // { // Bucket *bucket; // Node *node; // // if (!find(&node, &bucket, ptr)) { // return false; // RETURN // } // // if (bucket->first() == node) { // if (bucket->last() == node) { // bucket->reset(); // } // else { // bucket->setFirst(node->nextLink()); // } // } // else if (bucket->last() == node) { // bucket->setLast(node->previousLink()); // } // // --d_numNodes; // Util::unlink(node); // // d_allocator_p->deallocate(node); // // checkInvariants(); // // return true; // } // // bool PtrHashSet::insert(void *ptr) // { // Bucket *bucket; // Node *insertionPoint; // // if (find(&insertionPoint, &bucket, ptr)) { // // Already in set, do nothing. // // return false; // RETURN // } // // if (bucketArraySize() * d_maxLoadFactor < d_numNodes + 1) { // grow(); // bool found = find(&insertionPoint, &bucket, ptr); // BSLS_ASSERT_SAFE(!found); // } // // ++d_numNodes; // Node *node = (Node *) d_allocator_p->allocate(sizeof(Node)); // // Util::insertLinkBeforeTarget(node, insertionPoint); // node->value() = ptr; // if (listRootAddress() == insertionPoint) { // BSLS_ASSERT_SAFE(0 == node->previousLink()); // setListRootAddress(node); // } // // if (bucket->first()) { // BSLS_ASSERT_SAFE(bucket->first() == insertionPoint); // // bucket->setFirst(node); // } // else { // BSLS_ASSERT_SAFE(!bucket->last()); // // bucket->setFirstAndLast(node, node); // } // // assert(count(ptr)); // // checkInvariants(); // // return true; // } // // // ACCESSORS // std::size_t PtrHashSet::count(void *ptr) const // { // return find(0, 0, ptr); // } // // std::size_t PtrHashSet::size() const // { // return d_numNodes; // } //.. // Then, in 'main', we create a test allocator for use in this example to // ensure that no memory is leaked: //.. // bslma::TestAllocator ta("test", veryVeryVeryVerbose); //.. // Next, we declare our table using that allocator: //.. // PtrHashSet phs(&ta); //.. // Then, we create an area of memory from which our pointers will come: //.. // enum { SEGMENT_LENGTH = 1000 }; // char *pc = (char *) ta.allocate(SEGMENT_LENGTH); //.. // Next, we iterate through the length of the segment, insert those pointers // for which 'ptr - pc == N * 7' is true. We keep a count of the number of // items we insert into the table in the variable 'sevens': //.. // unsigned sevens = 0; // for (int i = 0; i < SEGMENT_LENGTH; i += 7) { // ++sevens; // bool status = phs.insert(&pc[i]); // assert(status); // } //.. // Then, we verify the number of objects we've placed in the table: //.. // assert(phs.size() == sevens); //.. // Next, we iterate through ALL pointers in the 'pc' array, using the 'count' // method to verify that the ones we expect are in the table: //.. // for (int i = 0; i < SEGMENT_LENGTH; ++i) { // assert(phs.count(&pc[i]) == (0 == i % 7)); // } //.. // Then, we iterate, deleting all elements from the table for which // 'ptr - pc == 3 * N' is true. We keep a count of the number of elements that // were deleted from the table in the variable 'killed': //.. // unsigned killed = 0; // for (int i = 0; i < SEGMENT_LENGTH; i += 3) { // const bool deleted = phs.erase(&pc[i]); // assert(deleted == (0 == i % 7)); // killed += deleted; // } //.. // Next, we verify the number of remaining elements in the table: //.. // assert(killed < sevens); // assert(phs.size() == sevens - killed); //.. // Then, in verbose mode we print our tallies: //.. // if (verbose) { // printf("sevens = %u, killed = %u, phs.size() = %u\n", sevens, // killed, (unsigned) phs.size()); // } //.. // Now, we iterate through every element of the 'pc' array, verifying that the // surviving elements are exactly those for which 'ptr - pc' was divisible by 7 // and not by 3: //.. // for (int i = 0; i < SEGMENT_LENGTH; ++i) { // const bool present = phs.count(&pc[i]); // assert(present == ((0 == i % 7) && (0 != i % 3))); // } //.. // Finally, we clean up our 'pc' array: //.. // ta.deallocate(pc); //.. #include <bslscm_version.h> #include <bslalg_bidirectionallink.h> #include <bslalg_scalarprimitives.h> #include <bslmf_istriviallycopyable.h> #include <bslmf_nestedtraitdeclaration.h> #include <bsls_assert.h> #include <cstddef> #ifndef BDE_DONT_ALLOW_TRANSITIVE_INCLUDES #include <bsls_nativestd.h> #endif // BDE_DONT_ALLOW_TRANSITIVE_INCLUDES namespace BloombergLP { namespace bslalg { struct HashTableBucket; // This is known to be a POD struct. // ============================= // class bslalg::HashTableAnchor // ============================= class HashTableAnchor { // This complex constrained *in*-*core* (value-semantic) attribute class // characterizes the key data elements of a hash table. See the // "Attributes" section under @DESCRIPTION in the component-level // documentation for/ information on the class attributes. Note that the // class invariant is the identically the complex constraint of this // component. // // This class: //: o supports a complete set of *value-semantic* operations //: o except for 'bdex' serialization and default construction //: o is *in-core* //: o is *exception-neutral* (agnostic) //: o is *alias-safe* //: o is 'const' *thread-safe* // For terminology see 'bsldoc_glossary'. // DATA HashTableBucket *d_bucketArrayAddress_p; // address of the array of // buckets (held, not owned) std::size_t d_bucketArraySize; // size of 'd_bucketArray' BidirectionalLink *d_listRootAddress_p; // head of the list of // elements in the hash-table // (held, not owned) public: // TRAITS BSLMF_NESTED_TRAIT_DECLARATION(HashTableAnchor, bsl::is_trivially_copyable); // CREATORS HashTableAnchor(HashTableBucket *bucketArrayAddress, std::size_t bucketArraySize, BidirectionalLink *listRootAddress); // Create a 'bslalg::HashTableAnchor' object having the specified // 'bucketArrayAddress', 'bucketArraySize', and 'listRootAddress' // attributes. The behavior is undefined unless 'bucketArrayAddress' // refers to a contiguous sequence of valid 'bslalg::HashTableBucket' // objects of at least 'bucketArraySize' or unless both // 'bucketArrayAddress' and 'bucketArraySize' are 0. HashTableAnchor(const HashTableAnchor& original); // Create a 'bslalg::HashTableAnchor' object having the same value // as the specified 'original' object. // ~bslalg::HashTableAnchor(); = default // Destroy this object. // MANIPULATORS bslalg::HashTableAnchor& operator=(const bslalg::HashTableAnchor& rhs); // Assign to this object the value of the specified 'rhs' object, and // return a reference providing modifiable access to this object. void setBucketArrayAddressAndSize(HashTableBucket *bucketArrayAddress, std::size_t bucketArraySize); // Set the bucket array address and bucket array size attributes of // this object to the specified 'bucketArrayAddress' and // 'bucketArraySize' values. The behavior is undefined unless // 'bucketArrayAddress' refers to a contiguous sequence of valid // 'bslalg::HashTableBucket' objects of at least 'bucketArraySize', or // unless both 'bucketArrayAddress' and 'bucketArraySize' are 0. void setListRootAddress(BidirectionalLink *value); // Set the 'listRootAddress' attribute of this object to the // specified 'value'. // Aspects void swap(HashTableAnchor& other); // Efficiently exchange the value of this object with the value of the // specified 'other' object. This method provides the no-throw // exception-safety guarantee. // ACCESSORS HashTableBucket *bucketArrayAddress() const; // Return the value of the 'bucketArrayAddress' attribute of this // object. std::size_t bucketArraySize() const; // Return the value of the 'bucketArraySize' attribute of this object. BidirectionalLink *listRootAddress() const; // Return the value 'listRootAddress' attribute of this object. }; // FREE OPERATORS bool operator==(const HashTableAnchor& lhs, const HashTableAnchor& rhs); // Return 'true' if the specified 'lhs' and 'rhs' objects have the same // value, and 'false' otherwise. Two 'bslalg::HashTableAnchor' objects // have the same value if all of the corresponding values of their // 'bucketArrayAddress', 'bucketArraySize', and 'listRootAddress' // attributes are the same. bool operator!=(const HashTableAnchor& lhs, const HashTableAnchor& rhs); // Return 'true' if the specified 'lhs' and 'rhs' objects do not have the // same value, and 'false' otherwise. Two 'bslalg::HashTableAnchor' // objects do not have the same value if any of the corresponding values of // their 'bucketArrayAddress', 'bucketArraySize', or 'listRootAddress' // attributes are not the same. // FREE FUNCTIONS void swap(HashTableAnchor& a, HashTableAnchor& b); // Efficiently exchange the values of the specified 'a' and 'b' objects. // This function provides the no-throw exception-safety guarantee. The // behavior is undefined unless the two objects were created with the same // allocator. // ============================================================================ // INLINE FUNCTION DEFINITIONS // ============================================================================ // ----------------------------- // class bslalg::HashTableAnchor // ----------------------------- // CREATORS inline HashTableAnchor::HashTableAnchor(bslalg::HashTableBucket *bucketArrayAddress, std::size_t bucketArraySize, bslalg::BidirectionalLink *listRootAddress) : d_bucketArrayAddress_p(bucketArrayAddress) , d_bucketArraySize(bucketArraySize) , d_listRootAddress_p(listRootAddress) { BSLS_ASSERT_SAFE( (!bucketArrayAddress && !bucketArraySize) || (bucketArrayAddress && 0 < bucketArraySize)); BSLS_ASSERT_SAFE(!listRootAddress || !(listRootAddress->previousLink())); } inline HashTableAnchor::HashTableAnchor(const HashTableAnchor& original) : d_bucketArrayAddress_p(original.d_bucketArrayAddress_p) , d_bucketArraySize(original.d_bucketArraySize) , d_listRootAddress_p(original.d_listRootAddress_p) { } // MANIPULATORS inline HashTableAnchor& HashTableAnchor::operator=(const HashTableAnchor& rhs) { d_bucketArrayAddress_p = rhs.d_bucketArrayAddress_p; d_bucketArraySize = rhs.d_bucketArraySize; d_listRootAddress_p = rhs.d_listRootAddress_p; return *this; } inline void HashTableAnchor::setBucketArrayAddressAndSize( HashTableBucket *bucketArrayAddress, std::size_t bucketArraySize) { BSLS_ASSERT_SAFE(( bucketArrayAddress && 0 < bucketArraySize) || (!bucketArrayAddress && !bucketArraySize)); d_bucketArrayAddress_p = bucketArrayAddress; d_bucketArraySize = bucketArraySize; } inline void HashTableAnchor::setListRootAddress(BidirectionalLink *value) { BSLS_ASSERT_SAFE(!value || !value->previousLink()); d_listRootAddress_p = value; } // Aspects inline void HashTableAnchor::swap(HashTableAnchor& other) { bslalg::ScalarPrimitives::swap(*this, other); } // ACCESSORS inline BidirectionalLink *HashTableAnchor::listRootAddress() const { return d_listRootAddress_p; } inline std::size_t HashTableAnchor::bucketArraySize() const { return d_bucketArraySize; } inline HashTableBucket *HashTableAnchor::bucketArrayAddress() const { return d_bucketArrayAddress_p; } } // close package namespace // FREE OPERATORS inline void bslalg::swap(bslalg::HashTableAnchor& a, bslalg::HashTableAnchor& b) { a.swap(b); } inline bool bslalg::operator==(const bslalg::HashTableAnchor& lhs, const bslalg::HashTableAnchor& rhs) { return lhs.bucketArrayAddress() == rhs.bucketArrayAddress() && lhs.bucketArraySize() == rhs.bucketArraySize() && lhs.listRootAddress() == rhs.listRootAddress(); } inline bool bslalg::operator!=(const bslalg::HashTableAnchor& lhs, const bslalg::HashTableAnchor& rhs) { return lhs.bucketArrayAddress() != rhs.bucketArrayAddress() || lhs.bucketArraySize() != rhs.bucketArraySize() || lhs.listRootAddress() != rhs.listRootAddress(); } } // close enterprise namespace #endif // ---------------------------------------------------------------------------- // Copyright 2013 Bloomberg Finance L.P. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ----------------------------- END-OF-FILE ----------------------------------