diff options
| author | Mike Buland <eichlan@xagasoft.com> | 2007-07-03 00:28:59 +0000 | 
|---|---|---|
| committer | Mike Buland <eichlan@xagasoft.com> | 2007-07-03 00:28:59 +0000 | 
| commit | ac517a2b7625e0aa0862679e961c6349f859ea3b (patch) | |
| tree | e3e27a6b9bd5e2be6150088495c91fc91786ad9d /src/hash.h | |
| parent | f8d4301e9fa4f3709258505941e37fab2eadadc6 (diff) | |
| parent | bd865cee5f89116c1f054cd0e5c275e97c2d0a9b (diff) | |
| download | libbu++-ac517a2b7625e0aa0862679e961c6349f859ea3b.tar.gz libbu++-ac517a2b7625e0aa0862679e961c6349f859ea3b.tar.bz2 libbu++-ac517a2b7625e0aa0862679e961c6349f859ea3b.tar.xz libbu++-ac517a2b7625e0aa0862679e961c6349f859ea3b.zip | |
The reorg is being put in trunk, I think it's ready.  Now we just get to find
out how many applications won't work anymore :)
Diffstat (limited to '')
| -rw-r--r-- | src/hash.h | 1488 | 
1 files changed, 907 insertions, 581 deletions
| @@ -1,745 +1,1071 @@ | |||
| 1 | #ifndef HASH_H | 1 | #ifndef BU_HASH_H | 
| 2 | #define HASH_H | 2 | #define BU_HASH_H | 
| 3 | 3 | ||
| 4 | #include <stddef.h> | 4 | #include <stddef.h> | 
| 5 | #include <string.h> | 5 | #include <string.h> | 
| 6 | #include <memory> | 6 | #include <memory> | 
| 7 | #include <iostream> | 7 | #include <iostream> | 
| 8 | #include <list> | 8 | #include <list> | 
| 9 | #include "exceptionbase.h" | 9 | #include <utility> | 
| 10 | #include "hashable.h" | 10 | #include "bu/exceptionbase.h" | 
| 11 | #include "serializable.h" | 11 | #include "bu/list.h" | 
| 12 | #include "serializer.h" | 12 | ///#include "archival.h" | 
| 13 | ///#include "archive.h" | ||
| 13 | 14 | ||
| 14 | #define bitsToBytes( n ) (n/32+(n%32>0 ? 1 : 0)) | 15 | #define bitsToBytes( n ) (n/32+(n%32>0 ? 1 : 0)) | 
| 15 | 16 | ||
| 16 | subExceptionDecl( HashException ) | 17 | namespace Bu | 
| 17 | |||
| 18 | enum eHashException | ||
| 19 | { | 18 | { | 
| 20 | excodeNotFilled | 19 | subExceptionDecl( HashException ) | 
| 21 | }; | ||
| 22 | |||
| 23 | template<typename T> | ||
| 24 | uint32_t __calcHashCode( const T &k ); | ||
| 25 | |||
| 26 | template<typename T> | ||
| 27 | bool __cmpHashKeys( const T &a, const T &b ); | ||
| 28 | 20 | ||
| 29 | struct __calcNextTSize_fast | 21 | enum eHashException | 
| 30 | { | ||
| 31 | uint32_t operator()( uint32_t nCapacity, uint32_t nFill, uint32_t nDeleted ) const | ||
| 32 | { | 22 | { | 
| 33 | if( nDeleted >= nCapacity/2 ) | 23 | excodeNotFilled | 
| 34 | return nCapacity; | 24 | }; | 
| 35 | return nCapacity*2+1; | ||
| 36 | } | ||
| 37 | }; | ||
| 38 | 25 | ||
| 39 | template<typename key, typename value, typename sizecalc = __calcNextTSize_fast, typename keyalloc = std::allocator<key>, typename valuealloc = std::allocator<value>, typename challoc = std::allocator<uint32_t> > | 26 | template<typename T> | 
| 40 | class Hash; | 27 | uint32_t __calcHashCode( const T &k ); | 
| 41 | 28 | ||
| 42 | template< typename key, typename _value, typename sizecalc = __calcNextTSize_fast, typename keyalloc = std::allocator<key>, typename valuealloc = std::allocator<_value>, typename challoc = std::allocator<uint32_t> > | 29 | template<typename T> | 
| 43 | struct HashProxy | 30 | bool __cmpHashKeys( const T &a, const T &b ); | 
| 44 | { | ||
| 45 | friend class Hash<key, _value, sizecalc, keyalloc, valuealloc, challoc>; | ||
| 46 | private: | ||
| 47 | HashProxy( Hash<key, _value, sizecalc, keyalloc, valuealloc, challoc> &h, key *k, uint32_t nPos, uint32_t hash ) : | ||
| 48 | hsh( h ), | ||
| 49 | pKey( k ), | ||
| 50 | nPos( nPos ), | ||
| 51 | hash( hash ), | ||
| 52 | bFilled( false ) | ||
| 53 | { | ||
| 54 | } | ||
| 55 | 31 | ||
| 56 | HashProxy( Hash<key, _value, sizecalc, keyalloc, valuealloc, challoc> &h, uint32_t nPos, _value *pValue ) : | 32 | struct __calcNextTSize_fast | 
| 57 | hsh( h ), | ||
| 58 | nPos( nPos ), | ||
| 59 | pValue( pValue ), | ||
| 60 | bFilled( true ) | ||
| 61 | { | 33 | { | 
| 62 | } | 34 | uint32_t operator()( uint32_t nCapacity, uint32_t nFill, uint32_t nDeleted ) const | 
| 35 | { | ||
| 36 | if( nDeleted >= nCapacity/2 ) | ||
| 37 | return nCapacity; | ||
| 38 | return nCapacity*2+1; | ||
| 39 | } | ||
| 40 | }; | ||
| 63 | 41 | ||
| 64 | Hash<key, _value, sizecalc, keyalloc, valuealloc, challoc> &hsh; | 42 | template<typename key, typename value, typename sizecalc = __calcNextTSize_fast, typename keyalloc = std::allocator<key>, typename valuealloc = std::allocator<value>, typename challoc = std::allocator<uint32_t> > | 
| 65 | key *pKey; | 43 | class Hash; | 
| 66 | uint32_t nPos; | ||
| 67 | _value *pValue; | ||
| 68 | uint32_t hash; | ||
| 69 | bool bFilled; | ||
| 70 | 44 | ||
| 71 | public: | 45 | template< typename key, typename _value, typename sizecalc = __calcNextTSize_fast, typename keyalloc = std::allocator<key>, typename valuealloc = std::allocator<_value>, typename challoc = std::allocator<uint32_t> > | 
| 72 | operator _value &() | 46 | struct HashProxy | 
| 73 | { | 47 | { | 
| 74 | if( bFilled == false ) | 48 | friend class Hash<key, _value, sizecalc, keyalloc, valuealloc, challoc>; | 
| 75 | throw HashException( | 49 | private: | 
| 76 | excodeNotFilled, | 50 | HashProxy( Hash<key, _value, sizecalc, keyalloc, valuealloc, challoc> &h, key *k, uint32_t nPos, uint32_t hash ) : | 
| 77 | "No data assosiated with that key." | 51 | hsh( h ), | 
| 78 | ); | 52 | pKey( k ), | 
| 79 | return *pValue; | 53 | nPos( nPos ), | 
| 80 | } | 54 | hash( hash ), | 
| 55 | bFilled( false ) | ||
| 56 | { | ||
| 57 | } | ||
| 81 | 58 | ||
| 82 | _value &value() | 59 | HashProxy( Hash<key, _value, sizecalc, keyalloc, valuealloc, challoc> &h, uint32_t nPos, _value *pValue ) : | 
| 83 | { | 60 | hsh( h ), | 
| 84 | if( bFilled == false ) | 61 | nPos( nPos ), | 
| 85 | throw HashException( | 62 | pValue( pValue ), | 
| 86 | excodeNotFilled, | 63 | bFilled( true ) | 
| 87 | "No data assosiated with that key." | 64 | { | 
| 88 | ); | 65 | } | 
| 89 | return *pValue; | ||
| 90 | } | ||
| 91 | 66 | ||
| 92 | bool isFilled() | 67 | Hash<key, _value, sizecalc, keyalloc, valuealloc, challoc> &hsh; | 
| 93 | { | 68 | key *pKey; | 
| 94 | return bFilled; | 69 | uint32_t nPos; | 
| 95 | } | 70 | _value *pValue; | 
| 71 | uint32_t hash; | ||
| 72 | bool bFilled; | ||
| 96 | 73 | ||
| 97 | void erase() | 74 | public: | 
| 98 | { | 75 | /** | 
| 99 | if( bFilled ) | 76 | * Cast operator for HashProxy. | 
| 77 | *@returns (value_type &) The value the HashProxy is pointing to. | ||
| 78 | */ | ||
| 79 | operator _value &() | ||
| 100 | { | 80 | { | 
| 101 | hsh._erase( nPos ); | 81 | if( bFilled == false ) | 
| 102 | hsh.onDelete(); | 82 | throw HashException( | 
| 83 | excodeNotFilled, | ||
| 84 | "No data assosiated with that key." | ||
| 85 | ); | ||
| 86 | return *pValue; | ||
| 103 | } | 87 | } | 
| 104 | } | ||
| 105 | 88 | ||
| 106 | _value operator=( _value nval ) | 89 | /** | 
| 107 | { | 90 | * Direct function for retrieving a value out of the HashProxy. | 
| 108 | if( bFilled ) | 91 | *@returns (value_type &) The value pointed to by this HashProxy. | 
| 92 | */ | ||
| 93 | _value &value() | ||
| 109 | { | 94 | { | 
| 110 | hsh.va.destroy( pValue ); | 95 | if( bFilled == false ) | 
| 111 | hsh.va.construct( pValue, nval ); | 96 | throw HashException( | 
| 112 | hsh.onUpdate(); | 97 | excodeNotFilled, | 
| 98 | "No data assosiated with that key." | ||
| 99 | ); | ||
| 100 | return *pValue; | ||
| 113 | } | 101 | } | 
| 114 | else | 102 | |
| 103 | /** | ||
| 104 | * Whether this HashProxy points to something real or not. | ||
| 105 | */ | ||
| 106 | bool isFilled() | ||
| 115 | { | 107 | { | 
| 116 | hsh.fill( nPos, *pKey, nval, hash ); | 108 | return bFilled; | 
| 117 | hsh.onInsert(); | ||
| 118 | } | 109 | } | 
| 119 | 110 | ||
| 120 | return nval; | 111 | /** | 
| 121 | } | 112 | * Erase the data pointed to by this HashProxy. | 
| 113 | */ | ||
| 114 | void erase() | ||
| 115 | { | ||
| 116 | if( bFilled ) | ||
| 117 | { | ||
| 118 | hsh._erase( nPos ); | ||
| 119 | hsh.onDelete(); | ||
| 120 | } | ||
| 121 | } | ||
| 122 | 122 | ||
| 123 | _value *operator->() | 123 | /** | 
| 124 | { | 124 | * Assign data to this point in the hash table. | 
| 125 | if( bFilled == false ) | 125 | *@param nval (value_type) the data to assign. | 
| 126 | throw HashException( | 126 | */ | 
| 127 | excodeNotFilled, | 127 | _value operator=( _value nval ) | 
| 128 | "No data assosiated with that key." | 128 | { | 
| 129 | ); | 129 | if( bFilled ) | 
| 130 | return pValue; | 130 | { | 
| 131 | } | 131 | hsh.va.destroy( pValue ); | 
| 132 | }; | 132 | hsh.va.construct( pValue, nval ); | 
| 133 | hsh.onUpdate(); | ||
| 134 | } | ||
| 135 | else | ||
| 136 | { | ||
| 137 | hsh.fill( nPos, *pKey, nval, hash ); | ||
| 138 | hsh.onInsert(); | ||
| 139 | } | ||
| 133 | 140 | ||
| 134 | template<typename key, typename value, typename sizecalc, typename keyalloc, typename valuealloc, typename challoc > | 141 | return nval; | 
| 135 | class Hash | 142 | } | 
| 136 | { | ||
| 137 | friend struct HashProxy<key, value, sizecalc, keyalloc, valuealloc, challoc>; | ||
| 138 | public: | ||
| 139 | Hash() : | ||
| 140 | nCapacity( 11 ), | ||
| 141 | nFilled( 0 ), | ||
| 142 | nDeleted( 0 ), | ||
| 143 | bFilled( NULL ), | ||
| 144 | bDeleted( NULL ), | ||
| 145 | aKeys( NULL ), | ||
| 146 | aValues( NULL ), | ||
| 147 | aHashCodes( NULL ) | ||
| 148 | { | ||
| 149 | nKeysSize = bitsToBytes( nCapacity ); | ||
| 150 | bFilled = ca.allocate( nKeysSize ); | ||
| 151 | bDeleted = ca.allocate( nKeysSize ); | ||
| 152 | clearBits(); | ||
| 153 | |||
| 154 | aHashCodes = ca.allocate( nCapacity ); | ||
| 155 | aKeys = ka.allocate( nCapacity ); | ||
| 156 | aValues = va.allocate( nCapacity ); | ||
| 157 | } | ||
| 158 | 143 | ||
| 159 | Hash( const Hash &src ) : | 144 | /** | 
| 160 | nCapacity( src.nCapacity ), | 145 | * Pointer extraction operator. Access to members of data pointed to | 
| 161 | nFilled( 0 ), | 146 | * by HashProxy. | 
| 162 | nDeleted( 0 ), | 147 | *@returns (value_type *) | 
| 163 | bFilled( NULL ), | 148 | */ | 
| 164 | bDeleted( NULL ), | 149 | _value *operator->() | 
| 165 | aKeys( NULL ), | 150 | { | 
| 166 | aValues( NULL ), | 151 | if( bFilled == false ) | 
| 167 | aHashCodes( NULL ) | 152 | throw HashException( | 
| 168 | { | 153 | excodeNotFilled, | 
| 169 | nKeysSize = bitsToBytes( nCapacity ); | 154 | "No data assosiated with that key." | 
| 170 | bFilled = ca.allocate( nKeysSize ); | 155 | ); | 
| 171 | bDeleted = ca.allocate( nKeysSize ); | 156 | return pValue; | 
| 172 | clearBits(); | 157 | } | 
| 158 | }; | ||
| 173 | 159 | ||
| 174 | aHashCodes = ca.allocate( nCapacity ); | 160 | /** | 
| 175 | aKeys = ka.allocate( nCapacity ); | 161 | * Libbu Template Hash Table | 
| 176 | aValues = va.allocate( nCapacity ); | 162 | *@param key (typename) The datatype of the hashtable keys | 
| 163 | *@param value (typename) The datatype of the hashtable data | ||
| 164 | *@param sizecalc (typename) Functor to compute new table size on rehash | ||
| 165 | *@param keyalloc (typename) Memory allocator for hashtable keys | ||
| 166 | *@param valuealloc (typename) Memory allocator for hashtable values | ||
| 167 | *@param challoc (typename) Byte allocator for bitflags | ||
| 168 | */ | ||
| 169 | template<typename key, typename value, typename sizecalc, typename keyalloc, typename valuealloc, typename challoc > | ||
| 170 | class Hash | ||
| 171 | { | ||
| 172 | friend struct HashProxy<key, value, sizecalc, keyalloc, valuealloc, challoc>; | ||
| 173 | public: | ||
| 174 | Hash() : | ||
| 175 | nCapacity( 11 ), | ||
| 176 | nFilled( 0 ), | ||
| 177 | nDeleted( 0 ), | ||
| 178 | bFilled( NULL ), | ||
| 179 | bDeleted( NULL ), | ||
| 180 | aKeys( NULL ), | ||
| 181 | aValues( NULL ), | ||
| 182 | aHashCodes( NULL ) | ||
| 183 | { | ||
| 184 | nKeysSize = bitsToBytes( nCapacity ); | ||
| 185 | bFilled = ca.allocate( nKeysSize ); | ||
| 186 | bDeleted = ca.allocate( nKeysSize ); | ||
| 187 | clearBits(); | ||
| 188 | |||
| 189 | aHashCodes = ca.allocate( nCapacity ); | ||
| 190 | aKeys = ka.allocate( nCapacity ); | ||
| 191 | aValues = va.allocate( nCapacity ); | ||
| 192 | } | ||
| 177 | 193 | ||
| 178 | for( uint32_t j = 0; j < src.nCapacity; j++ ) | 194 | Hash( const Hash &src ) : | 
| 195 | nCapacity( src.nCapacity ), | ||
| 196 | nFilled( 0 ), | ||
| 197 | nDeleted( 0 ), | ||
| 198 | bFilled( NULL ), | ||
| 199 | bDeleted( NULL ), | ||
| 200 | aKeys( NULL ), | ||
| 201 | aValues( NULL ), | ||
| 202 | aHashCodes( NULL ) | ||
| 179 | { | 203 | { | 
| 180 | if( src.isFilled( j ) ) | 204 | nKeysSize = bitsToBytes( nCapacity ); | 
| 205 | bFilled = ca.allocate( nKeysSize ); | ||
| 206 | bDeleted = ca.allocate( nKeysSize ); | ||
| 207 | clearBits(); | ||
| 208 | |||
| 209 | aHashCodes = ca.allocate( nCapacity ); | ||
| 210 | aKeys = ka.allocate( nCapacity ); | ||
| 211 | aValues = va.allocate( nCapacity ); | ||
| 212 | |||
| 213 | for( uint32_t j = 0; j < src.nCapacity; j++ ) | ||
| 181 | { | 214 | { | 
| 182 | insert( src.aKeys[j], src.aValues[j] ); | 215 | if( src.isFilled( j ) ) | 
| 216 | { | ||
| 217 | insert( src.aKeys[j], src.aValues[j] ); | ||
| 218 | } | ||
| 183 | } | 219 | } | 
| 184 | } | 220 | } | 
| 185 | } | ||
| 186 | 221 | ||
| 187 | Hash &operator=( const Hash &src ) | 222 | /** | 
| 188 | { | 223 | * Hashtable assignment operator. Clears this hashtable and | 
| 189 | for( uint32_t j = 0; j < nCapacity; j++ ) | 224 | * copies RH into it. | 
| 225 | */ | ||
| 226 | Hash &operator=( const Hash &src ) | ||
| 190 | { | 227 | { | 
| 191 | if( isFilled( j ) ) | 228 | for( uint32_t j = 0; j < nCapacity; j++ ) | 
| 192 | if( !isDeleted( j ) ) | 229 | { | 
| 230 | if( isFilled( j ) ) | ||
| 231 | if( !isDeleted( j ) ) | ||
| 232 | { | ||
| 233 | va.destroy( &aValues[j] ); | ||
| 234 | ka.destroy( &aKeys[j] ); | ||
| 235 | } | ||
| 236 | } | ||
| 237 | va.deallocate( aValues, nCapacity ); | ||
| 238 | ka.deallocate( aKeys, nCapacity ); | ||
| 239 | ca.deallocate( bFilled, nKeysSize ); | ||
| 240 | ca.deallocate( bDeleted, nKeysSize ); | ||
| 241 | ca.deallocate( aHashCodes, nCapacity ); | ||
| 242 | |||
| 243 | nFilled = 0; | ||
| 244 | nDeleted = 0; | ||
| 245 | nCapacity = src.nCapacity; | ||
| 246 | nKeysSize = bitsToBytes( nCapacity ); | ||
| 247 | bFilled = ca.allocate( nKeysSize ); | ||
| 248 | bDeleted = ca.allocate( nKeysSize ); | ||
| 249 | clearBits(); | ||
| 250 | |||
| 251 | aHashCodes = ca.allocate( nCapacity ); | ||
| 252 | aKeys = ka.allocate( nCapacity ); | ||
| 253 | aValues = va.allocate( nCapacity ); | ||
| 254 | |||
| 255 | for( uint32_t j = 0; j < src.nCapacity; j++ ) | ||
| 256 | { | ||
| 257 | if( src.isFilled( j ) ) | ||
| 193 | { | 258 | { | 
| 194 | va.destroy( &aValues[j] ); | 259 | insert( src.aKeys[j], src.aValues[j] ); | 
| 195 | ka.destroy( &aKeys[j] ); | ||
| 196 | } | 260 | } | 
| 197 | } | 261 | } | 
| 198 | va.deallocate( aValues, nCapacity ); | ||
| 199 | ka.deallocate( aKeys, nCapacity ); | ||
| 200 | ca.deallocate( bFilled, nKeysSize ); | ||
| 201 | ca.deallocate( bDeleted, nKeysSize ); | ||
| 202 | ca.deallocate( aHashCodes, nCapacity ); | ||
| 203 | |||
| 204 | nFilled = 0; | ||
| 205 | nDeleted = 0; | ||
| 206 | nCapacity = src.nCapacity; | ||
| 207 | nKeysSize = bitsToBytes( nCapacity ); | ||
| 208 | bFilled = ca.allocate( nKeysSize ); | ||
| 209 | bDeleted = ca.allocate( nKeysSize ); | ||
| 210 | clearBits(); | ||
| 211 | 262 | ||
| 212 | aHashCodes = ca.allocate( nCapacity ); | 263 | return *this; | 
| 213 | aKeys = ka.allocate( nCapacity ); | 264 | } | 
| 214 | aValues = va.allocate( nCapacity ); | ||
| 215 | 265 | ||
| 216 | for( uint32_t j = 0; j < src.nCapacity; j++ ) | 266 | virtual ~Hash() | 
| 217 | { | 267 | { | 
| 218 | if( src.isFilled( j ) ) | 268 | for( uint32_t j = 0; j < nCapacity; j++ ) | 
| 219 | { | 269 | { | 
| 220 | insert( src.aKeys[j], src.aValues[j] ); | 270 | if( isFilled( j ) ) | 
| 271 | if( !isDeleted( j ) ) | ||
| 272 | { | ||
| 273 | va.destroy( &aValues[j] ); | ||
| 274 | ka.destroy( &aKeys[j] ); | ||
| 275 | } | ||
| 221 | } | 276 | } | 
| 277 | va.deallocate( aValues, nCapacity ); | ||
| 278 | ka.deallocate( aKeys, nCapacity ); | ||
| 279 | ca.deallocate( bFilled, nKeysSize ); | ||
| 280 | ca.deallocate( bDeleted, nKeysSize ); | ||
| 281 | ca.deallocate( aHashCodes, nCapacity ); | ||
| 222 | } | 282 | } | 
| 223 | 283 | ||
| 224 | return *this; | 284 | /** | 
| 225 | } | 285 | * Get the current hash table capacity. (Changes at re-hash) | 
| 226 | 286 | *@returns (uint32_t) The current capacity. | |
| 227 | virtual ~Hash() | 287 | */ | 
| 228 | { | 288 | uint32_t getCapacity() | 
| 229 | for( uint32_t j = 0; j < nCapacity; j++ ) | ||
| 230 | { | 289 | { | 
| 231 | if( isFilled( j ) ) | 290 | return nCapacity; | 
| 232 | if( !isDeleted( j ) ) | ||
| 233 | { | ||
| 234 | va.destroy( &aValues[j] ); | ||
| 235 | ka.destroy( &aKeys[j] ); | ||
| 236 | } | ||
| 237 | } | 291 | } | 
| 238 | va.deallocate( aValues, nCapacity ); | ||
| 239 | ka.deallocate( aKeys, nCapacity ); | ||
| 240 | ca.deallocate( bFilled, nKeysSize ); | ||
| 241 | ca.deallocate( bDeleted, nKeysSize ); | ||
| 242 | ca.deallocate( aHashCodes, nCapacity ); | ||
| 243 | } | ||
| 244 | 292 | ||
| 245 | uint32_t getCapacity() | 293 | /** | 
| 246 | { | 294 | * Get the number of hash locations spoken for. (Including | 
| 247 | return nCapacity; | 295 | * not-yet-cleaned-up deleted items.) | 
| 248 | } | 296 | *@returns (uint32_t) The current fill state. | 
| 297 | */ | ||
| 298 | uint32_t getFill() | ||
| 299 | { | ||
| 300 | return nFilled; | ||
| 301 | } | ||
| 249 | 302 | ||
| 250 | uint32_t getFill() | 303 | /** | 
| 251 | { | 304 | * Get the number of items stored in the hash table. | 
| 252 | return nFilled; | 305 | *@returns (uint32_t) The number of items stored in the hash table. | 
| 253 | } | 306 | */ | 
| 307 | uint32_t size() | ||
| 308 | { | ||
| 309 | return nFilled-nDeleted; | ||
| 310 | } | ||
| 254 | 311 | ||
| 255 | uint32_t size() | 312 | /** | 
| 256 | { | 313 | * Get the number of items which have been deleted, but not yet | 
| 257 | return nFilled-nDeleted; | 314 | * cleaned up. | 
| 258 | } | 315 | *@returns (uint32_t) The number of deleted items. | 
| 316 | */ | ||
| 317 | uint32_t getDeleted() | ||
| 318 | { | ||
| 319 | return nDeleted; | ||
| 320 | } | ||
| 259 | 321 | ||
| 260 | uint32_t getDeleted() | 322 | /** | 
| 261 | { | 323 | * Hash table index operator | 
| 262 | return nDeleted; | 324 | *@param k (key_type) Key of data to be retrieved. | 
| 263 | } | 325 | *@returns (HashProxy) Proxy pointing to the data. | 
| 326 | */ | ||
| 327 | virtual HashProxy<key, value, sizecalc, keyalloc, valuealloc, challoc> operator[]( key k ) | ||
| 328 | { | ||
| 329 | uint32_t hash = __calcHashCode( k ); | ||
| 330 | bool bFill; | ||
| 331 | uint32_t nPos = probe( hash, k, bFill ); | ||
| 264 | 332 | ||
| 265 | virtual HashProxy<key, value, sizecalc, keyalloc, valuealloc, challoc> operator[]( key k ) | 333 | if( bFill ) | 
| 266 | { | 334 | { | 
| 267 | uint32_t hash = __calcHashCode( k ); | 335 | return HashProxy<key, value, sizecalc, keyalloc, valuealloc, challoc>( *this, nPos, &aValues[nPos] ); | 
| 268 | bool bFill; | 336 | } | 
| 269 | uint32_t nPos = probe( hash, k, bFill ); | 337 | else | 
| 338 | { | ||
| 339 | return HashProxy<key, value, sizecalc, keyalloc, valuealloc, challoc>( *this, &k, nPos, hash ); | ||
| 340 | } | ||
| 341 | } | ||
| 270 | 342 | ||
| 271 | if( bFill ) | 343 | /** | 
| 344 | * Insert a value (v) under key (k) into the hash table | ||
| 345 | *@param k (key_type) Key to list the value under. | ||
| 346 | *@param v (value_type) Value to store in the hash table. | ||
| 347 | */ | ||
| 348 | virtual void insert( key k, value v ) | ||
| 272 | { | 349 | { | 
| 273 | return HashProxy<key, value, sizecalc, keyalloc, valuealloc, challoc>( *this, nPos, &aValues[nPos] ); | 350 | uint32_t hash = __calcHashCode( k ); | 
| 351 | bool bFill; | ||
| 352 | uint32_t nPos = probe( hash, k, bFill ); | ||
| 353 | |||
| 354 | if( bFill ) | ||
| 355 | { | ||
| 356 | va.destroy( &aValues[nPos] ); | ||
| 357 | va.construct( &aValues[nPos], v ); | ||
| 358 | onUpdate(); | ||
| 359 | } | ||
| 360 | else | ||
| 361 | { | ||
| 362 | fill( nPos, k, v, hash ); | ||
| 363 | onInsert(); | ||
| 364 | } | ||
| 274 | } | 365 | } | 
| 275 | else | 366 | |
| 367 | /** | ||
| 368 | * Remove a value from the hash table. | ||
| 369 | *@param k (key_type) The data under this key will be erased. | ||
| 370 | */ | ||
| 371 | virtual void erase( key k ) | ||
| 276 | { | 372 | { | 
| 277 | return HashProxy<key, value, sizecalc, keyalloc, valuealloc, challoc>( *this, &k, nPos, hash ); | 373 | uint32_t hash = __calcHashCode( k ); | 
| 374 | bool bFill; | ||
| 375 | uint32_t nPos = probe( hash, k, bFill ); | ||
| 376 | |||
| 377 | if( bFill ) | ||
| 378 | { | ||
| 379 | _erase( nPos ); | ||
| 380 | onDelete(); | ||
| 381 | } | ||
| 278 | } | 382 | } | 
| 279 | } | ||
| 280 | 383 | ||
| 281 | virtual void insert( key k, value v ) | 384 | struct iterator; | 
| 282 | { | ||
| 283 | uint32_t hash = __calcHashCode( k ); | ||
| 284 | bool bFill; | ||
| 285 | uint32_t nPos = probe( hash, k, bFill ); | ||
| 286 | 385 | ||
| 287 | if( bFill ) | 386 | /** | 
| 387 | * Remove a value from the hash pointed to from an iterator. | ||
| 388 | *@param i (iterator &) The data to be erased. | ||
| 389 | */ | ||
| 390 | virtual void erase( struct iterator &i ) | ||
| 288 | { | 391 | { | 
| 289 | va.destroy( &aValues[nPos] ); | 392 | if( this != &i.hsh ) | 
| 290 | va.construct( &aValues[nPos], v ); | 393 | throw HashException("This iterator didn't come from this Hash."); | 
| 291 | onUpdate(); | 394 | if( isFilled( i.nPos ) && !isDeleted( i.nPos ) ) | 
| 395 | { | ||
| 396 | _erase( i.nPos ); | ||
| 397 | onDelete(); | ||
| 398 | } | ||
| 292 | } | 399 | } | 
| 293 | else | 400 | |
| 401 | /** | ||
| 402 | * Remove all data from the hash table. | ||
| 403 | */ | ||
| 404 | virtual void clear() | ||
| 294 | { | 405 | { | 
| 295 | fill( nPos, k, v, hash ); | 406 | for( uint32_t j = 0; j < nCapacity; j++ ) | 
| 296 | onInsert(); | 407 | { | 
| 408 | if( isFilled( j ) ) | ||
| 409 | if( !isDeleted( j ) ) | ||
| 410 | { | ||
| 411 | va.destroy( &aValues[j] ); | ||
| 412 | ka.destroy( &aKeys[j] ); | ||
| 413 | onDelete(); | ||
| 414 | } | ||
| 415 | } | ||
| 416 | |||
| 417 | clearBits(); | ||
| 297 | } | 418 | } | 
| 298 | } | ||
| 299 | 419 | ||
| 300 | virtual void erase( key k ) | 420 | /** | 
| 301 | { | 421 | * Get an item of data from the hash table. | 
| 302 | uint32_t hash = __calcHashCode( k ); | 422 | *@param k (key_type) Key pointing to the data to be retrieved. | 
| 303 | bool bFill; | 423 | *@returns (value_type &) The data pointed to by (k). | 
| 304 | uint32_t nPos = probe( hash, k, bFill ); | 424 | */ | 
| 425 | virtual value &get( key k ) | ||
| 426 | { | ||
| 427 | uint32_t hash = __calcHashCode( k ); | ||
| 428 | bool bFill; | ||
| 429 | uint32_t nPos = probe( hash, k, bFill ); | ||
| 305 | 430 | ||
| 306 | if( bFill ) | 431 | if( bFill ) | 
| 432 | { | ||
| 433 | return aValues[nPos]; | ||
| 434 | } | ||
| 435 | else | ||
| 436 | { | ||
| 437 | throw HashException( | ||
| 438 | excodeNotFilled, | ||
| 439 | "No data assosiated with that key." | ||
| 440 | ); | ||
| 441 | } | ||
| 442 | } | ||
| 443 | |||
| 444 | /** | ||
| 445 | * Get a const item of data from the hash table. | ||
| 446 | *@param k (key_type) Key pointing to the data to be retrieved. | ||
| 447 | *@returns (const value_type &) A const version of the data pointed | ||
| 448 | * to by (k). | ||
| 449 | */ | ||
| 450 | virtual const value &get( key k ) const | ||
| 307 | { | 451 | { | 
| 308 | _erase( nPos ); | 452 | uint32_t hash = __calcHashCode( k ); | 
| 309 | onDelete(); | 453 | bool bFill; | 
| 454 | uint32_t nPos = probe( hash, k, bFill ); | ||
| 455 | |||
| 456 | if( bFill ) | ||
| 457 | { | ||
| 458 | return aValues[nPos]; | ||
| 459 | } | ||
| 460 | else | ||
| 461 | { | ||
| 462 | throw HashException( | ||
| 463 | excodeNotFilled, | ||
| 464 | "No data assosiated with that key." | ||
| 465 | ); | ||
| 466 | } | ||
| 310 | } | 467 | } | 
| 311 | } | ||
| 312 | 468 | ||
| 313 | struct iterator; | 469 | /** | 
| 314 | virtual void erase( struct iterator &i ) | 470 | * Does the hash table contain an item under key (k). | 
| 315 | { | 471 | *@param k (key_type) The key to check. | 
| 316 | if( this != &i.hsh ) | 472 | *@returns (bool) Whether there was an item in the hash under key (k). | 
| 317 | throw HashException("This iterator didn't come from this Hash."); | 473 | */ | 
| 318 | if( isFilled( i.nPos ) && !isDeleted( i.nPos ) ) | 474 | virtual bool has( key k ) | 
| 319 | { | 475 | { | 
| 320 | _erase( i.nPos ); | 476 | bool bFill; | 
| 321 | onDelete(); | 477 | probe( __calcHashCode( k ), k, bFill, false ); | 
| 478 | |||
| 479 | return bFill; | ||
| 322 | } | 480 | } | 
| 323 | } | ||
| 324 | 481 | ||
| 325 | virtual void clear() | 482 | /** | 
| 326 | { | 483 | * Iteration structure for iterating through the hash. | 
| 327 | for( uint32_t j = 0; j < nCapacity; j++ ) | 484 | */ | 
| 485 | typedef struct iterator | ||
| 328 | { | 486 | { | 
| 329 | if( isFilled( j ) ) | 487 | friend class Hash<key, value, sizecalc, keyalloc, valuealloc, challoc>; | 
| 330 | if( !isDeleted( j ) ) | 488 | private: | 
| 489 | iterator( Hash<key, value, sizecalc, keyalloc, valuealloc, challoc> &hsh ) : | ||
| 490 | hsh( hsh ), | ||
| 491 | nPos( 0 ), | ||
| 492 | bFinished( false ) | ||
| 493 | { | ||
| 494 | nPos = hsh.getFirstPos( bFinished ); | ||
| 495 | } | ||
| 496 | |||
| 497 | iterator( Hash<key, value, sizecalc, keyalloc, valuealloc, challoc> &hsh, bool bDone ) : | ||
| 498 | hsh( hsh ), | ||
| 499 | nPos( 0 ), | ||
| 500 | bFinished( bDone ) | ||
| 501 | { | ||
| 502 | } | ||
| 503 | |||
| 504 | Hash<key, value, sizecalc, keyalloc, valuealloc, challoc> &hsh; | ||
| 505 | uint32_t nPos; | ||
| 506 | bool bFinished; | ||
| 507 | |||
| 508 | public: | ||
| 509 | /** | ||
| 510 | * Iterator incrementation operator. Move the iterator forward. | ||
| 511 | */ | ||
| 512 | iterator operator++( int ) | ||
| 513 | { | ||
| 514 | if( bFinished == false ) | ||
| 515 | nPos = hsh.getNextPos( nPos, bFinished ); | ||
| 516 | |||
| 517 | return *this; | ||
| 518 | } | ||
| 519 | |||
| 520 | /** | ||
| 521 | * Iterator incrementation operator. Move the iterator forward. | ||
| 522 | */ | ||
| 523 | iterator operator++() | ||
| 524 | { | ||
| 525 | if( bFinished == false ) | ||
| 526 | nPos = hsh.getNextPos( nPos, bFinished ); | ||
| 527 | |||
| 528 | return *this; | ||
| 529 | } | ||
| 530 | |||
| 531 | /** | ||
| 532 | * Iterator equality comparison operator. Iterators the same? | ||
| 533 | */ | ||
| 534 | bool operator==( const iterator &oth ) | ||
| 535 | { | ||
| 536 | if( bFinished != oth.bFinished ) | ||
| 537 | return false; | ||
| 538 | if( bFinished == true ) | ||
| 331 | { | 539 | { | 
| 332 | va.destroy( &aValues[j] ); | 540 | return true; | 
| 333 | ka.destroy( &aKeys[j] ); | ||
| 334 | onDelete(); | ||
| 335 | } | 541 | } | 
| 336 | } | 542 | else | 
| 337 | 543 | { | |
| 338 | clearBits(); | 544 | if( oth.nPos == nPos ) | 
| 339 | } | 545 | return true; | 
| 546 | return false; | ||
| 547 | } | ||
| 548 | } | ||
| 549 | |||
| 550 | /** | ||
| 551 | * Iterator not equality comparison operator. Not the same? | ||
| 552 | */ | ||
| 553 | bool operator!=( const iterator &oth ) | ||
| 554 | { | ||
| 555 | return !(*this == oth ); | ||
| 556 | } | ||
| 340 | 557 | ||
| 341 | virtual value &get( key k ) | 558 | /** | 
| 342 | { | 559 | * Iterator assignment operator. | 
| 343 | uint32_t hash = __calcHashCode( k ); | 560 | */ | 
| 344 | bool bFill; | 561 | iterator operator=( const iterator &oth ) | 
| 345 | uint32_t nPos = probe( hash, k, bFill ); | 562 | { | 
| 563 | if( &hsh != &oth.hsh ) | ||
| 564 | throw HashException( | ||
| 565 | "Cannot mix iterators from different hash objects."); | ||
| 566 | nPos = oth.nPos; | ||
| 567 | bFinished = oth.bFinished; | ||
| 568 | } | ||
| 346 | 569 | ||
| 347 | if( bFill ) | 570 | /** | 
| 348 | { | 571 | * Iterator dereference operator... err.. get the value | 
| 349 | return aValues[nPos]; | 572 | *@returns (value_type &) The value behind this iterator. | 
| 350 | } | 573 | */ | 
| 351 | else | 574 | value &operator *() | 
| 575 | { | ||
| 576 | return hsh.getValueAtPos( nPos ); | ||
| 577 | } | ||
| 578 | |||
| 579 | /** | ||
| 580 | * Get the key behind this iterator. | ||
| 581 | *@returns (key_type &) The key behind this iterator. | ||
| 582 | */ | ||
| 583 | key &getKey() | ||
| 584 | { | ||
| 585 | return hsh.getKeyAtPos( nPos ); | ||
| 586 | } | ||
| 587 | |||
| 588 | /** | ||
| 589 | * Get the value behind this iterator. | ||
| 590 | *@returs (value_type &) The value behind this iterator. | ||
| 591 | */ | ||
| 592 | value &getValue() | ||
| 593 | { | ||
| 594 | return hsh.getValueAtPos( nPos ); | ||
| 595 | } | ||
| 596 | }; | ||
| 597 | |||
| 598 | /** | ||
| 599 | * Iteration structure for iterating through the hash (const). | ||
| 600 | */ | ||
| 601 | typedef struct const_iterator | ||
| 352 | { | 602 | { | 
| 353 | throw HashException( | 603 | friend class Hash<key, value, sizecalc, keyalloc, valuealloc, challoc>; | 
| 354 | excodeNotFilled, | 604 | private: | 
| 355 | "No data assosiated with that key." | 605 | const_iterator( const Hash<key, value, sizecalc, keyalloc, valuealloc, challoc> &hsh ) : | 
| 356 | ); | 606 | hsh( hsh ), | 
| 357 | } | 607 | nPos( 0 ), | 
| 358 | } | 608 | bFinished( false ) | 
| 609 | { | ||
| 610 | nPos = hsh.getFirstPos( bFinished ); | ||
| 611 | } | ||
| 612 | |||
| 613 | const_iterator( const Hash<key, value, sizecalc, keyalloc, valuealloc, challoc> &hsh, bool bDone ) : | ||
| 614 | hsh( hsh ), | ||
| 615 | nPos( 0 ), | ||
| 616 | bFinished( bDone ) | ||
| 617 | { | ||
| 618 | } | ||
| 359 | 619 | ||
| 360 | virtual bool has( key k ) | 620 | const Hash<key, value, sizecalc, keyalloc, valuealloc, challoc> &hsh; | 
| 361 | { | 621 | uint32_t nPos; | 
| 362 | bool bFill; | 622 | bool bFinished; | 
| 363 | probe( __calcHashCode( k ), k, bFill, false ); | ||
| 364 | 623 | ||
| 365 | return bFill; | 624 | public: | 
| 366 | } | 625 | /** | 
| 626 | * Iterator incrementation operator. Move the iterator forward. | ||
| 627 | */ | ||
| 628 | const_iterator operator++( int ) | ||
| 629 | { | ||
| 630 | if( bFinished == false ) | ||
| 631 | nPos = hsh.getNextPos( nPos, bFinished ); | ||
| 367 | 632 | ||
| 368 | typedef struct iterator | 633 | return *this; | 
| 369 | { | 634 | } | 
| 370 | friend class Hash<key, value, sizecalc, keyalloc, valuealloc, challoc>; | 635 | |
| 371 | private: | 636 | /** | 
| 372 | iterator( Hash<key, value, sizecalc, keyalloc, valuealloc, challoc> &hsh ) : | 637 | * Iterator incrementation operator. Move the iterator forward. | 
| 373 | hsh( hsh ), | 638 | */ | 
| 374 | nPos( 0 ), | 639 | const_iterator operator++() | 
| 375 | bFinished( false ) | 640 | { | 
| 641 | if( bFinished == false ) | ||
| 642 | nPos = hsh.getNextPos( nPos, bFinished ); | ||
| 643 | |||
| 644 | return *this; | ||
| 645 | } | ||
| 646 | |||
| 647 | /** | ||
| 648 | * Iterator equality comparison operator. Iterators the same? | ||
| 649 | */ | ||
| 650 | bool operator==( const const_iterator &oth ) | ||
| 651 | { | ||
| 652 | if( bFinished != oth.bFinished ) | ||
| 653 | return false; | ||
| 654 | if( bFinished == true ) | ||
| 655 | { | ||
| 656 | return true; | ||
| 657 | } | ||
| 658 | else | ||
| 659 | { | ||
| 660 | if( oth.nPos == nPos ) | ||
| 661 | return true; | ||
| 662 | return false; | ||
| 663 | } | ||
| 664 | } | ||
| 665 | |||
| 666 | /** | ||
| 667 | * Iterator not equality comparison operator. Not the same? | ||
| 668 | */ | ||
| 669 | bool operator!=( const const_iterator &oth ) | ||
| 670 | { | ||
| 671 | return !(*this == oth ); | ||
| 672 | } | ||
| 673 | |||
| 674 | /** | ||
| 675 | * Iterator assignment operator. | ||
| 676 | */ | ||
| 677 | const_iterator operator=( const const_iterator &oth ) | ||
| 678 | { | ||
| 679 | if( &hsh != &oth.hsh ) | ||
| 680 | throw HashException( | ||
| 681 | "Cannot mix iterators from different hash objects."); | ||
| 682 | nPos = oth.nPos; | ||
| 683 | bFinished = oth.bFinished; | ||
| 684 | } | ||
| 685 | |||
| 686 | /** | ||
| 687 | * Iterator dereference operator... err.. get the value | ||
| 688 | *@returns (value_type &) The value behind this iterator. | ||
| 689 | */ | ||
| 690 | const value &operator *() const | ||
| 691 | { | ||
| 692 | return hsh.getValueAtPos( nPos ); | ||
| 693 | } | ||
| 694 | |||
| 695 | /** | ||
| 696 | * Get the key behind this iterator. | ||
| 697 | *@returns (key_type &) The key behind this iterator. | ||
| 698 | */ | ||
| 699 | const key &getKey() const | ||
| 700 | { | ||
| 701 | return hsh.getKeyAtPos( nPos ); | ||
| 702 | } | ||
| 703 | |||
| 704 | /** | ||
| 705 | * Get the value behind this iterator. | ||
| 706 | *@returs (value_type &) The value behind this iterator. | ||
| 707 | */ | ||
| 708 | const value &getValue() const | ||
| 709 | { | ||
| 710 | return hsh.getValueAtPos( nPos ); | ||
| 711 | } | ||
| 712 | }; | ||
| 713 | |||
| 714 | /** | ||
| 715 | * Get an iterator pointing to the first item in the hash table. | ||
| 716 | *@returns (iterator) An iterator pointing to the first item in the | ||
| 717 | * hash table. | ||
| 718 | */ | ||
| 719 | iterator begin() | ||
| 376 | { | 720 | { | 
| 377 | nPos = hsh.getFirstPos( bFinished ); | 721 | return iterator( *this ); | 
| 378 | } | 722 | } | 
| 379 | 723 | ||
| 380 | iterator( Hash<key, value, sizecalc, keyalloc, valuealloc, challoc> &hsh, bool bDone ) : | 724 | const_iterator begin() const | 
| 381 | hsh( hsh ), | ||
| 382 | nPos( 0 ), | ||
| 383 | bFinished( bDone ) | ||
| 384 | { | 725 | { | 
| 726 | return const_iterator( *this ); | ||
| 385 | } | 727 | } | 
| 386 | 728 | ||
| 387 | Hash<key, value, sizecalc, keyalloc, valuealloc, challoc> &hsh; | 729 | /** | 
| 388 | uint32_t nPos; | 730 | * Get an iterator pointing to a point just past the last item in the | 
| 389 | bool bFinished; | 731 | * hash table. | 
| 390 | 732 | *@returns (iterator) An iterator pointing to a point just past the | |
| 391 | public: | 733 | * last item in the hash table. | 
| 392 | iterator operator++( int ) | 734 | */ | 
| 735 | iterator end() | ||
| 393 | { | 736 | { | 
| 394 | if( bFinished == false ) | 737 | return iterator( *this, true ); | 
| 395 | nPos = hsh.getNextPos( nPos, bFinished ); | ||
| 396 | |||
| 397 | return *this; | ||
| 398 | } | 738 | } | 
| 399 | 739 | ||
| 400 | iterator operator++() | 740 | const_iterator end() const | 
| 401 | { | 741 | { | 
| 402 | if( bFinished == false ) | 742 | return const_iterator( *this, true ); | 
| 403 | nPos = hsh.getNextPos( nPos, bFinished ); | ||
| 404 | |||
| 405 | return *this; | ||
| 406 | } | 743 | } | 
| 407 | 744 | ||
| 408 | bool operator==( const iterator &oth ) | 745 | /** | 
| 746 | * Get a list of all the keys in the hash table. | ||
| 747 | *@returns (std::list<key_type>) The list of keys in the hash table. | ||
| 748 | */ | ||
| 749 | Bu::List<key> getKeys() const | ||
| 409 | { | 750 | { | 
| 410 | if( bFinished != oth.bFinished ) | 751 | Bu::List<key> lKeys; | 
| 411 | return false; | 752 | |
| 412 | if( bFinished == true ) | 753 | for( uint32_t j = 0; j < nCapacity; j++ ) | 
| 413 | { | 754 | { | 
| 414 | return true; | 755 | if( isFilled( j ) ) | 
| 756 | { | ||
| 757 | if( !isDeleted( j ) ) | ||
| 758 | { | ||
| 759 | lKeys.append( aKeys[j] ); | ||
| 760 | } | ||
| 761 | } | ||
| 415 | } | 762 | } | 
| 416 | else | 763 | |
| 764 | return lKeys; | ||
| 765 | } | ||
| 766 | |||
| 767 | protected: | ||
| 768 | virtual void onInsert() {} | ||
| 769 | virtual void onUpdate() {} | ||
| 770 | virtual void onDelete() {} | ||
| 771 | virtual void onReHash() {} | ||
| 772 | |||
| 773 | virtual void clearBits() | ||
| 774 | { | ||
| 775 | for( uint32_t j = 0; j < nKeysSize; j++ ) | ||
| 417 | { | 776 | { | 
| 418 | if( oth.nPos == nPos ) | 777 | bFilled[j] = bDeleted[j] = 0; | 
| 419 | return true; | ||
| 420 | return false; | ||
| 421 | } | 778 | } | 
| 422 | } | 779 | } | 
| 423 | 780 | ||
| 424 | bool operator!=( const iterator &oth ) | 781 | virtual void fill( uint32_t loc, key &k, value &v, uint32_t hash ) | 
| 425 | { | 782 | { | 
| 426 | return !(*this == oth ); | 783 | bFilled[loc/32] |= (1<<(loc%32)); | 
| 784 | va.construct( &aValues[loc], v ); | ||
| 785 | ka.construct( &aKeys[loc], k ); | ||
| 786 | aHashCodes[loc] = hash; | ||
| 787 | nFilled++; | ||
| 788 | //printf("Filled: %d, Deleted: %d, Capacity: %d\n", | ||
| 789 | // nFilled, nDeleted, nCapacity ); | ||
| 427 | } | 790 | } | 
| 428 | 791 | ||
| 429 | iterator operator=( const iterator &oth ) | 792 | virtual void _erase( uint32_t loc ) | 
| 430 | { | 793 | { | 
| 431 | if( &hsh != &oth.hsh ) | 794 | bDeleted[loc/32] |= (1<<(loc%32)); | 
| 432 | throw HashException( | 795 | va.destroy( &aValues[loc] ); | 
| 433 | "Cannot mix iterators from different hash objects."); | 796 | ka.destroy( &aKeys[loc] ); | 
| 434 | nPos = oth.nPos; | 797 | nDeleted++; | 
| 435 | bFinished = oth.bFinished; | 798 | //printf("Filled: %d, Deleted: %d, Capacity: %d\n", | 
| 799 | // nFilled, nDeleted, nCapacity ); | ||
| 436 | } | 800 | } | 
| 437 | 801 | ||
| 438 | std::pair<key,value> operator *() | 802 | virtual std::pair<key,value> getAtPos( uint32_t nPos ) | 
| 803 | { | ||
| 804 | return std::pair<key,value>(aKeys[nPos],aValues[nPos]); | ||
| 805 | } | ||
| 806 | |||
| 807 | virtual key &getKeyAtPos( uint32_t nPos ) | ||
| 439 | { | 808 | { | 
| 440 | return hsh.getAtPos( nPos ); | 809 | return aKeys[nPos]; | 
| 441 | } | 810 | } | 
| 442 | 811 | ||
| 443 | key &getKey() | 812 | virtual const key &getKeyAtPos( uint32_t nPos ) const | 
| 444 | { | 813 | { | 
| 445 | return hsh.getKeyAtPos( nPos ); | 814 | return aKeys[nPos]; | 
| 815 | } | ||
| 816 | |||
| 817 | virtual value &getValueAtPos( uint32_t nPos ) | ||
| 818 | { | ||
| 819 | return aValues[nPos]; | ||
| 820 | } | ||
| 821 | |||
| 822 | virtual const value &getValueAtPos( uint32_t nPos ) const | ||
| 823 | { | ||
| 824 | return aValues[nPos]; | ||
| 446 | } | 825 | } | 
| 447 | 826 | ||
| 448 | value &getValue() | 827 | virtual uint32_t getFirstPos( bool &bFinished ) const | 
| 449 | { | 828 | { | 
| 450 | return hsh.getValueAtPos( nPos ); | 829 | for( uint32_t j = 0; j < nCapacity; j++ ) | 
| 830 | { | ||
| 831 | if( isFilled( j ) ) | ||
| 832 | if( !isDeleted( j ) ) | ||
| 833 | return j; | ||
| 834 | } | ||
| 835 | |||
| 836 | bFinished = true; | ||
| 837 | return 0; | ||
| 451 | } | 838 | } | 
| 452 | }; | ||
| 453 | 839 | ||
| 454 | iterator begin() | 840 | virtual uint32_t getNextPos( uint32_t nPos, bool &bFinished ) const | 
| 455 | { | 841 | { | 
| 456 | return iterator( *this ); | 842 | for( uint32_t j = nPos+1; j < nCapacity; j++ ) | 
| 457 | } | 843 | { | 
| 458 | 844 | if( isFilled( j ) ) | |
| 459 | iterator end() | 845 | if( !isDeleted( j ) ) | 
| 460 | { | 846 | return j; | 
| 461 | return iterator( *this, true ); | 847 | } | 
| 462 | } | ||
| 463 | 848 | ||
| 464 | std::list<key> getKeys() | 849 | bFinished = true; | 
| 465 | { | 850 | return 0; | 
| 466 | std::list<key> lKeys; | 851 | } | 
| 467 | 852 | ||
| 468 | for( uint32_t j = 0; j < nCapacity; j++ ) | 853 | uint32_t probe( uint32_t hash, key k, bool &bFill, bool rehash=true ) | 
| 469 | { | 854 | { | 
| 470 | if( isFilled( j ) ) | 855 | uint32_t nCur = hash%nCapacity; | 
| 856 | |||
| 857 | // First we scan to see if the key is already there, abort if we | ||
| 858 | // run out of probing room, or we find a non-filled entry | ||
| 859 | for( int8_t j = 0; | ||
| 860 | isFilled( nCur ) && j < 32; | ||
| 861 | nCur = (nCur + (1<<j))%nCapacity, j++ | ||
| 862 | ) | ||
| 471 | { | 863 | { | 
| 472 | if( !isDeleted( j ) ) | 864 | // Is this the same hash code we were looking for? | 
| 865 | if( hash == aHashCodes[nCur] ) | ||
| 473 | { | 866 | { | 
| 474 | lKeys.push_back( aKeys[j] ); | 867 | // Skip over deleted entries. Deleted entries are also filled, | 
| 868 | // so we only have to do this check here. | ||
| 869 | if( isDeleted( nCur ) ) | ||
| 870 | continue; | ||
| 871 | |||
| 872 | // Is it really the same key? (for safety) | ||
| 873 | if( __cmpHashKeys( aKeys[nCur], k ) == true ) | ||
| 874 | { | ||
| 875 | bFill = true; | ||
| 876 | return nCur; | ||
| 877 | } | ||
| 475 | } | 878 | } | 
| 476 | } | 879 | } | 
| 477 | } | ||
| 478 | 880 | ||
| 479 | return lKeys; | 881 | // This is our insurance, if the table is full, then go ahead and | 
| 480 | } | 882 | // rehash, then try again. | 
| 883 | if( isFilled( nCur ) && rehash == true ) | ||
| 884 | { | ||
| 885 | reHash( szCalc(getCapacity(), getFill(), getDeleted()) ); | ||
| 481 | 886 | ||
| 482 | protected: | 887 | // This is potentially dangerous, and could cause an infinite loop. | 
| 483 | virtual void onInsert() {} | 888 | // Be careful writing probe, eh? | 
| 484 | virtual void onUpdate() {} | 889 | return probe( hash, k, bFill ); | 
| 485 | virtual void onDelete() {} | 890 | } | 
| 486 | virtual void onReHash() {} | ||
| 487 | 891 | ||
| 488 | virtual void clearBits() | 892 | bFill = false; | 
| 489 | { | 893 | return nCur; | 
| 490 | for( uint32_t j = 0; j < nKeysSize; j++ ) | ||
| 491 | { | ||
| 492 | bFilled[j] = bDeleted[j] = 0; | ||
| 493 | } | 894 | } | 
| 494 | } | 895 | |
| 495 | 896 | uint32_t probe( uint32_t hash, key k, bool &bFill, bool rehash=true ) const | |
| 496 | virtual void fill( uint32_t loc, key &k, value &v, uint32_t hash ) | ||
| 497 | { | ||
| 498 | bFilled[loc/32] |= (1<<(loc%32)); | ||
| 499 | va.construct( &aValues[loc], v ); | ||
| 500 | ka.construct( &aKeys[loc], k ); | ||
| 501 | aHashCodes[loc] = hash; | ||
| 502 | nFilled++; | ||
| 503 | //printf("Filled: %d, Deleted: %d, Capacity: %d\n", | ||
| 504 | // nFilled, nDeleted, nCapacity ); | ||
| 505 | } | ||
| 506 | |||
| 507 | virtual void _erase( uint32_t loc ) | ||
| 508 | { | ||
| 509 | bDeleted[loc/32] |= (1<<(loc%32)); | ||
| 510 | va.destroy( &aValues[loc] ); | ||
| 511 | ka.destroy( &aKeys[loc] ); | ||
| 512 | nDeleted++; | ||
| 513 | //printf("Filled: %d, Deleted: %d, Capacity: %d\n", | ||
| 514 | // nFilled, nDeleted, nCapacity ); | ||
| 515 | } | ||
| 516 | |||
| 517 | virtual std::pair<key,value> getAtPos( uint32_t nPos ) | ||
| 518 | { | ||
| 519 | return std::pair<key,value>(aKeys[nPos],aValues[nPos]); | ||
| 520 | } | ||
| 521 | |||
| 522 | virtual key &getKeyAtPos( uint32_t nPos ) | ||
| 523 | { | ||
| 524 | return aKeys[nPos]; | ||
| 525 | } | ||
| 526 | |||
| 527 | virtual value &getValueAtPos( uint32_t nPos ) | ||
| 528 | { | ||
| 529 | return aValues[nPos]; | ||
| 530 | } | ||
| 531 | |||
| 532 | virtual uint32_t getFirstPos( bool &bFinished ) | ||
| 533 | { | ||
| 534 | for( uint32_t j = 0; j < nCapacity; j++ ) | ||
| 535 | { | 897 | { | 
| 536 | if( isFilled( j ) ) | 898 | uint32_t nCur = hash%nCapacity; | 
| 537 | if( !isDeleted( j ) ) | 899 | |
| 538 | return j; | 900 | // First we scan to see if the key is already there, abort if we | 
| 539 | } | 901 | // run out of probing room, or we find a non-filled entry | 
| 902 | for( int8_t j = 0; | ||
| 903 | isFilled( nCur ) && j < 32; | ||
| 904 | nCur = (nCur + (1<<j))%nCapacity, j++ | ||
| 905 | ) | ||
| 906 | { | ||
| 907 | // Is this the same hash code we were looking for? | ||
| 908 | if( hash == aHashCodes[nCur] ) | ||
| 909 | { | ||
| 910 | // Skip over deleted entries. Deleted entries are also filled, | ||
| 911 | // so we only have to do this check here. | ||
| 912 | if( isDeleted( nCur ) ) | ||
| 913 | continue; | ||
| 914 | |||
| 915 | // Is it really the same key? (for safety) | ||
| 916 | if( __cmpHashKeys( aKeys[nCur], k ) == true ) | ||
| 917 | { | ||
| 918 | bFill = true; | ||
| 919 | return nCur; | ||
| 920 | } | ||
| 921 | } | ||
| 922 | } | ||
| 540 | 923 | ||
| 541 | bFinished = true; | 924 | bFill = false; | 
| 542 | return 0; | 925 | return nCur; | 
| 543 | } | 926 | } | 
| 544 | 927 | ||
| 545 | virtual uint32_t getNextPos( uint32_t nPos, bool &bFinished ) | 928 | void reHash( uint32_t nNewSize ) | 
| 546 | { | ||
| 547 | for( uint32_t j = nPos+1; j < nCapacity; j++ ) | ||
| 548 | { | 929 | { | 
| 549 | if( isFilled( j ) ) | 930 | //printf("---REHASH---"); | 
| 550 | if( !isDeleted( j ) ) | 931 | //printf("Filled: %d, Deleted: %d, Capacity: %d\n", | 
| 551 | return j; | 932 | // nFilled, nDeleted, nCapacity ); | 
| 552 | } | 933 | |
| 934 | // Save all the old data | ||
| 935 | uint32_t nOldCapacity = nCapacity; | ||
| 936 | uint32_t *bOldFilled = bFilled; | ||
| 937 | uint32_t *aOldHashCodes = aHashCodes; | ||
| 938 | uint32_t nOldKeysSize = nKeysSize; | ||
| 939 | uint32_t *bOldDeleted = bDeleted; | ||
| 940 | value *aOldValues = aValues; | ||
| 941 | key *aOldKeys = aKeys; | ||
| 942 | |||
| 943 | // Calculate new sizes | ||
| 944 | nCapacity = nNewSize; | ||
| 945 | nKeysSize = bitsToBytes( nCapacity ); | ||
| 946 | |||
| 947 | // Allocate new memory + prep | ||
| 948 | bFilled = ca.allocate( nKeysSize ); | ||
| 949 | bDeleted = ca.allocate( nKeysSize ); | ||
| 950 | clearBits(); | ||
| 553 | 951 | ||
| 554 | bFinished = true; | 952 | aHashCodes = ca.allocate( nCapacity ); | 
| 555 | return 0; | 953 | aKeys = ka.allocate( nCapacity ); | 
| 556 | } | 954 | aValues = va.allocate( nCapacity ); | 
| 557 | 955 | ||
| 558 | uint32_t probe( uint32_t hash, key k, bool &bFill, bool rehash=true ) | 956 | nDeleted = nFilled = 0; | 
| 559 | { | ||
| 560 | uint32_t nCur = hash%nCapacity; | ||
| 561 | 957 | ||
| 562 | // First we scan to see if the key is already there, abort if we | 958 | // Re-insert all of the old data (except deleted items) | 
| 563 | // run out of probing room, or we find a non-filled entry | 959 | for( uint32_t j = 0; j < nOldCapacity; j++ ) | 
| 564 | for( int8_t j = 0; | ||
| 565 | isFilled( nCur ) && j < 32; | ||
| 566 | nCur = (nCur + (1<<j))%nCapacity, j++ | ||
| 567 | ) | ||
| 568 | { | ||
| 569 | // Is this the same hash code we were looking for? | ||
| 570 | if( hash == aHashCodes[nCur] ) | ||
| 571 | { | 960 | { | 
| 572 | // Skip over deleted entries. Deleted entries are also filled, | 961 | if( (bOldFilled[j/32]&(1<<(j%32)))!=0 && | 
| 573 | // so we only have to do this check here. | 962 | (bOldDeleted[j/32]&(1<<(j%32)))==0 ) | 
| 574 | if( isDeleted( nCur ) ) | 963 | { | 
| 575 | continue; | 964 | insert( aOldKeys[j], aOldValues[j] ); | 
| 965 | } | ||
| 966 | } | ||
| 576 | 967 | ||
| 577 | // Is it really the same key? (for safety) | 968 | // Delete all of the old data | 
| 578 | if( __cmpHashKeys( aKeys[nCur], k ) == true ) | 969 | for( uint32_t j = 0; j < nOldCapacity; j++ ) | 
| 970 | { | ||
| 971 | if( (bOldFilled[j/32]&(1<<(j%32)))!=0 ) | ||
| 579 | { | 972 | { | 
| 580 | bFill = true; | 973 | va.destroy( &aOldValues[j] ); | 
| 581 | return nCur; | 974 | ka.destroy( &aOldKeys[j] ); | 
| 582 | } | 975 | } | 
| 583 | } | 976 | } | 
| 977 | va.deallocate( aOldValues, nOldCapacity ); | ||
| 978 | ka.deallocate( aOldKeys, nOldCapacity ); | ||
| 979 | ca.deallocate( bOldFilled, nOldKeysSize ); | ||
| 980 | ca.deallocate( bOldDeleted, nOldKeysSize ); | ||
| 981 | ca.deallocate( aOldHashCodes, nOldCapacity ); | ||
| 584 | } | 982 | } | 
| 585 | 983 | ||
| 586 | // This is our insurance, if the table is full, then go ahead and | 984 | virtual bool isFilled( uint32_t loc ) const | 
| 587 | // rehash, then try again. | ||
| 588 | if( isFilled( nCur ) && rehash == true ) | ||
| 589 | { | 985 | { | 
| 590 | reHash( szCalc(getCapacity(), getFill(), getDeleted()) ); | 986 | return (bFilled[loc/32]&(1<<(loc%32)))!=0; | 
| 987 | } | ||
| 591 | 988 | ||
| 592 | // This is potentially dangerous, and could cause an infinite loop. | 989 | virtual bool isDeleted( uint32_t loc ) const | 
| 593 | // Be careful writing probe, eh? | 990 | { | 
| 594 | return probe( hash, k, bFill ); | 991 | return (bDeleted[loc/32]&(1<<(loc%32)))!=0; | 
| 595 | } | 992 | } | 
| 596 | 993 | ||
| 597 | bFill = false; | 994 | protected: | 
| 598 | return nCur; | 995 | uint32_t nCapacity; | 
| 599 | } | 996 | uint32_t nFilled; | 
| 997 | uint32_t nDeleted; | ||
| 998 | uint32_t *bFilled; | ||
| 999 | uint32_t *bDeleted; | ||
| 1000 | uint32_t nKeysSize; | ||
| 1001 | key *aKeys; | ||
| 1002 | value *aValues; | ||
| 1003 | uint32_t *aHashCodes; | ||
| 1004 | valuealloc va; | ||
| 1005 | keyalloc ka; | ||
| 1006 | challoc ca; | ||
| 1007 | sizecalc szCalc; | ||
| 1008 | }; | ||
| 600 | 1009 | ||
| 601 | void reHash( uint32_t nNewSize ) | 1010 | template<> uint32_t __calcHashCode<int>( const int &k ); | 
| 602 | { | 1011 | template<> bool __cmpHashKeys<int>( const int &a, const int &b ); | 
| 603 | //printf("---REHASH---"); | ||
| 604 | //printf("Filled: %d, Deleted: %d, Capacity: %d\n", | ||
| 605 | // nFilled, nDeleted, nCapacity ); | ||
| 606 | |||
| 607 | // Save all the old data | ||
| 608 | uint32_t nOldCapacity = nCapacity; | ||
| 609 | uint32_t *bOldFilled = bFilled; | ||
| 610 | uint32_t *aOldHashCodes = aHashCodes; | ||
| 611 | uint32_t nOldKeysSize = nKeysSize; | ||
| 612 | uint32_t *bOldDeleted = bDeleted; | ||
| 613 | value *aOldValues = aValues; | ||
| 614 | key *aOldKeys = aKeys; | ||
| 615 | |||
| 616 | // Calculate new sizes | ||
| 617 | nCapacity = nNewSize; | ||
| 618 | nKeysSize = bitsToBytes( nCapacity ); | ||
| 619 | |||
| 620 | // Allocate new memory + prep | ||
| 621 | bFilled = ca.allocate( nKeysSize ); | ||
| 622 | bDeleted = ca.allocate( nKeysSize ); | ||
| 623 | clearBits(); | ||
| 624 | 1012 | ||
| 625 | aHashCodes = ca.allocate( nCapacity ); | 1013 | template<> uint32_t __calcHashCode<unsigned int>( const unsigned int &k ); | 
| 626 | aKeys = ka.allocate( nCapacity ); | 1014 | template<> bool __cmpHashKeys<unsigned int>( const unsigned int &a, const unsigned int &b ); | 
| 627 | aValues = va.allocate( nCapacity ); | ||
| 628 | 1015 | ||
| 629 | nDeleted = nFilled = 0; | 1016 | template<> uint32_t __calcHashCode<const char *>( const char * const &k ); | 
| 1017 | template<> bool __cmpHashKeys<const char *>( const char * const &a, const char * const &b ); | ||
| 630 | 1018 | ||
| 631 | // Re-insert all of the old data (except deleted items) | 1019 | template<> uint32_t __calcHashCode<char *>( char * const &k ); | 
| 632 | for( uint32_t j = 0; j < nOldCapacity; j++ ) | 1020 | template<> bool __cmpHashKeys<char *>( char * const &a, char * const &b ); | 
| 633 | { | ||
| 634 | if( (bOldFilled[j/32]&(1<<(j%32)))!=0 && | ||
| 635 | (bOldDeleted[j/32]&(1<<(j%32)))==0 ) | ||
| 636 | { | ||
| 637 | insert( aOldKeys[j], aOldValues[j] ); | ||
| 638 | } | ||
| 639 | } | ||
| 640 | 1021 | ||
| 641 | // Delete all of the old data | 1022 | template<> uint32_t __calcHashCode<std::string>( const std::string &k ); | 
| 642 | for( uint32_t j = 0; j < nOldCapacity; j++ ) | 1023 | template<> bool __cmpHashKeys<std::string>( const std::string &a, const std::string &b ); | 
| 1024 | |||
| 1025 | /* | ||
| 1026 | template<typename key, typename value> | ||
| 1027 | Archive &operator<<( Archive &ar, Hash<key,value> &h ) | ||
| 1028 | { | ||
| 1029 | ar << h.size(); | ||
| 1030 | for( typename Hash<key,value>::iterator i = h.begin(); i != h.end(); i++ ) | ||
| 643 | { | 1031 | { | 
| 644 | if( (bOldFilled[j/32]&(1<<(j%32)))!=0 && | 1032 | std::pair<key,value> p = *i; | 
| 645 | (bOldDeleted[j/32]&(1<<(j%32)))==0 ) | 1033 | ar << p.first << p.second; | 
| 646 | { | ||
| 647 | va.destroy( &aOldValues[j] ); | ||
| 648 | ka.destroy( &aOldKeys[j] ); | ||
| 649 | } | ||
| 650 | } | 1034 | } | 
| 651 | va.deallocate( aOldValues, nOldCapacity ); | ||
| 652 | ka.deallocate( aOldKeys, nOldCapacity ); | ||
| 653 | ca.deallocate( bOldFilled, nOldKeysSize ); | ||
| 654 | ca.deallocate( bOldDeleted, nOldKeysSize ); | ||
| 655 | ca.deallocate( aOldHashCodes, nOldCapacity ); | ||
| 656 | } | ||
| 657 | 1035 | ||
| 658 | virtual bool isFilled( uint32_t loc ) const | 1036 | return ar; | 
| 659 | { | ||
| 660 | return (bFilled[loc/32]&(1<<(loc%32)))!=0; | ||
| 661 | } | 1037 | } | 
| 662 | 1038 | ||
| 663 | virtual bool isDeleted( uint32_t loc ) | 1039 | template<typename key, typename value> | 
| 1040 | Archive &operator>>( Archive &ar, Hash<key,value> &h ) | ||
| 664 | { | 1041 | { | 
| 665 | return (bDeleted[loc/32]&(1<<(loc%32)))!=0; | 1042 | h.clear(); | 
| 666 | } | 1043 | uint32_t nSize; | 
| 1044 | ar >> nSize; | ||
| 667 | 1045 | ||
| 668 | protected: | 1046 | for( uint32_t j = 0; j < nSize; j++ ) | 
| 669 | uint32_t nCapacity; | 1047 | { | 
| 670 | uint32_t nFilled; | 1048 | key k; value v; | 
| 671 | uint32_t nDeleted; | 1049 | ar >> k >> v; | 
| 672 | uint32_t *bFilled; | 1050 | h.insert( k, v ); | 
| 673 | uint32_t *bDeleted; | 1051 | } | 
| 674 | uint32_t nKeysSize; | ||
| 675 | key *aKeys; | ||
| 676 | value *aValues; | ||
| 677 | uint32_t *aHashCodes; | ||
| 678 | valuealloc va; | ||
| 679 | keyalloc ka; | ||
| 680 | challoc ca; | ||
| 681 | sizecalc szCalc; | ||
| 682 | }; | ||
| 683 | |||
| 684 | template<> uint32_t __calcHashCode<int>( const int &k ); | ||
| 685 | template<> bool __cmpHashKeys<int>( const int &a, const int &b ); | ||
| 686 | |||
| 687 | template<> uint32_t __calcHashCode<unsigned int>( const unsigned int &k ); | ||
| 688 | template<> bool __cmpHashKeys<unsigned int>( const unsigned int &a, const unsigned int &b ); | ||
| 689 | |||
| 690 | template<> uint32_t __calcHashCode<const char *>( const char * const &k ); | ||
| 691 | template<> bool __cmpHashKeys<const char *>( const char * const &a, const char * const &b ); | ||
| 692 | |||
| 693 | template<> uint32_t __calcHashCode<char *>( char * const &k ); | ||
| 694 | template<> bool __cmpHashKeys<char *>( char * const &a, char * const &b ); | ||
| 695 | |||
| 696 | template<> uint32_t __calcHashCode<std::string>( const std::string &k ); | ||
| 697 | template<> bool __cmpHashKeys<std::string>( const std::string &a, const std::string &b ); | ||
| 698 | |||
| 699 | template<> uint32_t __calcHashCode<Hashable>( const Hashable &k ); | ||
| 700 | template<> bool __cmpHashKeys<Hashable>( const Hashable &a, const Hashable &b ); | ||
| 701 | |||
| 702 | template<typename key, typename value> | ||
| 703 | Serializer &operator<<( Serializer &ar, Hash<key,value> &h ) | ||
| 704 | { | ||
| 705 | ar << h.size(); | ||
| 706 | for( typename Hash<key,value>::iterator i = h.begin(); i != h.end(); i++ ) | ||
| 707 | { | ||
| 708 | std::pair<key,value> p = *i; | ||
| 709 | ar << p.first << p.second; | ||
| 710 | } | ||
| 711 | 1052 | ||
| 712 | return ar; | 1053 | return ar; | 
| 713 | } | 1054 | }*/ | 
| 714 | 1055 | ||
| 715 | template<typename key, typename value> | 1056 | /* | 
| 716 | Serializer &operator>>( Serializer &ar, Hash<key,value> &h ) | 1057 | template<typename key, typename value> | 
| 717 | { | 1058 | Serializer &operator&&( Serializer &ar, Hash<key,value> &h ) | 
| 718 | h.clear(); | ||
| 719 | uint32_t nSize; | ||
| 720 | ar >> nSize; | ||
| 721 | |||
| 722 | for( uint32_t j = 0; j < nSize; j++ ) | ||
| 723 | { | 1059 | { | 
| 724 | key k; value v; | 1060 | if( ar.isLoading() ) | 
| 725 | ar >> k >> v; | 1061 | { | 
| 726 | h.insert( k, v ); | 1062 | return ar >> h; | 
| 727 | } | 1063 | } | 
| 728 | 1064 | else | |
| 729 | return ar; | 1065 | { | 
| 730 | } | 1066 | return ar << h; | 
| 731 | 1067 | } | |
| 732 | template<typename key, typename value> | 1068 | }*/ | 
| 733 | Serializer &operator&&( Serializer &ar, Hash<key,value> &h ) | ||
| 734 | { | ||
| 735 | if( ar.isLoading() ) | ||
| 736 | { | ||
| 737 | return ar >> h; | ||
| 738 | } | ||
| 739 | else | ||
| 740 | { | ||
| 741 | return ar << h; | ||
| 742 | } | ||
| 743 | } | 1069 | } | 
| 744 | 1070 | ||
| 745 | #endif | 1071 | #endif | 
