// This program exercises the Hashtable class. int main(int argc, char ** argv) { CompleteSetupSystem css; Message temp; if (ParseArgs(argc, argv, temp) == B_NO_ERROR) HandleStandardDaemonArgs(temp); if (temp.HasName("inter")) return DoInteractiveTest(); // Make sure that setting equal to an empty Hashtable clears the buffer (FogBugz #10274) { Hashtable<String,String> table; for (int32 i=0; i<1000; i++) table.Put(String("xxx%1").Arg(i), "foo"); printf("After population of " UINT32_FORMAT_SPEC " items, table size is " UINT32_FORMAT_SPEC "\n", table.GetNumItems(), table.GetNumAllocatedItemSlots()); if (table.ShrinkToFit() == B_NO_ERROR) printf("After shrink-to-fit, table allocation is " UINT32_FORMAT_SPEC " for " UINT32_FORMAT_SPEC " items\n", table.GetNumAllocatedItemSlots(), table.GetNumItems()); else printf("Shrink-to-fit failed!?\n"); printf("Before copy-from-empty, table allocation is " UINT32_FORMAT_SPEC "\n", table.GetNumAllocatedItemSlots()); table = GetDefaultObjectForType< Hashtable<String,String> > (); printf(" After copy-from-empty, table allocation is " UINT32_FORMAT_SPEC "\n", table.GetNumAllocatedItemSlots()); } // Test C++11 move semantics to make sure they aren't stealing { String key = "key"; String value = "value"; Hashtable<String,String> table; table.Put(key, value); if (key != "key") {printf("ERROR, Hashtable stole my key!\n"); exit(10);} if (value != "value") {printf("ERROR, Hashtable stole my value!\n"); exit(10);} } // Test muscleSwap() TestMuscleSwap<Hashtable<String,String> >("Hashtable"); TestMuscleSwap<OrderedKeysHashtable<String,String> >("OrderedKeysHashtable"); TestMuscleSwap<OrderedValuesHashtable<String,String> >("OrderedValuesHashtable"); // Test iterator behaviour when deleting keys TestIteratorSanityOnRemoval(false); TestIteratorSanityOnRemoval(true); { LogTime(MUSCLE_LOG_INFO, "Testing a keys-only Hashtable value...\n"); Hashtable<int, Void> keysOnly; printf("sizeof(keysOnly)=%u\n", (unsigned int) sizeof(keysOnly)); keysOnly.PutWithDefault(1); keysOnly.PutWithDefault(2); keysOnly.PutWithDefault(5); keysOnly.PutWithDefault(10); for (HashtableIterator<int, Void> iter(keysOnly); iter.HasData(); iter++) printf("key=%i\n", iter.GetKey()); } { LogTime(MUSCLE_LOG_INFO, "Testing Tuple as a Hashtable key...\n"); // A quick test of the Tuple class as a Hashtable key typedef Tuple<2,int> MyType; Hashtable<MyType, int> tupleTable; MyType a; a[0] = 5; a[1] = 6; MyType b; b[0] = 7; b[1] = 8; tupleTable.Put(a, 1); tupleTable.Put(b, 2); for (HashtableIterator<MyType, int> iter(tupleTable); iter.HasData(); iter++) { const MyType & key = iter.GetKey(); printf("key=%i,%i val=%i\n", key[0], key[1], iter.GetValue()); } int * ra = tupleTable.Get(a); int * rb = tupleTable.Get(b); printf("tuple: ra=[%i] rb=[%i]\n", ra?*ra:666, rb?*rb:666); } { LogTime(MUSCLE_LOG_INFO, "Testing Rect as a Hashtable key...\n"); // A quick test of the Tuple class as a Hashtable key Hashtable<Rect, int> tupleTable; Rect a(1,2,3,4); Rect b(5,6,7,8); tupleTable.Put(a, 1); tupleTable.Put(b, 2); for (HashtableIterator<Rect, int> iter(tupleTable); iter.HasData(); iter++) { const Rect & key = iter.GetKey(); printf("key=%f,%f,%f,%f val=%i\n", key.left(), key.top(), key.right(), key.bottom(), iter.GetValue()); } int * ra = tupleTable.Get(a); int * rb = tupleTable.Get(b); printf("Rect: ra=[%p] rb=[%p]\n", ra, rb); } { LogTime(MUSCLE_LOG_INFO, "Testing Point as a Hashtable key...\n"); // A quick test of the Tuple class as a Hashtable key Hashtable<Point, int> tupleTable; Point a(9,10); Point b(-11,-12); tupleTable.Put(a, 1); tupleTable.Put(b, 2); for (HashtableIterator<Point, int> iter(tupleTable); iter.HasData(); iter++) { const Point & key = iter.GetKey(); printf("key=%f,%f val=%i\n", key.x(), key.y(), iter.GetValue()); } int * ra = tupleTable.Get(a); int * rb = tupleTable.Get(b); printf("Point: ra=[%p] rb=[%p]\n", ra, rb); } { LogTime(MUSCLE_LOG_INFO, "Preparing large table for sort...\n"); const uint32 numItems = 100000; Hashtable<int, Void> table; (void) table.EnsureSize(100000); for (uint32 i=0; i<numItems; i++) table.PutWithDefault((int)rand()); uint32 actualNumItems = table.GetNumItems(); // may be smaller than numItems, due to duplicate values! (void) table.CountAverageLookupComparisons(true); LogTime(MUSCLE_LOG_INFO, "Sorting...\n"); uint64 start = GetRunTime64(); table.SortByKey(); uint64 end = GetRunTime64(); LogTime(MUSCLE_LOG_INFO, "Time to sort " UINT32_FORMAT_SPEC" items: " UINT64_FORMAT_SPEC "ms\n", numItems, (end-start)/1000); // Check the resulting sorted table for correctness in both directions CheckTable(table, actualNumItems, false); CheckTable(table, actualNumItems, true); } // Test the sort algorithm for efficiency and correctness { LogTime(MUSCLE_LOG_INFO, "Preparing large table for sort...\n"); const uint32 numItems = 100000; Hashtable<int, Void> table; for (uint32 i=0; i<numItems; i++) table.PutWithDefault((int)rand()); uint32 actualNumItems = table.GetNumItems(); // may be smaller than numItems, due to duplicate values! LogTime(MUSCLE_LOG_INFO, "Sorting...\n"); uint64 start = GetRunTime64(); table.SortByKey(); uint64 end = GetRunTime64(); LogTime(MUSCLE_LOG_INFO, "Time to sort " UINT32_FORMAT_SPEC" items: " UINT64_FORMAT_SPEC "ms\n", numItems, (end-start)/1000); // Check the resulting sorted table for correctness in both directions CheckTable(table, actualNumItems, false); CheckTable(table, actualNumItems, true); } Hashtable<String, String> table; { table.Put("Hello", "World"); table.Put("Peanut Butter", "Jelly"); table.Put("Ham", "Eggs"); table.Put("Pork", "Beans"); table.Put("Slash", "Dot"); table.Put("Data", "Mining"); table.Put("TestDouble", "Play"); table.Put("Abbot", "Costello"); table.Put("Laurel", "Hardy"); table.Put("Thick", "Thin"); table.Put("Butter", "Parkay"); table.Put("Total", "Carnage"); table.Put("Summer", "Time"); table.Put("Terrible", "Twos"); table.CountAverageLookupComparisons(true); printf("table[\"Summer\"] = [%s]\n", table["Summer"]()); printf("table[\"Butter\"] = [%s]\n", table["Butter"]()); printf("table[\"Total\"] = [%s]\n", table["Total"]()); printf("table[\"Winter\"] = [%s] (should be blank!)\n", table["Winter"]()); if (table.GetNumItems() != 14) { LogTime(MUSCLE_LOG_CRITICALERROR, "String table has %i entries in it, expected 14!\n", table.GetNumItems()); ExitWithoutCleanup(10); } { LogTime(MUSCLE_LOG_INFO, "Test partial backwards iteration\n"); for (HashtableIterator<String, String> iter(table, "Slash", HTIT_FLAG_BACKWARDS); iter.HasData(); iter++) LogTime(MUSCLE_LOG_INFO,"[%s] <-> [%s]\n", iter.GetKey()(), iter.GetValue()()); } String lookup; if (table.Get(String("Hello"), lookup) == B_NO_ERROR) LogTime(MUSCLE_LOG_DEBUG, "Hello -> %s\n", lookup()); else bomb("Lookup 1 failed.\n"); if (table.Get(String("Peanut Butter"), lookup) == B_NO_ERROR) LogTime(MUSCLE_LOG_DEBUG, "Peanut Butter -> %s\n", lookup()); else bomb("Lookup 2 failed.\n"); LogTime(MUSCLE_LOG_INFO, "Testing delete-as-you-go traveral\n"); for (HashtableIterator<String, String> st(table); st.HasData(); st++) { LogTime(MUSCLE_LOG_INFO, "t3 = %s -> %s (tableSize=" UINT32_FORMAT_SPEC")\n", st.GetKey()(), st.GetValue()(), table.GetNumItems()); if (table.Remove(st.GetKey()) != B_NO_ERROR) bomb("Could not remove string!\n"); #if 0 for (HashtableIterator<String,String> st2(table); st2.HasData(); st2++) printf(" tx = %s -> %s\n", nextKeyString(), nextValueString()); #endif } Hashtable<uint32, const char *> sillyTable; sillyTable.Put(15, "Fifteen"); sillyTable.Put(100, "One Hundred"); sillyTable.Put(150, "One Hundred and Fifty"); sillyTable.Put(200, "Two Hundred"); sillyTable.Put((uint32)-1, "2^32 - 1!"); if (sillyTable.ContainsKey((uint32)-1) == false) bomb("large value failed!"); const char * temp = NULL; sillyTable.Get(100, temp); sillyTable.Get(101, temp); // will fail printf("100 -> %s\n",temp); printf("Entries in sillyTable:\n"); for (HashtableIterator<uint32, const char *> it(sillyTable); it.HasData(); it++) { const char * nextValue = NULL; status_t ret = sillyTable.Get(it.GetKey(), nextValue); printf("%i %s: " UINT32_FORMAT_SPEC" -> %s\n", it.HasData(), (ret == B_NO_ERROR) ? "OK" : "ERROR", it.GetKey(), nextValue); } } table.Clear(); { const uint32 NUM_ITEMS = 1000000; const uint32 NUM_RUNS = 3; Hashtable<int, int> testCopy; Hashtable<String, double> tallies; for (uint32 t=0; t<NUM_RUNS; t++) { Hashtable<int, int> table; (void) table.EnsureSize(NUM_ITEMS); printf("SORT SPEED TEST ROUND " UINT32_FORMAT_SPEC"/" UINT32_FORMAT_SPEC":\n", t+1, NUM_RUNS); uint64 startTime = GetRunTime64(); srand(0); for (uint32 i=0; i<NUM_ITEMS; i++) table.Put(rand(), rand()); // we want this to be repeatable, hence srand(0) AddTally(tallies, "place", startTime, NUM_ITEMS); startTime = GetRunTime64(); table.SortByValue(); AddTally(tallies, "sort", startTime, NUM_ITEMS); startTime = GetRunTime64(); testCopy = table; // just to make sure copying a table works AddTally(tallies, "copy", startTime, NUM_ITEMS); startTime = GetRunTime64(); if (testCopy != table) bomb("Copy was not the same!"); AddTally(tallies, "compare", startTime, NUM_ITEMS); startTime = GetRunTime64(); if (testCopy.IsEqualTo(table, true) == false) bomb("Copy was not the same, considering ordering!"); AddTally(tallies, "o-compare", startTime, NUM_ITEMS); startTime = GetRunTime64(); table.Clear(); AddTally(tallies, "clear", startTime, NUM_ITEMS); } printf("GRAND AVERAGES OVER ALL " UINT32_FORMAT_SPEC" RUNS ARE:\n", NUM_RUNS); for (HashtableIterator<String, double> iter(tallies); iter.HasData(); iter++) printf(" %f items/second for %s\n", iter.GetValue()/NUM_RUNS, iter.GetKey()()); } // Now some timing test with String keys and values, for testing of the C++11 move semantics PrintAndClearStringCopyCounts("Before String Sort test"); { const uint32 NUM_ITEMS = 1000000; const uint32 NUM_RUNS = 3; Hashtable<String, String> testCopy; Hashtable<String, double> tallies; for (uint32 t=0; t<NUM_RUNS; t++) { Hashtable<String, String> table; (void) table.EnsureSize(NUM_ITEMS); printf("STRING SORT SPEED TEST ROUND " UINT32_FORMAT_SPEC"/" UINT32_FORMAT_SPEC":\n", t+1, NUM_RUNS); uint64 startTime = GetRunTime64(); srand(0); for (uint32 i=0; i<NUM_ITEMS; i++) table.Put(String("%1").Arg(rand()), String("%1").Arg(rand())); // we want this to be repeatable, hence srand(0) AddTally(tallies, "place", startTime, NUM_ITEMS); startTime = GetRunTime64(); table.SortByValue(); AddTally(tallies, "sort", startTime, NUM_ITEMS); startTime = GetRunTime64(); testCopy = table; // just to make sure copying a table works AddTally(tallies, "copy", startTime, NUM_ITEMS); startTime = GetRunTime64(); if (testCopy != table) bomb("Copy was not the same!"); AddTally(tallies, "compare", startTime, NUM_ITEMS); startTime = GetRunTime64(); if (testCopy.IsEqualTo(table, true) == false) bomb("Copy was not the same, considering ordering!"); AddTally(tallies, "o-compare", startTime, NUM_ITEMS); startTime = GetRunTime64(); table.Clear(); AddTally(tallies, "clear", startTime, NUM_ITEMS); } printf("STRING GRAND AVERAGES OVER ALL " UINT32_FORMAT_SPEC" RUNS ARE:\n", NUM_RUNS); for (HashtableIterator<String, double> iter(tallies); iter.HasData(); iter++) printf(" STRING %f items/second for %s\n", iter.GetValue()/NUM_RUNS, iter.GetKey()()); } PrintAndClearStringCopyCounts("After String Sort test"); printf("Begin torture test!\n"); _state = 4; { bool fastClear = false; Hashtable<String, uint32> t; for (uint32 numEntries=1; numEntries < 1000; numEntries++) { uint32 half = numEntries/2; bool ok = true; printf(UINT32_FORMAT_SPEC" ", numEntries); fflush(stdout); _state = 5; { for(uint32 i=0; i<numEntries; i++) { char temp[300]; muscleSprintf(temp, UINT32_FORMAT_SPEC, i); if (t.Put(temp, i) != B_NO_ERROR) { printf("Whoops, (hopefully simulated) memory failure! (Put(" UINT32_FORMAT_SPEC"/" UINT32_FORMAT_SPEC") failed) ... recovering\n", i, numEntries); ok = false; numEntries--; // let's do this one over half = i; // so the remove code won't freak out about not everything being there break; } } } if (ok) { //printf("Checking that all entries are still there...\n"); _state = 6; { if (t.GetNumItems() != numEntries) bomb("ERROR, WRONG SIZE %i vs %i!\n", t.GetNumItems(), numEntries); for (int32 i=((int32)numEntries)-1; i>=0; i--) { char temp[300]; muscleSprintf(temp, UINT32_FORMAT_SPEC, i); uint32 tv = 0; if (t.Get(temp, tv) != B_NO_ERROR) bomb("ERROR, MISSING KEY [%s]\n", temp); if (tv != ((uint32)i)) bomb("ERROR, WRONG KEY %s != " UINT32_FORMAT_SPEC"!\n", temp, tv); } } //printf("Iterating through table...\n"); _state = 7; { uint32 count = 0; for (HashtableIterator<String, uint32> iter(t); iter.HasData(); iter++) { char buf[300]; muscleSprintf(buf, UINT32_FORMAT_SPEC, count); if (iter.GetKey() != buf) bomb("ERROR: iteration was wrong, item " UINT32_FORMAT_SPEC" was [%s] not [%s]!\n", count, iter.GetKey()(), buf); if (iter.GetValue() != count) bomb("ERROR: iteration value was wrong, item " UINT32_FORMAT_SPEC" was " UINT32_FORMAT_SPEC" not " UINT32_FORMAT_SPEC"!i!\n", count, iter.GetValue(), count); count++; } } //printf("Removing the second half of the entries...\n"); _state = 8; { for (uint32 i=half; i<numEntries; i++) { char temp[64]; muscleSprintf(temp, UINT32_FORMAT_SPEC, i); uint32 tv = 0; // just to shut the compiler up if (t.Remove(temp, tv) != B_NO_ERROR) bomb("ERROR, MISSING REMOVE KEY [%s] A\n", temp); if (tv != i) bomb("ERROR, REMOVE WAS WRONG VALUE " UINT32_FORMAT_SPEC"\n", tv); } } //printf("Iterating over only first half...\n"); _state = 9; { uint32 sum = 0; for (uint32 i=0; i<half; i++) sum += i; uint32 count = 0, checkSum = 0; for (HashtableIterator<String, uint32> iter(t); iter.HasData(); iter++) { count++; checkSum += iter.GetValue(); } if (count != half) bomb("ERROR: Count mismatch " UINT32_FORMAT_SPEC" vs " UINT32_FORMAT_SPEC"!\n", count, numEntries); if (checkSum != sum) bomb("ERROR: Sum mismatch " UINT32_FORMAT_SPEC" vs " UINT32_FORMAT_SPEC"!\n", sum, checkSum); } } //printf("Clearing Table (%s)\n", fastClear ? "Quickly" : "Slowly"); _state = 10; if (fastClear) t.Clear(); else { for (uint32 i=0; i<half; i++) { char temp[300]; muscleSprintf(temp, UINT32_FORMAT_SPEC, i); uint32 tv = 0; // just to shut the compiler up if (t.Remove(temp, tv) != B_NO_ERROR) bomb("ERROR, MISSING REMOVE KEY [%s] (" UINT32_FORMAT_SPEC"/" UINT32_FORMAT_SPEC") B\n", temp, i, half); if (tv != i) bomb("ERROR, REMOVE WAS WRONG VALUE " UINT32_FORMAT_SPEC"\n", tv); } } HashtableIterator<String, uint32> paranoia(t); if (paranoia.HasData()) bomb("ERROR, ITERATOR CONTAINED ITEMS AFTER CLEAR!\n"); if (t.HasItems()) bomb("ERROR, SIZE WAS NON-ZERO (" UINT32_FORMAT_SPEC") AFTER CLEAR!\n", t.GetNumItems()); fastClear = !fastClear; } printf("Finished torture test successfully!\n"); } #ifdef MUSCLE_AVOID_THREAD_SAFE_HASHTABLE_ITERATORS printf("Thread-safe hashtable iterators were disabled at compile time, so I won't test them!\n"); return 0; #else return DoThreadTest(); #endif }
static unsigned __stdcall StdinThreadEntryFunc(void *) { if (_stdinHandle != INVALID_HANDLE_VALUE) { DWORD oldConsoleMode = 0; GetConsoleMode(_stdinHandle, &oldConsoleMode); SetConsoleMode(_stdinHandle, oldConsoleMode | ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT); // The only time this thread is allowed to exit is if stdin is closed. Otherwise it runs until the // process terminates, because Windows has no way to unblock the call to ReadFile()! Hashtable<uint32, ConstSocketRef> temp; // declared out here only to avoid reallocations on every loop iteration char buf[4096]; DWORD numBytesRead; while(ReadFile(_stdinHandle, buf, sizeof(buf), &numBytesRead, NULL)) { // Grab a temporary copy of the listeners-set. That we we don't risk blocking in SendData() while holding the mutex. if (_slaveSocketsMutex.Lock() == B_NO_ERROR) { temp = _slaveSockets; _slaveSocketsMutex.Unlock(); } // Now send the data we read from stdin to all the registered sockets bool trim = false; for (HashtableIterator<uint32, ConstSocketRef> iter(temp); iter.HasData(); iter++) if (SendData(iter.GetValue(), buf, numBytesRead, true) != numBytesRead) {trim = true; iter.GetValue().Reset();} // Lastly, remove from the registered-sockets-set any sockets that SendData() errored out on. // This will cause the socket connection to be closed and the master thread(s) to be notified. if ((trim)&&(_slaveSocketsMutex.Lock() == B_NO_ERROR)) { for (HashtableIterator<uint32, ConstSocketRef> iter(_slaveSockets); iter.HasData(); iter++) { const ConstSocketRef * v = temp.Get(iter.GetKey()); if ((v)&&(v->IsValid() == false)) (void) _slaveSockets.Remove(iter.GetKey()); } _slaveSocketsMutex.Unlock(); } temp.Clear(); // it's important not to have extra Refs hanging around in case the process exits! } // Restore the old console mode before we leave SetConsoleMode(_stdinHandle, oldConsoleMode); } // Oops, stdin failed... clear the slave sockets table so that the client objects will know to close up shop if (_slaveSocketsMutex.Lock() == B_NO_ERROR) { _stdinThreadStatus = STDIN_THREAD_STATUS_EXITED; _slaveSockets.Clear(); // We'll close our own handle, thankyouverymuch if (_slaveThread != INVALID_HANDLE_VALUE) { CloseHandle(_slaveThread); _slaveThread = INVALID_HANDLE_VALUE; } if (_stdinHandle != INVALID_HANDLE_VALUE) { CloseHandle(_stdinHandle); _stdinHandle = INVALID_HANDLE_VALUE; } _slaveSocketsMutex.Unlock(); } return 0; }