| 1 | // Licensed to the .NET Foundation under one or more agreements. |
| 2 | // The .NET Foundation licenses this file to you under the MIT license. |
| 3 | // See the LICENSE file in the project root for more information. |
| 4 | |
| 5 | #ifndef _SHASH_INL_ |
| 6 | #define _SHASH_INL_ |
| 7 | |
| 8 | // Many SHash functions do not throw on their own, but may propagate an exception |
| 9 | // from Hash, Equals, or GetKey. |
| 10 | #define NOTHROW_UNLESS_TRAITS_THROWS if (TRAITS::s_NoThrow) NOTHROW; else THROWS |
| 11 | |
| 12 | void DECLSPEC_NORETURN ThrowOutOfMemory(); |
| 13 | |
| 14 | template <typename TRAITS> |
| 15 | SHash<TRAITS>::SHash() |
| 16 | : m_table(nullptr), |
| 17 | m_tableSize(0), |
| 18 | m_tableCount(0), |
| 19 | m_tableOccupied(0), |
| 20 | m_tableMax(0) |
| 21 | { |
| 22 | LIMITED_METHOD_CONTRACT; |
| 23 | |
| 24 | #ifndef __GNUC__ // these crash GCC |
| 25 | static_assert_no_msg(SHash<TRAITS>::s_growth_factor_numerator > SHash<TRAITS>::s_growth_factor_denominator); |
| 26 | static_assert_no_msg(SHash<TRAITS>::s_density_factor_numerator < SHash<TRAITS>::s_density_factor_denominator); |
| 27 | #endif |
| 28 | } |
| 29 | |
| 30 | template <typename TRAITS> |
| 31 | SHash<TRAITS>::~SHash() |
| 32 | { |
| 33 | LIMITED_METHOD_CONTRACT; |
| 34 | |
| 35 | if (TRAITS::s_DestructPerEntryCleanupAction) |
| 36 | { |
| 37 | for (Iterator i = Begin(); i != End(); i++) |
| 38 | { |
| 39 | TRAITS::OnDestructPerEntryCleanupAction(*i); |
| 40 | } |
| 41 | } |
| 42 | |
| 43 | delete [] m_table; |
| 44 | } |
| 45 | |
| 46 | template <typename TRAITS> |
| 47 | typename SHash<TRAITS>::count_t SHash<TRAITS>::GetCount() const |
| 48 | { |
| 49 | LIMITED_METHOD_CONTRACT; |
| 50 | |
| 51 | return m_tableCount; |
| 52 | } |
| 53 | |
| 54 | template <typename TRAITS> |
| 55 | typename SHash<TRAITS>::element_t SHash<TRAITS>::Lookup(key_t key) const |
| 56 | { |
| 57 | CONTRACT(element_t) |
| 58 | { |
| 59 | NOTHROW_UNLESS_TRAITS_THROWS; |
| 60 | GC_NOTRIGGER; |
| 61 | INSTANCE_CHECK; |
| 62 | POSTCONDITION(TRAITS::IsNull(RETVAL) || TRAITS::Equals(key, TRAITS::GetKey(RETVAL))); |
| 63 | SUPPORTS_DAC_WRAPPER; |
| 64 | } |
| 65 | CONTRACT_END; |
| 66 | |
| 67 | const element_t *pRet = Lookup(m_table, m_tableSize, key); |
| 68 | RETURN ((pRet != NULL) ? (*pRet) : TRAITS::Null()); |
| 69 | } |
| 70 | |
| 71 | template <typename TRAITS> |
| 72 | const typename SHash<TRAITS>::element_t * SHash<TRAITS>::LookupPtr(key_t key) const |
| 73 | { |
| 74 | CONTRACT(const element_t *) |
| 75 | { |
| 76 | NOTHROW_UNLESS_TRAITS_THROWS; |
| 77 | GC_NOTRIGGER; |
| 78 | INSTANCE_CHECK; |
| 79 | POSTCONDITION(RETVAL == NULL || TRAITS::Equals(key, TRAITS::GetKey(*RETVAL))); |
| 80 | } |
| 81 | CONTRACT_END; |
| 82 | |
| 83 | RETURN Lookup(m_table, m_tableSize, key); |
| 84 | } |
| 85 | |
| 86 | template <typename TRAITS> |
| 87 | void SHash<TRAITS>::Add(const element_t & element) |
| 88 | { |
| 89 | CONTRACT_VOID |
| 90 | { |
| 91 | THROWS; |
| 92 | GC_NOTRIGGER; |
| 93 | INSTANCE_CHECK; |
| 94 | POSTCONDITION(TRAITS::Equals(TRAITS::GetKey(element), TRAITS::GetKey(*LookupPtr(TRAITS::GetKey(element))))); |
| 95 | } |
| 96 | CONTRACT_END; |
| 97 | |
| 98 | CheckGrowth(); |
| 99 | |
| 100 | Add_GrowthChecked(element); |
| 101 | |
| 102 | RETURN; |
| 103 | } |
| 104 | |
| 105 | template <typename TRAITS> |
| 106 | void SHash<TRAITS>::Add_GrowthChecked(const element_t & element) |
| 107 | { |
| 108 | CONTRACT_VOID |
| 109 | { |
| 110 | NOTHROW_UNLESS_TRAITS_THROWS; |
| 111 | GC_NOTRIGGER; |
| 112 | INSTANCE_CHECK; |
| 113 | POSTCONDITION(TRAITS::Equals(TRAITS::GetKey(element), TRAITS::GetKey(*LookupPtr(TRAITS::GetKey(element))))); |
| 114 | } |
| 115 | CONTRACT_END; |
| 116 | |
| 117 | if (Add(m_table, m_tableSize, element)) |
| 118 | m_tableOccupied++; |
| 119 | m_tableCount++; |
| 120 | |
| 121 | RETURN; |
| 122 | } |
| 123 | |
| 124 | template <typename TRAITS> |
| 125 | void SHash<TRAITS>::AddOrReplace(const element_t &element) |
| 126 | { |
| 127 | CONTRACT_VOID |
| 128 | { |
| 129 | THROWS; |
| 130 | GC_NOTRIGGER; |
| 131 | INSTANCE_CHECK; |
| 132 | static_assert(!TRAITS::s_supports_remove, "SHash::AddOrReplace is not implemented for SHash with support for remove operations." ); |
| 133 | POSTCONDITION(TRAITS::Equals(TRAITS::GetKey(element), TRAITS::GetKey(*LookupPtr(TRAITS::GetKey(element))))); |
| 134 | } |
| 135 | CONTRACT_END; |
| 136 | |
| 137 | CheckGrowth(); |
| 138 | |
| 139 | AddOrReplace(m_table, m_tableSize, element); |
| 140 | |
| 141 | RETURN; |
| 142 | } |
| 143 | |
| 144 | template <typename TRAITS> |
| 145 | void SHash<TRAITS>::Remove(key_t key) |
| 146 | { |
| 147 | CONTRACT_VOID |
| 148 | { |
| 149 | NOTHROW_UNLESS_TRAITS_THROWS; |
| 150 | GC_NOTRIGGER; |
| 151 | INSTANCE_CHECK; |
| 152 | static_assert(TRAITS::s_supports_remove, "This SHash does not support remove operations." ); |
| 153 | PRECONDITION(!(TRAITS::IsNull(Lookup(key)))); |
| 154 | } |
| 155 | CONTRACT_END; |
| 156 | |
| 157 | Remove(m_table, m_tableSize, key); |
| 158 | |
| 159 | RETURN; |
| 160 | } |
| 161 | |
| 162 | template <typename TRAITS> |
| 163 | void SHash<TRAITS>::Remove(Iterator& i) |
| 164 | { |
| 165 | CONTRACT_VOID |
| 166 | { |
| 167 | NOTHROW; |
| 168 | GC_NOTRIGGER; |
| 169 | INSTANCE_CHECK; |
| 170 | static_assert(TRAITS::s_supports_remove, "This SHash does not support remove operations." ); |
| 171 | PRECONDITION(!(TRAITS::IsNull(*i))); |
| 172 | PRECONDITION(!(TRAITS::IsDeleted(*i))); |
| 173 | } |
| 174 | CONTRACT_END; |
| 175 | |
| 176 | RemoveElement(m_table, m_tableSize, (element_t*)&(*i)); |
| 177 | |
| 178 | RETURN; |
| 179 | } |
| 180 | |
| 181 | template <typename TRAITS> |
| 182 | void SHash<TRAITS>::Remove(KeyIterator& i) |
| 183 | { |
| 184 | CONTRACT_VOID |
| 185 | { |
| 186 | NOTHROW; |
| 187 | GC_NOTRIGGER; |
| 188 | INSTANCE_CHECK; |
| 189 | static_assert(TRAITS::s_supports_remove, "This SHash does not support remove operations." ); |
| 190 | PRECONDITION(!(TRAITS::IsNull(*i))); |
| 191 | PRECONDITION(!(TRAITS::IsDeleted(*i))); |
| 192 | } |
| 193 | CONTRACT_END; |
| 194 | |
| 195 | RemoveElement(m_table, m_tableSize, (element_t*)&(*i)); |
| 196 | |
| 197 | RETURN; |
| 198 | } |
| 199 | |
| 200 | template <typename TRAITS> |
| 201 | void SHash<TRAITS>::RemovePtr(element_t * p) |
| 202 | { |
| 203 | CONTRACT_VOID |
| 204 | { |
| 205 | NOTHROW; |
| 206 | GC_NOTRIGGER; |
| 207 | INSTANCE_CHECK; |
| 208 | static_assert(TRAITS::s_supports_remove, "This SHash does not support remove operations." ); |
| 209 | PRECONDITION(!(TRAITS::IsNull(*p))); |
| 210 | PRECONDITION(!(TRAITS::IsDeleted(*p))); |
| 211 | } |
| 212 | CONTRACT_END; |
| 213 | |
| 214 | RemoveElement(m_table, m_tableSize, p); |
| 215 | |
| 216 | RETURN; |
| 217 | } |
| 218 | |
| 219 | template <typename TRAITS> |
| 220 | void SHash<TRAITS>::RemoveAll() |
| 221 | { |
| 222 | CONTRACT_VOID |
| 223 | { |
| 224 | NOTHROW; |
| 225 | GC_NOTRIGGER; |
| 226 | INSTANCE_CHECK; |
| 227 | } |
| 228 | CONTRACT_END; |
| 229 | |
| 230 | delete [] m_table; |
| 231 | |
| 232 | m_table = NULL; |
| 233 | m_tableSize = 0; |
| 234 | m_tableCount = 0; |
| 235 | m_tableOccupied = 0; |
| 236 | m_tableMax = 0; |
| 237 | |
| 238 | RETURN; |
| 239 | } |
| 240 | |
| 241 | template <typename TRAITS> |
| 242 | typename SHash<TRAITS>::Iterator SHash<TRAITS>::Begin() const |
| 243 | { |
| 244 | LIMITED_METHOD_CONTRACT; |
| 245 | |
| 246 | Iterator i(this, TRUE); |
| 247 | i.First(); |
| 248 | return i; |
| 249 | } |
| 250 | |
| 251 | template <typename TRAITS> |
| 252 | typename SHash<TRAITS>::Iterator SHash<TRAITS>::End() const |
| 253 | { |
| 254 | LIMITED_METHOD_CONTRACT; |
| 255 | |
| 256 | return Iterator(this, FALSE); |
| 257 | } |
| 258 | |
| 259 | template <typename TRAITS> |
| 260 | typename SHash<TRAITS>::KeyIterator SHash<TRAITS>::Begin(key_t key) const |
| 261 | { |
| 262 | LIMITED_METHOD_CONTRACT; |
| 263 | |
| 264 | KeyIterator k(this, TRUE); |
| 265 | k.SetKey(key); |
| 266 | return k; |
| 267 | } |
| 268 | |
| 269 | template <typename TRAITS> |
| 270 | typename SHash<TRAITS>::KeyIterator SHash<TRAITS>::End(key_t key) const |
| 271 | { |
| 272 | LIMITED_METHOD_CONTRACT; |
| 273 | |
| 274 | return KeyIterator(this, FALSE); |
| 275 | } |
| 276 | |
| 277 | template <typename TRAITS> |
| 278 | BOOL SHash<TRAITS>::CheckGrowth() |
| 279 | { |
| 280 | CONTRACT(BOOL) |
| 281 | { |
| 282 | THROWS; |
| 283 | GC_NOTRIGGER; |
| 284 | INSTANCE_CHECK; |
| 285 | } |
| 286 | CONTRACT_END; |
| 287 | |
| 288 | if (m_tableOccupied == m_tableMax) |
| 289 | { |
| 290 | Grow(); |
| 291 | RETURN TRUE; |
| 292 | } |
| 293 | |
| 294 | RETURN FALSE; |
| 295 | } |
| 296 | |
| 297 | template <typename TRAITS> |
| 298 | typename SHash<TRAITS>::element_t * |
| 299 | SHash<TRAITS>::CheckGrowth_OnlyAllocateNewTable(count_t * pcNewSize) |
| 300 | { |
| 301 | CONTRACT(element_t *) |
| 302 | { |
| 303 | THROWS; |
| 304 | GC_NOTRIGGER; |
| 305 | INSTANCE_CHECK; |
| 306 | } |
| 307 | CONTRACT_END; |
| 308 | |
| 309 | if (m_tableOccupied == m_tableMax) |
| 310 | { |
| 311 | RETURN Grow_OnlyAllocateNewTable(pcNewSize); |
| 312 | } |
| 313 | |
| 314 | RETURN NULL; |
| 315 | } |
| 316 | |
| 317 | template <typename TRAITS> |
| 318 | void SHash<TRAITS>::Grow() |
| 319 | { |
| 320 | CONTRACT_VOID |
| 321 | { |
| 322 | THROWS; |
| 323 | GC_NOTRIGGER; |
| 324 | INSTANCE_CHECK; |
| 325 | } |
| 326 | CONTRACT_END; |
| 327 | |
| 328 | count_t newSize; |
| 329 | element_t * newTable = Grow_OnlyAllocateNewTable(&newSize); |
| 330 | element_t * oldTable = ReplaceTable(newTable, newSize); |
| 331 | DeleteOldTable(oldTable); |
| 332 | |
| 333 | RETURN; |
| 334 | } |
| 335 | |
| 336 | template <typename TRAITS> |
| 337 | typename SHash<TRAITS>::element_t * |
| 338 | SHash<TRAITS>::Grow_OnlyAllocateNewTable(count_t * pcNewSize) |
| 339 | { |
| 340 | CONTRACT(element_t *) |
| 341 | { |
| 342 | THROWS; |
| 343 | GC_NOTRIGGER; |
| 344 | INSTANCE_CHECK; |
| 345 | } |
| 346 | CONTRACT_END; |
| 347 | |
| 348 | count_t newSize = (count_t) (m_tableCount |
| 349 | * TRAITS::s_growth_factor_numerator / TRAITS::s_growth_factor_denominator |
| 350 | * TRAITS::s_density_factor_denominator / TRAITS::s_density_factor_numerator); |
| 351 | if (newSize < TRAITS::s_minimum_allocation) |
| 352 | newSize = TRAITS::s_minimum_allocation; |
| 353 | |
| 354 | // handle potential overflow |
| 355 | if (newSize < m_tableCount) |
| 356 | ThrowOutOfMemory(); |
| 357 | |
| 358 | RETURN AllocateNewTable(newSize, pcNewSize); |
| 359 | } |
| 360 | |
| 361 | template <typename TRAITS> |
| 362 | void SHash<TRAITS>::Reallocate(count_t requestedSize) |
| 363 | { |
| 364 | CONTRACT_VOID |
| 365 | { |
| 366 | THROWS; |
| 367 | GC_NOTRIGGER; |
| 368 | INSTANCE_CHECK; |
| 369 | } |
| 370 | CONTRACT_END; |
| 371 | |
| 372 | count_t newTableSize; |
| 373 | element_t * newTable = AllocateNewTable(requestedSize, &newTableSize); |
| 374 | element_t * oldTable = ReplaceTable(newTable, newTableSize); |
| 375 | DeleteOldTable(oldTable); |
| 376 | |
| 377 | RETURN; |
| 378 | } |
| 379 | |
| 380 | template <typename TRAITS> |
| 381 | template <typename Functor> |
| 382 | void SHash<TRAITS>::ForEach(Functor &functor) |
| 383 | { |
| 384 | WRAPPER_NO_CONTRACT; // LIMITED_METHOD_CONTRACT + Functor |
| 385 | |
| 386 | for (count_t i = 0; i < m_tableSize; i++) |
| 387 | { |
| 388 | element_t element = m_table[i]; |
| 389 | if (!TRAITS::IsNull(element) && !TRAITS::IsDeleted(element)) |
| 390 | { |
| 391 | functor(element); |
| 392 | } |
| 393 | } |
| 394 | } |
| 395 | |
| 396 | template <typename TRAITS> |
| 397 | typename SHash<TRAITS>::element_t * |
| 398 | SHash<TRAITS>::AllocateNewTable(count_t requestedSize, count_t * pcNewTableSize) |
| 399 | { |
| 400 | CONTRACT(element_t *) |
| 401 | { |
| 402 | THROWS; |
| 403 | GC_NOTRIGGER; |
| 404 | INSTANCE_CHECK; |
| 405 | PRECONDITION(requestedSize >= |
| 406 | (count_t) (GetCount() * TRAITS::s_density_factor_denominator / TRAITS::s_density_factor_numerator)); |
| 407 | } |
| 408 | CONTRACT_END; |
| 409 | |
| 410 | // Allocation size must be a prime number. This is necessary so that hashes uniformly |
| 411 | // distribute to all indices, and so that chaining will visit all indices in the hash table. |
| 412 | *pcNewTableSize = NextPrime(requestedSize); |
| 413 | |
| 414 | element_t * newTable = new element_t [*pcNewTableSize]; |
| 415 | |
| 416 | element_t * p = newTable; |
| 417 | element_t * pEnd = newTable + *pcNewTableSize; |
| 418 | while (p < pEnd) |
| 419 | { |
| 420 | *p = TRAITS::Null(); |
| 421 | p++; |
| 422 | } |
| 423 | |
| 424 | RETURN newTable; |
| 425 | } |
| 426 | |
| 427 | template <typename TRAITS> |
| 428 | typename SHash<TRAITS>::element_t * |
| 429 | SHash<TRAITS>::ReplaceTable(element_t * newTable, count_t newTableSize) |
| 430 | { |
| 431 | CONTRACT(element_t *) |
| 432 | { |
| 433 | NOTHROW_UNLESS_TRAITS_THROWS; |
| 434 | GC_NOTRIGGER; |
| 435 | INSTANCE_CHECK; |
| 436 | PRECONDITION(newTableSize >= |
| 437 | (count_t) (GetCount() * TRAITS::s_density_factor_denominator / TRAITS::s_density_factor_numerator)); |
| 438 | } |
| 439 | CONTRACT_END; |
| 440 | |
| 441 | element_t * oldTable = m_table; |
| 442 | |
| 443 | // Move all entries over to new table. |
| 444 | for (Iterator i = Begin(), end = End(); i != end; i++) |
| 445 | { |
| 446 | const element_t & cur = (*i); |
| 447 | if (!TRAITS::IsNull(cur) && !TRAITS::IsDeleted(cur)) |
| 448 | Add(newTable, newTableSize, cur); |
| 449 | } |
| 450 | |
| 451 | m_table = PTR_element_t(newTable); |
| 452 | m_tableSize = newTableSize; |
| 453 | m_tableMax = (count_t) (newTableSize * TRAITS::s_density_factor_numerator / TRAITS::s_density_factor_denominator); |
| 454 | m_tableOccupied = m_tableCount; |
| 455 | |
| 456 | RETURN oldTable; |
| 457 | } |
| 458 | |
| 459 | template <typename TRAITS> |
| 460 | void |
| 461 | SHash<TRAITS>::DeleteOldTable(element_t * oldTable) |
| 462 | { |
| 463 | CONTRACT_VOID |
| 464 | { |
| 465 | NOTHROW; |
| 466 | GC_NOTRIGGER; |
| 467 | } |
| 468 | CONTRACT_END; |
| 469 | |
| 470 | // @todo: |
| 471 | // We might want to try to delay this cleanup to allow asynchronous readers |
| 472 | if (oldTable != NULL) |
| 473 | delete [] oldTable; |
| 474 | |
| 475 | RETURN; |
| 476 | } |
| 477 | |
| 478 | template <typename TRAITS> |
| 479 | const typename SHash<TRAITS>::element_t * SHash<TRAITS>::Lookup(PTR_element_t table, count_t tableSize, key_t key) |
| 480 | { |
| 481 | CONTRACT(const element_t *) |
| 482 | { |
| 483 | NOTHROW_UNLESS_TRAITS_THROWS; |
| 484 | GC_NOTRIGGER; |
| 485 | POSTCONDITION(RETVAL == NULL || TRAITS::Equals(key, TRAITS::GetKey(*RETVAL))); |
| 486 | SUPPORTS_DAC_WRAPPER; // supports DAC only if the traits class does |
| 487 | } |
| 488 | CONTRACT_END; |
| 489 | |
| 490 | if (tableSize == 0) |
| 491 | RETURN NULL; |
| 492 | |
| 493 | count_t hash = TRAITS::Hash(key); |
| 494 | count_t index = hash % tableSize; |
| 495 | count_t increment = 0; // delay computation |
| 496 | |
| 497 | while (TRUE) |
| 498 | { |
| 499 | element_t& current = table[index]; |
| 500 | |
| 501 | if (TRAITS::IsNull(current)) |
| 502 | RETURN NULL; |
| 503 | |
| 504 | if (!TRAITS::IsDeleted(current) |
| 505 | && TRAITS::Equals(key, TRAITS::GetKey(current))) |
| 506 | { |
| 507 | RETURN ¤t; |
| 508 | } |
| 509 | |
| 510 | if (increment == 0) |
| 511 | increment = (hash % (tableSize-1)) + 1; |
| 512 | |
| 513 | index += increment; |
| 514 | if (index >= tableSize) |
| 515 | index -= tableSize; |
| 516 | } |
| 517 | } |
| 518 | |
| 519 | template <typename TRAITS> |
| 520 | BOOL SHash<TRAITS>::Add(element_t * table, count_t tableSize, const element_t & element) |
| 521 | { |
| 522 | CONTRACT(BOOL) |
| 523 | { |
| 524 | NOTHROW_UNLESS_TRAITS_THROWS; |
| 525 | GC_NOTRIGGER; |
| 526 | POSTCONDITION(TRAITS::Equals(TRAITS::GetKey(element), TRAITS::GetKey(*Lookup(table, tableSize, TRAITS::GetKey(element))))); |
| 527 | } |
| 528 | CONTRACT_END; |
| 529 | |
| 530 | key_t key = TRAITS::GetKey(element); |
| 531 | |
| 532 | count_t hash = TRAITS::Hash(key); |
| 533 | count_t index = hash % tableSize; |
| 534 | count_t increment = 0; // delay computation |
| 535 | |
| 536 | while (TRUE) |
| 537 | { |
| 538 | element_t & current = table[index]; |
| 539 | |
| 540 | if (TRAITS::IsNull(current)) |
| 541 | { |
| 542 | table[index] = element; |
| 543 | RETURN TRUE; |
| 544 | } |
| 545 | |
| 546 | if (TRAITS::IsDeleted(current)) |
| 547 | { |
| 548 | table[index] = element; |
| 549 | RETURN FALSE; |
| 550 | } |
| 551 | |
| 552 | if (increment == 0) |
| 553 | increment = (hash % (tableSize-1)) + 1; |
| 554 | |
| 555 | index += increment; |
| 556 | if (index >= tableSize) |
| 557 | index -= tableSize; |
| 558 | } |
| 559 | } |
| 560 | |
| 561 | template <typename TRAITS> |
| 562 | void SHash<TRAITS>::AddOrReplace(element_t *table, count_t tableSize, const element_t &element) |
| 563 | { |
| 564 | CONTRACT_VOID |
| 565 | { |
| 566 | NOTHROW_UNLESS_TRAITS_THROWS; |
| 567 | GC_NOTRIGGER; |
| 568 | static_assert(!TRAITS::s_supports_remove, "SHash::AddOrReplace is not implemented for SHash with support for remove operations." ); |
| 569 | POSTCONDITION(TRAITS::Equals(TRAITS::GetKey(element), TRAITS::GetKey(*Lookup(table, tableSize, TRAITS::GetKey(element))))); |
| 570 | } |
| 571 | CONTRACT_END; |
| 572 | |
| 573 | key_t key = TRAITS::GetKey(element); |
| 574 | |
| 575 | count_t hash = TRAITS::Hash(key); |
| 576 | count_t index = hash % tableSize; |
| 577 | count_t increment = 0; // delay computation |
| 578 | |
| 579 | while (TRUE) |
| 580 | { |
| 581 | element_t& current = table[index]; |
| 582 | _ASSERTE(!TRAITS::IsDeleted(current)); |
| 583 | |
| 584 | if (TRAITS::IsNull(current)) |
| 585 | { |
| 586 | table[index] = element; |
| 587 | m_tableCount++; |
| 588 | m_tableOccupied++; |
| 589 | RETURN; |
| 590 | } |
| 591 | else if (TRAITS::Equals(key, TRAITS::GetKey(current))) |
| 592 | { |
| 593 | table[index] = element; |
| 594 | RETURN; |
| 595 | } |
| 596 | |
| 597 | if (increment == 0) |
| 598 | increment = (hash % (tableSize-1)) + 1; |
| 599 | |
| 600 | index += increment; |
| 601 | if (index >= tableSize) |
| 602 | index -= tableSize; |
| 603 | } |
| 604 | } |
| 605 | |
| 606 | template <typename TRAITS> |
| 607 | void SHash<TRAITS>::Remove(element_t *table, count_t tableSize, key_t key) |
| 608 | { |
| 609 | CONTRACT_VOID |
| 610 | { |
| 611 | NOTHROW_UNLESS_TRAITS_THROWS; |
| 612 | GC_NOTRIGGER; |
| 613 | static_assert(TRAITS::s_supports_remove, "This SHash does not support remove operations." ); |
| 614 | PRECONDITION(Lookup(table, tableSize, key) != NULL); |
| 615 | } |
| 616 | CONTRACT_END; |
| 617 | |
| 618 | count_t hash = TRAITS::Hash(key); |
| 619 | count_t index = hash % tableSize; |
| 620 | count_t increment = 0; // delay computation |
| 621 | |
| 622 | while (TRUE) |
| 623 | { |
| 624 | element_t& current = table[index]; |
| 625 | |
| 626 | if (TRAITS::IsNull(current)) |
| 627 | RETURN; |
| 628 | |
| 629 | if (!TRAITS::IsDeleted(current) |
| 630 | && TRAITS::Equals(key, TRAITS::GetKey(current))) |
| 631 | { |
| 632 | table[index] = TRAITS::Deleted(); |
| 633 | m_tableCount--; |
| 634 | RETURN; |
| 635 | } |
| 636 | |
| 637 | if (increment == 0) |
| 638 | increment = (hash % (tableSize-1)) + 1; |
| 639 | |
| 640 | index += increment; |
| 641 | if (index >= tableSize) |
| 642 | index -= tableSize; |
| 643 | } |
| 644 | } |
| 645 | |
| 646 | template <typename TRAITS> |
| 647 | void SHash<TRAITS>::RemoveElement(element_t *table, count_t tableSize, element_t *element) |
| 648 | { |
| 649 | CONTRACT_VOID |
| 650 | { |
| 651 | NOTHROW; |
| 652 | GC_NOTRIGGER; |
| 653 | static_assert(TRAITS::s_supports_remove, "This SHash does not support remove operations." ); |
| 654 | PRECONDITION(table <= element && element < table + tableSize); |
| 655 | PRECONDITION(!TRAITS::IsNull(*element) && !TRAITS::IsDeleted(*element)); |
| 656 | } |
| 657 | CONTRACT_END; |
| 658 | |
| 659 | *element = TRAITS::Deleted(); |
| 660 | m_tableCount--; |
| 661 | RETURN; |
| 662 | } |
| 663 | |
| 664 | template <typename TRAITS> |
| 665 | BOOL SHash<TRAITS>::IsPrime(COUNT_T number) |
| 666 | { |
| 667 | CONTRACT(BOOL) |
| 668 | { |
| 669 | NOTHROW; |
| 670 | GC_NOTRIGGER; |
| 671 | } |
| 672 | CONTRACT_END; |
| 673 | |
| 674 | // This is a very low-tech check for primality, which doesn't scale very well. |
| 675 | // There are more efficient tests if this proves to be burdensome for larger |
| 676 | // tables. |
| 677 | |
| 678 | if ((number & 1) == 0) |
| 679 | RETURN FALSE; |
| 680 | |
| 681 | COUNT_T factor = 3; |
| 682 | while (factor * factor <= number) |
| 683 | { |
| 684 | if ((number % factor) == 0) |
| 685 | RETURN FALSE; |
| 686 | factor += 2; |
| 687 | } |
| 688 | |
| 689 | RETURN TRUE; |
| 690 | } |
| 691 | |
| 692 | // allow coexistence with simplerhash.inl |
| 693 | #ifndef _HASH_PRIMES_DEFINED |
| 694 | #define _HASH_PRIMES_DEFINED |
| 695 | |
| 696 | namespace |
| 697 | { |
| 698 | const COUNT_T g_shash_primes[] = { |
| 699 | 11,17,23,29,37,47,59,71,89,107,131,163,197,239,293,353,431,521,631,761,919, |
| 700 | 1103,1327,1597,1931,2333,2801,3371,4049,4861,5839,7013,8419,10103,12143,14591, |
| 701 | 17519,21023,25229,30293,36353,43627,52361,62851,75431,90523, 108631, 130363, |
| 702 | 156437, 187751, 225307, 270371, 324449, 389357, 467237, 560689, 672827, 807403, |
| 703 | 968897, 1162687, 1395263, 1674319, 2009191, 2411033, 2893249, 3471899, 4166287, |
| 704 | 4999559, 5999471, 7199369 }; |
| 705 | } |
| 706 | |
| 707 | #endif //_HASH_PRIMES_DEFINED |
| 708 | |
| 709 | template <typename TRAITS> |
| 710 | COUNT_T SHash<TRAITS>::NextPrime(COUNT_T number) |
| 711 | { |
| 712 | CONTRACT(COUNT_T) |
| 713 | { |
| 714 | NOTHROW; |
| 715 | GC_NOTRIGGER; |
| 716 | POSTCONDITION(IsPrime(RETVAL)); |
| 717 | } |
| 718 | CONTRACT_END; |
| 719 | |
| 720 | for (int i = 0; i < (int) (sizeof(g_shash_primes) / sizeof(g_shash_primes[0])); i++) { |
| 721 | if (g_shash_primes[i] >= number) |
| 722 | RETURN g_shash_primes[i]; |
| 723 | } |
| 724 | |
| 725 | if ((number&1) == 0) |
| 726 | number++; |
| 727 | |
| 728 | while (number != 1) { |
| 729 | if (IsPrime(number)) |
| 730 | RETURN number; |
| 731 | number +=2; |
| 732 | } |
| 733 | |
| 734 | // overflow |
| 735 | ThrowOutOfMemory(); |
| 736 | } |
| 737 | |
| 738 | template <typename TRAITS> |
| 739 | SHash<TRAITS>::AddPhases::AddPhases() |
| 740 | { |
| 741 | LIMITED_METHOD_CONTRACT; |
| 742 | |
| 743 | m_pHash = NULL; |
| 744 | m_newTable = NULL; |
| 745 | m_newTableSize = 0; |
| 746 | m_oldTable = NULL; |
| 747 | |
| 748 | INDEBUG(dbg_m_fAddCalled = FALSE;) |
| 749 | } |
| 750 | |
| 751 | template <typename TRAITS> |
| 752 | SHash<TRAITS>::AddPhases::~AddPhases() |
| 753 | { |
| 754 | LIMITED_METHOD_CONTRACT; |
| 755 | |
| 756 | if (m_newTable != NULL) |
| 757 | { // The new table was not applied to the hash yet |
| 758 | _ASSERTE((m_pHash != NULL) && (m_newTableSize != 0) && (m_oldTable == NULL)); |
| 759 | |
| 760 | delete [] m_newTable; |
| 761 | } |
| 762 | DeleteOldTable(); |
| 763 | } |
| 764 | |
| 765 | template <typename TRAITS> |
| 766 | void SHash<TRAITS>::AddPhases::PreallocateForAdd(SHash * pHash) |
| 767 | { |
| 768 | CONTRACT_VOID |
| 769 | { |
| 770 | THROWS; |
| 771 | GC_NOTRIGGER; |
| 772 | } |
| 773 | CONTRACT_END; |
| 774 | |
| 775 | _ASSERTE((m_pHash == NULL) && (m_newTable == NULL) && (m_newTableSize == 0) && (m_oldTable == NULL)); |
| 776 | |
| 777 | m_pHash = pHash; |
| 778 | // May return NULL if the allocation was not needed |
| 779 | m_newTable = m_pHash->CheckGrowth_OnlyAllocateNewTable(&m_newTableSize); |
| 780 | |
| 781 | #ifdef _DEBUG |
| 782 | dbg_m_table = pHash->m_table; |
| 783 | dbg_m_tableSize = pHash->m_tableSize; |
| 784 | dbg_m_tableCount = pHash->m_tableCount; |
| 785 | dbg_m_tableOccupied = pHash->m_tableOccupied; |
| 786 | dbg_m_tableMax = pHash->m_tableMax; |
| 787 | #endif //_DEBUG |
| 788 | |
| 789 | RETURN; |
| 790 | } |
| 791 | |
| 792 | template <typename TRAITS> |
| 793 | void SHash<TRAITS>::AddPhases::Add(const element_t & element) |
| 794 | { |
| 795 | CONTRACT_VOID |
| 796 | { |
| 797 | NOTHROW_UNLESS_TRAITS_THROWS; |
| 798 | GC_NOTRIGGER; |
| 799 | } |
| 800 | CONTRACT_END; |
| 801 | |
| 802 | _ASSERTE((m_pHash != NULL) && (m_oldTable == NULL)); |
| 803 | // Add can be called only once on this object |
| 804 | _ASSERTE(!dbg_m_fAddCalled); |
| 805 | |
| 806 | // Check that the hash table didn't change since call to code:PreallocateForAdd |
| 807 | _ASSERTE(dbg_m_table == m_pHash->m_table); |
| 808 | _ASSERTE(dbg_m_tableSize == m_pHash->m_tableSize); |
| 809 | _ASSERTE(dbg_m_tableCount >= m_pHash->m_tableCount); // Remove operation might have removed elements |
| 810 | _ASSERTE(dbg_m_tableOccupied == m_pHash->m_tableOccupied); |
| 811 | _ASSERTE(dbg_m_tableMax == m_pHash->m_tableMax); |
| 812 | |
| 813 | if (m_newTable != NULL) |
| 814 | { // We have pre-allocated table from code:PreallocateForAdd, use it. |
| 815 | _ASSERTE(m_newTableSize != 0); |
| 816 | |
| 817 | // May return NULL if there was not table allocated yet |
| 818 | m_oldTable = m_pHash->ReplaceTable(m_newTable, m_newTableSize); |
| 819 | |
| 820 | m_newTable = NULL; |
| 821 | m_newTableSize = 0; |
| 822 | } |
| 823 | // We know that we have enough space, direcly add the element |
| 824 | m_pHash->Add_GrowthChecked(element); |
| 825 | |
| 826 | INDEBUG(dbg_m_fAddCalled = TRUE;) |
| 827 | |
| 828 | RETURN; |
| 829 | } |
| 830 | |
| 831 | template <typename TRAITS> |
| 832 | void SHash<TRAITS>::AddPhases::AddNothing_PublishPreallocatedTable() |
| 833 | { |
| 834 | CONTRACT_VOID |
| 835 | { |
| 836 | NOTHROW_UNLESS_TRAITS_THROWS; |
| 837 | GC_NOTRIGGER; |
| 838 | } |
| 839 | CONTRACT_END; |
| 840 | |
| 841 | _ASSERTE((m_pHash != NULL) && (m_oldTable == NULL)); |
| 842 | // Add can be called only once on this object |
| 843 | _ASSERTE(!dbg_m_fAddCalled); |
| 844 | |
| 845 | // Check that the hash table didn't change since call to code:PreallocateForAdd |
| 846 | _ASSERTE(dbg_m_table == m_pHash->m_table); |
| 847 | _ASSERTE(dbg_m_tableSize == m_pHash->m_tableSize); |
| 848 | _ASSERTE(dbg_m_tableCount >= m_pHash->m_tableCount); // Remove operation might have removed elements |
| 849 | _ASSERTE(dbg_m_tableOccupied == m_pHash->m_tableOccupied); |
| 850 | _ASSERTE(dbg_m_tableMax == m_pHash->m_tableMax); |
| 851 | |
| 852 | if (m_newTable != NULL) |
| 853 | { // We have pre-allocated table from code:PreallocateForAdd, use it. |
| 854 | _ASSERTE(m_newTableSize != 0); |
| 855 | |
| 856 | // May return NULL if there was not table allocated yet |
| 857 | m_oldTable = m_pHash->ReplaceTable(m_newTable, m_newTableSize); |
| 858 | |
| 859 | m_newTable = NULL; |
| 860 | m_newTableSize = 0; |
| 861 | } |
| 862 | |
| 863 | INDEBUG(dbg_m_fAddCalled = TRUE;) |
| 864 | |
| 865 | RETURN; |
| 866 | } |
| 867 | |
| 868 | template <typename TRAITS> |
| 869 | void SHash<TRAITS>::AddPhases::DeleteOldTable() |
| 870 | { |
| 871 | LIMITED_METHOD_CONTRACT; |
| 872 | |
| 873 | if (m_oldTable != NULL) |
| 874 | { |
| 875 | _ASSERTE((m_pHash != NULL) && (m_newTable == NULL) && (m_newTableSize == 0)); |
| 876 | |
| 877 | delete [] m_oldTable; |
| 878 | m_oldTable = NULL; |
| 879 | } |
| 880 | } |
| 881 | |
| 882 | template <typename TRAITS> |
| 883 | template <typename LockHolderT, |
| 884 | typename AddLockHolderT, |
| 885 | typename LockT, |
| 886 | typename AddLockT> |
| 887 | bool SHash<TRAITS>::CheckAddInPhases( |
| 888 | element_t const & elem, |
| 889 | LockT & lock, |
| 890 | AddLockT & addLock, |
| 891 | IUnknown * addRefObject) |
| 892 | { |
| 893 | CONTRACTL |
| 894 | { |
| 895 | THROWS; |
| 896 | GC_NOTRIGGER; |
| 897 | INSTANCE_CHECK; |
| 898 | } |
| 899 | CONTRACTL_END; |
| 900 | |
| 901 | AddLockHolderT hAddLock(&addLock); |
| 902 | AddPhases addCall; |
| 903 | |
| 904 | // 1. Preallocate one element |
| 905 | addCall.PreallocateForAdd(this); |
| 906 | { |
| 907 | // 2. Take the reader lock. Host callouts now forbidden. |
| 908 | LockHolderT hLock(&lock); |
| 909 | |
| 910 | element_t const * pEntry = LookupPtr(TRAITS::GetKey(elem)); |
| 911 | if (pEntry != nullptr) |
| 912 | { |
| 913 | // 3a. Use the newly allocated table (if any) to avoid later redundant allocation. |
| 914 | addCall.AddNothing_PublishPreallocatedTable(); |
| 915 | return false; |
| 916 | } |
| 917 | else |
| 918 | { |
| 919 | // 3b. Add the element to the hash table. |
| 920 | addCall.Add(elem); |
| 921 | |
| 922 | if (addRefObject != nullptr) |
| 923 | { |
| 924 | clr::SafeAddRef(addRefObject); |
| 925 | } |
| 926 | |
| 927 | return true; |
| 928 | } |
| 929 | } |
| 930 | |
| 931 | // 4. addCall's destructor will take care of any required cleanup. |
| 932 | } |
| 933 | |
| 934 | |
| 935 | #endif // _SHASH_INL_ |
| 936 | |