Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

569 lines
14 KiB

  1. //----------------------------------------------------------------------------
  2. //
  3. //----------------------------------------------------------------------------
  4. #include "private.h"
  5. #include "strlist.h"
  6. #define TF_THISMODULE TF_STRINGLIST
  7. //----------------------------------------------------------------------------
  8. // CWCStringList
  9. CWCStringList::CWCStringList()
  10. {
  11. // We may be able to remove this Clear() call if we guarantee that
  12. // 1) the new operator zero inits
  13. // 2) we don't use this class on the stack
  14. Clear();
  15. }
  16. CWCStringList::~CWCStringList()
  17. {
  18. #ifdef DEBUG
  19. if (m_iNumStrings > 100)
  20. SpewHashStats(TRUE);
  21. #endif
  22. CleanUp();
  23. }
  24. // Clean up any allocated memory
  25. void CWCStringList::CleanUp()
  26. {
  27. // Must free buffers even if not m_fValid, because we may have
  28. // gotten only partway through Init() before running into a problem.
  29. if (m_pBuffer)
  30. {
  31. MemFree(m_pBuffer);
  32. m_pBuffer = NULL;
  33. }
  34. if (m_psiStrings)
  35. {
  36. MemFree(m_psiStrings);
  37. m_psiStrings = NULL;
  38. }
  39. m_fValid = FALSE;
  40. }
  41. // Clear our internal structures to prepare to be initialized. Assumes we
  42. // have no allocated memory (call CleanUp())
  43. void CWCStringList::Clear()
  44. {
  45. m_fValid = FALSE;
  46. m_iBufEnd = m_iBufSize = m_iNumStrings = m_iMaxStrings = 0;
  47. m_pBuffer = NULL;
  48. m_psiStrings = NULL;
  49. ZeroMemory(m_Hash, sizeof(m_Hash));
  50. }
  51. void CWCStringList::Reset()
  52. {
  53. if (m_fValid || m_pBuffer || m_psiStrings)
  54. {
  55. CleanUp();
  56. Clear();
  57. }
  58. }
  59. BOOL CWCStringList::Init(int iInitBufSize)
  60. {
  61. if (m_fValid)
  62. {
  63. DBG("WCStringList::Init called when already initialized");
  64. Reset();
  65. }
  66. if (iInitBufSize <= 0)
  67. {
  68. iInitBufSize = DEFAULT_INIT_BUF_SIZE;
  69. }
  70. m_iMaxStrings = iInitBufSize >> 5; // this is relatively arbitrary but doesn't matter much
  71. m_pBuffer = (LPSTR)MemAlloc(LMEM_FIXED, iInitBufSize);
  72. m_psiStrings = (LPSTRING_INDEX)MemAlloc(LMEM_FIXED, m_iMaxStrings * sizeof(STRING_INDEX));
  73. if ((NULL == m_psiStrings) ||
  74. (NULL == m_pBuffer))
  75. {
  76. DBG_WARN("Init() memory allocation failed");
  77. CleanUp();
  78. return FALSE;
  79. }
  80. *m_pBuffer = 0;
  81. m_iBufSize = iInitBufSize;
  82. m_iBufEnd = 0;
  83. m_fValid = TRUE;
  84. return TRUE;
  85. }
  86. // Sets up our internal data structures (hash and string_index)
  87. // Sets m_iBufEnd.
  88. // We must already be Init()ialized and the data in m_pBuffer
  89. BOOL CWCStringList::InitializeFromBuffer()
  90. {
  91. LPCWSTR pNext;
  92. int iLen;
  93. if (!m_fValid)
  94. return FALSE;
  95. pNext = (LPCWSTR)m_pBuffer;
  96. while (((LPSTR)pNext-m_pBuffer) < m_iBufSize)
  97. {
  98. iLen = lstrlenW(pNext);
  99. InsertToHash(pNext, iLen, FALSE);
  100. pNext += iLen+1;
  101. }
  102. m_iBufEnd = (int)((LPSTR)pNext - m_pBuffer);
  103. return TRUE;
  104. }
  105. //
  106. // IPersistStream members
  107. //
  108. // We save
  109. // DWORD containing total length in bytes that follows. Will be
  110. // multiple of four; may have 0-4 extra pad bytes on the end.
  111. // String data.
  112. //
  113. // Smallest data we store is 4 bytes of zeroes. We still end up taking
  114. // memory when we get restored. Don't instantiate one of these objects
  115. // until you're going to use it.
  116. STDMETHODIMP CWCStringList::IsDirty(void)
  117. {
  118. DBG("CWCStringList::IsDirty returning S_OK (true) as always");
  119. return S_OK;
  120. }
  121. STDMETHODIMP CWCStringList::Load(IStream *pStm)
  122. {
  123. HRESULT hr;
  124. ULONG cbRead;
  125. DWORD dwDataSize;
  126. DBG("CWCStringList::Load");
  127. if (NULL==pStm)
  128. return E_POINTER;
  129. // Clean up our object
  130. Reset();
  131. // Load our data
  132. hr = pStm->Read(&dwDataSize, sizeof(DWORD), &cbRead);
  133. if (FAILED(hr) || cbRead != sizeof(DWORD))
  134. return STG_E_READFAULT;
  135. if (0 == dwDataSize)
  136. {
  137. if (!Init(512)) // Start with small buffer since we're empty
  138. return E_OUTOFMEMORY;
  139. return S_OK;
  140. }
  141. if (!Init(dwDataSize))
  142. return E_OUTOFMEMORY;
  143. ASSERT(dwDataSize <= (DWORD)m_iBufSize);
  144. // Read in the string data
  145. hr = pStm->Read(m_pBuffer, dwDataSize, &cbRead);
  146. if (FAILED(hr) || cbRead != dwDataSize)
  147. return STG_E_READFAULT;
  148. // Set up hash tables etc.
  149. InitializeFromBuffer();
  150. DBG("CWCStringList::Load success");
  151. return NOERROR;
  152. }
  153. STDMETHODIMP CWCStringList::Save(IStream *pStm, BOOL fClearDirty)
  154. {
  155. HRESULT hr;
  156. ULONG cbWritten;
  157. DWORD dwDataSize, dwZero=0;
  158. DWORD dwZeroPad;
  159. DBG("CWCStringList::Save");
  160. if (NULL==pStm)
  161. return E_POINTER;
  162. // First write our data
  163. dwDataSize = (m_iBufEnd+3) & 0xFFFFFFFC; // multiple of four
  164. if ((0 == m_iBufSize) || (0 == m_iNumStrings))
  165. {
  166. dwDataSize = 0;
  167. }
  168. hr = pStm->Write(&dwDataSize, sizeof(DWORD), &cbWritten);
  169. if (FAILED(hr) || sizeof(DWORD) != cbWritten)
  170. return STG_E_WRITEFAULT;
  171. if (dwDataSize > 0)
  172. {
  173. hr = pStm->Write(m_pBuffer, m_iBufSize, &cbWritten);
  174. if (FAILED(hr) || sizeof(DWORD) != cbWritten)
  175. return STG_E_WRITEFAULT;
  176. dwZeroPad = dwDataSize - m_iBufSize;
  177. ASSERT(dwZeroPad<4);
  178. if (dwZeroPad && dwZeroPad<4)
  179. {
  180. hr = pStm->Write(&dwZero, dwZeroPad, &cbWritten);
  181. if (FAILED(hr) || (dwZeroPad != cbWritten))
  182. return STG_E_WRITEFAULT;
  183. }
  184. }
  185. DBG("CWCStringList::Save success");
  186. return NOERROR;
  187. }
  188. STDMETHODIMP CWCStringList::GetSizeMax(ULARGE_INTEGER *pcbSize)
  189. {
  190. DBG("CWCStringList::GetSizeMax");
  191. if (NULL==pcbSize)
  192. return E_POINTER;
  193. pcbSize->LowPart = 0;
  194. pcbSize->HighPart = 0;
  195. pcbSize->LowPart = m_iBufEnd + 8;
  196. return NOERROR;
  197. }
  198. // Returns a BSTR
  199. BSTR CWCStringList::GetBSTR(int iNum)
  200. {
  201. LPCWSTR lpStr = GetString(iNum);
  202. return SysAllocStringLen(lpStr, GetStringLen(iNum));
  203. }
  204. // Returns FALSE if string is not found
  205. // Places string index (for GetString()) in *piNum only if string is found.
  206. BOOL CWCStringList::FindString(LPCWSTR lpwstr, int iLen, int *piNum/*=NULL*/)
  207. {
  208. int iHash;
  209. LPSTRING_INDEX psi;
  210. if (!lpwstr)
  211. return FALSE;
  212. if (iLen < 0)
  213. iLen = lstrlenW(lpwstr);
  214. iHash = Hash(lpwstr, iLen);
  215. for (psi = m_Hash[iHash]; psi; psi = psi->psiNext)
  216. {
  217. if ((psi->iLen == iLen) && memcmp(psi->lpwstr, lpwstr, iLen * sizeof(WCHAR)) == 0)
  218. {
  219. if (piNum)
  220. *piNum = (int) (psi-m_psiStrings);
  221. return TRUE; // String is a duplicate
  222. }
  223. }
  224. return FALSE;
  225. }
  226. // returns STRLST_FAIL on failure,
  227. // STRLST_DUPLICATE if the string already existed, and
  228. // STRLST_ADDED if it's new
  229. int CWCStringList::AddString(LPCWSTR lpwstr, DWORD_PTR dwData /*=NULL*/, int *piNum /*=NULL*/)
  230. {
  231. int iSize, iLen;
  232. if (!lpwstr)
  233. return STRLST_FAIL;
  234. iLen = lstrlenW(lpwstr);
  235. if (!m_fValid || !m_pBuffer)
  236. {
  237. DBG_WARN("WCStringList: AddString() called with invalid instance");
  238. return STRLST_FAIL;
  239. }
  240. if (dwData != 0)
  241. DBG_WARN("Value for dwData passed into CWCStringList::AddString");
  242. if (FindString(lpwstr, iLen, piNum))
  243. return STRLST_DUPLICATE; // String is a duplicate
  244. // iSize will be size in bytes including null term
  245. iSize = (iLen+1)*sizeof(WCHAR);
  246. // Append string to current buffer
  247. if (iSize >= (m_iBufSize - m_iBufEnd))
  248. {
  249. int iOldBufSize = m_iBufSize;
  250. // Grow buffer.
  251. m_iBufSize *= 2; // This way the number of reallocs drops off logarithmically
  252. if (m_iBufEnd + iSize > m_iBufSize)
  253. {
  254. DBG("StringList special growing size");
  255. m_iBufSize = m_iBufEnd + iSize;
  256. }
  257. TraceMsg(TF_THISMODULE,"StringList growing to size %d",m_iBufSize);
  258. LPSTR pBuf = (LPSTR)MemReAlloc((HLOCAL)m_pBuffer, m_iBufSize, LMEM_MOVEABLE);
  259. if (!pBuf)
  260. {
  261. m_iBufSize = iOldBufSize;
  262. DBG_WARN("WCStringList: ReAlloc() failure");
  263. // Realloc failure: our old memory is still present
  264. return 0;
  265. }
  266. // Let's be clever and fix all our pointers instead of getting faults
  267. if (m_pBuffer != pBuf)
  268. {
  269. int i;
  270. LPSTRING_INDEX psi;
  271. for (i=0, psi=&m_psiStrings[0]; i<m_iNumStrings; i++, psi++)
  272. {
  273. psi->lpwstr = (LPWSTR)(((LPSTR)psi->lpwstr - m_pBuffer) + pBuf);
  274. }
  275. m_pBuffer = pBuf;
  276. }
  277. }
  278. if (piNum)
  279. *piNum = m_iNumStrings;
  280. LPWSTR pBufEnd = (LPWSTR)(m_pBuffer + m_iBufEnd);
  281. StrCpyNW(pBufEnd, lpwstr, (m_iBufSize - m_iBufEnd) / sizeof(WCHAR));
  282. if (!InsertToHash(pBufEnd, iLen, TRUE))
  283. return 0;
  284. m_iBufEnd += iSize;
  285. return STRLST_ADDED; // indicate we added a new string
  286. }
  287. BOOL CWCStringList::InsertToHash(LPCWSTR lpwstr, int iLen, BOOL fAlreadyHashed)
  288. {
  289. int iHash = fAlreadyHashed ? m_iLastHash : Hash(lpwstr, iLen);
  290. // grow psiStrings if needed
  291. ASSERT(m_iNumStrings <= m_iMaxStrings);
  292. if (m_iNumStrings >= m_iMaxStrings)
  293. {
  294. m_iMaxStrings *= 2;
  295. TraceMsg(TF_THISMODULE, "StringList growing max strings to %d", m_iMaxStrings);
  296. LPSTRING_INDEX psiBuf = (LPSTRING_INDEX)MemReAlloc((HLOCAL)m_psiStrings,
  297. m_iMaxStrings * sizeof(STRING_INDEX), LMEM_MOVEABLE);
  298. if (!psiBuf)
  299. {
  300. // Realloc failure: Old memory still present
  301. DBG_WARN("WCStringList::InsertToHash() ReAlloc failure");
  302. m_iMaxStrings /= 2;
  303. return FALSE;
  304. }
  305. // More cleverness
  306. if (m_psiStrings != psiBuf)
  307. {
  308. int i;
  309. LPSTRING_INDEX psi, *ppsi;
  310. for (i=0, psi=psiBuf; i<m_iNumStrings; i++, psi++)
  311. {
  312. if (psi->psiNext)
  313. psi->psiNext = (psi->psiNext - m_psiStrings) + psiBuf;
  314. }
  315. for (i=0, ppsi=m_Hash; i<STRING_HASH_SIZE; i++, ppsi++)
  316. {
  317. if (*ppsi)
  318. *ppsi = (*ppsi - m_psiStrings) + psiBuf;
  319. }
  320. m_psiStrings = psiBuf;
  321. }
  322. }
  323. m_psiStrings[m_iNumStrings].lpwstr = lpwstr;
  324. m_psiStrings[m_iNumStrings].iLen = iLen;
  325. m_psiStrings[m_iNumStrings].psiNext = m_Hash[iHash];
  326. m_Hash[iHash] = &m_psiStrings[m_iNumStrings];
  327. m_iNumStrings++;
  328. return TRUE;
  329. }
  330. #ifdef DEBUG
  331. // WARNING: this clobbers the hash
  332. void CWCStringList::SpewHashStats(BOOL fVerbose)
  333. {
  334. /*
  335. int i;
  336. for (i = 0; i < STRING_HASH_SIZE; ++i)
  337. {
  338. int c = 0;
  339. for (tagStringIndex *p = m_Hash[i]; p; p = p->psiNext)
  340. ++c;
  341. if (c)
  342. TraceMsg(TF_THISMODULE,"%10d%12d", i, c);
  343. }
  344. */
  345. TraceMsg(TF_THISMODULE,"### Hash size: %d Num. entries:%7d", STRING_HASH_SIZE, m_iNumStrings);
  346. int i,n;
  347. if (fVerbose)
  348. {
  349. TraceMsg(TF_THISMODULE," # of entries # of keys with that many");
  350. for (i=0,n=0; n<STRING_HASH_SIZE; i++)
  351. {
  352. int k=0;
  353. for (int j=0; j<STRING_HASH_SIZE; j++)
  354. {
  355. int c=0;
  356. for (tagStringIndex* p=m_Hash[j]; p; p=p->psiNext)
  357. c++;
  358. if (c == i)
  359. k++;
  360. }
  361. if (k)
  362. {
  363. TraceMsg(TF_THISMODULE,"%10d%12d", i, k);
  364. n += k;
  365. }
  366. }
  367. }
  368. /*
  369. int total=0;
  370. if (fVerbose)
  371. {
  372. TraceMsg(TF_THISMODULE," length # of strings with that length",1);
  373. for (i=0,n=0; n<m_iNumStrings; i++)
  374. {
  375. int k=0;
  376. for (int j=0; j<m_iNumStrings; j++)
  377. {
  378. if (m_psiStrings[j].iLen == i)
  379. k++;
  380. }
  381. if (k)
  382. {
  383. if (fVerbose)
  384. TraceMsg(TF_THISMODULE,"%5d%10d", i, k);
  385. n += k;
  386. total += k*(k+1)/2;
  387. }
  388. }
  389. }
  390. TraceMsg(TF_THISMODULE,"### Average compares without hash * 100:%5d", total*100/m_iNumStrings);
  391. total=0;
  392. for (i=0; i<STRING_HASH_SIZE; i++)
  393. {
  394. for (tagStringIndex* p=m_Hash[i]; p; p=p->psiNext)
  395. {
  396. if (p->iLen < 0) continue;
  397. int n=1;
  398. for (tagStringIndex* q=p->psiNext; q; q=q->psiNext)
  399. {
  400. if (p->iLen == q->iLen)
  401. {
  402. n++;
  403. q->iLen = -1;
  404. }
  405. }
  406. total += n*(n+1)/2;
  407. }
  408. }
  409. TraceMsg(TF_THISMODULE,"### Average compares with hash * 100:%8d", total*100/m_iNumStrings);
  410. */
  411. }
  412. #endif
  413. //----------------------------------------------------------------------------
  414. // CWCDwordStringList
  415. CWCDwordStringList::CWCDwordStringList() : CWCStringList()
  416. {
  417. }
  418. CWCDwordStringList::~CWCDwordStringList()
  419. {
  420. if (m_pData)
  421. MemFree((HLOCAL)m_pData);
  422. }
  423. BOOL CWCDwordStringList::Init(int iInitBufSize/*=-1*/)
  424. {
  425. if (!CWCStringList::Init(iInitBufSize))
  426. return FALSE;
  427. m_pData = (DWORD_PTR*)MemAlloc(LMEM_FIXED, m_iMaxStrings * sizeof(DWORD));
  428. if (NULL == m_pData)
  429. return FALSE;
  430. return TRUE;
  431. }
  432. int CWCDwordStringList::AddString(LPCWSTR psz, DWORD_PTR dwData/*=0*/, int* piNum/*=NULL*/)
  433. {
  434. int iOldMaxStrings = m_iMaxStrings; // track changes in m_iMaxStrings by this call:
  435. int iNum;
  436. int iResult = CWCStringList::AddString(psz, 0, &iNum);
  437. if (iResult == 0)
  438. return 0;
  439. if (iOldMaxStrings != m_iMaxStrings) // make sure we have enough data space
  440. {
  441. DWORD_PTR *pData;
  442. // TraceMsg(TF_THISMODULE, "DwordStringList expanding dwords to %d", m_iMaxStrings);
  443. pData = (DWORD_PTR*)MemReAlloc((HLOCAL)m_pData, m_iMaxStrings * sizeof(DWORD),
  444. LMEM_MOVEABLE);
  445. ASSERT(pData);
  446. if (!pData)
  447. {
  448. DBG_WARN("Realloc failure in DwordStringList");
  449. MemFree(m_pData);
  450. m_pData = NULL; // This is bad
  451. return 0;
  452. }
  453. m_pData = pData;
  454. }
  455. if (iResult == 2) // only set data value if this is a new string
  456. {
  457. m_pData[iNum] = dwData;
  458. }
  459. if (piNum)
  460. *piNum = iNum;
  461. return iResult;
  462. }