/*++ Copyright (c) 1998-2000 Microsoft Corporation Module Name : IniFile.cpp Abstract: Test harness for LKRhash Author: George V. Reilly (GeorgeRe) 06-Jan-1998 Environment: Win32 - User Mode Project: Internet Information Server RunTime Library Revision History: --*/ #include "precomp.hxx" #include "WordHash.h" #include "IniFile.h" enum INI_TYPE { INI_WORD = 1, INI_DWORD, INI_DOUBLE, INI_STRING, INI_BOOL, }; typedef struct _ParseOptions { int m_nFieldOffset; const char* m_pszName; unsigned m_cchName; INI_TYPE m_type; DWORD_PTR m_default; } ParseOptions; #define INI_ENTRY(_member, _name, _type, _default) \ { \ FIELD_OFFSET(CIniFileSettings, _member), \ _name, \ sizeof(_name)-1, \ _type, \ (DWORD_PTR) _default, \ } \ static const ParseOptions g_po[] = { INI_ENTRY(m_tszDataFile, "DataFile", INI_STRING, _TEXT("??")), INI_ENTRY(m_nMaxKeys, "MaxKeys", INI_DWORD, MAXKEYS), INI_ENTRY(m_dblHighLoad, "MaxLoadFactor", INI_DOUBLE, LK_DFLT_MAXLOAD), INI_ENTRY(m_nInitSize, "InitSize", INI_DWORD, LK_DFLT_INITSIZE), INI_ENTRY(m_nSubTables, "NumSubTables", INI_DWORD,LK_DFLT_NUM_SUBTBLS), INI_ENTRY(m_nLookupFreq, "LookupFrequency",INI_DWORD, 5), INI_ENTRY(m_nMinThreads, "MinThreads", INI_DWORD, 1), INI_ENTRY(m_nMaxThreads, "MaxThreads", INI_DWORD, 4), INI_ENTRY(m_nRounds, "NumRounds", INI_DWORD, 1), INI_ENTRY(m_nSeed, "RandomSeed", INI_DWORD, 1234), INI_ENTRY(m_fCaseInsensitive,"CaseInsensitive",INI_BOOL, FALSE), INI_ENTRY(m_fMemCmp, "MemCmp", INI_BOOL, FALSE), INI_ENTRY(m_nLastChars, "NumLastChars", INI_DWORD, 0), INI_ENTRY(m_wTableSpin, "TableLockSpinCount",INI_WORD, LOCK_DEFAULT_SPINS), INI_ENTRY(m_wBucketSpin, "BucketLockSpinCount",INI_WORD,LOCK_DEFAULT_SPINS), INI_ENTRY(m_dblSpinAdjFctr,"SpinAdjustmentFactor",INI_DOUBLE, 1), INI_ENTRY(m_fTestIterators,"TestIterators", INI_BOOL, FALSE), INI_ENTRY(m_nInsertIfNotFound, "InsertIfNotFound",INI_DWORD, 0), INI_ENTRY(m_nFindKeyCopy, "FindKeyCopy", INI_DWORD, 0), INI_ENTRY(m_fNonPagedAllocs,"NonPagedAllocs", INI_BOOL, TRUE), INI_ENTRY(m_fRefTrace, "RefTrace", INI_BOOL, FALSE), {-1} // last entry }; void CIniFileSettings::Dump( LPCTSTR ptszProlog, LPCTSTR ptszEpilog) const { TCHAR tsz[50]; _tprintf(_TEXT("%s\n"), ptszProlog); _tprintf(_TEXT("IniFile=\"%s\"\n"), m_tszIniFile); _tprintf(_TEXT("DataFile=\"%s\". %s keys.\n"), m_tszDataFile, CommaNumber(m_nMaxKeys, tsz)); _tprintf(_TEXT("Max load = %.1f, initsize = %d, %d subtables.\n"), m_dblHighLoad, m_nInitSize, m_nSubTables); _tprintf(_TEXT("Lookup freq = %d, %d-%d threads, %d round%s.\n"), m_nLookupFreq, m_nMinThreads, m_nMaxThreads, m_nRounds, (m_nRounds==1 ? "" : "s")); _tprintf(_TEXT("Seed=%d, CaseInsensitive=%d, MemCmp=%d, LastChars=%d\n"), m_nSeed, m_fCaseInsensitive, m_fMemCmp, m_nLastChars); _tprintf(_TEXT("Spin Count: Table = %hd, Bucket = %hd, AdjFactor=%.1f\n"), m_wTableSpin, m_wBucketSpin, m_dblSpinAdjFctr); _tprintf(_TEXT("TestIterators=%d, InsertIfNotFound=%d, FindKeyCopy=%d\n"), m_fTestIterators, m_nInsertIfNotFound, m_nFindKeyCopy); _tprintf(_TEXT("NonPagedAllocs=%d, RefTrace=%d\n"), m_fNonPagedAllocs, m_fRefTrace); _tprintf(_TEXT("%s\n"), ptszEpilog); } #if defined(IRTLDEBUG) # define DUMP_INIFILE(pifs, Pro, Epi) pifs->Dump(Pro, Epi) #else # define DUMP_INIFILE(pifs, Pro, Epi) ((void) 0) #endif DWORD ReadFileIntoBuffer( LPCTSTR ptszFile, PBYTE pbBuffer, DWORD cbBuffer) { #ifndef LKRHASH_KERNEL_MODE HANDLE hFile = CreateFile( ptszFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) return 0; DWORD cbRead = 0, cbFileSizeLow, cbFileSizeHigh; cbFileSizeLow = GetFileSize(hFile, &cbFileSizeHigh); bool fBadFile = (cbFileSizeHigh != 0 || cbFileSizeLow > cbBuffer || !ReadFile(hFile, pbBuffer, cbFileSizeLow, &cbRead, NULL)); CloseHandle(hFile); return fBadFile ? 0 : cbRead; #else return 0; #endif // !LKRHASH_KERNEL_MODE } // Do a case-insensitive match of first `cchStr' chars of ptszBuffer // against ptszStr. Strings assumed to be alphanumeric bool StrMatch( LPCSTR pszBuffer, LPCSTR pszStr, unsigned cchStr) { LPCSTR psz1 = pszBuffer; LPCSTR psz2 = pszStr; unsigned i, j; bool fMatch = true; for (i = 0; i < cchStr; ++i) { IRTLASSERT(isalnum(*psz1) && isalnum(*psz2)); if (((*psz1++ ^ *psz2++) & 0xDF) != 0) { fMatch = false; break; } } IRTLTRACE0("\tStrMatch: \""); for (j = 0; j < i + !fMatch; ++j) IRTLTRACE1("%hc", pszBuffer[j]); IRTLTRACE0(fMatch ? "\" == \"" : "\" != \""); for (j = 0; j < i + !fMatch; ++j) IRTLTRACE1("%hc", pszStr[j]); IRTLTRACE0("\"\n"); return fMatch; } bool GetNum( char*& rpch, int& rn) { int fNegative = ('-' == *rpch); rn = 0; if (fNegative) ++rpch; else if (!('0' <= *rpch && *rpch <= '9')) return false; while ('0' <= *rpch && *rpch <= '9') rn = rn * 10 + (*rpch++ - '0'); if (fNegative) rn = -rn; return true; } bool GetDouble( char*& rpch, double& rdbl) { rdbl = 0; int n; bool fValidInt = GetNum(rpch, n); if (fValidInt) { rdbl = n; // BUGBUG: ignore fractional part, if any if ('.' == *rpch) { ++rpch; GetNum(rpch, n); } } return fValidInt; } bool GetString( char*& rpch, char* pszOutput, unsigned cchOutput) { // TODO: handle quoted strings and trailing blanks bool fGotChars = false; while ('\0' != *rpch && '\r' != *rpch && '\n' != *rpch ) { fGotChars = true; if (cchOutput-- > 0) *pszOutput++ = *rpch++; else ++rpch; } if (cchOutput > 0) *pszOutput = '\0'; return fGotChars; } // TODO: break the dependency upon g_po. int CIniFileSettings::ParseIniFile( LPCSTR pszIniFile) { strncpy(m_tszIniFile, pszIniFile, _MAX_PATH); int i, iMaxIndex = -1; int cMembers = 0; for (i = 0; ; ++i) { if (g_po[i].m_nFieldOffset < 0) { iMaxIndex = i; break; } PBYTE pbMember = g_po[i].m_nFieldOffset + ((BYTE*) this); // Initialize the members of `this' with their default values switch (g_po[i].m_type) { case INI_WORD: * (WORD*) pbMember = (WORD) g_po[i].m_default; break; case INI_DWORD: * (DWORD*) pbMember = (DWORD) g_po[i].m_default; break; case INI_DOUBLE: * (double*) pbMember = (float) g_po[i].m_default; break; case INI_STRING: strcpy((char*) pbMember, (const char*) g_po[i].m_default); break; case INI_BOOL: * (bool*) pbMember = (bool) (g_po[i].m_default != 0); break; default: IRTLASSERT(! "invalid INI_TYPE"); } } DUMP_INIFILE(this, "Before", ""); BYTE abBuffer[2049]; DWORD cbRead = ReadFileIntoBuffer(m_tszIniFile, abBuffer, sizeof(abBuffer)-1); if (cbRead == 0) { _tprintf(_TEXT("Can't open IniFile `%s'.\n"), m_tszIniFile) ; return 0; } abBuffer[cbRead] = '\0'; bool fInSection = false, fSkipLine = false; bool fDone = false; const char szSectionName[] = "HashTest"; unsigned cchSectionName = strlen(szSectionName); char* pch = (char*) abBuffer; char* pszEOB = (char*) (abBuffer + cbRead); // parse the in-memory buffer while ('\0' != *pch) { while (' ' == *pch || '\r' == *pch || '\n' == *pch || '\t' == *pch) ++pch; if ('\0' == *pch) break; IRTLTRACE(_TEXT("Line starts with '%hc%hc%hc%hc'\n"), pch[0], pch[1], pch[2], pch[3]); // Is this a section name? if ('[' == *pch) { fInSection = false; ++pch; while (' ' == *pch || '\t' == *pch) ++pch; if (pch + cchSectionName < pszEOB && StrMatch(pch, szSectionName, cchSectionName)) { pch += cchSectionName; while (' ' == *pch || '\t' == *pch) ++pch; if (']' == *pch) { ++pch; fInSection = true; } } else fSkipLine = true; continue; } // skip comments and entire lines if we're not in the right section if (fSkipLine || ';' == *pch || !fInSection) { // skip to end of line while ('\0' != *pch && '\r' != *pch && '\n' != *pch) { IRTLTRACE1("%hc", *pch); ++pch; } IRTLTRACE0("\n"); fSkipLine = false; continue; } fSkipLine = true; // try to match name=value for (i = 0; i < iMaxIndex; ++i) { IRTLASSERT(isalnum(*pch)); if (pch + g_po[i].m_cchName >= pszEOB || !StrMatch(pch, g_po[i].m_pszName, g_po[i].m_cchName)) continue; pch += g_po[i].m_cchName; while (' ' == *pch || '\t' == *pch) ++pch; if ('=' != *pch) { IRTLTRACE1("'=' not seen after <%hs>\n", g_po[i].m_pszName); break; } ++pch; while (' ' == *pch || '\t' == *pch) ++pch; PBYTE pbMember = g_po[i].m_nFieldOffset + ((BYTE*) this); int n; char sz[_MAX_PATH]; double dbl; IRTLTRACE1("<%hs>=", g_po[i].m_pszName); switch (g_po[i].m_type) { case INI_WORD: if (GetNum(pch, n)) { IRTLTRACE1("%hu\n", (WORD) n); * (WORD*) pbMember = (WORD) n; } else IRTLTRACE("bad word\n"); break; case INI_DWORD: if (GetNum(pch, n)) { IRTLTRACE1("%u\n", (DWORD) n); * (DWORD*) pbMember = (DWORD) n; } else IRTLTRACE("bad dword\n"); break; case INI_DOUBLE: if (GetDouble(pch, dbl)) { IRTLTRACE1("%.1f\n", dbl); * (double*) pbMember = dbl; } else IRTLTRACE("bad double\n"); break; case INI_STRING: if (GetString(pch, sz, sizeof(sz)/sizeof(sz[0]))) { IRTLTRACE1("%hs\n", sz); strcpy((char*) pbMember, sz); } else IRTLTRACE("bad string\n"); break; case INI_BOOL: if (GetNum(pch, n)) { IRTLTRACE1("%d\n", n); * (bool*) pbMember = (n != 0); } else IRTLTRACE("bad bool\n"); break; default: IRTLASSERT(! "invalid INI_TYPE"); } ++cMembers; fSkipLine = false; break; } } DUMP_INIFILE(this, "Parsed", "---"); return cMembers; } void CIniFileSettings::ReadIniFile( LPCTSTR ptszIniFile) { ParseIniFile(ptszIniFile); m_nMaxKeys = min(max(1, m_nMaxKeys), MAXKEYS); m_dblHighLoad = max(1, m_dblHighLoad); m_nMinThreads = max(1, m_nMinThreads); m_nMaxThreads = min(MAX_THREADS, max(1, m_nMaxThreads)); // If we're not using a real lock, then we're not threadsafe if (CWordHash::TableLock::LockType() == LOCK_FAKELOCK || CWordHash::BucketLock::LockType() == LOCK_FAKELOCK) m_nMinThreads = m_nMaxThreads = 1; m_nRounds = max(1, m_nRounds); CWordHash::sm_fCaseInsensitive = m_fCaseInsensitive; CWordHash::sm_fMemCmp = m_fMemCmp; CWordHash::sm_nLastChars = m_nLastChars; CWordHash::sm_fNonPagedAllocs = m_fNonPagedAllocs; CWordHash::sm_fRefTrace = m_fRefTrace; #ifdef LOCK_DEFAULT_SPIN_IMPLEMENTATION # ifdef LKRHASH_GLOBAL_LOCK CWordHash::GlobalLock::SetDefaultSpinAdjustmentFactor(m_dblSpinAdjFctr); # endif CWordHash::TableLock::SetDefaultSpinAdjustmentFactor(m_dblSpinAdjFctr); CWordHash::BucketLock::SetDefaultSpinAdjustmentFactor(m_dblSpinAdjFctr); #endif // LOCK_DEFAULT_SPIN_IMPLEMENTATION if (CWordHash::TableLock::Recursion() != LOCK_RECURSIVE || CWordHash::BucketLock::LockType() != LOCK_RECURSIVE) m_nInsertIfNotFound = 0; }