mirror of https://github.com/PCSX2/pcsx2.git
Added googlecode's sparsehash / densehash classes, which may or may not be useful in the future.
Removed various instances of legacy VM code that is no longer needed for reference purposes. git-svn-id: http://pcsx2.googlecode.com/svn/trunk@695 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
parent
620ba58085
commit
23e0ca1b1f
|
@ -0,0 +1,261 @@
|
|||
// Copyright (c) 2005, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// ----
|
||||
// Author: Craig Silverstein
|
||||
//
|
||||
// This is just a very thin wrapper over densehashtable.h, just
|
||||
// like sgi stl's stl_hash_map is a very thin wrapper over
|
||||
// stl_hashtable. The major thing we define is operator[], because
|
||||
// we have a concept of a data_type which stl_hashtable doesn't
|
||||
// (it only has a key and a value).
|
||||
//
|
||||
// NOTE: this is exactly like sparse_hash_map.h, with the word
|
||||
// "sparse" replaced by "dense", except for the addition of
|
||||
// set_empty_key().
|
||||
//
|
||||
// YOU MUST CALL SET_EMPTY_KEY() IMMEDIATELY AFTER CONSTRUCTION.
|
||||
//
|
||||
// Otherwise your program will die in mysterious ways.
|
||||
//
|
||||
// In other respects, we adhere mostly to the STL semantics for
|
||||
// hash-map. One important exception is that insert() invalidates
|
||||
// iterators entirely. On the plus side, though, erase() doesn't
|
||||
// invalidate iterators at all, or even change the ordering of elements.
|
||||
//
|
||||
// Here are a few "power user" tips:
|
||||
//
|
||||
// 1) set_deleted_key():
|
||||
// If you want to use erase() you must call set_deleted_key(),
|
||||
// in addition to set_empty_key(), after construction.
|
||||
// The deleted and empty keys must differ.
|
||||
//
|
||||
// 2) resize(0):
|
||||
// When an item is deleted, its memory isn't freed right
|
||||
// away. This allows you to iterate over a hashtable,
|
||||
// and call erase(), without invalidating the iterator.
|
||||
// To force the memory to be freed, call resize(0).
|
||||
//
|
||||
// 3) set_resizing_parameters(0.0, 0.8):
|
||||
// Setting the shrink_resize_percent to 0.0 guarantees
|
||||
// that the hash table will never shrink.
|
||||
//
|
||||
// Guide to what kind of hash_map to use:
|
||||
// (1) dense_hash_map: fastest, uses the most memory
|
||||
// (2) sparse_hash_map: slowest, uses the least memory
|
||||
// (3) hash_map (STL): in the middle
|
||||
// Typically I use sparse_hash_map when I care about space and/or when
|
||||
// I need to save the hashtable on disk. I use hash_map otherwise. I
|
||||
// don't personally use dense_hash_map ever; the only use of
|
||||
// dense_hash_map I know of is to work around malloc() bugs in some
|
||||
// systems (dense_hash_map has a particularly simple allocation scheme).
|
||||
//
|
||||
// - dense_hash_map has, typically, a factor of 2 memory overhead (if your
|
||||
// data takes up X bytes, the hash_map uses X more bytes in overhead).
|
||||
// - sparse_hash_map has about 2 bits overhead per entry.
|
||||
// - sparse_hash_map can be 3-7 times slower than the others for lookup and,
|
||||
// especially, inserts. See time_hash_map.cc for details.
|
||||
//
|
||||
// See /usr/(local/)?doc/sparsehash-0.1/dense_hash_map.html
|
||||
// for information about how to use this class.
|
||||
|
||||
#ifndef _DENSE_HASH_MAP_H_
|
||||
#define _DENSE_HASH_MAP_H_
|
||||
|
||||
#include <google/sparsehash/sparseconfig.h>
|
||||
#include <stdio.h> // for FILE * in read()/write()
|
||||
#include <algorithm> // for the default template args
|
||||
#include <functional> // for equal_to
|
||||
#include <memory> // for alloc<>
|
||||
#include <utility> // for pair<>
|
||||
#include HASH_FUN_H // defined in config.h
|
||||
#include <google/sparsehash/densehashtable.h>
|
||||
|
||||
|
||||
_START_GOOGLE_NAMESPACE_
|
||||
|
||||
using STL_NAMESPACE::pair;
|
||||
|
||||
template <class Key, class T,
|
||||
class HashFcn = SPARSEHASH_HASH<Key>, // defined in sparseconfig.h
|
||||
class EqualKey = STL_NAMESPACE::equal_to<Key>,
|
||||
class Alloc = STL_NAMESPACE::allocator<T> >
|
||||
class dense_hash_map {
|
||||
private:
|
||||
// Apparently select1st is not stl-standard, so we define our own
|
||||
struct SelectKey {
|
||||
const Key& operator()(const pair<const Key, T>& p) const {
|
||||
return p.first;
|
||||
}
|
||||
};
|
||||
|
||||
// The actual data
|
||||
typedef dense_hashtable<pair<const Key, T>, Key, HashFcn,
|
||||
SelectKey, EqualKey, Alloc> ht;
|
||||
ht rep;
|
||||
|
||||
public:
|
||||
typedef typename ht::key_type key_type;
|
||||
typedef T data_type;
|
||||
typedef T mapped_type;
|
||||
typedef typename ht::value_type value_type;
|
||||
typedef typename ht::hasher hasher;
|
||||
typedef typename ht::key_equal key_equal;
|
||||
|
||||
typedef typename ht::size_type size_type;
|
||||
typedef typename ht::difference_type difference_type;
|
||||
typedef typename ht::pointer pointer;
|
||||
typedef typename ht::const_pointer const_pointer;
|
||||
typedef typename ht::reference reference;
|
||||
typedef typename ht::const_reference const_reference;
|
||||
|
||||
typedef typename ht::iterator iterator;
|
||||
typedef typename ht::const_iterator const_iterator;
|
||||
|
||||
// Iterator functions
|
||||
iterator begin() { return rep.begin(); }
|
||||
iterator end() { return rep.end(); }
|
||||
const_iterator begin() const { return rep.begin(); }
|
||||
const_iterator end() const { return rep.end(); }
|
||||
|
||||
|
||||
// Accessor functions
|
||||
hasher hash_funct() const { return rep.hash_funct(); }
|
||||
key_equal key_eq() const { return rep.key_eq(); }
|
||||
|
||||
|
||||
// Constructors
|
||||
explicit dense_hash_map(size_type expected_max_items_in_table = 0,
|
||||
const hasher& hf = hasher(),
|
||||
const key_equal& eql = key_equal())
|
||||
: rep(expected_max_items_in_table, hf, eql) { }
|
||||
|
||||
template <class InputIterator>
|
||||
dense_hash_map(InputIterator f, InputIterator l,
|
||||
size_type expected_max_items_in_table = 0,
|
||||
const hasher& hf = hasher(),
|
||||
const key_equal& eql = key_equal())
|
||||
: rep(expected_max_items_in_table, hf, eql) {
|
||||
rep.insert(f, l);
|
||||
}
|
||||
// We use the default copy constructor
|
||||
// We use the default operator=()
|
||||
// We use the default destructor
|
||||
|
||||
void clear() { rep.clear(); }
|
||||
// This clears the hash map without resizing it down to the minimum
|
||||
// bucket count, but rather keeps the number of buckets constant
|
||||
void clear_no_resize() { rep.clear_no_resize(); }
|
||||
void swap(dense_hash_map& hs) { rep.swap(hs.rep); }
|
||||
|
||||
|
||||
// Functions concerning size
|
||||
size_type size() const { return rep.size(); }
|
||||
size_type max_size() const { return rep.max_size(); }
|
||||
bool empty() const { return rep.empty(); }
|
||||
size_type bucket_count() const { return rep.bucket_count(); }
|
||||
size_type max_bucket_count() const { return rep.max_bucket_count(); }
|
||||
|
||||
void resize(size_type hint) { rep.resize(hint); }
|
||||
|
||||
void set_resizing_parameters(float shrink, float grow) {
|
||||
return rep.set_resizing_parameters(shrink, grow);
|
||||
}
|
||||
|
||||
// Lookup routines
|
||||
iterator find(const key_type& key) { return rep.find(key); }
|
||||
const_iterator find(const key_type& key) const { return rep.find(key); }
|
||||
|
||||
data_type& operator[](const key_type& key) { // This is our value-add!
|
||||
iterator it = find(key);
|
||||
if (it != end()) {
|
||||
return it->second;
|
||||
} else {
|
||||
return insert(value_type(key, data_type())).first->second;
|
||||
}
|
||||
}
|
||||
|
||||
size_type count(const key_type& key) const { return rep.count(key); }
|
||||
|
||||
pair<iterator, iterator> equal_range(const key_type& key) {
|
||||
return rep.equal_range(key);
|
||||
}
|
||||
pair<const_iterator, const_iterator> equal_range(const key_type& key) const {
|
||||
return rep.equal_range(key);
|
||||
}
|
||||
|
||||
// Insertion routines
|
||||
pair<iterator, bool> insert(const value_type& obj) { return rep.insert(obj); }
|
||||
template <class InputIterator>
|
||||
void insert(InputIterator f, InputIterator l) { rep.insert(f, l); }
|
||||
void insert(const_iterator f, const_iterator l) { rep.insert(f, l); }
|
||||
// required for std::insert_iterator; the passed-in iterator is ignored
|
||||
iterator insert(iterator, const value_type& obj) { return insert(obj).first; }
|
||||
|
||||
|
||||
// Deletion and empty routines
|
||||
// THESE ARE NON-STANDARD! I make you specify an "impossible" key
|
||||
// value to identify deleted and empty buckets. You can change the
|
||||
// deleted key as time goes on, or get rid of it entirely to be insert-only.
|
||||
void set_empty_key(const key_type& key) { // YOU MUST CALL THIS!
|
||||
rep.set_empty_key(value_type(key, data_type())); // rep wants a value
|
||||
}
|
||||
void set_deleted_key(const key_type& key) {
|
||||
rep.set_deleted_key(value_type(key, data_type())); // rep wants a value
|
||||
}
|
||||
void clear_deleted_key() { rep.clear_deleted_key(); }
|
||||
|
||||
// These are standard
|
||||
size_type erase(const key_type& key) { return rep.erase(key); }
|
||||
void erase(iterator it) { rep.erase(it); }
|
||||
void erase(iterator f, iterator l) { rep.erase(f, l); }
|
||||
|
||||
|
||||
// Comparison
|
||||
bool operator==(const dense_hash_map& hs) const { return rep == hs.rep; }
|
||||
bool operator!=(const dense_hash_map& hs) const { return rep != hs.rep; }
|
||||
|
||||
|
||||
// I/O -- this is an add-on for writing metainformation to disk
|
||||
bool write_metadata(FILE *fp) { return rep.write_metadata(fp); }
|
||||
bool read_metadata(FILE *fp) { return rep.read_metadata(fp); }
|
||||
bool write_nopointer_data(FILE *fp) { return rep.write_nopointer_data(fp); }
|
||||
bool read_nopointer_data(FILE *fp) { return rep.read_nopointer_data(fp); }
|
||||
};
|
||||
|
||||
// We need a global swap as well
|
||||
template <class Key, class T, class HashFcn, class EqualKey, class Alloc>
|
||||
inline void swap(dense_hash_map<Key, T, HashFcn, EqualKey, Alloc>& hm1,
|
||||
dense_hash_map<Key, T, HashFcn, EqualKey, Alloc>& hm2) {
|
||||
hm1.swap(hm2);
|
||||
}
|
||||
|
||||
_END_GOOGLE_NAMESPACE_
|
||||
|
||||
#endif /* _DENSE_HASH_MAP_H_ */
|
|
@ -0,0 +1,245 @@
|
|||
// Copyright (c) 2005, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// ---
|
||||
// Author: Craig Silverstein
|
||||
//
|
||||
// This is just a very thin wrapper over densehashtable.h, just
|
||||
// like sgi stl's stl_hash_set is a very thin wrapper over
|
||||
// stl_hashtable. The major thing we define is operator[], because
|
||||
// we have a concept of a data_type which stl_hashtable doesn't
|
||||
// (it only has a key and a value).
|
||||
//
|
||||
// This is more different from dense_hash_map than you might think,
|
||||
// because all iterators for sets are const (you obviously can't
|
||||
// change the key, and for sets there is no value).
|
||||
//
|
||||
// NOTE: this is exactly like sparse_hash_set.h, with the word
|
||||
// "sparse" replaced by "dense", except for the addition of
|
||||
// set_empty_key().
|
||||
//
|
||||
// YOU MUST CALL SET_EMPTY_KEY() IMMEDIATELY AFTER CONSTRUCTION.
|
||||
//
|
||||
// Otherwise your program will die in mysterious ways.
|
||||
//
|
||||
// In other respects, we adhere mostly to the STL semantics for
|
||||
// hash-set. One important exception is that insert() invalidates
|
||||
// iterators entirely. On the plus side, though, erase() doesn't
|
||||
// invalidate iterators at all, or even change the ordering of elements.
|
||||
//
|
||||
// Here are a few "power user" tips:
|
||||
//
|
||||
// 1) set_deleted_key():
|
||||
// If you want to use erase() you must call set_deleted_key(),
|
||||
// in addition to set_empty_key(), after construction.
|
||||
// The deleted and empty keys must differ.
|
||||
//
|
||||
// 2) resize(0):
|
||||
// When an item is deleted, its memory isn't freed right
|
||||
// away. This allows you to iterate over a hashtable,
|
||||
// and call erase(), without invalidating the iterator.
|
||||
// To force the memory to be freed, call resize(0).
|
||||
//
|
||||
// 3) set_resizing_parameters(0.0, 0.8):
|
||||
// Setting the shrink_resize_percent to 0.0 guarantees
|
||||
// that the hash table will never shrink.
|
||||
//
|
||||
// Guide to what kind of hash_set to use:
|
||||
// (1) dense_hash_set: fastest, uses the most memory
|
||||
// (2) sparse_hash_set: slowest, uses the least memory
|
||||
// (3) hash_set (STL): in the middle
|
||||
// Typically I use sparse_hash_set when I care about space and/or when
|
||||
// I need to save the hashtable on disk. I use hash_set otherwise. I
|
||||
// don't personally use dense_hash_set ever; the only use of
|
||||
// dense_hash_set I know of is to work around malloc() bugs in some
|
||||
// systems (dense_hash_set has a particularly simple allocation scheme).
|
||||
//
|
||||
// - dense_hash_set has, typically, a factor of 2 memory overhead (if your
|
||||
// data takes up X bytes, the hash_set uses X more bytes in overhead).
|
||||
// - sparse_hash_set has about 2 bits overhead per entry.
|
||||
// - sparse_hash_map can be 3-7 times slower than the others for lookup and,
|
||||
// especially, inserts. See time_hash_map.cc for details.
|
||||
//
|
||||
// See /usr/(local/)?doc/sparsehash-0.1/dense_hash_set.html
|
||||
// for information about how to use this class.
|
||||
|
||||
#ifndef _DENSE_HASH_SET_H_
|
||||
#define _DENSE_HASH_SET_H_
|
||||
|
||||
#include <google/sparsehash/sparseconfig.h>
|
||||
#include <stdio.h> // for FILE * in read()/write()
|
||||
#include <algorithm> // for the default template args
|
||||
#include <functional> // for equal_to
|
||||
#include <memory> // for alloc<>
|
||||
#include <utility> // for pair<>
|
||||
#include HASH_FUN_H // defined in config.h
|
||||
#include <google/sparsehash/densehashtable.h>
|
||||
|
||||
|
||||
_START_GOOGLE_NAMESPACE_
|
||||
|
||||
using STL_NAMESPACE::pair;
|
||||
|
||||
template <class Value,
|
||||
class HashFcn = SPARSEHASH_HASH<Value>, // defined in sparseconfig.h
|
||||
class EqualKey = STL_NAMESPACE::equal_to<Value>,
|
||||
class Alloc = STL_NAMESPACE::allocator<Value> >
|
||||
class dense_hash_set {
|
||||
private:
|
||||
// Apparently identity is not stl-standard, so we define our own
|
||||
struct Identity {
|
||||
Value& operator()(Value& v) const { return v; }
|
||||
const Value& operator()(const Value& v) const { return v; }
|
||||
};
|
||||
|
||||
// The actual data
|
||||
typedef dense_hashtable<Value, Value, HashFcn, Identity, EqualKey, Alloc> ht;
|
||||
ht rep;
|
||||
|
||||
public:
|
||||
typedef typename ht::key_type key_type;
|
||||
typedef typename ht::value_type value_type;
|
||||
typedef typename ht::hasher hasher;
|
||||
typedef typename ht::key_equal key_equal;
|
||||
|
||||
typedef typename ht::size_type size_type;
|
||||
typedef typename ht::difference_type difference_type;
|
||||
typedef typename ht::const_pointer pointer;
|
||||
typedef typename ht::const_pointer const_pointer;
|
||||
typedef typename ht::const_reference reference;
|
||||
typedef typename ht::const_reference const_reference;
|
||||
|
||||
typedef typename ht::const_iterator iterator;
|
||||
typedef typename ht::const_iterator const_iterator;
|
||||
|
||||
|
||||
// Iterator functions -- recall all iterators are const
|
||||
iterator begin() const { return rep.begin(); }
|
||||
iterator end() const { return rep.end(); }
|
||||
|
||||
|
||||
// Accessor functions
|
||||
hasher hash_funct() const { return rep.hash_funct(); }
|
||||
key_equal key_eq() const { return rep.key_eq(); }
|
||||
|
||||
|
||||
// Constructors
|
||||
explicit dense_hash_set(size_type expected_max_items_in_table = 0,
|
||||
const hasher& hf = hasher(),
|
||||
const key_equal& eql = key_equal())
|
||||
: rep(expected_max_items_in_table, hf, eql) { }
|
||||
|
||||
template <class InputIterator>
|
||||
dense_hash_set(InputIterator f, InputIterator l,
|
||||
size_type expected_max_items_in_table = 0,
|
||||
const hasher& hf = hasher(),
|
||||
const key_equal& eql = key_equal())
|
||||
: rep(expected_max_items_in_table, hf, eql) {
|
||||
rep.insert(f, l);
|
||||
}
|
||||
// We use the default copy constructor
|
||||
// We use the default operator=()
|
||||
// We use the default destructor
|
||||
|
||||
void clear() { rep.clear(); }
|
||||
// This clears the hash set without resizing it down to the minimum
|
||||
// bucket count, but rather keeps the number of buckets constant
|
||||
void clear_no_resize() { rep.clear_no_resize(); }
|
||||
void swap(dense_hash_set& hs) { rep.swap(hs.rep); }
|
||||
|
||||
|
||||
// Functions concerning size
|
||||
size_type size() const { return rep.size(); }
|
||||
size_type max_size() const { return rep.max_size(); }
|
||||
bool empty() const { return rep.empty(); }
|
||||
size_type bucket_count() const { return rep.bucket_count(); }
|
||||
size_type max_bucket_count() const { return rep.max_bucket_count(); }
|
||||
|
||||
void resize(size_type hint) { rep.resize(hint); }
|
||||
|
||||
void set_resizing_parameters(float shrink, float grow) {
|
||||
return rep.set_resizing_parameters(shrink, grow);
|
||||
}
|
||||
|
||||
// Lookup routines
|
||||
iterator find(const key_type& key) const { return rep.find(key); }
|
||||
|
||||
size_type count(const key_type& key) const { return rep.count(key); }
|
||||
|
||||
pair<iterator, iterator> equal_range(const key_type& key) const {
|
||||
return rep.equal_range(key);
|
||||
}
|
||||
|
||||
// Insertion routines
|
||||
pair<iterator, bool> insert(const value_type& obj) {
|
||||
pair<typename ht::iterator, bool> p = rep.insert(obj);
|
||||
return pair<iterator, bool>(p.first, p.second); // const to non-const
|
||||
}
|
||||
template <class InputIterator>
|
||||
void insert(InputIterator f, InputIterator l) { rep.insert(f, l); }
|
||||
void insert(const_iterator f, const_iterator l) { rep.insert(f, l); }
|
||||
// required for std::insert_iterator; the passed-in iterator is ignored
|
||||
iterator insert(iterator, const value_type& obj) { return insert(obj).first; }
|
||||
|
||||
|
||||
// Deletion and empty routines
|
||||
// THESE ARE NON-STANDARD! I make you specify an "impossible" key
|
||||
// value to identify deleted and empty buckets. You can change the
|
||||
// deleted key as time goes on, or get rid of it entirely to be insert-only.
|
||||
void set_empty_key(const key_type& key) { rep.set_empty_key(key); }
|
||||
void set_deleted_key(const key_type& key) { rep.set_deleted_key(key); }
|
||||
void clear_deleted_key() { rep.clear_deleted_key(); }
|
||||
|
||||
// These are standard
|
||||
size_type erase(const key_type& key) { return rep.erase(key); }
|
||||
void erase(iterator it) { rep.erase(it); }
|
||||
void erase(iterator f, iterator l) { rep.erase(f, l); }
|
||||
|
||||
|
||||
// Comparison
|
||||
bool operator==(const dense_hash_set& hs) const { return rep == hs.rep; }
|
||||
bool operator!=(const dense_hash_set& hs) const { return rep != hs.rep; }
|
||||
|
||||
|
||||
// I/O -- this is an add-on for writing metainformation to disk
|
||||
bool write_metadata(FILE *fp) { return rep.write_metadata(fp); }
|
||||
bool read_metadata(FILE *fp) { return rep.read_metadata(fp); }
|
||||
bool write_nopointer_data(FILE *fp) { return rep.write_nopointer_data(fp); }
|
||||
bool read_nopointer_data(FILE *fp) { return rep.read_nopointer_data(fp); }
|
||||
};
|
||||
|
||||
template <class Val, class HashFcn, class EqualKey, class Alloc>
|
||||
inline void swap(dense_hash_set<Val, HashFcn, EqualKey, Alloc>& hs1,
|
||||
dense_hash_set<Val, HashFcn, EqualKey, Alloc>& hs2) {
|
||||
hs1.swap(hs2);
|
||||
}
|
||||
|
||||
_END_GOOGLE_NAMESPACE_
|
||||
|
||||
#endif /* _DENSE_HASH_SET_H_ */
|
|
@ -0,0 +1,246 @@
|
|||
// Copyright (c) 2005, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// ---
|
||||
// Author: Craig Silverstein
|
||||
//
|
||||
// This is just a very thin wrapper over sparsehashtable.h, just
|
||||
// like sgi stl's stl_hash_map is a very thin wrapper over
|
||||
// stl_hashtable. The major thing we define is operator[], because
|
||||
// we have a concept of a data_type which stl_hashtable doesn't
|
||||
// (it only has a key and a value).
|
||||
//
|
||||
// We adhere mostly to the STL semantics for hash-map. One important
|
||||
// exception is that insert() invalidates iterators entirely. On the
|
||||
// plus side, though, delete() doesn't invalidate iterators at all, or
|
||||
// even change the ordering of elements.
|
||||
//
|
||||
// Here are a few "power user" tips:
|
||||
//
|
||||
// 1) set_deleted_key():
|
||||
// Unlike STL's hash_map, if you want to use erase() you
|
||||
// must call set_deleted_key() after construction.
|
||||
//
|
||||
// 2) resize(0):
|
||||
// When an item is deleted, its memory isn't freed right
|
||||
// away. This is what allows you to iterate over a hashtable
|
||||
// and call erase() without invalidating the iterator.
|
||||
// To force the memory to be freed, call resize(0).
|
||||
//
|
||||
// 3) set_resizing_parameters(0.0, 0.8):
|
||||
// Setting the shrink_resize_percent to 0.0 guarantees
|
||||
// that the hash table will never shrink.
|
||||
//
|
||||
// Guide to what kind of hash_map to use:
|
||||
// (1) dense_hash_map: fastest, uses the most memory
|
||||
// (2) sparse_hash_map: slowest, uses the least memory
|
||||
// (3) hash_map (STL): in the middle
|
||||
// Typically I use sparse_hash_map when I care about space and/or when
|
||||
// I need to save the hashtable on disk. I use hash_map otherwise. I
|
||||
// don't personally use dense_hash_map ever; the only use of
|
||||
// dense_hash_map I know of is to work around malloc() bugs in some
|
||||
// systems (dense_hash_map has a particularly simple allocation scheme).
|
||||
//
|
||||
// - dense_hash_map has, typically, a factor of 2 memory overhead (if your
|
||||
// data takes up X bytes, the hash_map uses X more bytes in overhead).
|
||||
// - sparse_hash_map has about 2 bits overhead per entry.
|
||||
// - sparse_hash_map can be 3-7 times slower than the others for lookup and,
|
||||
// especially, inserts. See time_hash_map.cc for details.
|
||||
//
|
||||
// See /usr/(local/)?doc/sparsehash-0.1/sparse_hash_map.html
|
||||
// for information about how to use this class.
|
||||
|
||||
#ifndef _SPARSE_HASH_MAP_H_
|
||||
#define _SPARSE_HASH_MAP_H_
|
||||
|
||||
#include <google/sparsehash/sparseconfig.h>
|
||||
#include <stdio.h> // for FILE * in read()/write()
|
||||
#include <algorithm> // for the default template args
|
||||
#include <functional> // for equal_to
|
||||
#include <memory> // for alloc<>
|
||||
#include <utility> // for pair<>
|
||||
#include HASH_FUN_H // defined in config.h
|
||||
#include <google/sparsehash/sparsehashtable.h>
|
||||
|
||||
|
||||
_START_GOOGLE_NAMESPACE_
|
||||
|
||||
using STL_NAMESPACE::pair;
|
||||
|
||||
template <class Key, class T,
|
||||
class HashFcn = SPARSEHASH_HASH<Key>, // defined in sparseconfig.h
|
||||
class EqualKey = STL_NAMESPACE::equal_to<Key>,
|
||||
class Alloc = STL_NAMESPACE::allocator<T> >
|
||||
class sparse_hash_map {
|
||||
private:
|
||||
// Apparently select1st is not stl-standard, so we define our own
|
||||
struct SelectKey {
|
||||
const Key& operator()(const pair<const Key, T>& p) const {
|
||||
return p.first;
|
||||
}
|
||||
};
|
||||
|
||||
// The actual data
|
||||
typedef sparse_hashtable<pair<const Key, T>, Key, HashFcn,
|
||||
SelectKey, EqualKey, Alloc> ht;
|
||||
ht rep;
|
||||
|
||||
public:
|
||||
typedef typename ht::key_type key_type;
|
||||
typedef T data_type;
|
||||
typedef T mapped_type;
|
||||
typedef typename ht::value_type value_type;
|
||||
typedef typename ht::hasher hasher;
|
||||
typedef typename ht::key_equal key_equal;
|
||||
|
||||
typedef typename ht::size_type size_type;
|
||||
typedef typename ht::difference_type difference_type;
|
||||
typedef typename ht::pointer pointer;
|
||||
typedef typename ht::const_pointer const_pointer;
|
||||
typedef typename ht::reference reference;
|
||||
typedef typename ht::const_reference const_reference;
|
||||
|
||||
typedef typename ht::iterator iterator;
|
||||
typedef typename ht::const_iterator const_iterator;
|
||||
|
||||
// Iterator functions
|
||||
iterator begin() { return rep.begin(); }
|
||||
iterator end() { return rep.end(); }
|
||||
const_iterator begin() const { return rep.begin(); }
|
||||
const_iterator end() const { return rep.end(); }
|
||||
|
||||
|
||||
// Accessor functions
|
||||
hasher hash_funct() const { return rep.hash_funct(); }
|
||||
key_equal key_eq() const { return rep.key_eq(); }
|
||||
|
||||
|
||||
// Constructors
|
||||
explicit sparse_hash_map(size_type expected_max_items_in_table = 0,
|
||||
const hasher& hf = hasher(),
|
||||
const key_equal& eql = key_equal())
|
||||
: rep(expected_max_items_in_table, hf, eql) { }
|
||||
|
||||
template <class InputIterator>
|
||||
sparse_hash_map(InputIterator f, InputIterator l,
|
||||
size_type expected_max_items_in_table = 0,
|
||||
const hasher& hf = hasher(),
|
||||
const key_equal& eql = key_equal())
|
||||
: rep(expected_max_items_in_table, hf, eql) {
|
||||
rep.insert(f, l);
|
||||
}
|
||||
// We use the default copy constructor
|
||||
// We use the default operator=()
|
||||
// We use the default destructor
|
||||
|
||||
void clear() { rep.clear(); }
|
||||
void swap(sparse_hash_map& hs) { rep.swap(hs.rep); }
|
||||
|
||||
|
||||
// Functions concerning size
|
||||
size_type size() const { return rep.size(); }
|
||||
size_type max_size() const { return rep.max_size(); }
|
||||
bool empty() const { return rep.empty(); }
|
||||
size_type bucket_count() const { return rep.bucket_count(); }
|
||||
size_type max_bucket_count() const { return rep.max_bucket_count(); }
|
||||
|
||||
void resize(size_type hint) { rep.resize(hint); }
|
||||
|
||||
void set_resizing_parameters(float shrink, float grow) {
|
||||
return rep.set_resizing_parameters(shrink, grow);
|
||||
}
|
||||
|
||||
// Lookup routines
|
||||
iterator find(const key_type& key) { return rep.find(key); }
|
||||
const_iterator find(const key_type& key) const { return rep.find(key); }
|
||||
|
||||
data_type& operator[](const key_type& key) { // This is our value-add!
|
||||
iterator it = find(key);
|
||||
if (it != end()) {
|
||||
return it->second;
|
||||
} else {
|
||||
return insert(value_type(key, data_type())).first->second;
|
||||
}
|
||||
}
|
||||
|
||||
size_type count(const key_type& key) const { return rep.count(key); }
|
||||
|
||||
pair<iterator, iterator> equal_range(const key_type& key) {
|
||||
return rep.equal_range(key);
|
||||
}
|
||||
pair<const_iterator, const_iterator> equal_range(const key_type& key) const {
|
||||
return rep.equal_range(key);
|
||||
}
|
||||
|
||||
// Insertion routines
|
||||
pair<iterator, bool> insert(const value_type& obj) { return rep.insert(obj); }
|
||||
template <class InputIterator>
|
||||
void insert(InputIterator f, InputIterator l) { rep.insert(f, l); }
|
||||
void insert(const_iterator f, const_iterator l) { rep.insert(f, l); }
|
||||
// required for std::insert_iterator; the passed-in iterator is ignored
|
||||
iterator insert(iterator, const value_type& obj) { return insert(obj).first; }
|
||||
|
||||
|
||||
// Deletion routines
|
||||
// THESE ARE NON-STANDARD! I make you specify an "impossible" key
|
||||
// value to identify deleted buckets. You can change the key as
|
||||
// time goes on, or get rid of it entirely to be insert-only.
|
||||
void set_deleted_key(const key_type& key) {
|
||||
rep.set_deleted_key(value_type(key, data_type())); // rep wants a value
|
||||
}
|
||||
void clear_deleted_key() { rep.clear_deleted_key(); }
|
||||
|
||||
// These are standard
|
||||
size_type erase(const key_type& key) { return rep.erase(key); }
|
||||
void erase(iterator it) { rep.erase(it); }
|
||||
void erase(iterator f, iterator l) { rep.erase(f, l); }
|
||||
|
||||
|
||||
// Comparison
|
||||
bool operator==(const sparse_hash_map& hs) const { return rep == hs.rep; }
|
||||
bool operator!=(const sparse_hash_map& hs) const { return rep != hs.rep; }
|
||||
|
||||
|
||||
// I/O -- this is an add-on for writing metainformation to disk
|
||||
bool write_metadata(FILE *fp) { return rep.write_metadata(fp); }
|
||||
bool read_metadata(FILE *fp) { return rep.read_metadata(fp); }
|
||||
bool write_nopointer_data(FILE *fp) { return rep.write_nopointer_data(fp); }
|
||||
bool read_nopointer_data(FILE *fp) { return rep.read_nopointer_data(fp); }
|
||||
};
|
||||
|
||||
// We need a global swap as well
|
||||
template <class Key, class T, class HashFcn, class EqualKey, class Alloc>
|
||||
inline void swap(sparse_hash_map<Key, T, HashFcn, EqualKey, Alloc>& hm1,
|
||||
sparse_hash_map<Key, T, HashFcn, EqualKey, Alloc>& hm2) {
|
||||
hm1.swap(hm2);
|
||||
}
|
||||
|
||||
_END_GOOGLE_NAMESPACE_
|
||||
|
||||
#endif /* _SPARSE_HASH_MAP_H_ */
|
|
@ -0,0 +1,231 @@
|
|||
// Copyright (c) 2005, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// ---
|
||||
// Author: Craig Silverstein
|
||||
//
|
||||
// This is just a very thin wrapper over sparsehashtable.h, just
|
||||
// like sgi stl's stl_hash_set is a very thin wrapper over
|
||||
// stl_hashtable. The major thing we define is operator[], because
|
||||
// we have a concept of a data_type which stl_hashtable doesn't
|
||||
// (it only has a key and a value).
|
||||
//
|
||||
// This is more different from sparse_hash_map than you might think,
|
||||
// because all iterators for sets are const (you obviously can't
|
||||
// change the key, and for sets there is no value).
|
||||
//
|
||||
// We adhere mostly to the STL semantics for hash-set. One important
|
||||
// exception is that insert() invalidates iterators entirely. On the
|
||||
// plus side, though, delete() doesn't invalidate iterators at all, or
|
||||
// even change the ordering of elements.
|
||||
//
|
||||
// Here are a few "power user" tips:
|
||||
//
|
||||
// 1) set_deleted_key():
|
||||
// Unlike STL's hash_map, if you want to use erase() you
|
||||
// must call set_deleted_key() after construction.
|
||||
//
|
||||
// 2) resize(0):
|
||||
// When an item is deleted, its memory isn't freed right
|
||||
// away. This allows you to iterate over a hashtable,
|
||||
// and call erase(), without invalidating the iterator.
|
||||
// To force the memory to be freed, call resize(0).
|
||||
//
|
||||
// 3) set_resizing_parameters(0.0, 0.8):
|
||||
// Setting the shrink_resize_percent to 0.0 guarantees
|
||||
// that the hash table will never shrink.
|
||||
//
|
||||
// Guide to what kind of hash_set to use:
|
||||
// (1) dense_hash_set: fastest, uses the most memory
|
||||
// (2) sparse_hash_set: slowest, uses the least memory
|
||||
// (3) hash_set (STL): in the middle
|
||||
// Typically I use sparse_hash_set when I care about space and/or when
|
||||
// I need to save the hashtable on disk. I use hash_set otherwise. I
|
||||
// don't personally use dense_hash_set ever; the only use of
|
||||
// dense_hash_set I know of is to work around malloc() bugs in some
|
||||
// systems (dense_hash_set has a particularly simple allocation scheme).
|
||||
//
|
||||
// - dense_hash_set has, typically, a factor of 2 memory overhead (if your
|
||||
// data takes up X bytes, the hash_set uses X more bytes in overhead).
|
||||
// - sparse_hash_set has about 2 bits overhead per entry.
|
||||
// - sparse_hash_map can be 3-7 times slower than the others for lookup and,
|
||||
// especially, inserts. See time_hash_map.cc for details.
|
||||
//
|
||||
// See /usr/(local/)?doc/sparsehash-0.1/sparse_hash_set.html
|
||||
// for information about how to use this class.
|
||||
|
||||
#ifndef _SPARSE_HASH_SET_H_
|
||||
#define _SPARSE_HASH_SET_H_
|
||||
|
||||
#include <google/sparsehash/sparseconfig.h>
|
||||
#include <stdio.h> // for FILE * in read()/write()
|
||||
#include <algorithm> // for the default template args
|
||||
#include <functional> // for equal_to
|
||||
#include <memory> // for alloc<>
|
||||
#include <utility> // for pair<>
|
||||
#include HASH_FUN_H // defined in config.h
|
||||
#include <google/sparsehash/sparsehashtable.h>
|
||||
|
||||
_START_GOOGLE_NAMESPACE_
|
||||
|
||||
using STL_NAMESPACE::pair;
|
||||
|
||||
template <class Value,
|
||||
class HashFcn = SPARSEHASH_HASH<Value>, // defined in sparseconfig.h
|
||||
class EqualKey = STL_NAMESPACE::equal_to<Value>,
|
||||
class Alloc = STL_NAMESPACE::allocator<Value> >
|
||||
class sparse_hash_set {
|
||||
private:
|
||||
// Apparently identity is not stl-standard, so we define our own
|
||||
struct Identity {
|
||||
Value& operator()(Value& v) const { return v; }
|
||||
const Value& operator()(const Value& v) const { return v; }
|
||||
};
|
||||
|
||||
// The actual data
|
||||
typedef sparse_hashtable<Value, Value, HashFcn, Identity, EqualKey, Alloc> ht;
|
||||
ht rep;
|
||||
|
||||
public:
|
||||
typedef typename ht::key_type key_type;
|
||||
typedef typename ht::value_type value_type;
|
||||
typedef typename ht::hasher hasher;
|
||||
typedef typename ht::key_equal key_equal;
|
||||
|
||||
typedef typename ht::size_type size_type;
|
||||
typedef typename ht::difference_type difference_type;
|
||||
typedef typename ht::const_pointer pointer;
|
||||
typedef typename ht::const_pointer const_pointer;
|
||||
typedef typename ht::const_reference reference;
|
||||
typedef typename ht::const_reference const_reference;
|
||||
|
||||
typedef typename ht::const_iterator iterator;
|
||||
typedef typename ht::const_iterator const_iterator;
|
||||
|
||||
|
||||
// Iterator functions -- recall all iterators are const
|
||||
iterator begin() const { return rep.begin(); }
|
||||
iterator end() const { return rep.end(); }
|
||||
|
||||
|
||||
// Accessor functions
|
||||
hasher hash_funct() const { return rep.hash_funct(); }
|
||||
key_equal key_eq() const { return rep.key_eq(); }
|
||||
|
||||
|
||||
// Constructors
|
||||
explicit sparse_hash_set(size_type expected_max_items_in_table = 0,
|
||||
const hasher& hf = hasher(),
|
||||
const key_equal& eql = key_equal())
|
||||
: rep(expected_max_items_in_table, hf, eql) { }
|
||||
|
||||
template <class InputIterator>
|
||||
sparse_hash_set(InputIterator f, InputIterator l,
|
||||
size_type expected_max_items_in_table = 0,
|
||||
const hasher& hf = hasher(),
|
||||
const key_equal& eql = key_equal())
|
||||
: rep(expected_max_items_in_table, hf, eql) {
|
||||
rep.insert(f, l);
|
||||
}
|
||||
// We use the default copy constructor
|
||||
// We use the default operator=()
|
||||
// We use the default destructor
|
||||
|
||||
void clear() { rep.clear(); }
|
||||
void swap(sparse_hash_set& hs) { rep.swap(hs.rep); }
|
||||
|
||||
|
||||
// Functions concerning size
|
||||
size_type size() const { return rep.size(); }
|
||||
size_type max_size() const { return rep.max_size(); }
|
||||
bool empty() const { return rep.empty(); }
|
||||
size_type bucket_count() const { return rep.bucket_count(); }
|
||||
size_type max_bucket_count() const { return rep.max_bucket_count(); }
|
||||
|
||||
void resize(size_type hint) { rep.resize(hint); }
|
||||
|
||||
void set_resizing_parameters(float shrink, float grow) {
|
||||
return rep.set_resizing_parameters(shrink, grow);
|
||||
}
|
||||
|
||||
// Lookup routines
|
||||
iterator find(const key_type& key) const { return rep.find(key); }
|
||||
|
||||
size_type count(const key_type& key) const { return rep.count(key); }
|
||||
|
||||
pair<iterator, iterator> equal_range(const key_type& key) const {
|
||||
return rep.equal_range(key);
|
||||
}
|
||||
|
||||
// Insertion routines
|
||||
pair<iterator, bool> insert(const value_type& obj) {
|
||||
pair<typename ht::iterator, bool> p = rep.insert(obj);
|
||||
return pair<iterator, bool>(p.first, p.second); // const to non-const
|
||||
}
|
||||
template <class InputIterator>
|
||||
void insert(InputIterator f, InputIterator l) { rep.insert(f, l); }
|
||||
void insert(const_iterator f, const_iterator l) { rep.insert(f, l); }
|
||||
// required for std::insert_iterator; the passed-in iterator is ignored
|
||||
iterator insert(iterator, const value_type& obj) { return insert(obj).first; }
|
||||
|
||||
|
||||
// Deletion routines
|
||||
// THESE ARE NON-STANDARD! I make you specify an "impossible" key
|
||||
// value to identify deleted buckets. You can change the key as
|
||||
// time goes on, or get rid of it entirely to be insert-only.
|
||||
void set_deleted_key(const key_type& key) { rep.set_deleted_key(key); }
|
||||
void clear_deleted_key() { rep.clear_deleted_key(); }
|
||||
|
||||
// These are standard
|
||||
size_type erase(const key_type& key) { return rep.erase(key); }
|
||||
void erase(iterator it) { rep.erase(it); }
|
||||
void erase(iterator f, iterator l) { rep.erase(f, l); }
|
||||
|
||||
|
||||
// Comparison
|
||||
bool operator==(const sparse_hash_set& hs) const { return rep == hs.rep; }
|
||||
bool operator!=(const sparse_hash_set& hs) const { return rep != hs.rep; }
|
||||
|
||||
|
||||
// I/O -- this is an add-on for writing metainformation to disk
|
||||
bool write_metadata(FILE *fp) { return rep.write_metadata(fp); }
|
||||
bool read_metadata(FILE *fp) { return rep.read_metadata(fp); }
|
||||
bool write_nopointer_data(FILE *fp) { return rep.write_nopointer_data(fp); }
|
||||
bool read_nopointer_data(FILE *fp) { return rep.read_nopointer_data(fp); }
|
||||
};
|
||||
|
||||
template <class Val, class HashFcn, class EqualKey, class Alloc>
|
||||
inline void swap(sparse_hash_set<Val, HashFcn, EqualKey, Alloc>& hs1,
|
||||
sparse_hash_set<Val, HashFcn, EqualKey, Alloc>& hs2) {
|
||||
hs1.swap(hs2);
|
||||
}
|
||||
|
||||
_END_GOOGLE_NAMESPACE_
|
||||
|
||||
#endif /* _SPARSE_HASH_SET_H_ */
|
|
@ -0,0 +1,986 @@
|
|||
// Copyright (c) 2005, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// ---
|
||||
// Author: Craig Silverstein
|
||||
//
|
||||
// A dense hashtable is a particular implementation of
|
||||
// a hashtable: one that is meant to minimize memory allocation.
|
||||
// It does this by using an array to store all the data. We
|
||||
// steal a value from the key space to indicate "empty" array
|
||||
// elements (ie indices where no item lives) and another to indicate
|
||||
// "deleted" elements.
|
||||
//
|
||||
// (Note it is possible to change the value of the delete key
|
||||
// on the fly; you can even remove it, though after that point
|
||||
// the hashtable is insert_only until you set it again. The empty
|
||||
// value however can't be changed.)
|
||||
//
|
||||
// To minimize allocation and pointer overhead, we use internal
|
||||
// probing, in which the hashtable is a single table, and collisions
|
||||
// are resolved by trying to insert again in another bucket. The
|
||||
// most cache-efficient internal probing schemes are linear probing
|
||||
// (which suffers, alas, from clumping) and quadratic probing, which
|
||||
// is what we implement by default.
|
||||
//
|
||||
// Type requirements: value_type is required to be Copy Constructible
|
||||
// and Default Constructible. It is not required to be (and commonly
|
||||
// isn't) Assignable.
|
||||
//
|
||||
// You probably shouldn't use this code directly. Use
|
||||
// <google/dense_hash_map> or <google/dense_hash_set> instead.
|
||||
|
||||
// You can change the following below:
|
||||
// HT_OCCUPANCY_FLT -- how full before we double size
|
||||
// HT_EMPTY_FLT -- how empty before we halve size
|
||||
// HT_MIN_BUCKETS -- default smallest bucket size
|
||||
//
|
||||
// You can also change enlarge_resize_percent (which defaults to
|
||||
// HT_OCCUPANCY_FLT), and shrink_resize_percent (which defaults to
|
||||
// HT_EMPTY_FLT) with set_resizing_parameters().
|
||||
//
|
||||
// How to decide what values to use?
|
||||
// shrink_resize_percent's default of .4 * OCCUPANCY_FLT, is probably good.
|
||||
// HT_MIN_BUCKETS is probably unnecessary since you can specify
|
||||
// (indirectly) the starting number of buckets at construct-time.
|
||||
// For enlarge_resize_percent, you can use this chart to try to trade-off
|
||||
// expected lookup time to the space taken up. By default, this
|
||||
// code uses quadratic probing, though you can change it to linear
|
||||
// via _JUMP below if you really want to.
|
||||
//
|
||||
// From http://www.augustana.ca/~mohrj/courses/1999.fall/csc210/lecture_notes/hashing.html
|
||||
// NUMBER OF PROBES / LOOKUP Successful Unsuccessful
|
||||
// Quadratic collision resolution 1 - ln(1-L) - L/2 1/(1-L) - L - ln(1-L)
|
||||
// Linear collision resolution [1+1/(1-L)]/2 [1+1/(1-L)2]/2
|
||||
//
|
||||
// -- enlarge_resize_percent -- 0.10 0.50 0.60 0.75 0.80 0.90 0.99
|
||||
// QUADRATIC COLLISION RES.
|
||||
// probes/successful lookup 1.05 1.44 1.62 2.01 2.21 2.85 5.11
|
||||
// probes/unsuccessful lookup 1.11 2.19 2.82 4.64 5.81 11.4 103.6
|
||||
// LINEAR COLLISION RES.
|
||||
// probes/successful lookup 1.06 1.5 1.75 2.5 3.0 5.5 50.5
|
||||
// probes/unsuccessful lookup 1.12 2.5 3.6 8.5 13.0 50.0 5000.0
|
||||
|
||||
#ifndef _DENSEHASHTABLE_H_
|
||||
#define _DENSEHASHTABLE_H_
|
||||
|
||||
// The probing method
|
||||
// Linear probing
|
||||
// #define JUMP_(key, num_probes) ( 1 )
|
||||
// Quadratic-ish probing
|
||||
#define JUMP_(key, num_probes) ( num_probes )
|
||||
|
||||
|
||||
// Hashtable class, used to implement the hashed associative containers
|
||||
// hash_set and hash_map.
|
||||
|
||||
#include <google/sparsehash/sparseconfig.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h> // for abort()
|
||||
#include <algorithm> // For swap(), eg
|
||||
#include <iostream> // For cerr
|
||||
#include <memory> // For uninitialized_fill, uninitialized_copy
|
||||
#include <utility> // for pair<>
|
||||
#include <iterator> // for facts about iterator tags
|
||||
#include <google/type_traits.h> // for true_type, integral_constant, etc.
|
||||
|
||||
_START_GOOGLE_NAMESPACE_
|
||||
|
||||
using STL_NAMESPACE::pair;
|
||||
|
||||
template <class Value, class Key, class HashFcn,
|
||||
class ExtractKey, class EqualKey, class Alloc>
|
||||
class dense_hashtable;
|
||||
|
||||
template <class V, class K, class HF, class ExK, class EqK, class A>
|
||||
struct dense_hashtable_iterator;
|
||||
|
||||
template <class V, class K, class HF, class ExK, class EqK, class A>
|
||||
struct dense_hashtable_const_iterator;
|
||||
|
||||
// We're just an array, but we need to skip over empty and deleted elements
|
||||
template <class V, class K, class HF, class ExK, class EqK, class A>
|
||||
struct dense_hashtable_iterator {
|
||||
public:
|
||||
typedef dense_hashtable_iterator<V,K,HF,ExK,EqK,A> iterator;
|
||||
typedef dense_hashtable_const_iterator<V,K,HF,ExK,EqK,A> const_iterator;
|
||||
|
||||
typedef STL_NAMESPACE::forward_iterator_tag iterator_category;
|
||||
typedef V value_type;
|
||||
typedef ptrdiff_t difference_type;
|
||||
typedef size_t size_type;
|
||||
typedef V& reference; // Value
|
||||
typedef V* pointer;
|
||||
|
||||
// "Real" constructor and default constructor
|
||||
dense_hashtable_iterator(const dense_hashtable<V,K,HF,ExK,EqK,A> *h,
|
||||
pointer it, pointer it_end, bool advance)
|
||||
: ht(h), pos(it), end(it_end) {
|
||||
if (advance) advance_past_empty_and_deleted();
|
||||
}
|
||||
dense_hashtable_iterator() { }
|
||||
// The default destructor is fine; we don't define one
|
||||
// The default operator= is fine; we don't define one
|
||||
|
||||
// Happy dereferencer
|
||||
reference operator*() const { return *pos; }
|
||||
pointer operator->() const { return &(operator*()); }
|
||||
|
||||
// Arithmetic. The only hard part is making sure that
|
||||
// we're not on an empty or marked-deleted array element
|
||||
void advance_past_empty_and_deleted() {
|
||||
while ( pos != end && (ht->test_empty(*this) || ht->test_deleted(*this)) )
|
||||
++pos;
|
||||
}
|
||||
iterator& operator++() {
|
||||
assert(pos != end); ++pos; advance_past_empty_and_deleted(); return *this;
|
||||
}
|
||||
iterator operator++(int) { iterator tmp(*this); ++*this; return tmp; }
|
||||
|
||||
// Comparison.
|
||||
bool operator==(const iterator& it) const { return pos == it.pos; }
|
||||
bool operator!=(const iterator& it) const { return pos != it.pos; }
|
||||
|
||||
|
||||
// The actual data
|
||||
const dense_hashtable<V,K,HF,ExK,EqK,A> *ht;
|
||||
pointer pos, end;
|
||||
};
|
||||
|
||||
|
||||
// Now do it all again, but with const-ness!
|
||||
template <class V, class K, class HF, class ExK, class EqK, class A>
|
||||
struct dense_hashtable_const_iterator {
|
||||
public:
|
||||
typedef dense_hashtable_iterator<V,K,HF,ExK,EqK,A> iterator;
|
||||
typedef dense_hashtable_const_iterator<V,K,HF,ExK,EqK,A> const_iterator;
|
||||
|
||||
typedef STL_NAMESPACE::forward_iterator_tag iterator_category;
|
||||
typedef V value_type;
|
||||
typedef ptrdiff_t difference_type;
|
||||
typedef size_t size_type;
|
||||
typedef const V& reference; // Value
|
||||
typedef const V* pointer;
|
||||
|
||||
// "Real" constructor and default constructor
|
||||
dense_hashtable_const_iterator(const dense_hashtable<V,K,HF,ExK,EqK,A> *h,
|
||||
pointer it, pointer it_end, bool advance)
|
||||
: ht(h), pos(it), end(it_end) {
|
||||
if (advance) advance_past_empty_and_deleted();
|
||||
}
|
||||
dense_hashtable_const_iterator() { }
|
||||
// This lets us convert regular iterators to const iterators
|
||||
dense_hashtable_const_iterator(const iterator &it)
|
||||
: ht(it.ht), pos(it.pos), end(it.end) { }
|
||||
// The default destructor is fine; we don't define one
|
||||
// The default operator= is fine; we don't define one
|
||||
|
||||
// Happy dereferencer
|
||||
reference operator*() const { return *pos; }
|
||||
pointer operator->() const { return &(operator*()); }
|
||||
|
||||
// Arithmetic. The only hard part is making sure that
|
||||
// we're not on an empty or marked-deleted array element
|
||||
void advance_past_empty_and_deleted() {
|
||||
while ( pos != end && (ht->test_empty(*this) || ht->test_deleted(*this)) )
|
||||
++pos;
|
||||
}
|
||||
const_iterator& operator++() {
|
||||
assert(pos != end); ++pos; advance_past_empty_and_deleted(); return *this;
|
||||
}
|
||||
const_iterator operator++(int) { const_iterator tmp(*this); ++*this; return tmp; }
|
||||
|
||||
// Comparison.
|
||||
bool operator==(const const_iterator& it) const { return pos == it.pos; }
|
||||
bool operator!=(const const_iterator& it) const { return pos != it.pos; }
|
||||
|
||||
|
||||
// The actual data
|
||||
const dense_hashtable<V,K,HF,ExK,EqK,A> *ht;
|
||||
pointer pos, end;
|
||||
};
|
||||
|
||||
template <class Value, class Key, class HashFcn,
|
||||
class ExtractKey, class EqualKey, class Alloc>
|
||||
class dense_hashtable {
|
||||
public:
|
||||
typedef Key key_type;
|
||||
typedef Value value_type;
|
||||
typedef HashFcn hasher;
|
||||
typedef EqualKey key_equal;
|
||||
|
||||
typedef size_t size_type;
|
||||
typedef ptrdiff_t difference_type;
|
||||
typedef value_type* pointer;
|
||||
typedef const value_type* const_pointer;
|
||||
typedef value_type& reference;
|
||||
typedef const value_type& const_reference;
|
||||
typedef dense_hashtable_iterator<Value, Key, HashFcn,
|
||||
ExtractKey, EqualKey, Alloc>
|
||||
iterator;
|
||||
|
||||
typedef dense_hashtable_const_iterator<Value, Key, HashFcn,
|
||||
ExtractKey, EqualKey, Alloc>
|
||||
const_iterator;
|
||||
|
||||
// How full we let the table get before we resize. Knuth says .8 is
|
||||
// good -- higher causes us to probe too much, though saves memory
|
||||
static const float HT_OCCUPANCY_FLT; // = 0.8;
|
||||
|
||||
// How empty we let the table get before we resize lower.
|
||||
// (0.0 means never resize lower.)
|
||||
// It should be less than OCCUPANCY_FLT / 2 or we thrash resizing
|
||||
static const float HT_EMPTY_FLT; // = 0.4 * HT_OCCUPANCY_FLT
|
||||
|
||||
// Minimum size we're willing to let hashtables be.
|
||||
// Must be a power of two, and at least 4.
|
||||
// Note, however, that for a given hashtable, the initial size is a
|
||||
// function of the first constructor arg, and may be >HT_MIN_BUCKETS.
|
||||
static const size_t HT_MIN_BUCKETS = 4;
|
||||
|
||||
// By default, if you don't specify a hashtable size at
|
||||
// construction-time, we use this size. Must be a power of two, and
|
||||
// at least HT_MIN_BUCKETS.
|
||||
static const size_t HT_DEFAULT_STARTING_BUCKETS = 32;
|
||||
|
||||
|
||||
// ITERATOR FUNCTIONS
|
||||
iterator begin() { return iterator(this, table,
|
||||
table + num_buckets, true); }
|
||||
iterator end() { return iterator(this, table + num_buckets,
|
||||
table + num_buckets, true); }
|
||||
const_iterator begin() const { return const_iterator(this, table,
|
||||
table+num_buckets,true);}
|
||||
const_iterator end() const { return const_iterator(this, table + num_buckets,
|
||||
table+num_buckets,true);}
|
||||
|
||||
// ACCESSOR FUNCTIONS for the things we templatize on, basically
|
||||
hasher hash_funct() const { return hash; }
|
||||
key_equal key_eq() const { return equals; }
|
||||
|
||||
// Annoyingly, we can't copy values around, because they might have
|
||||
// const components (they're probably pair<const X, Y>). We use
|
||||
// explicit destructor invocation and placement new to get around
|
||||
// this. Arg.
|
||||
private:
|
||||
void set_value(value_type* dst, const value_type& src) {
|
||||
dst->~value_type();
|
||||
new(dst) value_type(src);
|
||||
}
|
||||
|
||||
void destroy_buckets(size_type first, size_type last) {
|
||||
for ( ; first != last; ++first)
|
||||
table[first].~value_type();
|
||||
}
|
||||
|
||||
// DELETE HELPER FUNCTIONS
|
||||
// This lets the user describe a key that will indicate deleted
|
||||
// table entries. This key should be an "impossible" entry --
|
||||
// if you try to insert it for real, you won't be able to retrieve it!
|
||||
// (NB: while you pass in an entire value, only the key part is looked
|
||||
// at. This is just because I don't know how to assign just a key.)
|
||||
private:
|
||||
void squash_deleted() { // gets rid of any deleted entries we have
|
||||
if ( num_deleted ) { // get rid of deleted before writing
|
||||
dense_hashtable tmp(*this); // copying will get rid of deleted
|
||||
swap(tmp); // now we are tmp
|
||||
}
|
||||
assert(num_deleted == 0);
|
||||
}
|
||||
|
||||
public:
|
||||
void set_deleted_key(const value_type &val) {
|
||||
// the empty indicator (if specified) and the deleted indicator
|
||||
// must be different
|
||||
assert(!use_empty || !equals(get_key(val), get_key(emptyval)));
|
||||
// It's only safe to change what "deleted" means if we purge deleted guys
|
||||
squash_deleted();
|
||||
use_deleted = true;
|
||||
set_value(&delval, val);
|
||||
}
|
||||
void clear_deleted_key() {
|
||||
squash_deleted();
|
||||
use_deleted = false;
|
||||
}
|
||||
|
||||
// These are public so the iterators can use them
|
||||
// True if the item at position bucknum is "deleted" marker
|
||||
bool test_deleted(size_type bucknum) const {
|
||||
// The num_deleted test is crucial for read(): after read(), the ht values
|
||||
// are garbage, and we don't want to think some of them are deleted.
|
||||
return (use_deleted && num_deleted > 0 &&
|
||||
equals(get_key(delval), get_key(table[bucknum])));
|
||||
}
|
||||
bool test_deleted(const iterator &it) const {
|
||||
return (use_deleted && num_deleted > 0 &&
|
||||
equals(get_key(delval), get_key(*it)));
|
||||
}
|
||||
bool test_deleted(const const_iterator &it) const {
|
||||
return (use_deleted && num_deleted > 0 &&
|
||||
equals(get_key(delval), get_key(*it)));
|
||||
}
|
||||
// Set it so test_deleted is true. true if object didn't used to be deleted
|
||||
// See below (at erase()) to explain why we allow const_iterators
|
||||
bool set_deleted(const_iterator &it) {
|
||||
assert(use_deleted); // bad if set_deleted_key() wasn't called
|
||||
bool retval = !test_deleted(it);
|
||||
// &* converts from iterator to value-type
|
||||
set_value(const_cast<value_type*>(&(*it)), delval);
|
||||
return retval;
|
||||
}
|
||||
// Set it so test_deleted is false. true if object used to be deleted
|
||||
bool clear_deleted(const_iterator &it) {
|
||||
assert(use_deleted); // bad if set_deleted_key() wasn't called
|
||||
// happens automatically when we assign something else in its place
|
||||
return test_deleted(it);
|
||||
}
|
||||
|
||||
// EMPTY HELPER FUNCTIONS
|
||||
// This lets the user describe a key that will indicate empty (unused)
|
||||
// table entries. This key should be an "impossible" entry --
|
||||
// if you try to insert it for real, you won't be able to retrieve it!
|
||||
// (NB: while you pass in an entire value, only the key part is looked
|
||||
// at. This is just because I don't know how to assign just a key.)
|
||||
public:
|
||||
// These are public so the iterators can use them
|
||||
// True if the item at position bucknum is "empty" marker
|
||||
bool test_empty(size_type bucknum) const {
|
||||
assert(use_empty); // we always need to know what's empty!
|
||||
return equals(get_key(emptyval), get_key(table[bucknum]));
|
||||
}
|
||||
bool test_empty(const iterator &it) const {
|
||||
assert(use_empty); // we always need to know what's empty!
|
||||
return equals(get_key(emptyval), get_key(*it));
|
||||
}
|
||||
bool test_empty(const const_iterator &it) const {
|
||||
assert(use_empty); // we always need to know what's empty!
|
||||
return equals(get_key(emptyval), get_key(*it));
|
||||
}
|
||||
|
||||
private:
|
||||
// You can either set a range empty or an individual element
|
||||
void set_empty(size_type bucknum) {
|
||||
assert(use_empty);
|
||||
set_value(&table[bucknum], emptyval);
|
||||
}
|
||||
void fill_range_with_empty(value_type* table_start, value_type* table_end) {
|
||||
// Like set_empty(range), but doesn't destroy previous contents
|
||||
STL_NAMESPACE::uninitialized_fill(table_start, table_end, emptyval);
|
||||
}
|
||||
void set_empty(size_type buckstart, size_type buckend) {
|
||||
assert(use_empty);
|
||||
destroy_buckets(buckstart, buckend);
|
||||
fill_range_with_empty(table + buckstart, table + buckend);
|
||||
}
|
||||
|
||||
public:
|
||||
// TODO(csilvers): change all callers of this to pass in a key instead,
|
||||
// and take a const key_type instead of const value_type.
|
||||
void set_empty_key(const value_type &val) {
|
||||
// Once you set the empty key, you can't change it
|
||||
assert(!use_empty);
|
||||
// The deleted indicator (if specified) and the empty indicator
|
||||
// must be different.
|
||||
assert(!use_deleted || !equals(get_key(val), get_key(delval)));
|
||||
use_empty = true;
|
||||
set_value(&emptyval, val);
|
||||
|
||||
assert(!table); // must set before first use
|
||||
// num_buckets was set in constructor even though table was NULL
|
||||
table = (value_type *) malloc(num_buckets * sizeof(*table));
|
||||
assert(table);
|
||||
fill_range_with_empty(table, table + num_buckets);
|
||||
}
|
||||
|
||||
// FUNCTIONS CONCERNING SIZE
|
||||
public:
|
||||
size_type size() const { return num_elements - num_deleted; }
|
||||
// Buckets are always a power of 2
|
||||
size_type max_size() const { return (size_type(-1) >> 1U) + 1; }
|
||||
bool empty() const { return size() == 0; }
|
||||
size_type bucket_count() const { return num_buckets; }
|
||||
size_type max_bucket_count() const { return max_size(); }
|
||||
size_type nonempty_bucket_count() const { return num_elements; }
|
||||
|
||||
private:
|
||||
// Because of the above, size_type(-1) is never legal; use it for errors
|
||||
static const size_type ILLEGAL_BUCKET = size_type(-1);
|
||||
|
||||
private:
|
||||
// This is the smallest size a hashtable can be without being too crowded
|
||||
// If you like, you can give a min #buckets as well as a min #elts
|
||||
size_type min_size(size_type num_elts, size_type min_buckets_wanted) {
|
||||
size_type sz = HT_MIN_BUCKETS; // min buckets allowed
|
||||
while ( sz < min_buckets_wanted || num_elts >= sz * enlarge_resize_percent )
|
||||
sz *= 2;
|
||||
return sz;
|
||||
}
|
||||
|
||||
// Used after a string of deletes
|
||||
void maybe_shrink() {
|
||||
assert(num_elements >= num_deleted);
|
||||
assert((bucket_count() & (bucket_count()-1)) == 0); // is a power of two
|
||||
assert(bucket_count() >= HT_MIN_BUCKETS);
|
||||
|
||||
// If you construct a hashtable with < HT_DEFAULT_STARTING_BUCKETS,
|
||||
// we'll never shrink until you get relatively big, and we'll never
|
||||
// shrink below HT_DEFAULT_STARTING_BUCKETS. Otherwise, something
|
||||
// like "dense_hash_set<int> x; x.insert(4); x.erase(4);" will
|
||||
// shrink us down to HT_MIN_BUCKETS buckets, which is too small.
|
||||
if (shrink_threshold > 0 &&
|
||||
(num_elements-num_deleted) < shrink_threshold &&
|
||||
bucket_count() > HT_DEFAULT_STARTING_BUCKETS ) {
|
||||
size_type sz = bucket_count() / 2; // find how much we should shrink
|
||||
while ( sz > HT_DEFAULT_STARTING_BUCKETS &&
|
||||
(num_elements - num_deleted) < sz * shrink_resize_percent )
|
||||
sz /= 2; // stay a power of 2
|
||||
dense_hashtable tmp(*this, sz); // Do the actual resizing
|
||||
swap(tmp); // now we are tmp
|
||||
}
|
||||
consider_shrink = false; // because we just considered it
|
||||
}
|
||||
|
||||
// We'll let you resize a hashtable -- though this makes us copy all!
|
||||
// When you resize, you say, "make it big enough for this many more elements"
|
||||
void resize_delta(size_type delta) {
|
||||
if ( consider_shrink ) // see if lots of deletes happened
|
||||
maybe_shrink();
|
||||
if ( bucket_count() > HT_MIN_BUCKETS &&
|
||||
(num_elements + delta) <= enlarge_threshold )
|
||||
return; // we're ok as we are
|
||||
|
||||
// Sometimes, we need to resize just to get rid of all the
|
||||
// "deleted" buckets that are clogging up the hashtable. So when
|
||||
// deciding whether to resize, count the deleted buckets (which
|
||||
// are currently taking up room). But later, when we decide what
|
||||
// size to resize to, *don't* count deleted buckets, since they
|
||||
// get discarded during the resize.
|
||||
const size_type needed_size = min_size(num_elements + delta, 0);
|
||||
if ( needed_size > bucket_count() ) { // we don't have enough buckets
|
||||
const size_type resize_to = min_size(num_elements - num_deleted + delta,
|
||||
0);
|
||||
dense_hashtable tmp(*this, resize_to);
|
||||
swap(tmp); // now we are tmp
|
||||
}
|
||||
}
|
||||
|
||||
// Increase number of buckets, assuming value_type has trivial copy
|
||||
// constructor and destructor. (Really, we want it to have "trivial
|
||||
// move", because that's what realloc does. But there's no way to
|
||||
// capture that using type_traits, so we pretend that move(x, y) is
|
||||
// equivalent to "x.~T(); new(x) T(y);" which is pretty much
|
||||
// correct, if a bit conservative.)
|
||||
void expand_array(size_t resize_to, true_type) {
|
||||
table = (value_type *) realloc(table, resize_to * sizeof(value_type));
|
||||
assert(table);
|
||||
fill_range_with_empty(table + num_buckets, table + resize_to);
|
||||
}
|
||||
|
||||
// Increase number of buckets, without special assumptions about value_type.
|
||||
// TODO(austern): make this exception safe. Handle exceptions from
|
||||
// value_type's copy constructor.
|
||||
void expand_array(size_t resize_to, false_type) {
|
||||
value_type* new_table =
|
||||
(value_type *) malloc(resize_to * sizeof(value_type));
|
||||
assert(new_table);
|
||||
STL_NAMESPACE::uninitialized_copy(table, table + num_buckets, new_table);
|
||||
fill_range_with_empty(new_table + num_buckets, new_table + resize_to);
|
||||
destroy_buckets(0, num_buckets);
|
||||
free(table);
|
||||
table = new_table;
|
||||
}
|
||||
|
||||
// Used to actually do the rehashing when we grow/shrink a hashtable
|
||||
void copy_from(const dense_hashtable &ht, size_type min_buckets_wanted) {
|
||||
clear(); // clear table, set num_deleted to 0
|
||||
|
||||
// If we need to change the size of our table, do it now
|
||||
const size_type resize_to = min_size(ht.size(), min_buckets_wanted);
|
||||
if ( resize_to > bucket_count() ) { // we don't have enough buckets
|
||||
typedef integral_constant<bool,
|
||||
(has_trivial_copy<value_type>::value &&
|
||||
has_trivial_destructor<value_type>::value)>
|
||||
realloc_ok; // we pretend mv(x,y) == "x.~T(); new(x) T(y)"
|
||||
expand_array(resize_to, realloc_ok());
|
||||
num_buckets = resize_to;
|
||||
reset_thresholds();
|
||||
}
|
||||
|
||||
// We use a normal iterator to get non-deleted bcks from ht
|
||||
// We could use insert() here, but since we know there are
|
||||
// no duplicates and no deleted items, we can be more efficient
|
||||
assert((bucket_count() & (bucket_count()-1)) == 0); // a power of two
|
||||
for ( const_iterator it = ht.begin(); it != ht.end(); ++it ) {
|
||||
size_type num_probes = 0; // how many times we've probed
|
||||
size_type bucknum;
|
||||
const size_type bucket_count_minus_one = bucket_count() - 1;
|
||||
for (bucknum = hash(get_key(*it)) & bucket_count_minus_one;
|
||||
!test_empty(bucknum); // not empty
|
||||
bucknum = (bucknum + JUMP_(key, num_probes)) & bucket_count_minus_one) {
|
||||
++num_probes;
|
||||
assert(num_probes < bucket_count()); // or else the hashtable is full
|
||||
}
|
||||
set_value(&table[bucknum], *it); // copies the value to here
|
||||
num_elements++;
|
||||
}
|
||||
}
|
||||
|
||||
// Required by the spec for hashed associative container
|
||||
public:
|
||||
// Though the docs say this should be num_buckets, I think it's much
|
||||
// more useful as req_elements. As a special feature, calling with
|
||||
// req_elements==0 will cause us to shrink if we can, saving space.
|
||||
void resize(size_type req_elements) { // resize to this or larger
|
||||
if ( consider_shrink || req_elements == 0 )
|
||||
maybe_shrink();
|
||||
if ( req_elements > num_elements )
|
||||
return resize_delta(req_elements - num_elements);
|
||||
}
|
||||
|
||||
// Change the value of shrink_resize_percent and
|
||||
// enlarge_resize_percent. The description at the beginning of this
|
||||
// file explains how to choose the values. Setting the shrink
|
||||
// parameter to 0.0 ensures that the table never shrinks.
|
||||
void set_resizing_parameters(float shrink, float grow) {
|
||||
assert(shrink >= 0.0);
|
||||
assert(grow <= 1.0);
|
||||
assert(shrink <= grow/2.0);
|
||||
shrink_resize_percent = shrink;
|
||||
enlarge_resize_percent = grow;
|
||||
reset_thresholds();
|
||||
}
|
||||
|
||||
// CONSTRUCTORS -- as required by the specs, we take a size,
|
||||
// but also let you specify a hashfunction, key comparator,
|
||||
// and key extractor. We also define a copy constructor and =.
|
||||
// DESTRUCTOR -- needs to free the table
|
||||
explicit dense_hashtable(size_type expected_max_items_in_table = 0,
|
||||
const HashFcn& hf = HashFcn(),
|
||||
const EqualKey& eql = EqualKey(),
|
||||
const ExtractKey& ext = ExtractKey())
|
||||
: hash(hf), equals(eql), get_key(ext), num_deleted(0),
|
||||
use_deleted(false), use_empty(false),
|
||||
delval(), emptyval(), enlarge_resize_percent(HT_OCCUPANCY_FLT),
|
||||
shrink_resize_percent(HT_EMPTY_FLT), table(NULL),
|
||||
num_buckets(expected_max_items_in_table == 0
|
||||
? HT_DEFAULT_STARTING_BUCKETS
|
||||
: min_size(expected_max_items_in_table, 0)),
|
||||
num_elements(0) {
|
||||
// table is NULL until emptyval is set. However, we set num_buckets
|
||||
// here so we know how much space to allocate once emptyval is set
|
||||
reset_thresholds();
|
||||
}
|
||||
|
||||
// As a convenience for resize(), we allow an optional second argument
|
||||
// which lets you make this new hashtable a different size than ht
|
||||
dense_hashtable(const dense_hashtable& ht,
|
||||
size_type min_buckets_wanted = HT_DEFAULT_STARTING_BUCKETS)
|
||||
: hash(ht.hash), equals(ht.equals), get_key(ht.get_key), num_deleted(0),
|
||||
use_deleted(ht.use_deleted), use_empty(ht.use_empty),
|
||||
delval(ht.delval), emptyval(ht.emptyval),
|
||||
enlarge_resize_percent(ht.enlarge_resize_percent),
|
||||
shrink_resize_percent(ht.shrink_resize_percent), table(NULL),
|
||||
num_buckets(0), num_elements(0) {
|
||||
reset_thresholds();
|
||||
copy_from(ht, min_buckets_wanted); // copy_from() ignores deleted entries
|
||||
}
|
||||
|
||||
dense_hashtable& operator= (const dense_hashtable& ht) {
|
||||
if (&ht == this) return *this; // don't copy onto ourselves
|
||||
clear();
|
||||
hash = ht.hash;
|
||||
equals = ht.equals;
|
||||
get_key = ht.get_key;
|
||||
use_deleted = ht.use_deleted;
|
||||
use_empty = ht.use_empty;
|
||||
set_value(&delval, ht.delval);
|
||||
set_value(&emptyval, ht.emptyval);
|
||||
enlarge_resize_percent = ht.enlarge_resize_percent;
|
||||
shrink_resize_percent = ht.shrink_resize_percent;
|
||||
copy_from(ht, HT_MIN_BUCKETS); // sets num_deleted to 0 too
|
||||
return *this;
|
||||
}
|
||||
|
||||
~dense_hashtable() {
|
||||
if (table) {
|
||||
destroy_buckets(0, num_buckets);
|
||||
free(table);
|
||||
}
|
||||
}
|
||||
|
||||
// Many STL algorithms use swap instead of copy constructors
|
||||
void swap(dense_hashtable& ht) {
|
||||
STL_NAMESPACE::swap(hash, ht.hash);
|
||||
STL_NAMESPACE::swap(equals, ht.equals);
|
||||
STL_NAMESPACE::swap(get_key, ht.get_key);
|
||||
STL_NAMESPACE::swap(num_deleted, ht.num_deleted);
|
||||
STL_NAMESPACE::swap(use_deleted, ht.use_deleted);
|
||||
STL_NAMESPACE::swap(use_empty, ht.use_empty);
|
||||
STL_NAMESPACE::swap(enlarge_resize_percent, ht.enlarge_resize_percent);
|
||||
STL_NAMESPACE::swap(shrink_resize_percent, ht.shrink_resize_percent);
|
||||
{ value_type tmp; // for annoying reasons, swap() doesn't work
|
||||
set_value(&tmp, delval);
|
||||
set_value(&delval, ht.delval);
|
||||
set_value(&ht.delval, tmp);
|
||||
}
|
||||
{ value_type tmp; // for annoying reasons, swap() doesn't work
|
||||
set_value(&tmp, emptyval);
|
||||
set_value(&emptyval, ht.emptyval);
|
||||
set_value(&ht.emptyval, tmp);
|
||||
}
|
||||
STL_NAMESPACE::swap(table, ht.table);
|
||||
STL_NAMESPACE::swap(num_buckets, ht.num_buckets);
|
||||
STL_NAMESPACE::swap(num_elements, ht.num_elements);
|
||||
reset_thresholds();
|
||||
ht.reset_thresholds();
|
||||
}
|
||||
|
||||
// It's always nice to be able to clear a table without deallocating it
|
||||
void clear() {
|
||||
if (table)
|
||||
destroy_buckets(0, num_buckets);
|
||||
num_buckets = min_size(0,0); // our new size
|
||||
reset_thresholds();
|
||||
table = (value_type *) realloc(table, num_buckets * sizeof(*table));
|
||||
assert(table);
|
||||
fill_range_with_empty(table, table + num_buckets);
|
||||
num_elements = 0;
|
||||
num_deleted = 0;
|
||||
}
|
||||
|
||||
// Clear the table without resizing it.
|
||||
// Mimicks the stl_hashtable's behaviour when clear()-ing in that it
|
||||
// does not modify the bucket count
|
||||
void clear_no_resize() {
|
||||
if (table) {
|
||||
set_empty(0, num_buckets);
|
||||
}
|
||||
// don't consider to shrink before another erase()
|
||||
reset_thresholds();
|
||||
num_elements = 0;
|
||||
num_deleted = 0;
|
||||
}
|
||||
|
||||
// LOOKUP ROUTINES
|
||||
private:
|
||||
// Returns a pair of positions: 1st where the object is, 2nd where
|
||||
// it would go if you wanted to insert it. 1st is ILLEGAL_BUCKET
|
||||
// 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) {
|
||||
// First, double-check we're not inserting delval or emptyval
|
||||
assert(!use_empty || !equals(get_key(obj), get_key(emptyval)));
|
||||
assert(!use_deleted || !equals(get_key(obj), get_key(delval)));
|
||||
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) {
|
||||
// First, double-check we're not trying to erase delval or emptyval
|
||||
assert(!use_empty || !equals(key, get_key(emptyval)));
|
||||
assert(!use_deleted || !equals(key, get_key(delval)));
|
||||
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 {
|
||||
if (size() != ht.size()) {
|
||||
return false;
|
||||
} else if (this == &ht) {
|
||||
return true;
|
||||
} else {
|
||||
// Iterate through the elements in "this" and see if the
|
||||
// corresponding element is in ht
|
||||
for ( const_iterator it = begin(); it != end(); ++it ) {
|
||||
const_iterator it2 = ht.find(get_key(*it));
|
||||
if ((it2 == ht.end()) || (*it != *it2)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
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);
|
||||
fill_range_with_empty(table, table + 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 value_type is a POD type
|
||||
// that contains 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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
private:
|
||||
// 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
|
||||
value_type delval; // which key marks deleted entries
|
||||
value_type emptyval; // which key marks unused entries
|
||||
float enlarge_resize_percent; // how full before resize
|
||||
float shrink_resize_percent; // how empty before resize
|
||||
size_type shrink_threshold; // num_buckets * shrink_resize_percent
|
||||
size_type enlarge_threshold; // num_buckets * enlarge_resize_percent
|
||||
value_type *table;
|
||||
size_type num_buckets;
|
||||
size_type num_elements;
|
||||
bool consider_shrink; // true if we should try to shrink before next insert
|
||||
|
||||
void reset_thresholds() {
|
||||
enlarge_threshold = static_cast<size_type>(num_buckets
|
||||
* enlarge_resize_percent);
|
||||
shrink_threshold = static_cast<size_type>(num_buckets
|
||||
* shrink_resize_percent);
|
||||
consider_shrink = false; // whatever caused us to reset already considered
|
||||
}
|
||||
};
|
||||
|
||||
// We need a global swap as well
|
||||
template <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);
|
||||
}
|
||||
|
||||
#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 memory
|
||||
template <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 resizing
|
||||
template <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.4f *
|
||||
dense_hashtable<V,K,HF,ExK,EqK,A>::HT_OCCUPANCY_FLT;
|
||||
|
||||
_END_GOOGLE_NAMESPACE_
|
||||
|
||||
#endif /* _DENSEHASHTABLE_H_ */
|
|
@ -0,0 +1,74 @@
|
|||
#ifndef SPARSEHASH_SPARSECONFIG_H__
|
||||
#define SPARSEHASH_SPARSECONFIG_H__
|
||||
|
||||
// [AIR] : I couldn't make the google "windows" folder concept work.
|
||||
// This does, and we only care of GCC and MSVC right now anyway.
|
||||
|
||||
#if defined( _MSC_VER )
|
||||
|
||||
#define GOOGLE_NAMESPACE google
|
||||
#define HASH_NAMESPACE stdext
|
||||
#define HASH_FUN_H <hash_map>
|
||||
#define SPARSEHASH_HASH HASH_NAMESPACE::hash_compare
|
||||
#undef HAVE_UINT16_T
|
||||
#undef HAVE_U_INT16_T
|
||||
#define HAVE___UINT16 1
|
||||
#define HAVE_LONG_LONG 1
|
||||
#define HAVE_SYS_TYPES_H 1
|
||||
#undef HAVE_STDINT_H
|
||||
#undef HAVE_INTTYPES_H
|
||||
#define HAVE_MEMCPY 1
|
||||
#define STL_NAMESPACE std
|
||||
#define _END_GOOGLE_NAMESPACE_ }
|
||||
#define _START_GOOGLE_NAMESPACE_ namespace GOOGLE_NAMESPACE {
|
||||
|
||||
#else //if defined( GNUC )
|
||||
|
||||
/* Namespace for Google classes */
|
||||
#define GOOGLE_NAMESPACE google
|
||||
|
||||
/* the location of <hash_fun.h>/<stl_hash_fun.h> */
|
||||
#define HASH_FUN_H <ext/hash_fun.h>
|
||||
|
||||
/* the namespace of hash_map/hash_set */
|
||||
#define HASH_NAMESPACE __gnu_cxx
|
||||
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#define HAVE_INTTYPES_H 1
|
||||
|
||||
/* Define to 1 if the system has the type `long long'. */
|
||||
#define HAVE_LONG_LONG 1
|
||||
|
||||
/* Define to 1 if you have the `memcpy' function. */
|
||||
#define HAVE_MEMCPY 1
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#define HAVE_STDINT_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||
#define HAVE_SYS_TYPES_H 1
|
||||
|
||||
/* Define to 1 if the system has the type `uint16_t'. */
|
||||
#define HAVE_UINT16_T 1
|
||||
|
||||
/* Define to 1 if the system has the type `u_int16_t'. */
|
||||
#define HAVE_U_INT16_T 1
|
||||
|
||||
/* Define to 1 if the system has the type `__uint16'. */
|
||||
/* #undef HAVE___UINT16 */
|
||||
|
||||
/* The system-provided hash function including the namespace. */
|
||||
#define SPARSEHASH_HASH HASH_NAMESPACE::hash
|
||||
|
||||
/* the namespace where STL code like vector<> is defined */
|
||||
#define STL_NAMESPACE std
|
||||
|
||||
/* Stops putting the code inside the Google namespace */
|
||||
#define _END_GOOGLE_NAMESPACE_ }
|
||||
|
||||
/* Puts following code inside the Google namespace */
|
||||
#define _START_GOOGLE_NAMESPACE_ namespace google {
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,941 @@
|
|||
// Copyright (c) 2005, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// ---
|
||||
// Author: Craig Silverstein
|
||||
//
|
||||
// A sparse hashtable is a particular implementation of
|
||||
// a hashtable: one that is meant to minimize memory use.
|
||||
// It does this by using a *sparse table* (cf sparsetable.h),
|
||||
// which uses between 1 and 2 bits to store empty buckets
|
||||
// (we may need another bit for hashtables that support deletion).
|
||||
//
|
||||
// When empty buckets are so cheap, an appealing hashtable
|
||||
// implementation is internal probing, in which the hashtable
|
||||
// is a single table, and collisions are resolved by trying
|
||||
// to insert again in another bucket. The most cache-efficient
|
||||
// internal probing schemes are linear probing (which suffers,
|
||||
// alas, from clumping) and quadratic probing, which is what
|
||||
// we implement by default.
|
||||
//
|
||||
// Deleted buckets are a bit of a pain. We have to somehow mark
|
||||
// deleted buckets (the probing must distinguish them from empty
|
||||
// buckets). The most principled way is to have another bitmap,
|
||||
// but that's annoying and takes up space. Instead we let the
|
||||
// user specify an "impossible" key. We set deleted buckets
|
||||
// to have the impossible key.
|
||||
//
|
||||
// Note it is possible to change the value of the delete key
|
||||
// on the fly; you can even remove it, though after that point
|
||||
// the hashtable is insert_only until you set it again.
|
||||
//
|
||||
// You probably shouldn't use this code directly. Use
|
||||
// <google/sparse_hash_table> or <google/sparse_hash_set> instead.
|
||||
//
|
||||
// You can modify the following, below:
|
||||
// HT_OCCUPANCY_FLT -- how full before we double size
|
||||
// HT_EMPTY_FLT -- how empty before we halve size
|
||||
// HT_MIN_BUCKETS -- smallest bucket size
|
||||
// HT_DEFAULT_STARTING_BUCKETS -- default bucket size at construct-time
|
||||
//
|
||||
// You can also change enlarge_resize_percent (which defaults to
|
||||
// HT_OCCUPANCY_FLT), and shrink_resize_percent (which defaults to
|
||||
// HT_EMPTY_FLT) with set_resizing_parameters().
|
||||
//
|
||||
// How to decide what values to use?
|
||||
// shrink_resize_percent's default of .4 * OCCUPANCY_FLT, is probably good.
|
||||
// HT_MIN_BUCKETS is probably unnecessary since you can specify
|
||||
// (indirectly) the starting number of buckets at construct-time.
|
||||
// For enlarge_resize_percent, you can use this chart to try to trade-off
|
||||
// expected lookup time to the space taken up. By default, this
|
||||
// code uses quadratic probing, though you can change it to linear
|
||||
// via _JUMP below if you really want to.
|
||||
//
|
||||
// From http://www.augustana.ca/~mohrj/courses/1999.fall/csc210/lecture_notes/hashing.html
|
||||
// NUMBER OF PROBES / LOOKUP Successful Unsuccessful
|
||||
// Quadratic collision resolution 1 - ln(1-L) - L/2 1/(1-L) - L - ln(1-L)
|
||||
// Linear collision resolution [1+1/(1-L)]/2 [1+1/(1-L)2]/2
|
||||
//
|
||||
// -- enlarge_resize_percent -- 0.10 0.50 0.60 0.75 0.80 0.90 0.99
|
||||
// QUADRATIC COLLISION RES.
|
||||
// probes/successful lookup 1.05 1.44 1.62 2.01 2.21 2.85 5.11
|
||||
// probes/unsuccessful lookup 1.11 2.19 2.82 4.64 5.81 11.4 103.6
|
||||
// LINEAR COLLISION RES.
|
||||
// probes/successful lookup 1.06 1.5 1.75 2.5 3.0 5.5 50.5
|
||||
// probes/unsuccessful lookup 1.12 2.5 3.6 8.5 13.0 50.0 5000.0
|
||||
//
|
||||
// The value type is required to be copy constructible and default
|
||||
// constructible, but it need not be (and commonly isn't) assignable.
|
||||
|
||||
#ifndef _SPARSEHASHTABLE_H_
|
||||
#define _SPARSEHASHTABLE_H_
|
||||
|
||||
#ifndef SPARSEHASH_STAT_UPDATE
|
||||
#define SPARSEHASH_STAT_UPDATE(x) ((void) 0)
|
||||
#endif
|
||||
|
||||
// The probing method
|
||||
// Linear probing
|
||||
// #define JUMP_(key, num_probes) ( 1 )
|
||||
// Quadratic-ish probing
|
||||
#define JUMP_(key, num_probes) ( num_probes )
|
||||
|
||||
|
||||
// Hashtable class, used to implement the hashed associative containers
|
||||
// hash_set and hash_map.
|
||||
|
||||
#include <google/sparsehash/sparseconfig.h>
|
||||
#include <assert.h>
|
||||
#include <algorithm> // For swap(), eg
|
||||
#include <iterator> // for facts about iterator tags
|
||||
#include <utility> // for pair<>
|
||||
#include <google/sparsetable> // Since that's basically what we are
|
||||
|
||||
_START_GOOGLE_NAMESPACE_
|
||||
|
||||
using STL_NAMESPACE::pair;
|
||||
|
||||
// Alloc is completely ignored. It is present as a template parameter only
|
||||
// for the sake of being compatible with the old SGI hashtable interface.
|
||||
// TODO(csilvers): is that the right thing to do?
|
||||
|
||||
template <class Value, class Key, class HashFcn,
|
||||
class ExtractKey, class EqualKey, class Alloc>
|
||||
class sparse_hashtable;
|
||||
|
||||
template <class V, class K, class HF, class ExK, class EqK, class A>
|
||||
struct sparse_hashtable_iterator;
|
||||
|
||||
template <class V, class K, class HF, class ExK, class EqK, class A>
|
||||
struct sparse_hashtable_const_iterator;
|
||||
|
||||
// As far as iterating, we're basically just a sparsetable
|
||||
// that skips over deleted elements.
|
||||
template <class V, class K, class HF, class ExK, class EqK, class A>
|
||||
struct sparse_hashtable_iterator {
|
||||
public:
|
||||
typedef sparse_hashtable_iterator<V,K,HF,ExK,EqK,A> iterator;
|
||||
typedef sparse_hashtable_const_iterator<V,K,HF,ExK,EqK,A> const_iterator;
|
||||
typedef typename sparsetable<V>::nonempty_iterator st_iterator;
|
||||
|
||||
typedef STL_NAMESPACE::forward_iterator_tag iterator_category;
|
||||
typedef V value_type;
|
||||
typedef ptrdiff_t difference_type;
|
||||
typedef size_t size_type;
|
||||
typedef V& reference; // Value
|
||||
typedef V* pointer;
|
||||
|
||||
// "Real" constructor and default constructor
|
||||
sparse_hashtable_iterator(const sparse_hashtable<V,K,HF,ExK,EqK,A> *h,
|
||||
st_iterator it, st_iterator it_end)
|
||||
: ht(h), pos(it), end(it_end) { advance_past_deleted(); }
|
||||
sparse_hashtable_iterator() { } // not ever used internally
|
||||
// The default destructor is fine; we don't define one
|
||||
// The default operator= is fine; we don't define one
|
||||
|
||||
// Happy dereferencer
|
||||
reference operator*() const { return *pos; }
|
||||
pointer operator->() const { return &(operator*()); }
|
||||
|
||||
// Arithmetic. The only hard part is making sure that
|
||||
// we're not on a marked-deleted array element
|
||||
void advance_past_deleted() {
|
||||
while ( pos != end && ht->test_deleted(*this) )
|
||||
++pos;
|
||||
}
|
||||
iterator& operator++() {
|
||||
assert(pos != end); ++pos; advance_past_deleted(); return *this;
|
||||
}
|
||||
iterator operator++(int) { iterator tmp(*this); ++*this; return tmp; }
|
||||
|
||||
// Comparison.
|
||||
bool operator==(const iterator& it) const { return pos == it.pos; }
|
||||
bool operator!=(const iterator& it) const { return pos != it.pos; }
|
||||
|
||||
|
||||
// The actual data
|
||||
const sparse_hashtable<V,K,HF,ExK,EqK,A> *ht;
|
||||
st_iterator pos, end;
|
||||
};
|
||||
|
||||
// Now do it all again, but with const-ness!
|
||||
template <class V, class K, class HF, class ExK, class EqK, class A>
|
||||
struct sparse_hashtable_const_iterator {
|
||||
public:
|
||||
typedef sparse_hashtable_iterator<V,K,HF,ExK,EqK,A> iterator;
|
||||
typedef sparse_hashtable_const_iterator<V,K,HF,ExK,EqK,A> const_iterator;
|
||||
typedef typename sparsetable<V>::const_nonempty_iterator st_iterator;
|
||||
|
||||
typedef STL_NAMESPACE::forward_iterator_tag iterator_category;
|
||||
typedef V value_type;
|
||||
typedef ptrdiff_t difference_type;
|
||||
typedef size_t size_type;
|
||||
typedef const V& reference; // Value
|
||||
typedef const V* pointer;
|
||||
|
||||
// "Real" constructor and default constructor
|
||||
sparse_hashtable_const_iterator(const sparse_hashtable<V,K,HF,ExK,EqK,A> *h,
|
||||
st_iterator it, st_iterator it_end)
|
||||
: ht(h), pos(it), end(it_end) { advance_past_deleted(); }
|
||||
// This lets us convert regular iterators to const iterators
|
||||
sparse_hashtable_const_iterator() { } // never used internally
|
||||
sparse_hashtable_const_iterator(const iterator &it)
|
||||
: ht(it.ht), pos(it.pos), end(it.end) { }
|
||||
// The default destructor is fine; we don't define one
|
||||
// The default operator= is fine; we don't define one
|
||||
|
||||
// Happy dereferencer
|
||||
reference operator*() const { return *pos; }
|
||||
pointer operator->() const { return &(operator*()); }
|
||||
|
||||
// Arithmetic. The only hard part is making sure that
|
||||
// we're not on a marked-deleted array element
|
||||
void advance_past_deleted() {
|
||||
while ( pos != end && ht->test_deleted(*this) )
|
||||
++pos;
|
||||
}
|
||||
const_iterator& operator++() {
|
||||
assert(pos != end); ++pos; advance_past_deleted(); return *this;
|
||||
}
|
||||
const_iterator operator++(int) { const_iterator tmp(*this); ++*this; return tmp; }
|
||||
|
||||
// Comparison.
|
||||
bool operator==(const const_iterator& it) const { return pos == it.pos; }
|
||||
bool operator!=(const const_iterator& it) const { return pos != it.pos; }
|
||||
|
||||
|
||||
// The actual data
|
||||
const sparse_hashtable<V,K,HF,ExK,EqK,A> *ht;
|
||||
st_iterator pos, end;
|
||||
};
|
||||
|
||||
// And once again, but this time freeing up memory as we iterate
|
||||
template <class V, class K, class HF, class ExK, class EqK, class A>
|
||||
struct sparse_hashtable_destructive_iterator {
|
||||
public:
|
||||
typedef sparse_hashtable_destructive_iterator<V,K,HF,ExK,EqK,A> iterator;
|
||||
typedef typename sparsetable<V>::destructive_iterator st_iterator;
|
||||
|
||||
typedef STL_NAMESPACE::forward_iterator_tag iterator_category;
|
||||
typedef V value_type;
|
||||
typedef ptrdiff_t difference_type;
|
||||
typedef size_t size_type;
|
||||
typedef V& reference; // Value
|
||||
typedef V* pointer;
|
||||
|
||||
// "Real" constructor and default constructor
|
||||
sparse_hashtable_destructive_iterator(const
|
||||
sparse_hashtable<V,K,HF,ExK,EqK,A> *h,
|
||||
st_iterator it, st_iterator it_end)
|
||||
: ht(h), pos(it), end(it_end) { advance_past_deleted(); }
|
||||
sparse_hashtable_destructive_iterator() { } // never used internally
|
||||
// The default destructor is fine; we don't define one
|
||||
// The default operator= is fine; we don't define one
|
||||
|
||||
// Happy dereferencer
|
||||
reference operator*() const { return *pos; }
|
||||
pointer operator->() const { return &(operator*()); }
|
||||
|
||||
// Arithmetic. The only hard part is making sure that
|
||||
// we're not on a marked-deleted array element
|
||||
void advance_past_deleted() {
|
||||
while ( pos != end && ht->test_deleted(*this) )
|
||||
++pos;
|
||||
}
|
||||
iterator& operator++() {
|
||||
assert(pos != end); ++pos; advance_past_deleted(); return *this;
|
||||
}
|
||||
iterator operator++(int) { iterator tmp(*this); ++*this; return tmp; }
|
||||
|
||||
// Comparison.
|
||||
bool operator==(const iterator& it) const { return pos == it.pos; }
|
||||
bool operator!=(const iterator& it) const { return pos != it.pos; }
|
||||
|
||||
|
||||
// The actual data
|
||||
const sparse_hashtable<V,K,HF,ExK,EqK,A> *ht;
|
||||
st_iterator pos, end;
|
||||
};
|
||||
|
||||
|
||||
template <class Value, class Key, class HashFcn,
|
||||
class ExtractKey, class EqualKey, class Alloc>
|
||||
class sparse_hashtable {
|
||||
public:
|
||||
typedef Key key_type;
|
||||
typedef Value value_type;
|
||||
typedef HashFcn hasher;
|
||||
typedef EqualKey key_equal;
|
||||
|
||||
typedef size_t size_type;
|
||||
typedef ptrdiff_t difference_type;
|
||||
typedef value_type* pointer;
|
||||
typedef const value_type* const_pointer;
|
||||
typedef value_type& reference;
|
||||
typedef const value_type& const_reference;
|
||||
typedef sparse_hashtable_iterator<Value, Key, HashFcn,
|
||||
ExtractKey, EqualKey, Alloc>
|
||||
iterator;
|
||||
|
||||
typedef sparse_hashtable_const_iterator<Value, Key, HashFcn,
|
||||
ExtractKey, EqualKey, Alloc>
|
||||
const_iterator;
|
||||
|
||||
typedef sparse_hashtable_destructive_iterator<Value, Key, HashFcn,
|
||||
ExtractKey, EqualKey, Alloc>
|
||||
destructive_iterator;
|
||||
|
||||
|
||||
// How full we let the table get before we resize. Knuth says .8 is
|
||||
// good -- higher causes us to probe too much, though saves memory
|
||||
static const float HT_OCCUPANCY_FLT; // = 0.8f;
|
||||
|
||||
// How empty we let the table get before we resize lower.
|
||||
// It should be less than OCCUPANCY_FLT / 2 or we thrash resizing
|
||||
static const float HT_EMPTY_FLT; // = 0.4 * HT_OCCUPANCY_FLT;
|
||||
|
||||
// Minimum size we're willing to let hashtables be.
|
||||
// Must be a power of two, and at least 4.
|
||||
// Note, however, that for a given hashtable, the minimum size is
|
||||
// determined by the first constructor arg, and may be >HT_MIN_BUCKETS.
|
||||
static const size_t HT_MIN_BUCKETS = 4;
|
||||
|
||||
// By default, if you don't specify a hashtable size at
|
||||
// construction-time, we use this size. Must be a power of two, and
|
||||
// at least HT_MIN_BUCKETS.
|
||||
static const size_t HT_DEFAULT_STARTING_BUCKETS = 32;
|
||||
|
||||
// ITERATOR FUNCTIONS
|
||||
iterator begin() { return iterator(this, table.nonempty_begin(),
|
||||
table.nonempty_end()); }
|
||||
iterator end() { return iterator(this, table.nonempty_end(),
|
||||
table.nonempty_end()); }
|
||||
const_iterator begin() const { return const_iterator(this,
|
||||
table.nonempty_begin(),
|
||||
table.nonempty_end()); }
|
||||
const_iterator end() const { return const_iterator(this,
|
||||
table.nonempty_end(),
|
||||
table.nonempty_end()); }
|
||||
|
||||
// This is used when resizing
|
||||
destructive_iterator destructive_begin() {
|
||||
return destructive_iterator(this, table.destructive_begin(),
|
||||
table.destructive_end());
|
||||
}
|
||||
destructive_iterator destructive_end() {
|
||||
return destructive_iterator(this, table.destructive_end(),
|
||||
table.destructive_end());
|
||||
}
|
||||
|
||||
|
||||
// ACCESSOR FUNCTIONS for the things we templatize on, basically
|
||||
hasher hash_funct() const { return hash; }
|
||||
key_equal key_eq() const { return equals; }
|
||||
|
||||
// We need to copy values when we set the special marker for deleted
|
||||
// elements, but, annoyingly, we can't just use the copy assignment
|
||||
// operator because value_type might not be assignable (it's often
|
||||
// pair<const X, Y>). We use explicit destructor invocation and
|
||||
// placement new to get around this. Arg.
|
||||
private:
|
||||
void set_value(value_type* dst, const value_type src) {
|
||||
dst->~value_type(); // delete the old value, if any
|
||||
new(dst) value_type(src);
|
||||
}
|
||||
|
||||
// This is used as a tag for the copy constructor, saying to destroy its
|
||||
// arg We have two ways of destructively copying: with potentially growing
|
||||
// the hashtable as we copy, and without. To make sure the outside world
|
||||
// can't do a destructive copy, we make the typename private.
|
||||
enum MoveDontCopyT {MoveDontCopy, MoveDontGrow};
|
||||
|
||||
|
||||
// DELETE HELPER FUNCTIONS
|
||||
// This lets the user describe a key that will indicate deleted
|
||||
// table entries. This key should be an "impossible" entry --
|
||||
// if you try to insert it for real, you won't be able to retrieve it!
|
||||
// (NB: while you pass in an entire value, only the key part is looked
|
||||
// at. This is just because I don't know how to assign just a key.)
|
||||
private:
|
||||
void squash_deleted() { // gets rid of any deleted entries we have
|
||||
if ( num_deleted ) { // get rid of deleted before writing
|
||||
sparse_hashtable tmp(MoveDontGrow, *this);
|
||||
swap(tmp); // now we are tmp
|
||||
}
|
||||
assert(num_deleted == 0);
|
||||
}
|
||||
|
||||
public:
|
||||
void set_deleted_key(const value_type &val) {
|
||||
// It's only safe to change what "deleted" means if we purge deleted guys
|
||||
squash_deleted();
|
||||
use_deleted = true;
|
||||
set_value(&delval, val); // save the key (and rest of val too)
|
||||
}
|
||||
void clear_deleted_key() {
|
||||
squash_deleted();
|
||||
use_deleted = false;
|
||||
}
|
||||
|
||||
// These are public so the iterators can use them
|
||||
// True if the item at position bucknum is "deleted" marker
|
||||
bool test_deleted(size_type bucknum) const {
|
||||
// The num_deleted test is crucial for read(): after read(), the ht values
|
||||
// are garbage, and we don't want to think some of them are deleted.
|
||||
return (use_deleted && num_deleted > 0 && table.test(bucknum) &&
|
||||
equals(get_key(delval), get_key(table.get(bucknum))));
|
||||
}
|
||||
bool test_deleted(const iterator &it) const {
|
||||
return (use_deleted && num_deleted > 0 &&
|
||||
equals(get_key(delval), get_key(*it)));
|
||||
}
|
||||
bool test_deleted(const const_iterator &it) const {
|
||||
return (use_deleted && num_deleted > 0 &&
|
||||
equals(get_key(delval), get_key(*it)));
|
||||
}
|
||||
bool test_deleted(const destructive_iterator &it) const {
|
||||
return (use_deleted && num_deleted > 0 &&
|
||||
equals(get_key(delval), get_key(*it)));
|
||||
}
|
||||
// Set it so test_deleted is true. true if object didn't used to be deleted
|
||||
// See below (at erase()) to explain why we allow const_iterators
|
||||
bool set_deleted(const_iterator &it) {
|
||||
assert(use_deleted); // bad if set_deleted_key() wasn't called
|
||||
bool retval = !test_deleted(it);
|
||||
// &* converts from iterator to value-type
|
||||
set_value(const_cast<value_type*>(&(*it)), delval);
|
||||
return retval;
|
||||
}
|
||||
// Set it so test_deleted is false. true if object used to be deleted
|
||||
bool clear_deleted(const_iterator &it) {
|
||||
assert(use_deleted); // bad if set_deleted_key() wasn't called
|
||||
// happens automatically when we assign something else in its place
|
||||
return test_deleted(it);
|
||||
}
|
||||
|
||||
|
||||
// FUNCTIONS CONCERNING SIZE
|
||||
size_type size() const { return table.num_nonempty() - num_deleted; }
|
||||
// Buckets are always a power of 2
|
||||
size_type max_size() const { return (size_type(-1) >> 1U) + 1; }
|
||||
bool empty() const { return size() == 0; }
|
||||
size_type bucket_count() const { return table.size(); }
|
||||
size_type max_bucket_count() const { return max_size(); }
|
||||
|
||||
private:
|
||||
// Because of the above, size_type(-1) is never legal; use it for errors
|
||||
static const size_type ILLEGAL_BUCKET = size_type(-1);
|
||||
|
||||
private:
|
||||
// This is the smallest size a hashtable can be without being too crowded
|
||||
// If you like, you can give a min #buckets as well as a min #elts
|
||||
size_type min_size(size_type num_elts, size_type min_buckets_wanted) {
|
||||
size_type sz = HT_MIN_BUCKETS;
|
||||
while ( sz < min_buckets_wanted || num_elts >= sz * enlarge_resize_percent )
|
||||
sz *= 2;
|
||||
return sz;
|
||||
}
|
||||
|
||||
// Used after a string of deletes
|
||||
void maybe_shrink() {
|
||||
assert(table.num_nonempty() >= num_deleted);
|
||||
assert((bucket_count() & (bucket_count()-1)) == 0); // is a power of two
|
||||
assert(bucket_count() >= HT_MIN_BUCKETS);
|
||||
|
||||
// If you construct a hashtable with < HT_DEFAULT_STARTING_BUCKETS,
|
||||
// we'll never shrink until you get relatively big, and we'll never
|
||||
// shrink below HT_DEFAULT_STARTING_BUCKETS. Otherwise, something
|
||||
// like "dense_hash_set<int> x; x.insert(4); x.erase(4);" will
|
||||
// shrink us down to HT_MIN_BUCKETS buckets, which is too small.
|
||||
if (shrink_threshold > 0
|
||||
&& (table.num_nonempty()-num_deleted) < shrink_threshold &&
|
||||
bucket_count() > HT_DEFAULT_STARTING_BUCKETS ) {
|
||||
size_type sz = bucket_count() / 2; // find how much we should shrink
|
||||
while ( sz > HT_DEFAULT_STARTING_BUCKETS &&
|
||||
(table.num_nonempty() - num_deleted) <= sz *
|
||||
shrink_resize_percent )
|
||||
sz /= 2; // stay a power of 2
|
||||
sparse_hashtable tmp(MoveDontCopy, *this, sz);
|
||||
swap(tmp); // now we are tmp
|
||||
}
|
||||
consider_shrink = false; // because we just considered it
|
||||
}
|
||||
|
||||
// We'll let you resize a hashtable -- though this makes us copy all!
|
||||
// When you resize, you say, "make it big enough for this many more elements"
|
||||
void resize_delta(size_type delta) {
|
||||
if ( consider_shrink ) // see if lots of deletes happened
|
||||
maybe_shrink();
|
||||
if ( bucket_count() >= HT_MIN_BUCKETS &&
|
||||
(table.num_nonempty() + delta) <= enlarge_threshold )
|
||||
return; // we're ok as we are
|
||||
|
||||
// Sometimes, we need to resize just to get rid of all the
|
||||
// "deleted" buckets that are clogging up the hashtable. So when
|
||||
// deciding whether to resize, count the deleted buckets (which
|
||||
// are currently taking up room). But later, when we decide what
|
||||
// size to resize to, *don't* count deleted buckets, since they
|
||||
// get discarded during the resize.
|
||||
const size_type needed_size = min_size(table.num_nonempty() + delta, 0);
|
||||
if ( needed_size > bucket_count() ) { // we don't have enough buckets
|
||||
const size_type resize_to = min_size(table.num_nonempty() - num_deleted
|
||||
+ delta, 0);
|
||||
sparse_hashtable tmp(MoveDontCopy, *this, resize_to);
|
||||
swap(tmp); // now we are tmp
|
||||
}
|
||||
}
|
||||
|
||||
// Used to actually do the rehashing when we grow/shrink a hashtable
|
||||
void copy_from(const sparse_hashtable &ht, size_type min_buckets_wanted) {
|
||||
clear(); // clear table, set num_deleted to 0
|
||||
|
||||
// If we need to change the size of our table, do it now
|
||||
const size_type resize_to = min_size(ht.size(), min_buckets_wanted);
|
||||
if ( resize_to > bucket_count() ) { // we don't have enough buckets
|
||||
table.resize(resize_to); // sets the number of buckets
|
||||
reset_thresholds();
|
||||
}
|
||||
|
||||
// We use a normal iterator to get non-deleted bcks from ht
|
||||
// We could use insert() here, but since we know there are
|
||||
// no duplicates and no deleted items, we can be more efficient
|
||||
assert( (bucket_count() & (bucket_count()-1)) == 0); // a power of two
|
||||
for ( const_iterator it = ht.begin(); it != ht.end(); ++it ) {
|
||||
size_type num_probes = 0; // how many times we've probed
|
||||
size_type bucknum;
|
||||
const size_type bucket_count_minus_one = bucket_count() - 1;
|
||||
for (bucknum = hash(get_key(*it)) & bucket_count_minus_one;
|
||||
table.test(bucknum); // not empty
|
||||
bucknum = (bucknum + JUMP_(key, num_probes)) & bucket_count_minus_one) {
|
||||
++num_probes;
|
||||
assert(num_probes < bucket_count()); // or else the hashtable is full
|
||||
}
|
||||
table.set(bucknum, *it); // copies the value to here
|
||||
}
|
||||
}
|
||||
|
||||
// Implementation is like copy_from, but it destroys the table of the
|
||||
// "from" guy by freeing sparsetable memory as we iterate. This is
|
||||
// useful in resizing, since we're throwing away the "from" guy anyway.
|
||||
void move_from(MoveDontCopyT mover, sparse_hashtable &ht,
|
||||
size_type min_buckets_wanted) {
|
||||
clear(); // clear table, set num_deleted to 0
|
||||
|
||||
// If we need to change the size of our table, do it now
|
||||
size_t resize_to;
|
||||
if ( mover == MoveDontGrow )
|
||||
resize_to = ht.bucket_count(); // keep same size as old ht
|
||||
else // MoveDontCopy
|
||||
resize_to = min_size(ht.size(), min_buckets_wanted);
|
||||
if ( resize_to > bucket_count() ) { // we don't have enough buckets
|
||||
table.resize(resize_to); // sets the number of buckets
|
||||
reset_thresholds();
|
||||
}
|
||||
|
||||
// We use a normal iterator to get non-deleted bcks from ht
|
||||
// We could use insert() here, but since we know there are
|
||||
// no duplicates and no deleted items, we can be more efficient
|
||||
assert( (bucket_count() & (bucket_count()-1)) == 0); // a power of two
|
||||
// THIS IS THE MAJOR LINE THAT DIFFERS FROM COPY_FROM():
|
||||
for ( destructive_iterator it = ht.destructive_begin();
|
||||
it != ht.destructive_end(); ++it ) {
|
||||
size_type num_probes = 0; // how many times we've probed
|
||||
size_type bucknum;
|
||||
for ( bucknum = hash(get_key(*it)) & (bucket_count()-1); // h % buck_cnt
|
||||
table.test(bucknum); // not empty
|
||||
bucknum = (bucknum + JUMP_(key, num_probes)) & (bucket_count()-1) ) {
|
||||
++num_probes;
|
||||
assert(num_probes < bucket_count()); // or else the hashtable is full
|
||||
}
|
||||
table.set(bucknum, *it); // copies the value to here
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Required by the spec for hashed associative container
|
||||
public:
|
||||
// Though the docs say this should be num_buckets, I think it's much
|
||||
// more useful as num_elements. As a special feature, calling with
|
||||
// req_elements==0 will cause us to shrink if we can, saving space.
|
||||
void resize(size_type req_elements) { // resize to this or larger
|
||||
if ( consider_shrink || req_elements == 0 )
|
||||
maybe_shrink();
|
||||
if ( req_elements > table.num_nonempty() ) // we only grow
|
||||
resize_delta(req_elements - table.num_nonempty());
|
||||
}
|
||||
|
||||
// Change the value of shrink_resize_percent and
|
||||
// enlarge_resize_percent. The description at the beginning of this
|
||||
// file explains how to choose the values. Setting the shrink
|
||||
// parameter to 0.0 ensures that the table never shrinks.
|
||||
void set_resizing_parameters(float shrink, float grow) {
|
||||
assert(shrink >= 0.0);
|
||||
assert(grow <= 1.0);
|
||||
assert(shrink <= grow/2.0);
|
||||
shrink_resize_percent = shrink;
|
||||
enlarge_resize_percent = grow;
|
||||
reset_thresholds();
|
||||
}
|
||||
|
||||
// CONSTRUCTORS -- as required by the specs, we take a size,
|
||||
// but also let you specify a hashfunction, key comparator,
|
||||
// and key extractor. We also define a copy constructor and =.
|
||||
// DESTRUCTOR -- the default is fine, surprisingly.
|
||||
explicit sparse_hashtable(size_type expected_max_items_in_table = 0,
|
||||
const HashFcn& hf = HashFcn(),
|
||||
const EqualKey& eql = EqualKey(),
|
||||
const ExtractKey& ext = ExtractKey())
|
||||
: hash(hf), equals(eql), get_key(ext), num_deleted(0), use_deleted(false),
|
||||
delval(), enlarge_resize_percent(HT_OCCUPANCY_FLT),
|
||||
shrink_resize_percent(HT_EMPTY_FLT),
|
||||
table(expected_max_items_in_table == 0
|
||||
? HT_DEFAULT_STARTING_BUCKETS
|
||||
: min_size(expected_max_items_in_table, 0)) {
|
||||
reset_thresholds();
|
||||
}
|
||||
|
||||
// As a convenience for resize(), we allow an optional second argument
|
||||
// which lets you make this new hashtable a different size than ht.
|
||||
// We also provide a mechanism of saying you want to "move" the ht argument
|
||||
// into us instead of copying.
|
||||
sparse_hashtable(const sparse_hashtable& ht,
|
||||
size_type min_buckets_wanted = HT_DEFAULT_STARTING_BUCKETS)
|
||||
: hash(ht.hash), equals(ht.equals), get_key(ht.get_key),
|
||||
num_deleted(0), use_deleted(ht.use_deleted), delval(ht.delval),
|
||||
enlarge_resize_percent(ht.enlarge_resize_percent),
|
||||
shrink_resize_percent(ht.shrink_resize_percent),
|
||||
table() {
|
||||
reset_thresholds();
|
||||
copy_from(ht, min_buckets_wanted); // copy_from() ignores deleted entries
|
||||
}
|
||||
sparse_hashtable(MoveDontCopyT mover, sparse_hashtable& ht,
|
||||
size_type min_buckets_wanted = HT_DEFAULT_STARTING_BUCKETS)
|
||||
: hash(ht.hash), equals(ht.equals), get_key(ht.get_key),
|
||||
num_deleted(0), use_deleted(ht.use_deleted), delval(ht.delval),
|
||||
enlarge_resize_percent(ht.enlarge_resize_percent),
|
||||
shrink_resize_percent(ht.shrink_resize_percent),
|
||||
table() {
|
||||
reset_thresholds();
|
||||
move_from(mover, ht, min_buckets_wanted); // ignores deleted entries
|
||||
}
|
||||
|
||||
sparse_hashtable& operator= (const sparse_hashtable& ht) {
|
||||
if (&ht == this) return *this; // don't copy onto ourselves
|
||||
clear();
|
||||
hash = ht.hash;
|
||||
equals = ht.equals;
|
||||
get_key = ht.get_key;
|
||||
use_deleted = ht.use_deleted;
|
||||
set_value(&delval, ht.delval);
|
||||
copy_from(ht, HT_MIN_BUCKETS); // sets num_deleted to 0 too
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Many STL algorithms use swap instead of copy constructors
|
||||
void swap(sparse_hashtable& ht) {
|
||||
STL_NAMESPACE::swap(hash, ht.hash);
|
||||
STL_NAMESPACE::swap(equals, ht.equals);
|
||||
STL_NAMESPACE::swap(get_key, ht.get_key);
|
||||
STL_NAMESPACE::swap(num_deleted, ht.num_deleted);
|
||||
STL_NAMESPACE::swap(use_deleted, ht.use_deleted);
|
||||
STL_NAMESPACE::swap(enlarge_resize_percent, ht.enlarge_resize_percent);
|
||||
STL_NAMESPACE::swap(shrink_resize_percent, ht.shrink_resize_percent);
|
||||
{ value_type tmp; // for annoying reasons, swap() doesn't work
|
||||
set_value(&tmp, delval);
|
||||
set_value(&delval, ht.delval);
|
||||
set_value(&ht.delval, tmp);
|
||||
}
|
||||
table.swap(ht.table);
|
||||
reset_thresholds();
|
||||
ht.reset_thresholds();
|
||||
}
|
||||
|
||||
// It's always nice to be able to clear a table without deallocating it
|
||||
void clear() {
|
||||
table.clear();
|
||||
reset_thresholds();
|
||||
num_deleted = 0;
|
||||
}
|
||||
|
||||
|
||||
// LOOKUP ROUTINES
|
||||
private:
|
||||
// Returns a pair of positions: 1st where the object is, 2nd where
|
||||
// it would go if you wanted to insert it. 1st is ILLEGAL_BUCKET
|
||||
// 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
|
||||
SPARSEHASH_STAT_UPDATE(total_lookups += 1);
|
||||
while ( 1 ) { // probe until something happens
|
||||
if ( !table.test(bucknum) ) { // bucket is empty
|
||||
SPARSEHASH_STAT_UPDATE(total_probes += num_probes);
|
||||
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.get(bucknum))) ) {
|
||||
SPARSEHASH_STAT_UPDATE(total_probes += num_probes);
|
||||
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.get_iter(pos.first), table.nonempty_end());
|
||||
}
|
||||
|
||||
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.get_iter(pos.first), table.nonempty_end());
|
||||
}
|
||||
|
||||
// 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) {
|
||||
// First, double-check we're not inserting delval
|
||||
assert(!use_deleted || !equals(get_key(obj), get_key(delval)));
|
||||
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.get_iter(pos.first),
|
||||
table.nonempty_end()),
|
||||
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.
|
||||
// The set() below will undelete this object. We just worry about stats
|
||||
assert(num_deleted > 0);
|
||||
--num_deleted; // used to be, now it isn't
|
||||
}
|
||||
table.set(pos.second, obj);
|
||||
return pair<iterator,bool>(iterator(this, table.get_iter(pos.second),
|
||||
table.nonempty_end()),
|
||||
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) {
|
||||
// First, double-check we're not erasing delval
|
||||
assert(!use_deleted || !equals(key, get_key(delval)));
|
||||
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 sparse_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 sparse_hashtable& ht) const {
|
||||
return !(*this == ht);
|
||||
}
|
||||
|
||||
|
||||
// I/O
|
||||
// We support reading and writing hashtables to disk. NOTE that
|
||||
// this only stores the hashtable metadata, not the stuff you've
|
||||
// actually put in the hashtable! 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.
|
||||
bool write_metadata(FILE *fp) {
|
||||
squash_deleted(); // so we don't have to worry about delkey
|
||||
return table.write_metadata(fp);
|
||||
}
|
||||
|
||||
bool read_metadata(FILE *fp) {
|
||||
num_deleted = 0; // since we got rid before writing
|
||||
bool result = table.read_metadata(fp);
|
||||
reset_thresholds();
|
||||
return result;
|
||||
}
|
||||
|
||||
// Only meaningful if value_type is a POD.
|
||||
bool write_nopointer_data(FILE *fp) {
|
||||
return table.write_nopointer_data(fp);
|
||||
}
|
||||
|
||||
// Only meaningful if value_type is a POD.
|
||||
bool read_nopointer_data(FILE *fp) {
|
||||
return table.read_nopointer_data(fp);
|
||||
}
|
||||
|
||||
private:
|
||||
// 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
|
||||
value_type delval; // which key marks deleted entries
|
||||
float enlarge_resize_percent; // how full before resize
|
||||
float shrink_resize_percent; // how empty before resize
|
||||
size_type shrink_threshold; // table.size() * shrink_resize_percent
|
||||
size_type enlarge_threshold; // table.size() * enlarge_resize_percent
|
||||
sparsetable<value_type> table; // holds num_buckets and num_elements too
|
||||
bool consider_shrink; // true if we should try to shrink before next insert
|
||||
|
||||
void reset_thresholds() {
|
||||
enlarge_threshold = static_cast<size_type>(table.size()
|
||||
* enlarge_resize_percent);
|
||||
shrink_threshold = static_cast<size_type>(table.size()
|
||||
* shrink_resize_percent);
|
||||
consider_shrink = false; // whatever caused us to reset already considered
|
||||
}
|
||||
};
|
||||
|
||||
// We need a global swap as well
|
||||
template <class V, class K, class HF, class ExK, class EqK, class A>
|
||||
inline void swap(sparse_hashtable<V,K,HF,ExK,EqK,A> &x,
|
||||
sparse_hashtable<V,K,HF,ExK,EqK,A> &y) {
|
||||
x.swap(y);
|
||||
}
|
||||
|
||||
#undef JUMP_
|
||||
|
||||
template <class V, class K, class HF, class ExK, class EqK, class A>
|
||||
const typename sparse_hashtable<V,K,HF,ExK,EqK,A>::size_type
|
||||
sparse_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 memory
|
||||
template <class V, class K, class HF, class ExK, class EqK, class A>
|
||||
const float sparse_hashtable<V,K,HF,ExK,EqK,A>::HT_OCCUPANCY_FLT = 0.8f;
|
||||
|
||||
// How empty we let the table get before we resize lower.
|
||||
// It should be less than OCCUPANCY_FLT / 2 or we thrash resizing
|
||||
template <class V, class K, class HF, class ExK, class EqK, class A>
|
||||
const float sparse_hashtable<V,K,HF,ExK,EqK,A>::HT_EMPTY_FLT = 0.4f *
|
||||
sparse_hashtable<V,K,HF,ExK,EqK,A>::HT_OCCUPANCY_FLT;
|
||||
|
||||
_END_GOOGLE_NAMESPACE_
|
||||
|
||||
#endif /* _SPARSEHASHTABLE_H_ */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,250 @@
|
|||
// Copyright (c) 2006, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// ----
|
||||
// Author: Matt Austern
|
||||
//
|
||||
// Define a small subset of tr1 type traits. The traits we define are:
|
||||
// is_integral
|
||||
// is_floating_point
|
||||
// is_pointer
|
||||
// is_reference
|
||||
// is_pod
|
||||
// has_trivial_constructor
|
||||
// has_trivial_copy
|
||||
// has_trivial_assign
|
||||
// has_trivial_destructor
|
||||
// remove_const
|
||||
// remove_volatile
|
||||
// remove_cv
|
||||
// remove_reference
|
||||
// remove_pointer
|
||||
// is_convertible
|
||||
// We can add more type traits as required.
|
||||
|
||||
#ifndef BASE_TYPE_TRAITS_H_
|
||||
#define BASE_TYPE_TRAITS_H_
|
||||
|
||||
#include <google/sparsehash/sparseconfig.h>
|
||||
#include <utility> // For pair
|
||||
|
||||
_START_GOOGLE_NAMESPACE_
|
||||
|
||||
// integral_constant, defined in tr1, is a wrapper for an integer
|
||||
// value. We don't really need this generality; we could get away
|
||||
// with hardcoding the integer type to bool. We use the fully
|
||||
// general integer_constant for compatibility with tr1.
|
||||
|
||||
template<class T, T v>
|
||||
struct integral_constant {
|
||||
static const T value = v;
|
||||
typedef T value_type;
|
||||
typedef integral_constant<T, v> type;
|
||||
};
|
||||
|
||||
template <class T, T v> const T integral_constant<T, v>::value;
|
||||
|
||||
// Abbreviations: true_type and false_type are structs that represent
|
||||
// boolean true and false values.
|
||||
typedef integral_constant<bool, true> true_type;
|
||||
typedef integral_constant<bool, false> false_type;
|
||||
|
||||
// Types small_ and big_ are guaranteed such that sizeof(small_) <
|
||||
// sizeof(big_)
|
||||
typedef char small_;
|
||||
|
||||
struct big_ {
|
||||
char dummy[2];
|
||||
};
|
||||
|
||||
// is_integral is false except for the built-in integer types.
|
||||
template <class T> struct is_integral : false_type { };
|
||||
template<> struct is_integral<bool> : true_type { };
|
||||
template<> struct is_integral<char> : true_type { };
|
||||
template<> struct is_integral<unsigned char> : true_type { };
|
||||
template<> struct is_integral<signed char> : true_type { };
|
||||
#if defined(_MSC_VER)
|
||||
// wchar_t is not by default a distinct type from unsigned short in
|
||||
// Microsoft C.
|
||||
// See http://msdn2.microsoft.com/en-us/library/dh8che7s(VS.80).aspx
|
||||
template<> struct is_integral<__wchar_t> : true_type { };
|
||||
#else
|
||||
template<> struct is_integral<wchar_t> : true_type { };
|
||||
#endif
|
||||
template<> struct is_integral<short> : true_type { };
|
||||
template<> struct is_integral<unsigned short> : true_type { };
|
||||
template<> struct is_integral<int> : true_type { };
|
||||
template<> struct is_integral<unsigned int> : true_type { };
|
||||
template<> struct is_integral<long> : true_type { };
|
||||
template<> struct is_integral<unsigned long> : true_type { };
|
||||
#ifdef HAVE_LONG_LONG
|
||||
template<> struct is_integral<long long> : true_type { };
|
||||
template<> struct is_integral<unsigned long long> : true_type { };
|
||||
#endif
|
||||
|
||||
|
||||
// is_floating_point is false except for the built-in floating-point types.
|
||||
template <class T> struct is_floating_point : false_type { };
|
||||
template<> struct is_floating_point<float> : true_type { };
|
||||
template<> struct is_floating_point<double> : true_type { };
|
||||
template<> struct is_floating_point<long double> : true_type { };
|
||||
|
||||
|
||||
// is_pointer is false except for pointer types.
|
||||
template <class T> struct is_pointer : false_type { };
|
||||
template <class T> struct is_pointer<T*> : true_type { };
|
||||
|
||||
|
||||
// is_reference is false except for reference types.
|
||||
template<typename T> struct is_reference : false_type {};
|
||||
template<typename T> struct is_reference<T&> : true_type {};
|
||||
|
||||
|
||||
// We can't get is_pod right without compiler help, so fail conservatively.
|
||||
// We will assume it's false except for arithmetic types and pointers,
|
||||
// and const versions thereof. Note that std::pair is not a POD.
|
||||
template <class T> struct is_pod
|
||||
: integral_constant<bool, (is_integral<T>::value ||
|
||||
is_floating_point<T>::value ||
|
||||
is_pointer<T>::value)> { };
|
||||
template <class T> struct is_pod<const T> : is_pod<T> { };
|
||||
|
||||
|
||||
// We can't get has_trivial_constructor right without compiler help, so
|
||||
// fail conservatively. We will assume it's false except for: (1) types
|
||||
// for which is_pod is true. (2) std::pair of types with trivial
|
||||
// constructors. (3) array of a type with a trivial constructor.
|
||||
// (4) const versions thereof.
|
||||
template <class T> struct has_trivial_constructor : is_pod<T> { };
|
||||
template <class T, class U> struct has_trivial_constructor<std::pair<T, U> >
|
||||
: integral_constant<bool,
|
||||
(has_trivial_constructor<T>::value &&
|
||||
has_trivial_constructor<U>::value)> { };
|
||||
template <class A, int N> struct has_trivial_constructor<A[N]>
|
||||
: has_trivial_constructor<A> { };
|
||||
template <class T> struct has_trivial_constructor<const T>
|
||||
: has_trivial_constructor<T> { };
|
||||
|
||||
// We can't get has_trivial_copy right without compiler help, so fail
|
||||
// conservatively. We will assume it's false except for: (1) types
|
||||
// for which is_pod is true. (2) std::pair of types with trivial copy
|
||||
// constructors. (3) array of a type with a trivial copy constructor.
|
||||
// (4) const versions thereof.
|
||||
template <class T> struct has_trivial_copy : is_pod<T> { };
|
||||
template <class T, class U> struct has_trivial_copy<std::pair<T, U> >
|
||||
: integral_constant<bool,
|
||||
(has_trivial_copy<T>::value &&
|
||||
has_trivial_copy<U>::value)> { };
|
||||
template <class A, int N> struct has_trivial_copy<A[N]>
|
||||
: has_trivial_copy<A> { };
|
||||
template <class T> struct has_trivial_copy<const T> : has_trivial_copy<T> { };
|
||||
|
||||
// We can't get has_trivial_assign right without compiler help, so fail
|
||||
// conservatively. We will assume it's false except for: (1) types
|
||||
// for which is_pod is true. (2) std::pair of types with trivial copy
|
||||
// constructors. (3) array of a type with a trivial assign constructor.
|
||||
template <class T> struct has_trivial_assign : is_pod<T> { };
|
||||
template <class T, class U> struct has_trivial_assign<std::pair<T, U> >
|
||||
: integral_constant<bool,
|
||||
(has_trivial_assign<T>::value &&
|
||||
has_trivial_assign<U>::value)> { };
|
||||
template <class A, int N> struct has_trivial_assign<A[N]>
|
||||
: has_trivial_assign<A> { };
|
||||
|
||||
// We can't get has_trivial_destructor right without compiler help, so
|
||||
// fail conservatively. We will assume it's false except for: (1) types
|
||||
// for which is_pod is true. (2) std::pair of types with trivial
|
||||
// destructors. (3) array of a type with a trivial destructor.
|
||||
// (4) const versions thereof.
|
||||
template <class T> struct has_trivial_destructor : is_pod<T> { };
|
||||
template <class T, class U> struct has_trivial_destructor<std::pair<T, U> >
|
||||
: integral_constant<bool,
|
||||
(has_trivial_destructor<T>::value &&
|
||||
has_trivial_destructor<U>::value)> { };
|
||||
template <class A, int N> struct has_trivial_destructor<A[N]>
|
||||
: has_trivial_destructor<A> { };
|
||||
template <class T> struct has_trivial_destructor<const T>
|
||||
: has_trivial_destructor<T> { };
|
||||
|
||||
// Specified by TR1 [4.7.1]
|
||||
template<typename T> struct remove_const { typedef T type; };
|
||||
template<typename T> struct remove_const<T const> { typedef T type; };
|
||||
template<typename T> struct remove_volatile { typedef T type; };
|
||||
template<typename T> struct remove_volatile<T volatile> { typedef T type; };
|
||||
template<typename T> struct remove_cv {
|
||||
typedef typename remove_const<typename remove_volatile<T>::type>::type type;
|
||||
};
|
||||
|
||||
|
||||
// Specified by TR1 [4.7.2]
|
||||
template<typename T> struct remove_reference { typedef T type; };
|
||||
template<typename T> struct remove_reference<T&> { typedef T type; };
|
||||
|
||||
// Specified by TR1 [4.7.4] Pointer modifications.
|
||||
template<typename T> struct remove_pointer { typedef T type; };
|
||||
template<typename T> struct remove_pointer<T*> { typedef T type; };
|
||||
template<typename T> struct remove_pointer<T* const> { typedef T type; };
|
||||
template<typename T> struct remove_pointer<T* volatile> { typedef T type; };
|
||||
template<typename T> struct remove_pointer<T* const volatile> {
|
||||
typedef T type; };
|
||||
|
||||
// Specified by TR1 [4.6] Relationships between types
|
||||
#ifndef _MSC_VER
|
||||
namespace internal {
|
||||
|
||||
// This class is an implementation detail for is_convertible, and you
|
||||
// don't need to know how it works to use is_convertible. For those
|
||||
// who care: we declare two different functions, one whose argument is
|
||||
// of type To and one with a variadic argument list. We give them
|
||||
// return types of different size, so we can use sizeof to trick the
|
||||
// compiler into telling us which function it would have chosen if we
|
||||
// had called it with an argument of type From. See Alexandrescu's
|
||||
// _Modern C++ Design_ for more details on this sort of trick.
|
||||
|
||||
template <typename From, typename To>
|
||||
struct ConvertHelper {
|
||||
static small_ Test(To);
|
||||
static big_ Test(...);
|
||||
static From Create();
|
||||
};
|
||||
} // namespace internal
|
||||
|
||||
// Inherits from true_type if From is convertible to To, false_type otherwise.
|
||||
template <typename From, typename To>
|
||||
struct is_convertible
|
||||
: integral_constant<bool,
|
||||
sizeof(internal::ConvertHelper<From, To>::Test(
|
||||
internal::ConvertHelper<From, To>::Create()))
|
||||
== sizeof(small_)> {
|
||||
};
|
||||
#endif
|
||||
|
||||
_END_GOOGLE_NAMESPACE_
|
||||
|
||||
#endif // BASE_TYPE_TRAITS_H_
|
|
@ -130,13 +130,13 @@ int hwMFIFOWrite(u32 addr, u8 *data, u32 size) {
|
|||
/* it does, so first copy 's1' bytes from 'data' to 'addr' */
|
||||
dst = (u8*)PSM(addr);
|
||||
if (dst == NULL) return -1;
|
||||
Cpu->Clear(addr, s1/4);
|
||||
//Cpu->Clear(addr, s1/4);
|
||||
memcpy_fast(dst, data, s1);
|
||||
|
||||
/* and second copy 's2' bytes from '&data[s1]' to 'maddr' */
|
||||
dst = (u8*)PSM(psHu32(DMAC_RBOR));
|
||||
if (dst == NULL) return -1;
|
||||
Cpu->Clear(psHu32(DMAC_RBOR), s2/4);
|
||||
//Cpu->Clear(psHu32(DMAC_RBOR), s2/4);
|
||||
memcpy_fast(dst, &data[s1], s2);
|
||||
} else {
|
||||
//u32 * tempptr, * tempptr2;
|
||||
|
@ -144,7 +144,7 @@ int hwMFIFOWrite(u32 addr, u8 *data, u32 size) {
|
|||
/* it doesn't, so just copy 'size' bytes from 'data' to 'addr' */
|
||||
dst = (u8*)PSM(addr);
|
||||
if (dst == NULL) return -1;
|
||||
Cpu->Clear(addr, size/4);
|
||||
//Cpu->Clear(addr, size/4);
|
||||
memcpy_fast(dst, data, size);
|
||||
}
|
||||
|
||||
|
|
2140
pcsx2/MemoryVM.cpp
2140
pcsx2/MemoryVM.cpp
File diff suppressed because it is too large
Load Diff
|
@ -81,7 +81,7 @@ int _SPR0chain() {
|
|||
mfifotransferred += spr0->qwc;
|
||||
} else {
|
||||
memcpy_fast((u8*)pMem, &PS2MEM_SCRATCH[spr0->sadr & 0x3fff], spr0->qwc << 4);
|
||||
Cpu->Clear(spr0->madr, spr0->qwc<<2);
|
||||
//Cpu->Clear(spr0->madr, spr0->qwc<<2);
|
||||
// clear VU mem also!
|
||||
TestClearVUs(spr0->madr, spr0->qwc << 2); // Wtf is going on here? AFAIK, only VIF should affect VU micromem (cottonvibes)
|
||||
|
||||
|
@ -117,7 +117,7 @@ void _SPR0interleave() {
|
|||
hwMFIFOWrite(spr0->madr, (u8*)&PS2MEM_SCRATCH[spr0->sadr & 0x3fff], spr0->qwc<<4);
|
||||
mfifotransferred += spr0->qwc;
|
||||
} else {
|
||||
Cpu->Clear(spr0->madr, spr0->qwc<<2);
|
||||
//Cpu->Clear(spr0->madr, spr0->qwc<<2);
|
||||
// clear VU mem also!
|
||||
TestClearVUs(spr0->madr, spr0->qwc<<2);
|
||||
memcpy_fast((u8*)pMem, &PS2MEM_SCRATCH[spr0->sadr & 0x3fff], spr0->qwc<<4);
|
||||
|
|
|
@ -757,10 +757,6 @@
|
|||
/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\WinVM.cpp"
|
||||
>
|
||||
</File>
|
||||
<Filter
|
||||
Name="Debugger"
|
||||
>
|
||||
|
@ -911,6 +907,14 @@
|
|||
<Filter
|
||||
Name="Misc"
|
||||
>
|
||||
<File
|
||||
RelativePath="..\..\HashMap.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\HashTools.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\Misc.cpp"
|
||||
>
|
||||
|
@ -2049,10 +2053,6 @@
|
|||
RelativePath="..\..\x86\iR5900Branch.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\x86\iR5900CoissuedLoadStore.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\x86\iR5900Jump.h"
|
||||
>
|
||||
|
@ -2229,34 +2229,6 @@
|
|||
<Filter
|
||||
Name="Dynarec"
|
||||
>
|
||||
<File
|
||||
RelativePath="..\..\x86\iPsxMem.cpp"
|
||||
>
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32"
|
||||
ExcludedFromBuild="true"
|
||||
>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Devel|Win32"
|
||||
ExcludedFromBuild="true"
|
||||
>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Release|Win32"
|
||||
ExcludedFromBuild="true"
|
||||
>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\x86\iR3000A.cpp"
|
||||
>
|
||||
|
@ -2382,10 +2354,6 @@
|
|||
RelativePath="..\..\Memory.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\MemoryVM.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\x86\ix86-32\recVTLB.cpp"
|
||||
>
|
||||
|
@ -3106,10 +3074,6 @@
|
|||
RelativePath="..\..\Stats.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\WinDebugResource"
|
||||
>
|
||||
</File>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
|
|
|
@ -1,470 +0,0 @@
|
|||
/* Pcsx2 - Pc Ps2 Emulator
|
||||
* Copyright (C) 2002-2009 Pcsx2 Team
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
#include "win32.h"
|
||||
|
||||
|
||||
#ifdef PCSX2_VIRTUAL_MEM
|
||||
|
||||
// virtual memory/privileges
|
||||
#include "ntsecapi.h"
|
||||
|
||||
static wchar_t s_szUserName[255];
|
||||
|
||||
LRESULT WINAPI UserNameProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
|
||||
switch(uMsg) {
|
||||
case WM_INITDIALOG:
|
||||
SetWindowPos(hDlg, HWND_TOPMOST, 200, 100, 0, 0, SWP_NOSIZE);
|
||||
return TRUE;
|
||||
|
||||
case WM_COMMAND:
|
||||
switch(wParam) {
|
||||
case IDOK:
|
||||
{
|
||||
wchar_t str[255];
|
||||
GetWindowTextW(GetDlgItem(hDlg, IDC_USER_NAME), str, 255);
|
||||
swprintf(s_szUserName, 255, L"%hs", &str);
|
||||
EndDialog(hDlg, TRUE );
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
case IDCANCEL:
|
||||
EndDialog(hDlg, FALSE );
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL InitLsaString(
|
||||
PLSA_UNICODE_STRING pLsaString,
|
||||
LPCWSTR pwszString
|
||||
)
|
||||
{
|
||||
DWORD dwLen = 0;
|
||||
|
||||
if (NULL == pLsaString)
|
||||
return FALSE;
|
||||
|
||||
if (NULL != pwszString)
|
||||
{
|
||||
dwLen = wcslen(pwszString);
|
||||
if (dwLen > 0x7ffe) // String is too large
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Store the string.
|
||||
pLsaString->Buffer = (WCHAR *)pwszString;
|
||||
pLsaString->Length = (USHORT)dwLen * sizeof(WCHAR);
|
||||
pLsaString->MaximumLength= (USHORT)(dwLen+1) * sizeof(WCHAR);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
PLSA_TRANSLATED_SID2 GetSIDInformation (LPWSTR AccountName,LSA_HANDLE PolicyHandle)
|
||||
{
|
||||
LSA_UNICODE_STRING lucName;
|
||||
PLSA_TRANSLATED_SID2 ltsTranslatedSID;
|
||||
PLSA_REFERENCED_DOMAIN_LIST lrdlDomainList;
|
||||
//LSA_TRUST_INFORMATION myDomain;
|
||||
NTSTATUS ntsResult;
|
||||
PWCHAR DomainString = NULL;
|
||||
|
||||
// Initialize an LSA_UNICODE_STRING with the name.
|
||||
if (!InitLsaString(&lucName, AccountName))
|
||||
{
|
||||
wprintf(L"Failed InitLsaString\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ntsResult = LsaLookupNames2(
|
||||
PolicyHandle, // handle to a Policy object
|
||||
0,
|
||||
1, // number of names to look up
|
||||
&lucName, // pointer to an array of names
|
||||
&lrdlDomainList, // receives domain information
|
||||
<sTranslatedSID // receives relative SIDs
|
||||
);
|
||||
if (0 != ntsResult)
|
||||
{
|
||||
wprintf(L"Failed LsaLookupNames - %lu \n",
|
||||
LsaNtStatusToWinError(ntsResult));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Get the domain the account resides in.
|
||||
// myDomain = lrdlDomainList->Domains[ltsTranslatedSID->DomainIndex];
|
||||
// DomainString = (PWCHAR) LocalAlloc(LPTR, myDomain.Name.Length + 1);
|
||||
// wcsncpy(DomainString, myDomain.Name.Buffer, myDomain.Name.Length);
|
||||
|
||||
// Display the relative Id.
|
||||
// wprintf(L"Relative Id is %lu in domain %ws.\n",
|
||||
// ltsTranslatedSID->RelativeId,
|
||||
// DomainString);
|
||||
|
||||
LsaFreeMemory(lrdlDomainList);
|
||||
|
||||
return ltsTranslatedSID;
|
||||
}
|
||||
|
||||
BOOL AddPrivileges(PSID AccountSID, LSA_HANDLE PolicyHandle, BOOL bAdd)
|
||||
{
|
||||
LSA_UNICODE_STRING lucPrivilege;
|
||||
NTSTATUS ntsResult;
|
||||
|
||||
// Create an LSA_UNICODE_STRING for the privilege name(s).
|
||||
if (!InitLsaString(&lucPrivilege, L"SeLockMemoryPrivilege"))
|
||||
{
|
||||
wprintf(L"Failed InitLsaString\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if( bAdd ) {
|
||||
ntsResult = LsaAddAccountRights(
|
||||
PolicyHandle, // An open policy handle.
|
||||
AccountSID, // The target SID.
|
||||
&lucPrivilege, // The privilege(s).
|
||||
1 // Number of privileges.
|
||||
);
|
||||
}
|
||||
else {
|
||||
ntsResult = LsaRemoveAccountRights(
|
||||
PolicyHandle, // An open policy handle.
|
||||
AccountSID, // The target SID
|
||||
FALSE,
|
||||
&lucPrivilege, // The privilege(s).
|
||||
1 // Number of privileges.
|
||||
);
|
||||
}
|
||||
|
||||
if (ntsResult == 0)
|
||||
{
|
||||
wprintf(L"Privilege added.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
int err = LsaNtStatusToWinError(ntsResult);
|
||||
char str[255];
|
||||
_snprintf(str, 255, "Privilege was not added - %lu \n", LsaNtStatusToWinError(ntsResult));
|
||||
MessageBox(NULL, str, "Privilege error", MB_OK);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#define TARGET_SYSTEM_NAME L"mysystem"
|
||||
LSA_HANDLE GetPolicyHandle()
|
||||
{
|
||||
LSA_OBJECT_ATTRIBUTES ObjectAttributes;
|
||||
WCHAR SystemName[] = TARGET_SYSTEM_NAME;
|
||||
USHORT SystemNameLength;
|
||||
LSA_UNICODE_STRING lusSystemName;
|
||||
NTSTATUS ntsResult;
|
||||
LSA_HANDLE lsahPolicyHandle;
|
||||
|
||||
// Object attributes are reserved, so initialize to zeroes.
|
||||
ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes));
|
||||
|
||||
//Initialize an LSA_UNICODE_STRING to the server name.
|
||||
SystemNameLength = wcslen(SystemName);
|
||||
lusSystemName.Buffer = SystemName;
|
||||
lusSystemName.Length = SystemNameLength * sizeof(WCHAR);
|
||||
lusSystemName.MaximumLength = (SystemNameLength+1) * sizeof(WCHAR);
|
||||
|
||||
// Get a handle to the Policy object.
|
||||
ntsResult = LsaOpenPolicy(
|
||||
NULL, //Name of the target system.
|
||||
&ObjectAttributes, //Object attributes.
|
||||
POLICY_ALL_ACCESS, //Desired access permissions.
|
||||
&lsahPolicyHandle //Receives the policy handle.
|
||||
);
|
||||
|
||||
if (ntsResult != 0)
|
||||
{
|
||||
// An error occurred. Display it as a win32 error code.
|
||||
wprintf(L"OpenPolicy returned %lu\n",
|
||||
LsaNtStatusToWinError(ntsResult));
|
||||
return NULL;
|
||||
}
|
||||
return lsahPolicyHandle;
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************
|
||||
LoggedSetLockPagesPrivilege: a function to obtain, if possible, or
|
||||
release the privilege of locking physical pages.
|
||||
|
||||
Inputs:
|
||||
|
||||
HANDLE hProcess: Handle for the process for which the
|
||||
privilege is needed
|
||||
|
||||
BOOL bEnable: Enable (TRUE) or disable?
|
||||
|
||||
Return value: TRUE indicates success, FALSE failure.
|
||||
|
||||
*****************************************************************/
|
||||
BOOL SysLoggedSetLockPagesPrivilege ( HANDLE hProcess, BOOL bEnable)
|
||||
{
|
||||
struct {
|
||||
u32 Count;
|
||||
LUID_AND_ATTRIBUTES Privilege [1];
|
||||
} Info;
|
||||
|
||||
HANDLE Token;
|
||||
BOOL Result;
|
||||
|
||||
// Open the token.
|
||||
|
||||
Result = OpenProcessToken ( hProcess,
|
||||
TOKEN_ADJUST_PRIVILEGES,
|
||||
& Token);
|
||||
|
||||
if( Result != TRUE ) {
|
||||
Console::Error( "VirtualMemory Error > Cannot open process token." );
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Enable or disable?
|
||||
|
||||
Info.Count = 1;
|
||||
if( bEnable )
|
||||
{
|
||||
Info.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED;
|
||||
}
|
||||
else
|
||||
{
|
||||
Info.Privilege[0].Attributes = SE_PRIVILEGE_REMOVED;
|
||||
}
|
||||
|
||||
// Get the LUID.
|
||||
Result = LookupPrivilegeValue ( NULL,
|
||||
SE_LOCK_MEMORY_NAME,
|
||||
&(Info.Privilege[0].Luid));
|
||||
|
||||
if( Result != TRUE )
|
||||
{
|
||||
Console::Error( "VirtualMemory Error > Cannot get privilege value for %s.", params SE_LOCK_MEMORY_NAME );
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Adjust the privilege.
|
||||
|
||||
Result = AdjustTokenPrivileges ( Token, FALSE,
|
||||
(PTOKEN_PRIVILEGES) &Info,
|
||||
0, NULL, NULL);
|
||||
|
||||
// Check the result.
|
||||
if( Result != TRUE )
|
||||
{
|
||||
Console::Error( "VirtualMemory Error > Cannot adjust token privileges, error %u.", params GetLastError() );
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( GetLastError() != ERROR_SUCCESS )
|
||||
{
|
||||
|
||||
BOOL bSuc = FALSE;
|
||||
LSA_HANDLE policy;
|
||||
PLSA_TRANSLATED_SID2 ltsTranslatedSID;
|
||||
|
||||
// if( !DialogBox(gApp.hInstance, MAKEINTRESOURCE(IDD_USERNAME), gApp.hWnd, (DLGPROC)UserNameProc) )
|
||||
// return FALSE;
|
||||
DWORD len = sizeof(s_szUserName);
|
||||
GetUserNameW(s_szUserName, &len);
|
||||
|
||||
policy = GetPolicyHandle();
|
||||
|
||||
if( policy != NULL ) {
|
||||
|
||||
ltsTranslatedSID = GetSIDInformation(s_szUserName, policy);
|
||||
|
||||
if( ltsTranslatedSID != NULL ) {
|
||||
bSuc = AddPrivileges(ltsTranslatedSID->Sid, policy, bEnable);
|
||||
LsaFreeMemory(ltsTranslatedSID);
|
||||
}
|
||||
|
||||
LsaClose(policy);
|
||||
}
|
||||
|
||||
if( bSuc ) {
|
||||
// Get the LUID.
|
||||
LookupPrivilegeValue ( NULL, SE_LOCK_MEMORY_NAME, &(Info.Privilege[0].Luid));
|
||||
|
||||
bSuc = AdjustTokenPrivileges ( Token, FALSE, (PTOKEN_PRIVILEGES) &Info, 0, NULL, NULL);
|
||||
}
|
||||
|
||||
if( bSuc ) {
|
||||
if( MessageBox(NULL, "PCSX2 just changed your SE_LOCK_MEMORY privilege in order to gain access to physical memory.\n"
|
||||
"Log off/on and run pcsx2 again. Do you want to log off?\n",
|
||||
"Privilege changed query", MB_YESNO) == IDYES ) {
|
||||
ExitWindows(EWX_LOGOFF, 0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
MessageBox(NULL, "Failed adding SE_LOCK_MEMORY privilege, please check the local policy.\n"
|
||||
"Go to security settings->Local Policies->User Rights. There should be a \"Lock pages in memory\".\n"
|
||||
"Add your user to that and log off/on. This enables pcsx2 to run at real-time by allocating physical memory.\n"
|
||||
"Also can try Control Panel->Local Security Policy->... (this does not work on Windows XP Home)\n"
|
||||
"(zerofrog)\n", "Virtual Memory Access Denied", MB_OK);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CloseHandle( Token );
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static u32 s_dwPageSize = 0;
|
||||
int SysPhysicalAlloc(u32 size, PSMEMORYBLOCK* pblock)
|
||||
{
|
||||
//#ifdef WIN32_FILE_MAPPING
|
||||
// assert(0);
|
||||
//#endif
|
||||
ULONG_PTR NumberOfPagesInitial; // initial number of pages requested
|
||||
int PFNArraySize; // memory to request for PFN array
|
||||
BOOL bResult;
|
||||
|
||||
assert( pblock != NULL );
|
||||
memset(pblock, 0, sizeof(PSMEMORYBLOCK));
|
||||
|
||||
if( s_dwPageSize == 0 ) {
|
||||
SYSTEM_INFO sSysInfo; // useful system information
|
||||
GetSystemInfo(&sSysInfo); // fill the system information structure
|
||||
s_dwPageSize = sSysInfo.dwPageSize;
|
||||
|
||||
if( s_dwPageSize != 0x1000 ) {
|
||||
Msgbox::Alert("Error! OS page size must be 4Kb!\n"
|
||||
"If for some reason the OS cannot have 4Kb pages, then run the TLB build.");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the number of pages of memory to request.
|
||||
pblock->NumberPages = (size+s_dwPageSize-1)/s_dwPageSize;
|
||||
PFNArraySize = pblock->NumberPages * sizeof (ULONG_PTR);
|
||||
|
||||
pblock->aPFNs = (uptr*)HeapAlloc (GetProcessHeap (), 0, PFNArraySize);
|
||||
|
||||
if (pblock->aPFNs == NULL) {
|
||||
Console::Error("Failed to allocate on heap.");
|
||||
goto eCleanupAndExit;
|
||||
}
|
||||
|
||||
// Allocate the physical memory.
|
||||
NumberOfPagesInitial = pblock->NumberPages;
|
||||
bResult = AllocateUserPhysicalPages( GetCurrentProcess(), (PULONG_PTR)&pblock->NumberPages, (PULONG_PTR)pblock->aPFNs );
|
||||
|
||||
if( bResult != TRUE )
|
||||
{
|
||||
Console::Error("Virtual Memory Error %u > Cannot allocate physical pages.", params GetLastError() );
|
||||
goto eCleanupAndExit;
|
||||
}
|
||||
|
||||
if( NumberOfPagesInitial != pblock->NumberPages )
|
||||
{
|
||||
Console::Error("Virtual Memory > Physical allocation failed!\n\tAllocated only %p of %p pages.", params pblock->NumberPages, NumberOfPagesInitial );
|
||||
goto eCleanupAndExit;
|
||||
}
|
||||
|
||||
pblock->aVFNs = (uptr*)HeapAlloc(GetProcessHeap(), 0, PFNArraySize);
|
||||
|
||||
return 0;
|
||||
|
||||
eCleanupAndExit:
|
||||
SysPhysicalFree(pblock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void SysPhysicalFree(PSMEMORYBLOCK* pblock)
|
||||
{
|
||||
assert( pblock != NULL );
|
||||
|
||||
// Free the physical pages.
|
||||
FreeUserPhysicalPages( GetCurrentProcess(), (PULONG_PTR)&pblock->NumberPages, (PULONG_PTR)pblock->aPFNs );
|
||||
|
||||
if( pblock->aPFNs != NULL ) HeapFree(GetProcessHeap(), 0, pblock->aPFNs);
|
||||
if( pblock->aVFNs != NULL ) HeapFree(GetProcessHeap(), 0, pblock->aVFNs);
|
||||
memset(pblock, 0, sizeof(PSMEMORYBLOCK));
|
||||
}
|
||||
|
||||
int SysVirtualPhyAlloc(void* base, u32 size, PSMEMORYBLOCK* pblock)
|
||||
{
|
||||
BOOL bResult;
|
||||
int i;
|
||||
|
||||
LPVOID lpMemReserved = VirtualAlloc( base, size, MEM_RESERVE | MEM_PHYSICAL, PAGE_READWRITE );
|
||||
if( lpMemReserved == NULL || base != lpMemReserved )
|
||||
{
|
||||
Console::WriteLn("VirtualMemory Error %d > Cannot reserve memory at 0x%8.8x(%x).", params base, lpMemReserved, GetLastError());
|
||||
goto eCleanupAndExit;
|
||||
}
|
||||
|
||||
// Map the physical memory into the window.
|
||||
bResult = MapUserPhysicalPages( base, (ULONG_PTR)pblock->NumberPages, (PULONG_PTR)pblock->aPFNs );
|
||||
|
||||
for(i = 0; i < pblock->NumberPages; ++i)
|
||||
pblock->aVFNs[i] = (uptr)base + 0x1000*i;
|
||||
|
||||
if( bResult != TRUE )
|
||||
{
|
||||
Console::WriteLn("VirtualMemory Error %u > MapUserPhysicalPages failed to map.", params GetLastError() );
|
||||
goto eCleanupAndExit;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
eCleanupAndExit:
|
||||
SysVirtualFree(base, size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void SysVirtualFree(void* lpMemReserved, u32 size)
|
||||
{
|
||||
// unmap
|
||||
if( MapUserPhysicalPages( lpMemReserved, (size+s_dwPageSize-1)/s_dwPageSize, NULL ) != TRUE )
|
||||
{
|
||||
Console::WriteLn("VirtualMemory Error %u > MapUserPhysicalPages failed to unmap", params GetLastError() );
|
||||
return;
|
||||
}
|
||||
|
||||
// Free virtual memory.
|
||||
VirtualFree( lpMemReserved, 0, MEM_RELEASE );
|
||||
}
|
||||
|
||||
int SysMapUserPhysicalPages(void* Addr, uptr NumPages, uptr* pfn, int pageoffset)
|
||||
{
|
||||
BOOL bResult = MapUserPhysicalPages(Addr, NumPages, (PULONG_PTR)(pfn+pageoffset));
|
||||
|
||||
#ifdef _DEBUG
|
||||
//if( !bResult )
|
||||
//__Log("Failed to map user pages: 0x%x:0x%x, error = %d\n", Addr, NumPages, GetLastError());
|
||||
#endif
|
||||
|
||||
return bResult;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#endif
|
|
@ -1,298 +0,0 @@
|
|||
/* Pcsx2 - Pc Ps2 Emulator
|
||||
* Copyright (C) 2002-2009 Pcsx2 Team
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "Common.h"
|
||||
#include "VU.h"
|
||||
|
||||
#include "iR5900.h"
|
||||
|
||||
#include "GS.h"
|
||||
#include "DebugTools/Debug.h"
|
||||
|
||||
extern u8 g_RealGSMem[0x2000];
|
||||
#define PS2GS_BASE(mem) (g_RealGSMem+(mem&0x13ff))
|
||||
|
||||
// __thiscall -- Calling Convention Notes.
|
||||
|
||||
// ** MSVC passes the pointer to the object as ECX. Other parameters are passed normally
|
||||
// (_cdecl style). Stack is cleaned by the callee.
|
||||
|
||||
// ** GCC works just like a __cdecl, except the pointer to the object is pushed onto the
|
||||
// stack last (passed as the first parameter). Caller cleans up the stack.
|
||||
|
||||
// The GCC code below is untested. Hope it works. :| (air)
|
||||
|
||||
|
||||
// Used to send 8, 16, and 32 bit values to the MTGS.
|
||||
static void __fastcall _rec_mtgs_Send32orSmaller( GS_RINGTYPE ringtype, u32 mem, int mmreg )
|
||||
{
|
||||
iFlushCall(0);
|
||||
|
||||
PUSH32I( 0 );
|
||||
_callPushArg( mmreg, 0 );
|
||||
PUSH32I( mem&0x13ff );
|
||||
PUSH32I( ringtype );
|
||||
|
||||
#ifdef _MSC_VER
|
||||
MOV32ItoR( ECX, (uptr)mtgsThread );
|
||||
CALLFunc( mtgsThread->FnPtr_SimplePacket() );
|
||||
#else // GCC -->
|
||||
PUSH32I( (uptr)mtgsThread );
|
||||
CALLFunc( mtgsThread->FnPtr_SimplePacket() );
|
||||
ADD32ItoR( ESP, 20 );
|
||||
#endif
|
||||
}
|
||||
|
||||
// Used to send 64 and 128 bit values to the MTGS (called twice for 128's, which
|
||||
// is why it doesn't call iFlushCall)
|
||||
static void __fastcall _rec_mtgs_Send64( uptr gsbase, u32 mem, int mmreg )
|
||||
{
|
||||
PUSH32M( gsbase+4 );
|
||||
PUSH32M( gsbase );
|
||||
PUSH32I( mem&0x13ff );
|
||||
PUSH32I( GS_RINGTYPE_MEMWRITE64 );
|
||||
|
||||
#ifdef _MSC_VER
|
||||
MOV32ItoR( ECX, (uptr)mtgsThread );
|
||||
CALLFunc( mtgsThread->FnPtr_SimplePacket() );
|
||||
#else // GCC -->
|
||||
PUSH32I( (uptr)mtgsThread );
|
||||
CALLFunc( mtgsThread->FnPtr_SimplePacket() );
|
||||
ADD32ItoR( ESP, 20 );
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void gsConstWrite8(u32 mem, int mmreg)
|
||||
{
|
||||
switch (mem&~3) {
|
||||
case 0x12001000: // GS_CSR
|
||||
_eeMoveMMREGtoR(EAX, mmreg);
|
||||
iFlushCall(0);
|
||||
MOV32MtoR(ECX, (uptr)&CSRw);
|
||||
AND32ItoR(EAX, 0xff<<(mem&3)*8);
|
||||
AND32ItoR(ECX, ~(0xff<<(mem&3)*8));
|
||||
OR32ItoR(EAX, ECX);
|
||||
_callFunctionArg1((uptr)gsCSRwrite, EAX|MEM_X86TAG, 0);
|
||||
break;
|
||||
default:
|
||||
_eeWriteConstMem8( (uptr)PS2GS_BASE(mem), mmreg );
|
||||
|
||||
if( mtgsThread != NULL )
|
||||
_rec_mtgs_Send32orSmaller( GS_RINGTYPE_MEMWRITE8, mem, mmreg );
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void gsConstWrite16(u32 mem, int mmreg)
|
||||
{
|
||||
switch (mem&~3) {
|
||||
|
||||
case 0x12000010: // GS_SMODE1
|
||||
case 0x12000020: // GS_SMODE2
|
||||
// SMODE1 and SMODE2 fall back on the gsWrite library.
|
||||
iFlushCall(0);
|
||||
_callFunctionArg2((uptr)gsWrite16, MEM_CONSTTAG, mmreg, mem, 0 );
|
||||
break;
|
||||
|
||||
case 0x12001000: // GS_CSR
|
||||
|
||||
assert( !(mem&2) );
|
||||
_eeMoveMMREGtoR(EAX, mmreg);
|
||||
iFlushCall(0);
|
||||
|
||||
MOV32MtoR(ECX, (uptr)&CSRw);
|
||||
AND32ItoR(EAX, 0xffff<<(mem&2)*8);
|
||||
AND32ItoR(ECX, ~(0xffff<<(mem&2)*8));
|
||||
OR32ItoR(EAX, ECX);
|
||||
_callFunctionArg1((uptr)gsCSRwrite, EAX|MEM_X86TAG, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
_eeWriteConstMem16( (uptr)PS2GS_BASE(mem), mmreg );
|
||||
|
||||
if( mtgsThread != NULL )
|
||||
_rec_mtgs_Send32orSmaller( GS_RINGTYPE_MEMWRITE16, mem, mmreg );
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// (value&0x1f00)|0x6000
|
||||
void gsConstWriteIMR(int mmreg)
|
||||
{
|
||||
const u32 mem = 0x12001010;
|
||||
if( mmreg & MEM_XMMTAG ) {
|
||||
SSE2_MOVD_XMM_to_M32((uptr)PS2GS_BASE(mem), mmreg&0xf);
|
||||
AND32ItoM((uptr)PS2GS_BASE(mem), 0x1f00);
|
||||
OR32ItoM((uptr)PS2GS_BASE(mem), 0x6000);
|
||||
}
|
||||
else if( mmreg & MEM_MMXTAG ) {
|
||||
SetMMXstate();
|
||||
MOVDMMXtoM((uptr)PS2GS_BASE(mem), mmreg&0xf);
|
||||
AND32ItoM((uptr)PS2GS_BASE(mem), 0x1f00);
|
||||
OR32ItoM((uptr)PS2GS_BASE(mem), 0x6000);
|
||||
}
|
||||
else if( mmreg & MEM_EECONSTTAG ) {
|
||||
MOV32ItoM( (uptr)PS2GS_BASE(mem), (g_cpuConstRegs[(mmreg>>16)&0x1f].UL[0]&0x1f00)|0x6000);
|
||||
}
|
||||
else {
|
||||
AND32ItoR(mmreg, 0x1f00);
|
||||
OR32ItoR(mmreg, 0x6000);
|
||||
MOV32RtoM( (uptr)PS2GS_BASE(mem), mmreg );
|
||||
}
|
||||
|
||||
// IMR doesn't need to be updated in MTGS mode
|
||||
}
|
||||
|
||||
void gsConstWrite32(u32 mem, int mmreg) {
|
||||
|
||||
switch (mem) {
|
||||
|
||||
case 0x12000010: // GS_SMODE1
|
||||
case 0x12000020: // GS_SMODE2
|
||||
// SMODE1 and SMODE2 fall back on the gsWrite library.
|
||||
iFlushCall(0);
|
||||
_callFunctionArg2((uptr)gsWrite32, MEM_CONSTTAG, mmreg, mem, 0 );
|
||||
break;
|
||||
|
||||
case 0x12001000: // GS_CSR
|
||||
iFlushCall(0);
|
||||
_callFunctionArg1((uptr)gsCSRwrite, mmreg, 0);
|
||||
break;
|
||||
|
||||
case 0x12001010: // GS_IMR
|
||||
gsConstWriteIMR(mmreg);
|
||||
break;
|
||||
default:
|
||||
_eeWriteConstMem32( (uptr)PS2GS_BASE(mem), mmreg );
|
||||
|
||||
if( mtgsThread != NULL )
|
||||
_rec_mtgs_Send32orSmaller( GS_RINGTYPE_MEMWRITE32, mem, mmreg );
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void gsConstWrite64(u32 mem, int mmreg)
|
||||
{
|
||||
switch (mem) {
|
||||
case 0x12000010: // GS_SMODE1
|
||||
case 0x12000020: // GS_SMODE2
|
||||
// SMODE1 and SMODE2 fall back on the gsWrite library.
|
||||
// the low 32 bit dword is all the SMODE regs care about.
|
||||
iFlushCall(0);
|
||||
_callFunctionArg2((uptr)gsWrite32, MEM_CONSTTAG, mmreg, mem, 0 );
|
||||
break;
|
||||
|
||||
case 0x12001000: // GS_CSR
|
||||
iFlushCall(0);
|
||||
_callFunctionArg1((uptr)gsCSRwrite, mmreg, 0);
|
||||
break;
|
||||
|
||||
case 0x12001010: // GS_IMR
|
||||
gsConstWriteIMR(mmreg);
|
||||
break;
|
||||
|
||||
default:
|
||||
_eeWriteConstMem64((uptr)PS2GS_BASE(mem), mmreg);
|
||||
|
||||
if( mtgsThread != NULL )
|
||||
{
|
||||
iFlushCall( 0 );
|
||||
_rec_mtgs_Send64( (uptr)PS2GS_BASE(mem), mem, mmreg );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void gsConstWrite128(u32 mem, int mmreg)
|
||||
{
|
||||
switch (mem) {
|
||||
case 0x12000010: // GS_SMODE1
|
||||
case 0x12000020: // GS_SMODE2
|
||||
// SMODE1 and SMODE2 fall back on the gsWrite library.
|
||||
// the low 32 bit dword is all the SMODE regs care about.
|
||||
iFlushCall(0);
|
||||
_callFunctionArg2((uptr)gsWrite32, MEM_CONSTTAG, mmreg, mem, 0 );
|
||||
break;
|
||||
|
||||
case 0x12001000: // GS_CSR
|
||||
iFlushCall(0);
|
||||
_callFunctionArg1((uptr)gsCSRwrite, mmreg, 0);
|
||||
break;
|
||||
|
||||
case 0x12001010: // GS_IMR
|
||||
// (value&0x1f00)|0x6000
|
||||
gsConstWriteIMR(mmreg);
|
||||
break;
|
||||
|
||||
default:
|
||||
_eeWriteConstMem128( (uptr)PS2GS_BASE(mem), mmreg);
|
||||
|
||||
if( mtgsThread != NULL )
|
||||
{
|
||||
iFlushCall(0);
|
||||
_rec_mtgs_Send64( (uptr)PS2GS_BASE(mem), mem, mmreg );
|
||||
_rec_mtgs_Send64( (uptr)PS2GS_BASE(mem)+8, mem+8, mmreg );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int gsConstRead8(u32 x86reg, u32 mem, u32 sign)
|
||||
{
|
||||
GIF_LOG("GS read 8 %8.8lx (%8.8x), at %8.8lx\n", (uptr)PS2GS_BASE(mem), mem);
|
||||
_eeReadConstMem8(x86reg, (uptr)PS2GS_BASE(mem), sign);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gsConstRead16(u32 x86reg, u32 mem, u32 sign)
|
||||
{
|
||||
GIF_LOG("GS read 16 %8.8lx (%8.8x), at %8.8lx\n", (uptr)PS2GS_BASE(mem), mem);
|
||||
_eeReadConstMem16(x86reg, (uptr)PS2GS_BASE(mem), sign);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gsConstRead32(u32 x86reg, u32 mem)
|
||||
{
|
||||
GIF_LOG("GS read 32 %8.8lx (%8.8x), at %8.8lx\n", (uptr)PS2GS_BASE(mem), mem);
|
||||
_eeReadConstMem32(x86reg, (uptr)PS2GS_BASE(mem));
|
||||
return 0;
|
||||
}
|
||||
|
||||
void gsConstRead64(u32 mem, int mmreg)
|
||||
{
|
||||
GIF_LOG("GS read 64 %8.8lx (%8.8x), at %8.8lx\n", (uptr)PS2GS_BASE(mem), mem);
|
||||
if( IS_XMMREG(mmreg) ) SSE_MOVLPS_M64_to_XMM(mmreg&0xff, (uptr)PS2GS_BASE(mem));
|
||||
else {
|
||||
MOVQMtoR(mmreg, (uptr)PS2GS_BASE(mem));
|
||||
SetMMXstate();
|
||||
}
|
||||
}
|
||||
|
||||
void gsConstRead128(u32 mem, int xmmreg)
|
||||
{
|
||||
GIF_LOG("GS read 128 %8.8lx (%8.8x), at %8.8lx\n", (uptr)PS2GS_BASE(mem), mem);
|
||||
_eeReadConstMem128( xmmreg, (uptr)PS2GS_BASE(mem));
|
||||
}
|
1145
pcsx2/x86/iHw.cpp
1145
pcsx2/x86/iHw.cpp
File diff suppressed because it is too large
Load Diff
|
@ -1,203 +0,0 @@
|
|||
/* Pcsx2 - Pc Ps2 Emulator
|
||||
* Copyright (C) 2002-2008 Pcsx2 Team
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "Common.h"
|
||||
#include "iR5900.h"
|
||||
|
||||
#include "IPU.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// IPU Register Reads
|
||||
|
||||
int ipuConstRead32(u32 x86reg, u32 mem)
|
||||
{
|
||||
int workingreg, tempreg, tempreg2;
|
||||
iFlushCall(0);
|
||||
CALLFunc((u32)IPUProcessInterrupt);
|
||||
|
||||
// if( !(x86reg&(MEM_XMMTAG|MEM_MMXTAG)) ) {
|
||||
// if( x86reg == EAX ) {
|
||||
// tempreg = ECX;
|
||||
// tempreg2 = EDX;
|
||||
// }
|
||||
// else if( x86reg == ECX ) {
|
||||
// tempreg = EAX;
|
||||
// tempreg2 = EDX;
|
||||
// }
|
||||
// else if( x86reg == EDX ) {
|
||||
// tempreg = EAX;
|
||||
// tempreg2 = ECX;
|
||||
// }
|
||||
//
|
||||
// workingreg = x86reg;
|
||||
// }
|
||||
// else {
|
||||
workingreg = EAX;
|
||||
tempreg = ECX;
|
||||
tempreg2 = EDX;
|
||||
// }
|
||||
|
||||
switch (mem){
|
||||
|
||||
case 0x10002010: // IPU_CTRL
|
||||
|
||||
MOV32MtoR(workingreg, (u32)&ipuRegs->ctrl._u32);
|
||||
AND32ItoR(workingreg, ~0x3f0f); // save OFC
|
||||
OR8MtoR(workingreg, (u32)&g_BP.IFC);
|
||||
OR8MtoR(workingreg+4, (u32)&coded_block_pattern); // or ah, mem
|
||||
|
||||
// MOV32MtoR(workingreg, (u32)&ipuRegs->ctrl._u32);
|
||||
// AND32ItoR(workingreg, ~0x3fff);
|
||||
// MOV32MtoR(tempreg, (u32)&g_nIPU0Data);
|
||||
// MOV8MtoR(workingreg, (u32)&g_BP.IFC);
|
||||
//
|
||||
// CMP32ItoR(tempreg, 8);
|
||||
// j8Ptr[5] = JLE8(0);
|
||||
// MOV32ItoR(tempreg, 8);
|
||||
// x86SetJ8( j8Ptr[5] );
|
||||
// SHL32ItoR(tempreg, 4);
|
||||
//
|
||||
// OR8MtoR(workingreg+4, (u32)&coded_block_pattern); // or ah, mem
|
||||
// OR8RtoR(workingreg, tempreg);
|
||||
|
||||
#ifdef _DEBUG
|
||||
MOV32RtoM((u32)&ipuRegs->ctrl._u32, workingreg);
|
||||
#endif
|
||||
// NOTE: not updating ipuRegs->ctrl
|
||||
// if( x86reg & MEM_XMMTAG ) SSE2_MOVD_R_to_XMM(x86reg&0xf, workingreg);
|
||||
// else if( x86reg & MEM_MMXTAG ) MOVD32RtoMMX(x86reg&0xf, workingreg);
|
||||
return 1;
|
||||
|
||||
case 0x10002020: // IPU_BP
|
||||
|
||||
assert( (u32)&g_BP.FP + 1 == (u32)&g_BP.bufferhasnew );
|
||||
|
||||
MOVZX32M8toR(workingreg, (u32)&g_BP.BP);
|
||||
MOVZX32M8toR(tempreg, (u32)&g_BP.FP);
|
||||
AND8ItoR(workingreg, 0x7f);
|
||||
ADD8MtoR(tempreg, (u32)&g_BP.bufferhasnew);
|
||||
MOV8MtoR(workingreg+4, (u32)&g_BP.IFC);
|
||||
|
||||
SHL32ItoR(tempreg, 16);
|
||||
OR32RtoR(workingreg, tempreg);
|
||||
|
||||
#ifdef _DEBUG
|
||||
MOV32RtoM((u32)&ipuRegs->ipubp, workingreg);
|
||||
#endif
|
||||
// NOTE: not updating ipuRegs->ipubp
|
||||
// if( x86reg & MEM_XMMTAG ) SSE2_MOVD_R_to_XMM(x86reg&0xf, workingreg);
|
||||
// else if( x86reg & MEM_MMXTAG ) MOVD32RtoMMX(x86reg&0xf, workingreg);
|
||||
|
||||
return 1;
|
||||
|
||||
default:
|
||||
// ipu repeats every 0x100
|
||||
_eeReadConstMem32(x86reg, (u32)(((u8*)ipuRegs)+(mem&0xff)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ipuConstRead64(u32 mem, int mmreg)
|
||||
{
|
||||
iFlushCall(0);
|
||||
CALLFunc((u32)IPUProcessInterrupt);
|
||||
|
||||
if( IS_XMMREG(mmreg) ) SSE_MOVLPS_M64_to_XMM(mmreg&0xff, (u32)(((u8*)ipuRegs)+(mem&0xff)));
|
||||
else {
|
||||
MOVQMtoR(mmreg, (u32)(((u8*)ipuRegs)+(mem&0xff)));
|
||||
SetMMXstate();
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// IPU Register Writes!
|
||||
|
||||
void ipuConstWrite32(u32 mem, int mmreg)
|
||||
{
|
||||
iFlushCall(0);
|
||||
if( !(mmreg & (MEM_XMMTAG|MEM_MMXTAG|MEM_EECONSTTAG)) ) PUSH32R(mmreg);
|
||||
CALLFunc((u32)IPUProcessInterrupt);
|
||||
|
||||
switch (mem){
|
||||
case 0x10002000: // IPU_CMD
|
||||
if( (mmreg & (MEM_XMMTAG|MEM_MMXTAG|MEM_EECONSTTAG)) ) _recPushReg(mmreg);
|
||||
CALLFunc((u32)IPUCMD_WRITE);
|
||||
ADD32ItoR(ESP, 4);
|
||||
break;
|
||||
case 0x10002010: // IPU_CTRL
|
||||
if( mmreg & MEM_EECONSTTAG ) {
|
||||
u32 c = g_cpuConstRegs[(mmreg>>16)&0x1f].UL[0]&0x47f30000;
|
||||
|
||||
if( c & 0x40000000 ) {
|
||||
CALLFunc((u32)ipuSoftReset);
|
||||
}
|
||||
else {
|
||||
AND32ItoM((u32)&ipuRegs->ctrl._u32, 0x8000ffff);
|
||||
OR32ItoM((u32)&ipuRegs->ctrl._u32, c);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if( mmreg & MEM_XMMTAG ) SSE2_MOVD_XMM_to_R(EAX, mmreg&0xf);
|
||||
else if( mmreg & MEM_MMXTAG ) MOVD32MMXtoR(EAX, mmreg&0xf);
|
||||
else POP32R(EAX);
|
||||
|
||||
MOV32MtoR(ECX, (u32)&ipuRegs->ctrl._u32);
|
||||
AND32ItoR(EAX, 0x47f30000);
|
||||
AND32ItoR(ECX, 0x8000ffff);
|
||||
OR32RtoR(EAX, ECX);
|
||||
MOV32RtoM((u32)&ipuRegs->ctrl._u32, EAX);
|
||||
|
||||
TEST32ItoR(EAX, 0x40000000);
|
||||
j8Ptr[5] = JZ8(0);
|
||||
|
||||
// reset
|
||||
CALLFunc((u32)ipuSoftReset);
|
||||
|
||||
x86SetJ8( j8Ptr[5] );
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
if( !(mmreg & (MEM_XMMTAG|MEM_MMXTAG|MEM_EECONSTTAG)) ) POP32R(mmreg);
|
||||
_eeWriteConstMem32((u32)((u8*)ipuRegs + (mem&0xfff)), mmreg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ipuConstWrite64(u32 mem, int mmreg)
|
||||
{
|
||||
iFlushCall(0);
|
||||
CALLFunc((u32)IPUProcessInterrupt);
|
||||
|
||||
switch (mem){
|
||||
case 0x10002000:
|
||||
_recPushReg(mmreg);
|
||||
CALLFunc((u32)IPUCMD_WRITE);
|
||||
ADD32ItoR(ESP, 4);
|
||||
break;
|
||||
|
||||
default:
|
||||
_eeWriteConstMem64( (u32)((u8*)ipuRegs + (mem&0xfff)), mmreg);
|
||||
break;
|
||||
}
|
||||
}
|
1179
pcsx2/x86/iPsxHw.cpp
1179
pcsx2/x86/iPsxHw.cpp
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue