📄 densehashtable.h
字号:
// if object is not found; 2nd is ILLEGAL_BUCKET if it is. // Note: because of deletions where-to-insert is not trivial: it's the // first deleted bucket we see, as long as we don't find the key later pair<size_type, size_type> find_position(const key_type &key) const { size_type num_probes = 0; // how many times we've probed const size_type bucket_count_minus_one = bucket_count() - 1; size_type bucknum = hash(key) & bucket_count_minus_one; size_type insert_pos = ILLEGAL_BUCKET; // where we would insert while ( 1 ) { // probe until something happens if ( test_empty(bucknum) ) { // bucket is empty if ( insert_pos == ILLEGAL_BUCKET ) // found no prior place to insert return pair<size_type,size_type>(ILLEGAL_BUCKET, bucknum); else return pair<size_type,size_type>(ILLEGAL_BUCKET, insert_pos); } else if ( test_deleted(bucknum) ) {// keep searching, but mark to insert if ( insert_pos == ILLEGAL_BUCKET ) insert_pos = bucknum; } else if ( equals(key, get_key(table[bucknum])) ) { return pair<size_type,size_type>(bucknum, ILLEGAL_BUCKET); } ++num_probes; // we're doing another probe bucknum = (bucknum + JUMP_(key, num_probes)) & bucket_count_minus_one; assert(num_probes < bucket_count()); // don't probe too many times! } } public: iterator find(const key_type& key) { if ( size() == 0 ) return end(); pair<size_type, size_type> pos = find_position(key); if ( pos.first == ILLEGAL_BUCKET ) // alas, not there return end(); else return iterator(this, table + pos.first, table + num_buckets, false); } const_iterator find(const key_type& key) const { if ( size() == 0 ) return end(); pair<size_type, size_type> pos = find_position(key); if ( pos.first == ILLEGAL_BUCKET ) // alas, not there return end(); else return const_iterator(this, table + pos.first, table+num_buckets, false); } // Counts how many elements have key key. For maps, it's either 0 or 1. size_type count(const key_type &key) const { pair<size_type, size_type> pos = find_position(key); return pos.first == ILLEGAL_BUCKET ? 0 : 1; } // Likewise, equal_range doesn't really make sense for us. Oh well. pair<iterator,iterator> equal_range(const key_type& key) { const iterator pos = find(key); // either an iterator or end return pair<iterator,iterator>(pos, pos); } pair<const_iterator,const_iterator> equal_range(const key_type& key) const { const const_iterator pos = find(key); // either an iterator or end return pair<iterator,iterator>(pos, pos); } // INSERTION ROUTINES private: // If you know *this is big enough to hold obj, use this routine pair<iterator, bool> insert_noresize(const value_type& obj) { const pair<size_type,size_type> pos = find_position(get_key(obj)); if ( pos.first != ILLEGAL_BUCKET) { // object was already there return pair<iterator,bool>(iterator(this, table + pos.first, table + num_buckets, false), false); // false: we didn't insert } else { // pos.second says where to put it if ( test_deleted(pos.second) ) { // just replace if it's been del. const_iterator delpos(this, table + pos.second, // shrug: table + num_buckets, false);// shouldn't need const clear_deleted(delpos); assert( num_deleted > 0); --num_deleted; // used to be, now it isn't } else { ++num_elements; // replacing an empty bucket } set_value(&table[pos.second], obj); return pair<iterator,bool>(iterator(this, table + pos.second, table + num_buckets, false), true); // true: we did insert } } public: // This is the normal insert routine, used by the outside world pair<iterator, bool> insert(const value_type& obj) { resize_delta(1); // adding an object, grow if need be return insert_noresize(obj); } // When inserting a lot at a time, we specialize on the type of iterator template <class InputIterator> void insert(InputIterator f, InputIterator l) { // specializes on iterator type insert(f, l, typename STL_NAMESPACE::iterator_traits<InputIterator>::iterator_category()); } // Iterator supports operator-, resize before inserting template <class ForwardIterator> void insert(ForwardIterator f, ForwardIterator l, STL_NAMESPACE::forward_iterator_tag) { size_type n = STL_NAMESPACE::distance(f, l); // TODO(csilvers): standard? resize_delta(n); for ( ; n > 0; --n, ++f) insert_noresize(*f); } // Arbitrary iterator, can't tell how much to resize template <class InputIterator> void insert(InputIterator f, InputIterator l, STL_NAMESPACE::input_iterator_tag) { for ( ; f != l; ++f) insert(*f); } // DELETION ROUTINES size_type erase(const key_type& key) { const_iterator pos = find(key); // shrug: shouldn't need to be const if ( pos != end() ) { assert(!test_deleted(pos)); // or find() shouldn't have returned it set_deleted(pos); ++num_deleted; consider_shrink = true; // will think about shrink after next insert return 1; // because we deleted one thing } else { return 0; // because we deleted nothing } } // This is really evil: really it should be iterator, not const_iterator. // But...the only reason keys are const is to allow lookup. // Since that's a moot issue for deleted keys, we allow const_iterators void erase(const_iterator pos) { if ( pos == end() ) return; // sanity check if ( set_deleted(pos) ) { // true if object has been newly deleted ++num_deleted; consider_shrink = true; // will think about shrink after next insert } } void erase(const_iterator f, const_iterator l) { for ( ; f != l; ++f) { if ( set_deleted(f) ) // should always be true ++num_deleted; } consider_shrink = true; // will think about shrink after next insert } // COMPARISON bool operator==(const dense_hashtable& ht) const { // We really want to check that the hash functions are the same // but alas there's no way to do this. We just hope. return ( num_deleted == ht.num_deleted && table == ht.table ); } bool operator!=(const dense_hashtable& ht) const { return !(*this == ht); } // I/O // We support reading and writing hashtables to disk. Alas, since // I don't know how to write a hasher or key_equal, you have to make // sure everything but the table is the same. We compact before writing // // NOTE: These functions are currently TODO. They've not been implemented. bool write_metadata(FILE *fp) { squash_deleted(); // so we don't have to worry about delval return false; // TODO } bool read_metadata(FILE *fp) { num_deleted = 0; // since we got rid before writing assert(use_empty); // have to set this before calling us if (table) free(table); // we'll make our own // TODO: read magic number // TODO: read num_buckets reset_thresholds(); table = (value_type *) malloc(num_buckets * sizeof(*table)); assert(table); set_empty(0, num_buckets); // TODO: read num_elements for ( size_type i = 0; i < num_elements; ++i ) { // TODO: read bucket_num // TODO: set with non-empty, non-deleted value } return false; // TODO } // If your keys and values are simple enough, we can write them // to disk for you. "simple enough" means no pointers. // However, we don't try to normalize endianness bool write_nopointer_data(FILE *fp) const { for ( const_iterator it = begin(); it != end(); ++it ) { // TODO: skip empty/deleted values if ( !fwrite(&*it, sizeof(*it), 1, fp) ) return false; } return false; // TODO } // When reading, we have to override the potential const-ness of *it bool read_nopointer_data(FILE *fp) { for ( iterator it = begin(); it != end(); ++it ) { // TODO: skip empty/deleted values if ( !fread(reinterpret_cast<void*>(&(*it)), sizeof(*it), 1, fp) ) return false; } return false; // TODO } private: // We need to enforce that our value_type is a Plain Old Data Type // (so we know realloc and memmove will work). We use traits to // enforce this. The following gives a compile-time error if // is_POD_type is false (which is the default for user types). // // IF YOU GET AN ERROR HERE, make sure your class is a POD type, // and if so tell the compiler via code similar to this: // template<> struct __type_traits<classname> { // typedef __true_type has_trivial_default_constructor; // typedef __true_type has_trivial_copy_constructor; // typedef __true_type has_trivial_assignment_operator; // typedef __true_type has_trivial_destructor; // typedef __true_type is_POD_type; // }; // // If this is part of a hash_map, you need to make sure both the // Key and Value types are POD types, if they're user-defined.#ifdef UNDERSTANDS_TYPE_TRAITS static __true_type * const enforce_pod;#endif // The actual data hasher hash; // required by hashed_associative_container key_equal equals; ExtractKey get_key; size_type num_deleted; // how many occupied buckets are marked deleted bool use_deleted; // false until delval has been set bool use_empty; // you must do this before you start bool empty_is_zero; // can optimize this case when emptying value_type delval; // which key marks deleted entries value_type emptyval; // which key marks unused entries value_type *table; size_type num_buckets; size_type num_elements; size_type shrink_threshold; // num_buckets * HT_EMPTY_FLT size_type enlarge_threshold; // num_buckets * HT_OCCUPANCY_FLT bool consider_shrink; // true if we should try to shrink before next insert void reset_thresholds() { enlarge_threshold = static_cast<size_type>(num_buckets*HT_OCCUPANCY_FLT); shrink_threshold = static_cast<size_type>(num_buckets*HT_EMPTY_FLT); consider_shrink = false; // whatever caused us to reset already considered }};// We need a global swap as welltemplate <class V, class K, class HF, class ExK, class EqK, class A>inline void swap(dense_hashtable<V,K,HF,ExK,EqK,A> &x, dense_hashtable<V,K,HF,ExK,EqK,A> &y) { x.swap(y);}#ifdef UNDERSTANDS_TYPE_TRAITStemplate <class V, class K, class HF, class ExK, class EqK, class A>__true_type * const dense_hashtable<V,K,HF,ExK,EqK,A>::enforce_pod = static_cast<typename __type_traits<value_type>::is_POD_type *>(0);#endif#undef JUMP_template <class V, class K, class HF, class ExK, class EqK, class A>const typename dense_hashtable<V,K,HF,ExK,EqK,A>::size_type dense_hashtable<V,K,HF,ExK,EqK,A>::ILLEGAL_BUCKET;// How full we let the table get before we resize. Knuth says .8 is// good -- higher causes us to probe too much, though saves memorytemplate <class V, class K, class HF, class ExK, class EqK, class A>const float dense_hashtable<V,K,HF,ExK,EqK,A>::HT_OCCUPANCY_FLT = 0.5f;// How empty we let the table get before we resize lower.// It should be less than OCCUPANCY_FLT / 2 or we thrash resizingtemplate <class V, class K, class HF, class ExK, class EqK, class A>const float dense_hashtable<V,K,HF,ExK,EqK,A>::HT_EMPTY_FLT = 0.4 *dense_hashtable<V,K,HF,ExK,EqK,A>::HT_OCCUPANCY_FLT;_END_GOOGLE_NAMESPACE_#endif /* _DENSEHASHTABLE_H_ */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -