Compare commits

...

1 Commits

Author SHA1 Message Date
Christian Biesinger
3286a5fda9 Fork GCC's hash-table.h for use in GDB
And use it at one callsite in dwarf2read.
2019-09-24 19:53:35 -05:00
7 changed files with 1875 additions and 14 deletions

View File

@@ -1037,6 +1037,7 @@ COMMON_SFILES = \
go-lang.c \
go-typeprint.c \
go-valprint.c \
hash-table.c \
inf-child.c \
inf-loop.c \
infcall.c \

View File

@@ -90,6 +90,7 @@
#include <forward_list>
#include "rust-lang.h"
#include "gdbsupport/pathstuff.h"
#include "hash-map.h"
/* When == 1, print basic high level tracing messages.
When > 1, be more verbose.
@@ -5076,12 +5077,8 @@ dw_expand_symtabs_matching_file_matcher
objfile *const objfile = dwarf2_per_objfile->objfile;
htab_up visited_found (htab_create_alloc (10, htab_hash_pointer,
htab_eq_pointer,
NULL, xcalloc, xfree));
htab_up visited_not_found (htab_create_alloc (10, htab_hash_pointer,
htab_eq_pointer,
NULL, xcalloc, xfree));
hash_table<nofree_ptr_hash <quick_file_names>> visited_found (10);
hash_table<nofree_ptr_hash <quick_file_names>> visited_not_found (10);
/* The rule is CUs specify all the files, including those used by
any TU, so there's no need to scan TUs here. */
@@ -5100,9 +5097,9 @@ dw_expand_symtabs_matching_file_matcher
if (file_data == NULL)
continue;
if (htab_find (visited_not_found.get (), file_data) != NULL)
if (visited_not_found.find_slot (file_data, NO_INSERT) != NULL)
continue;
else if (htab_find (visited_found.get (), file_data) != NULL)
else if (visited_found.find_slot (file_data, NO_INSERT) != NULL)
{
per_cu->v.quick->mark = 1;
continue;
@@ -5132,12 +5129,10 @@ dw_expand_symtabs_matching_file_matcher
break;
}
}
void **slot = htab_find_slot (per_cu->v.quick->mark
? visited_found.get ()
: visited_not_found.get (),
file_data, INSERT);
*slot = file_data;
if (per_cu->v.quick->mark)
*visited_found.find_slot (file_data, INSERT) = file_data;
else
*visited_not_found.find_slot (file_data, INSERT) = file_data;
}
}

188
gdb/hash-map-traits.h Normal file
View File

@@ -0,0 +1,188 @@
/* A hash map traits.
Copyright (C) 2015-2019 Free Software Foundation, Inc.
This file is part of GCC.
GCC 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 3, or (at your option) any later
version.
GCC 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 GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#ifndef HASH_MAP_TRAITS_H
#define HASH_MAP_TRAITS_H
/* Bacause mem-stats.h uses default hashmap traits, we have to
put the class to this separate header file. */
#include "hash-traits.h"
/* Implement hash_map traits for a key with hash traits H. Empty and
deleted map entries are represented as empty and deleted keys. */
template <typename H, typename Value>
struct simple_hashmap_traits
{
typedef typename H::value_type key_type;
static const bool maybe_mx = true;
static inline hashval_t hash (const key_type &);
static inline bool equal_keys (const key_type &, const key_type &);
template <typename T> static inline void remove (T &);
template <typename T> static inline bool is_empty (const T &);
template <typename T> static inline bool is_deleted (const T &);
template <typename T> static inline void mark_empty (T &);
template <typename T> static inline void mark_deleted (T &);
};
template <typename H, typename Value>
inline hashval_t
simple_hashmap_traits <H, Value>::hash (const key_type &h)
{
return H::hash (h);
}
template <typename H, typename Value>
inline bool
simple_hashmap_traits <H, Value>::equal_keys (const key_type &k1,
const key_type &k2)
{
return H::equal (k1, k2);
}
template <typename H, typename Value>
template <typename T>
inline void
simple_hashmap_traits <H, Value>::remove (T &entry)
{
H::remove (entry.m_key);
entry.m_value.~Value ();
}
template <typename H, typename Value>
template <typename T>
inline bool
simple_hashmap_traits <H, Value>::is_empty (const T &entry)
{
return H::is_empty (entry.m_key);
}
template <typename H, typename Value>
template <typename T>
inline bool
simple_hashmap_traits <H, Value>::is_deleted (const T &entry)
{
return H::is_deleted (entry.m_key);
}
template <typename H, typename Value>
template <typename T>
inline void
simple_hashmap_traits <H, Value>::mark_empty (T &entry)
{
H::mark_empty (entry.m_key);
}
template <typename H, typename Value>
template <typename T>
inline void
simple_hashmap_traits <H, Value>::mark_deleted (T &entry)
{
H::mark_deleted (entry.m_key);
}
template <typename H, typename Value>
struct simple_cache_map_traits: public simple_hashmap_traits<H,Value>
{
static const bool maybe_mx = false;
};
/* Implement traits for a hash_map with values of type Value for cases
in which the key cannot represent empty and deleted slots. Instead
record empty and deleted entries in Value. Derived classes must
implement the hash and equal_keys functions. */
template <typename Value>
struct unbounded_hashmap_traits
{
template <typename T> static inline void remove (T &);
template <typename T> static inline bool is_empty (const T &);
template <typename T> static inline bool is_deleted (const T &);
template <typename T> static inline void mark_empty (T &);
template <typename T> static inline void mark_deleted (T &);
};
template <typename Value>
template <typename T>
inline void
unbounded_hashmap_traits <Value>::remove (T &entry)
{
default_hash_traits <Value>::remove (entry.m_value);
}
template <typename Value>
template <typename T>
inline bool
unbounded_hashmap_traits <Value>::is_empty (const T &entry)
{
return default_hash_traits <Value>::is_empty (entry.m_value);
}
template <typename Value>
template <typename T>
inline bool
unbounded_hashmap_traits <Value>::is_deleted (const T &entry)
{
return default_hash_traits <Value>::is_deleted (entry.m_value);
}
template <typename Value>
template <typename T>
inline void
unbounded_hashmap_traits <Value>::mark_empty (T &entry)
{
default_hash_traits <Value>::mark_empty (entry.m_value);
}
template <typename Value>
template <typename T>
inline void
unbounded_hashmap_traits <Value>::mark_deleted (T &entry)
{
default_hash_traits <Value>::mark_deleted (entry.m_value);
}
/* Implement traits for a hash_map from integer type Key to Value in
cases where Key has no spare values for recording empty and deleted
slots. */
template <typename Key, typename Value>
struct unbounded_int_hashmap_traits : unbounded_hashmap_traits <Value>
{
typedef Key key_type;
static inline hashval_t hash (Key);
static inline bool equal_keys (Key, Key);
};
template <typename Key, typename Value>
inline hashval_t
unbounded_int_hashmap_traits <Key, Value>::hash (Key k)
{
return k;
}
template <typename Key, typename Value>
inline bool
unbounded_int_hashmap_traits <Key, Value>::equal_keys (Key k1, Key k2)
{
return k1 == k2;
}
#endif // HASH_MAP_TRAITS_H

220
gdb/hash-map.h Normal file
View File

@@ -0,0 +1,220 @@
/* A type-safe hash map.
Copyright (C) 2014-2019 Free Software Foundation, Inc.
This file is part of GCC.
GCC 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 3, or (at your option) any later
version.
GCC 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 GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#ifndef hash_map_h
#define hash_map_h
#include "hash-table.h"
/* Class hash_map is a hash-value based container mapping objects of
KeyId type to those of the Value type.
Both KeyId and Value may be non-trivial (non-POD) types provided
a suitabe Traits class. A few default Traits specializations are
provided for basic types such as integers, pointers, and std::pair.
Inserted elements are value-initialized either to zero for POD types
or by invoking their default ctor. Removed elements are destroyed
by invoking their dtor. On hash_map destruction all elements are
removed. Objects of hash_map type are copy-constructible but not
assignable. */
template<typename KeyId, typename Value,
typename Traits /* = simple_hashmap_traits<default_hash_traits<Key>,
Value> */>
class hash_map
{
typedef typename Traits::key_type Key;
struct hash_entry
{
Key m_key;
Value m_value;
typedef hash_entry value_type;
typedef Key compare_type;
static hashval_t hash (const hash_entry &e)
{
return Traits::hash (e.m_key);
}
static bool equal (const hash_entry &a, const Key &b)
{
return Traits::equal_keys (a.m_key, b);
}
static void remove (hash_entry &e) { Traits::remove (e); }
static void mark_deleted (hash_entry &e) { Traits::mark_deleted (e); }
static bool is_deleted (const hash_entry &e)
{
return Traits::is_deleted (e);
}
static void mark_empty (hash_entry &e) { Traits::mark_empty (e); }
static bool is_empty (const hash_entry &e) { return Traits::is_empty (e); }
};
public:
explicit hash_map (size_t n = 13,
bool sanitize_eq_and_hash = true)
: m_table (n, sanitize_eq_and_hash)
{
}
explicit hash_map (const hash_map &h,
bool sanitize_eq_and_hash = true)
: m_table (h.m_table, sanitize_eq_and_hash) {}
/* If key k isn't already in the map add key k with value v to the map, and
return false. Otherwise set the value of the entry for key k to be v and
return true. */
bool put (const Key &k, const Value &v)
{
hash_entry *e = m_table.find_slot_with_hash (k, Traits::hash (k),
INSERT);
bool ins = hash_entry::is_empty (*e);
if (ins)
{
e->m_key = k;
new ((void *) &e->m_value) Value (v);
}
else
e->m_value = v;
return !ins;
}
/* if the passed in key is in the map return its value otherwise NULL. */
Value *get (const Key &k)
{
hash_entry &e = m_table.find_with_hash (k, Traits::hash (k));
return Traits::is_empty (e) ? NULL : &e.m_value;
}
/* Return a reference to the value for the passed in key, creating the entry
if it doesn't already exist. If existed is not NULL then it is set to
false if the key was not previously in the map, and true otherwise. */
Value &get_or_insert (const Key &k, bool *existed = NULL)
{
hash_entry *e = m_table.find_slot_with_hash (k, Traits::hash (k),
INSERT);
bool ins = Traits::is_empty (*e);
if (ins)
{
e->m_key = k;
new ((void *)&e->m_value) Value ();
}
if (existed != NULL)
*existed = !ins;
return e->m_value;
}
void remove (const Key &k)
{
m_table.remove_elt_with_hash (k, Traits::hash (k));
}
/* Call the call back on each pair of key and value with the passed in
arg. */
template<typename Arg, bool (*f)(const typename Traits::key_type &,
const Value &, Arg)>
void traverse (Arg a) const
{
for (typename hash_table<hash_entry>::iterator iter = m_table.begin ();
iter != m_table.end (); ++iter)
f ((*iter).m_key, (*iter).m_value, a);
}
template<typename Arg, bool (*f)(const typename Traits::key_type &,
Value *, Arg)>
void traverse (Arg a) const
{
for (typename hash_table<hash_entry>::iterator iter = m_table.begin ();
iter != m_table.end (); ++iter)
if (!f ((*iter).m_key, &(*iter).m_value, a))
break;
}
size_t elements () const { return m_table.elements (); }
void empty () { m_table.empty(); }
/* Return true when there are no elements in this hash map. */
bool is_empty () const { return m_table.is_empty (); }
class iterator
{
public:
explicit iterator (const typename hash_table<hash_entry>::iterator &iter) :
m_iter (iter) {}
iterator &operator++ ()
{
++m_iter;
return *this;
}
/* Can't use std::pair here, because GCC before 4.3 don't handle
std::pair where template parameters are references well.
See PR86739. */
class reference_pair {
public:
const Key &first;
Value &second;
reference_pair (const Key &key, Value &value) : first (key), second (value) {}
template <typename K, typename V>
operator std::pair<K, V> () const { return std::pair<K, V> (first, second); }
};
reference_pair operator* ()
{
hash_entry &e = *m_iter;
return reference_pair (e.m_key, e.m_value);
}
bool
operator != (const iterator &other) const
{
return m_iter != other.m_iter;
}
private:
typename hash_table<hash_entry>::iterator m_iter;
};
/* Standard iterator retrieval methods. */
iterator begin () const { return iterator (m_table.begin ()); }
iterator end () const { return iterator (m_table.end ()); }
private:
hash_table<hash_entry> m_table;
};
#endif

97
gdb/hash-table.c Normal file
View File

@@ -0,0 +1,97 @@
/* A type-safe hash table template.
Copyright (C) 2012-2019 Free Software Foundation, Inc.
Contributed by Lawrence Crowl <crowl@google.com>
This file is part of GCC.
GCC 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 3, or (at your option) any later
version.
GCC 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 GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
/* This file implements a typed hash table.
The implementation borrows from libiberty's hashtab. */
#include "defs.h"
#include "gdbtypes.h"
#include "hash-table.h"
/* Table of primes and multiplicative inverses.
Note that these are not minimally reduced inverses. Unlike when generating
code to divide by a constant, we want to be able to use the same algorithm
all the time. All of these inverses (are implied to) have bit 32 set.
For the record, here's the function that computed the table; it's a
vastly simplified version of the function of the same name from gcc. */
struct prime_ent const prime_tab[] = {
{ 7, 0x24924925, 0x9999999b, 2 },
{ 13, 0x3b13b13c, 0x745d1747, 3 },
{ 31, 0x08421085, 0x1a7b9612, 4 },
{ 61, 0x0c9714fc, 0x15b1e5f8, 5 },
{ 127, 0x02040811, 0x0624dd30, 6 },
{ 251, 0x05197f7e, 0x073260a5, 7 },
{ 509, 0x01824366, 0x02864fc8, 8 },
{ 1021, 0x00c0906d, 0x014191f7, 9 },
{ 2039, 0x0121456f, 0x0161e69e, 10 },
{ 4093, 0x00300902, 0x00501908, 11 },
{ 8191, 0x00080041, 0x00180241, 12 },
{ 16381, 0x000c0091, 0x00140191, 13 },
{ 32749, 0x002605a5, 0x002a06e6, 14 },
{ 65521, 0x000f00e2, 0x00110122, 15 },
{ 131071, 0x00008001, 0x00018003, 16 },
{ 262139, 0x00014002, 0x0001c004, 17 },
{ 524287, 0x00002001, 0x00006001, 18 },
{ 1048573, 0x00003001, 0x00005001, 19 },
{ 2097143, 0x00004801, 0x00005801, 20 },
{ 4194301, 0x00000c01, 0x00001401, 21 },
{ 8388593, 0x00001e01, 0x00002201, 22 },
{ 16777213, 0x00000301, 0x00000501, 23 },
{ 33554393, 0x00001381, 0x00001481, 24 },
{ 67108859, 0x00000141, 0x000001c1, 25 },
{ 134217689, 0x000004e1, 0x00000521, 26 },
{ 268435399, 0x00000391, 0x000003b1, 27 },
{ 536870909, 0x00000019, 0x00000029, 28 },
{ 1073741789, 0x0000008d, 0x00000095, 29 },
{ 2147483647, 0x00000003, 0x00000007, 30 },
/* Avoid "decimal constant so large it is unsigned" for 4294967291. */
{ 0xfffffffb, 0x00000006, 0x00000008, 31 }
};
/* Limit number of comparisons when calling hash_table<>::verify. */
unsigned int hash_table_sanitize_eq_limit;
/* The following function returns an index into the above table of the
nearest prime number which is greater than N, and near a power of two. */
unsigned int
hash_table_higher_prime_index (unsigned long n)
{
unsigned int low = 0;
unsigned int high = sizeof (prime_tab) / sizeof (prime_tab[0]);
while (low != high)
{
unsigned int mid = low + (high - low) / 2;
if (n > prime_tab[mid].prime)
low = mid + 1;
else
high = mid;
}
/* If we've run out of primes, abort. */
gdb_assert (n <= prime_tab[low].prime);
return low;
}

1038
gdb/hash-table.h Normal file

File diff suppressed because it is too large Load Diff

322
gdb/hash-traits.h Normal file
View File

@@ -0,0 +1,322 @@
/* Traits for hashable types.
Copyright (C) 2014-2019 Free Software Foundation, Inc.
This file is part of GCC.
GCC 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 3, or (at your option) any later
version.
GCC 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 GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#ifndef hash_traits_h
#define hash_traits_h
/* Helpful type for removing with free. */
template <typename Type>
struct typed_free_remove
{
static inline void remove (Type *p);
};
/* Remove with free. */
template <typename Type>
inline void
typed_free_remove <Type>::remove (Type *p)
{
free (p);
}
/* Helpful type for removing with delete. */
template <typename Type>
struct typed_delete_remove
{
static inline void remove (Type *p);
};
/* Remove with delete. */
template <typename Type>
inline void
typed_delete_remove <Type>::remove (Type *p)
{
delete p;
}
/* Helpful type for a no-op remove. */
template <typename Type>
struct typed_noop_remove
{
static inline void remove (Type &);
};
/* Remove doing nothing. */
template <typename Type>
inline void
typed_noop_remove <Type>::remove (Type &)
{
}
/* Hasher for integer type Type in which Empty is a spare value that can be
used to mark empty slots. If Deleted != Empty then Deleted is another
spare value that can be used for deleted slots; if Deleted == Empty then
hash table entries cannot be deleted. */
template <typename Type, Type Empty, Type Deleted = Empty>
struct int_hash : typed_noop_remove <Type>
{
typedef Type value_type;
typedef Type compare_type;
static inline hashval_t hash (value_type);
static inline bool equal (value_type existing, value_type candidate);
static inline void mark_deleted (Type &);
static inline void mark_empty (Type &);
static inline bool is_deleted (Type);
static inline bool is_empty (Type);
};
template <typename Type, Type Empty, Type Deleted>
inline hashval_t
int_hash <Type, Empty, Deleted>::hash (value_type x)
{
return x;
}
template <typename Type, Type Empty, Type Deleted>
inline bool
int_hash <Type, Empty, Deleted>::equal (value_type x, value_type y)
{
return x == y;
}
template <typename Type, Type Empty, Type Deleted>
inline void
int_hash <Type, Empty, Deleted>::mark_deleted (Type &x)
{
gdb_assert (Empty != Deleted);
x = Deleted;
}
template <typename Type, Type Empty, Type Deleted>
inline void
int_hash <Type, Empty, Deleted>::mark_empty (Type &x)
{
x = Empty;
}
template <typename Type, Type Empty, Type Deleted>
inline bool
int_hash <Type, Empty, Deleted>::is_deleted (Type x)
{
return Empty != Deleted && x == Deleted;
}
template <typename Type, Type Empty, Type Deleted>
inline bool
int_hash <Type, Empty, Deleted>::is_empty (Type x)
{
return x == Empty;
}
/* Pointer hasher based on pointer equality. Other types of pointer hash
can inherit this and override the hash and equal functions with some
other form of equality (such as string equality). */
template <typename Type>
struct pointer_hash
{
typedef Type *value_type;
typedef Type *compare_type;
static inline hashval_t hash (const value_type &);
static inline bool equal (const value_type &existing,
const compare_type &candidate);
static inline void mark_deleted (Type *&);
static inline void mark_empty (Type *&);
static inline bool is_deleted (Type *);
static inline bool is_empty (Type *);
};
template <typename Type>
inline hashval_t
pointer_hash <Type>::hash (const value_type &candidate)
{
/* This is a really poor hash function, but it is what the current code uses,
so I am reusing it to avoid an additional axis in testing. */
return (hashval_t) ((intptr_t)candidate >> 3);
}
template <typename Type>
inline bool
pointer_hash <Type>::equal (const value_type &existing,
const compare_type &candidate)
{
return existing == candidate;
}
template <typename Type>
inline void
pointer_hash <Type>::mark_deleted (Type *&e)
{
e = reinterpret_cast<Type *> (1);
}
template <typename Type>
inline void
pointer_hash <Type>::mark_empty (Type *&e)
{
e = NULL;
}
template <typename Type>
inline bool
pointer_hash <Type>::is_deleted (Type *e)
{
return e == reinterpret_cast<Type *> (1);
}
template <typename Type>
inline bool
pointer_hash <Type>::is_empty (Type *e)
{
return e == NULL;
}
/* Hasher for "const char *" strings, using string rather than pointer
equality. */
struct string_hash : pointer_hash <const char>
{
static inline hashval_t hash (const char *);
static inline bool equal (const char *, const char *);
};
inline hashval_t
string_hash::hash (const char *id)
{
return htab_hash_string (id);
}
inline bool
string_hash::equal (const char *id1, const char *id2)
{
return strcmp (id1, id2) == 0;
}
/* Traits for pointer elements that should not be freed when an element
is deleted. */
template <typename T>
struct nofree_ptr_hash : pointer_hash <T>, typed_noop_remove <T *> {};
/* Traits for pointer elements that should be freed via free() when an
element is deleted. */
template <typename T>
struct free_ptr_hash : pointer_hash <T>, typed_free_remove <T> {};
/* Traits for pointer elements that should be freed via delete operand when an
element is deleted. */
template <typename T>
struct delete_ptr_hash : pointer_hash <T>, typed_delete_remove <T> {};
/* Traits for string elements that should not be freed when an element
is deleted. */
struct nofree_string_hash : string_hash, typed_noop_remove <const char *> {};
/* Traits for pairs of values, using the first to record empty and
deleted slots. */
template <typename T1, typename T2>
struct pair_hash
{
typedef std::pair <typename T1::value_type,
typename T2::value_type> value_type;
typedef std::pair <typename T1::compare_type,
typename T2::compare_type> compare_type;
static inline hashval_t hash (const value_type &);
static inline bool equal (const value_type &, const compare_type &);
static inline void remove (value_type &);
static inline void mark_deleted (value_type &);
static inline void mark_empty (value_type &);
static inline bool is_deleted (const value_type &);
static inline bool is_empty (const value_type &);
};
template <typename T1, typename T2>
inline hashval_t
pair_hash <T1, T2>::hash (const value_type &x)
{
return iterative_hash_hashval_t (T1::hash (x.first), T2::hash (x.second));
}
template <typename T1, typename T2>
inline bool
pair_hash <T1, T2>::equal (const value_type &x, const compare_type &y)
{
return T1::equal (x.first, y.first) && T2::equal (x.second, y.second);
}
template <typename T1, typename T2>
inline void
pair_hash <T1, T2>::remove (value_type &x)
{
T1::remove (x.first);
T2::remove (x.second);
}
template <typename T1, typename T2>
inline void
pair_hash <T1, T2>::mark_deleted (value_type &x)
{
T1::mark_deleted (x.first);
}
template <typename T1, typename T2>
inline void
pair_hash <T1, T2>::mark_empty (value_type &x)
{
T1::mark_empty (x.first);
}
template <typename T1, typename T2>
inline bool
pair_hash <T1, T2>::is_deleted (const value_type &x)
{
return T1::is_deleted (x.first);
}
template <typename T1, typename T2>
inline bool
pair_hash <T1, T2>::is_empty (const value_type &x)
{
return T1::is_empty (x.first);
}
template <typename T> struct default_hash_traits : T {};
template <typename T>
struct default_hash_traits <T *> : pointer_hash <T> {};
#endif