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:
Jake.Stine 2009-03-06 01:11:17 +00:00
parent 620ba58085
commit 23e0ca1b1f
19 changed files with 4698 additions and 7222 deletions

261
3rdparty/google/dense_hash_map vendored Normal file
View File

@ -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_ */

245
3rdparty/google/dense_hash_set vendored Normal file
View File

@ -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_ */

246
3rdparty/google/sparse_hash_map vendored Normal file
View File

@ -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_ */

231
3rdparty/google/sparse_hash_set vendored Normal file
View File

@ -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_ */

View File

@ -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_ */

View File

@ -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

View File

@ -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_ */

1451
3rdparty/google/sparsetable vendored Normal file

File diff suppressed because it is too large Load Diff

250
3rdparty/google/type_traits.h vendored Normal file
View File

@ -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_

View File

@ -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);
}

File diff suppressed because it is too large Load Diff

View File

@ -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);

View File

@ -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>

View File

@ -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
&ltsTranslatedSID // 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

View File

@ -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));
}

File diff suppressed because it is too large Load Diff

View File

@ -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;
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff