Changeset 97 for trunk/include/core/RpDict.h
- Timestamp:
- Oct 10, 2005 9:00:37 PM (17 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/include/core/RpDict.h
r38 r97 10 10 11 11 /**************************************************************************/ 12 13 14 12 /**************************************************************************/ 15 13 … … 183 181 184 182 185 183 186 184 }; 187 185 … … 191 189 public: 192 190 193 191 194 192 // functionality for the user to access/adjust data members 195 193 196 194 // checks table size 197 195 /*virtual*/ const int size() const; … … 250 248 251 249 } 252 250 253 251 // copy constructor 254 252 // RpDict (const RpDict& dict); … … 270 268 const int SMALL_RP_DICT_SIZE; 271 269 const int REBUILD_MULTIPLIER; 272 270 273 271 RpDictEntry<KeyType,ValType> 274 272 **buckets; /* Pointer to bucket array. Each … … 312 310 /*--------------------------------------------------------------------------*/ 313 311 314 #include "../../src/core/RpDict.cc" 312 313 // public RpDict member functions 314 315 316 /************************************************************************** 317 * 318 * int RpDict::size() 319 * retrieve the size of the structure 320 * 321 * Results: 322 * Returns size of the hash table 323 * 324 * Side Effects: 325 * none. 326 * 327 * 328 *************************************************************************/ 329 330 template <typename KeyType, typename ValType> 331 const int 332 RpDict<KeyType,ValType>::size() const 333 { 334 return numEntries; 335 } 336 337 338 /************************************************************************** 339 * 340 * RpDict::set() 341 * checks to make sure the table exists. 342 * places a key/value pair into the hash table 343 * 344 * Results: 345 * Returns a reference to the RpDict object allowing the user to chain 346 * together different commands such as 347 * rpdict_obj.set(key).find(a).erase(a); 348 * 349 * Side Effects: 350 * if successful, the hash table will have a new entry 351 * 352 * 353 *************************************************************************/ 354 template <typename KeyType, typename ValType> 355 RpDict<KeyType,ValType>& 356 RpDict<KeyType,ValType>::set( KeyType& key, 357 ValType& value, 358 int* newPtr) 359 { 360 RpDictEntry<KeyType,ValType> *hPtr; 361 unsigned int hash; 362 int index; 363 364 assert(&key); 365 assert(&value); 366 367 // take care of the case where we are creating a NULL key entry. 368 if (&key) { 369 hash = (unsigned int) hashFxn(&key); 370 } 371 else { 372 hash = 0; 373 } 374 375 index = randomIndex(hash); 376 377 /* 378 * Search all of the entries in the appropriate bucket. 379 */ 380 for (hPtr = buckets[index]; hPtr != NULL; hPtr = hPtr->nextPtr) { 381 if (hash != (unsigned int) hPtr->hash) { 382 continue; 383 } 384 // if element already exists in hash, it should not be re-entered 385 if (key == *(hPtr->getKey())){ 386 387 // adjust the new flag if it was provided 388 if (newPtr) { 389 *newPtr = 0; 390 } 391 392 // adjust the value if it was provided 393 // memory management is left as an exercize for the caller 394 if (&value) { 395 hPtr->setValue(value); 396 } 397 398 // return a reference to the dictionary object 399 return *this; 400 } 401 } 402 403 /* 404 * Entry not found. Add a new one to the bucket. 405 */ 406 407 if (newPtr) { 408 *newPtr = 1; 409 } 410 411 hPtr = new RpDictEntry<KeyType,ValType>(key,value); 412 // hPtr->setValue(value); 413 // 414 // this is just a pointer that was allocated on the heap 415 // it wont stick with the RpDictEntry after the fxn exits... 416 // need to fix still. 417 hPtr->tablePtr = this; 418 hPtr->hash = hash; 419 hPtr->nextPtr = buckets[index]; 420 buckets[index] = hPtr; 421 numEntries++; 422 423 /* 424 * If the table has exceeded a decent size, rebuild it with many 425 * more buckets. 426 */ 427 428 if (numEntries >= rebuildSize) { 429 RebuildTable(); 430 } 431 432 // return a reference to the original object 433 return *this; 434 } 435 436 437 /* 438 *---------------------------------------------------------------------- 439 * 440 * RpDict::find(const char *key) 441 * 442 * Given a hash table find the entry with a matching key. 443 * 444 * Results: 445 * The return value is a token for the matching entry in the 446 * hash table, or NULL if there was no matching entry. 447 * 448 * Side effects: 449 * None. 450 * 451 *---------------------------------------------------------------------- 452 */ 453 454 template <typename KeyType, typename ValType> 455 RpDictEntry<KeyType,ValType>& 456 RpDict<KeyType,ValType>::find(KeyType& key) 457 { 458 RpDictEntry<KeyType,ValType> *hPtr; 459 unsigned int hash; 460 int index; 461 462 assert(&key); 463 464 // take care of the case where we are creating a NULL key entry. 465 if (&key) { 466 hash = (unsigned int) hashFxn(&key); 467 } 468 else { 469 hash = 0; 470 } 471 472 index = randomIndex(hash); 473 474 /* 475 * Search all of the entries in the appropriate bucket. 476 */ 477 478 for (hPtr = buckets[index]; hPtr != NULL; hPtr = hPtr->nextPtr) { 479 if (hash != (unsigned int) hPtr->hash) { 480 continue; 481 } 482 if (key == *(hPtr->getKey())) { 483 // return a reference to the found object 484 return *hPtr; 485 } 486 } 487 488 // return a reference to the null object 489 // find is not supposed to return a const, but i dont want the user 490 // changing this entry's data members... what to do? 491 return *nullEntry; 492 493 } 494 495 496 497 /************************************************************************** 498 * 499 * virtual RpDict& RpDictIterator::getTable() 500 * send the search iterator to the beginning of the hash table 501 * 502 * Results: 503 * returns pointer to the first hash entry of the hash table. 504 * 505 * Side Effects: 506 * moves iterator to the beginning of the hash table. 507 * 508 * 509 *************************************************************************/ 510 /* 511 template <typename KeyType,typename ValType> 512 RpDict<KeyType,ValType>& 513 RpDictIterator<KeyType,ValType>::getTable() 514 { 515 return tablePtr; 516 } 517 */ 518 519 /************************************************************************** 520 * 521 * virtual RpDictEntry& RpDict::first() 522 * send the search iterator to the beginning of the hash table 523 * 524 * Results: 525 * returns pointer to the first hash entry of the hash table. 526 * 527 * Side Effects: 528 * moves iterator to the beginning of the hash table. 529 * 530 * 531 *************************************************************************/ 532 template <typename KeyType,typename ValType> 533 RpDictEntry<KeyType,ValType>* 534 RpDictIterator<KeyType,ValType>::first() 535 { 536 srchNextIndex = 0; 537 srchNextEntryPtr = NULL; 538 return next(); 539 } 540 541 /************************************************************************** 542 * 543 * Tcl_HashEntry * RpDict::next() 544 * send the search iterator to the next entry of the hash table 545 * 546 * Results: 547 * returns pointer to the next hash entry of the hash table. 548 * if iterator is at the end of the hash table, NULL is returned 549 * and the iterator is left at the end of the hash table. 550 * 551 * Side Effects: 552 * moves iterator to the next entry of the hash table if it exists. 553 * 554 * 555 *************************************************************************/ 556 557 template <typename KeyType,typename ValType> 558 RpDictEntry<KeyType,ValType>* 559 RpDictIterator<KeyType,ValType>::next() 560 { 561 RpDictEntry<KeyType,ValType>* hPtr; 562 563 while (srchNextEntryPtr == NULL) { 564 if (srchNextIndex >= tablePtr.numBuckets) { 565 return NULL; 566 } 567 srchNextEntryPtr = tablePtr.buckets[srchNextIndex]; 568 srchNextIndex++; 569 } 570 hPtr = srchNextEntryPtr; 571 srchNextEntryPtr = hPtr->nextPtr; 572 573 return hPtr; 574 } 575 576 /************************************************************************** 577 * 578 * RpDict & clear() 579 * iterate through the table and call erase on each element 580 * 581 * Results: 582 * empty hash table 583 * 584 * Side Effects: 585 * every element of the hash table will be erased. 586 * 587 * 588 *************************************************************************/ 589 template <typename KeyType, typename ValType> 590 RpDict<KeyType,ValType>& 591 RpDict<KeyType,ValType>::clear() 592 { 593 RpDictEntry<KeyType,ValType> *hPtr; 594 RpDictIterator<KeyType,ValType> iter((RpDict&)*this); 595 596 hPtr = iter.first(); 597 598 while (hPtr) { 599 hPtr->erase(); 600 hPtr = iter.next(); 601 } 602 603 return *this; 604 } 605 606 /************************************************************************** 607 * 608 * RpDict & getNullEntry() 609 * get the nullEntry hash entry for initialization of references 610 * 611 * 612 * Results: 613 * nullEntry RpDictEntry related to this dictionary is returned 614 * 615 * Side Effects: 616 * none 617 * 618 * 619 *************************************************************************/ 620 template <typename KeyType, typename ValType> 621 RpDictEntry<KeyType,ValType>& 622 RpDict<KeyType,ValType>::getNullEntry() 623 { 624 return *nullEntry; 625 } 626 627 628 /* 629 *---------------------------------------------------------------------- 630 * 631 * void RpDictEntry::erase() 632 * 633 * Remove a single entry from a hash table. 634 * 635 * Results: 636 * None. 637 * 638 * Side effects: 639 * The entry given by entryPtr is deleted from its table and 640 * should never again be used by the caller. It is up to the 641 * caller to free the clientData field of the entry, if that 642 * is relevant. 643 * 644 *---------------------------------------------------------------------- 645 */ 646 647 template <typename KeyType, typename ValType> 648 void 649 RpDictEntry<KeyType,ValType>::erase() 650 { 651 RpDictEntry<KeyType,ValType> *prevPtr; 652 RpDictEntry<KeyType,ValType> **bucketPtr; 653 int index = 0; 654 655 // check to see if the object is associated with a table 656 // if object is not associated with a table, there is no 657 // need to try to remove it from the table. 658 if (tablePtr) { 659 660 index = tablePtr->randomIndex(hash); 661 662 // calculate which bucket the entry should be in. 663 bucketPtr = &(tablePtr->buckets[index]); 664 665 // remove the entry from the buckets 666 // 667 // if entry is the first entry in the bucket 668 // move the bucket to point to the next entry 669 if ((*bucketPtr)->key == this->key) { 670 *bucketPtr = nextPtr; 671 } 672 else { 673 // if the entry is not the first entry in the bucket 674 // search for the entry 675 for (prevPtr = *bucketPtr; ; prevPtr = prevPtr->nextPtr) { 676 677 // printf("malformed bucket chain in RpDictEntry::erase()"); 678 assert(prevPtr != NULL); 679 680 if (prevPtr->nextPtr == this) { 681 prevPtr->nextPtr = nextPtr; 682 break; 683 } 684 } // end for loop 685 } // end else 686 687 // update our table's information 688 tablePtr->numEntries--; 689 690 } // end if tablePtr 691 692 // invalidate the object 693 nextPtr = NULL; 694 tablePtr = NULL; 695 hash = 0; 696 // clientData = NULL; 697 // key = NULL; 698 699 // delete the object. 700 delete this; 701 702 } 703 704 705 /* 706 *---------------------------------------------------------------------- 707 * 708 * const char* RpDictEntry::getKey() const 709 * 710 * retrieve the key of the current object 711 * 712 * Results: 713 * the key is returned to the caller 714 * 715 * Side effects: 716 * None. 717 * 718 *---------------------------------------------------------------------- 719 */ 720 721 template <typename KeyType, typename ValType> 722 const KeyType* 723 RpDictEntry<KeyType,ValType>::getKey() const 724 { 725 return (const KeyType*) &key; 726 } 727 728 /* 729 *---------------------------------------------------------------------- 730 * 731 * const char* RpDictEntry::getValue() const 732 * 733 * retrieve the value of the current object 734 * 735 * Results: 736 * the value is returned to the caller 737 * 738 * Side effects: 739 * None. 740 * 741 *---------------------------------------------------------------------- 742 */ 743 744 template <typename KeyType, typename ValType> 745 const ValType* 746 RpDictEntry<KeyType,ValType>::getValue() const 747 { 748 return (const ValType*) &clientData; 749 } 750 751 /* 752 *---------------------------------------------------------------------- 753 * 754 * const void* RpDictEntry::setValue() 755 * 756 * retrieve the value of the current object 757 * 758 * Results: 759 * the value is returned to the caller 760 * 761 * Side effects: 762 * None. 763 * 764 *---------------------------------------------------------------------- 765 */ 766 767 template <typename KeyType, typename ValType> 768 const ValType* 769 RpDictEntry<KeyType,ValType>::setValue(const ValType& value) 770 { 771 clientData = value; 772 return (const ValType*) &clientData; 773 } 774 775 template <typename KeyType, typename ValType> 776 RpDictEntry<KeyType,ValType>::operator int() const 777 { 778 779 if (!tablePtr && hash == 0) 780 return 0; 781 else 782 return 1; 783 784 // return (key); 785 } 786 787 788 /*************************************************************************/ 789 /*************************************************************************/ 790 791 // private member functions 792 793 /* 794 *---------------------------------------------------------------------- 795 * 796 * RebuildTable -- 797 * 798 * This procedure is invoked when the ratio of entries to hash 799 * buckets becomes too large. It creates a new table with a 800 * larger bucket array and moves all of the entries into the 801 * new table. 802 * 803 * Results: 804 * None. 805 * 806 * Side effects: 807 * Memory gets reallocated and entries get re-hashed to new 808 * buckets. 809 * 810 *---------------------------------------------------------------------- 811 */ 812 813 template <typename KeyType, typename ValType> 814 void 815 RpDict<KeyType,ValType>::RebuildTable() 816 { 817 int oldSize=0, count=0, index=0; 818 RpDictEntry<KeyType,ValType> **oldBuckets; 819 RpDictEntry<KeyType,ValType> **oldChainPtr, **newChainPtr; 820 RpDictEntry<KeyType,ValType> *hPtr; 821 822 void *key; 823 824 oldSize = numBuckets; 825 oldBuckets = buckets; 826 827 /* 828 * Allocate and initialize the new bucket array, and set up 829 * hashing constants for new array size. 830 */ 831 832 833 numBuckets *= 4; 834 835 buckets = (RpDictEntry<KeyType,ValType> **) malloc((unsigned) 836 (numBuckets * sizeof(RpDictEntry<KeyType,ValType> *))); 837 838 for (count = numBuckets, newChainPtr = buckets; 839 count > 0; 840 count--, newChainPtr++) { 841 842 *newChainPtr = NULL; 843 } 844 845 rebuildSize *= 4; 846 downShift -= 2; 847 mask = (mask << 2) + 3; 848 849 /* 850 * Rehash all of the existing entries into the new bucket array. 851 */ 852 853 for (oldChainPtr = oldBuckets; oldSize > 0; oldSize--, oldChainPtr++) { 854 for (hPtr = *oldChainPtr; hPtr != NULL; hPtr = *oldChainPtr) { 855 *oldChainPtr = hPtr->nextPtr; 856 857 key = (void *) hPtr->getKey(); 858 859 index = randomIndex(hPtr->hash); 860 861 hPtr->nextPtr = buckets[index]; 862 buckets[index] = hPtr; 863 } 864 } 865 866 /* 867 * Free up the old bucket array, if it was dynamically allocated. 868 */ 869 870 if (oldBuckets != staticBuckets) { 871 free((char *) oldBuckets); 872 } 873 } 874 875 /* 876 *---------------------------------------------------------------------- 877 * 878 * hashFxn -- 879 * 880 * Compute a one-word summary of a text string, which can be 881 * used to generate a hash index. 882 * 883 * Results: 884 * The return value is a one-word summary of the information in 885 * string. 886 * 887 * Side effects: 888 * None. 889 * 890 *---------------------------------------------------------------------- 891 */ 892 893 template <typename KeyType, typename ValType> 894 unsigned int 895 RpDict<KeyType,ValType>::hashFxn(const void *keyPtr) const 896 { 897 const char *stopAddr = (const char *) keyPtr + sizeof(&keyPtr) - 1 ; 898 const char *str = (const char *) keyPtr; 899 unsigned int result; 900 int c; 901 902 result = 0; 903 904 while (str != stopAddr) { 905 c = *str; 906 result += (result<<3) + c; 907 str++; 908 } 909 910 return result; 911 } 912 913 /* 914 * Quote from Tcl's hash table code 915 * I tried a zillion different hash functions and asked many other 916 * people for advice. Many people had their own favorite functions, 917 * all different, but no-one had much idea why they were good ones. 918 * I chose the one below (multiply by 9 and add new character) 919 * because of the following reasons: 920 * 921 * 1. Multiplying by 10 is perfect for keys that are decimal strings, 922 * and multiplying by 9 is just about as good. 923 * 2. Times-9 is (shift-left-3) plus (old). This means that each 924 * character's bits hang around in the low-order bits of the 925 * hash value for ever, plus they spread fairly rapidly up to 926 * the high-order bits to fill out the hash value. This seems 927 * works well both for decimal and non-decimal strings. 928 */ 929 930 931 template <typename KeyType, typename ValType> 932 unsigned int 933 RpDict<KeyType,ValType>::hashFxn(std::string* keyPtr) const 934 { 935 const char *str = (const char *) (keyPtr->c_str()); 936 unsigned int result; 937 int c; 938 939 result = 0; 940 941 while (1) { 942 c = *str; 943 if (c == 0) { 944 break; 945 } 946 result += (result<<3) + c; 947 str++; 948 } 949 950 return result; 951 } 952 953 template <typename KeyType, typename ValType> 954 unsigned int 955 RpDict<KeyType,ValType>::hashFxn(char* keyPtr) const 956 { 957 const char *str = (const char *) (keyPtr); 958 unsigned int result; 959 int c; 960 961 result = 0; 962 963 while (1) { 964 c = *str; 965 if (c == 0) { 966 break; 967 } 968 result += (result<<3) + c; 969 str++; 970 } 971 972 return result; 973 } 974 975 /* 976 * --------------------------------------------------------------------- 977 * 978 * int RpDict::randomIndex(hash) 979 * 980 * The following macro takes a preliminary integer hash value and 981 * produces an index into a hash tables bucket list. The idea is 982 * to make it so that preliminary values that are arbitrarily similar 983 * will end up in different buckets. The hash function was taken 984 * from a random-number generator. 985 * 986 * --------------------------------------------------------------------- 987 */ 988 989 template <typename KeyType, typename ValType> 990 int 991 RpDict<KeyType,ValType>::randomIndex(unsigned int hash) 992 { 993 return (((((long) (hash))*1103515245) >> downShift) & mask); 994 } 995 996 315 997 316 998 #endif
Note: See TracChangeset
for help on using the changeset viewer.