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.

1264 lines
30 KiB

  1. /*
  2. * string.c - String table ADT module.
  3. */
  4. /*
  5. The string table ADT implemented in this module is set up as a hash table
  6. with HASH_TABLE_SIZE buckets. A hash function is calculated for each string to
  7. determine its bucket. Multiple strings in a single bucket are stored in a
  8. linked list. The string hash table allows us to keep only one copy of a string
  9. that is used multiple times. Strings are allocated in the heap by
  10. AllocateMemory().
  11. Every string has a list node structure associated with it. A string is
  12. accessed through its associated list node. Each hash bucket is a list of
  13. string nodes. A handle to a string table is a pointer to the base of the
  14. string table's array of hash buckets. String tables are allocated in the heap
  15. by AllocateMemory(). Each element in an array of hash buckets is a handle to a
  16. list of strings in the hash bucket. A handle to a string is a handle to a node
  17. in the string's hash bucket's list.
  18. Hash table ADTs are predicated on the idea that hash buckets will typically
  19. be shallow, so the search of a hash bucket will not take horrendously long.
  20. The data objects in hash buckets should be stored in sorted order to reduce
  21. search time. If hash buckets get too deep, increase the hash table size.
  22. Ideally, the hash table should be implemented as a container class that hashes
  23. arbitrary data objects given an initial hash table size, the size of the
  24. objects to be hashed, a hash function, and a data object comparison function.
  25. Currently the hash table ADT is restricted to strings, the strings in each
  26. hash bucket are stored in sorted order, and hash buckets are binary searched.
  27. */
  28. /* Headers
  29. **********/
  30. #include "project.h"
  31. #pragma hdrstop
  32. /* Types
  33. ********/
  34. /* string table */
  35. typedef struct _stringtable
  36. {
  37. /* number of hash buckets in string table */
  38. HASHBUCKETCOUNT hbc;
  39. /* pointer to array of hash buckets (HLISTs) */
  40. PHLIST phlistHashBuckets;
  41. }
  42. STRINGTABLE;
  43. DECLARE_STANDARD_TYPES(STRINGTABLE);
  44. /* string heap structure */
  45. typedef struct _string
  46. {
  47. /* lock count of string */
  48. ULONG ulcLock;
  49. /* actual string */
  50. TCHAR string[1];
  51. }
  52. STRING;
  53. DECLARE_STANDARD_TYPES(STRING);
  54. /* string table database structure header */
  55. typedef struct _stringtabledbheader
  56. {
  57. /*
  58. * length of longest string in string table, not including null terminator
  59. */
  60. DWORD dwcbMaxStringLen;
  61. /* number of strings in string table */
  62. LONG lcStrings;
  63. }
  64. STRINGTABLEDBHEADER;
  65. DECLARE_STANDARD_TYPES(STRINGTABLEDBHEADER);
  66. /* database string header */
  67. typedef struct _dbstringheader
  68. {
  69. /* old handle to this string */
  70. HSTRING hsOld;
  71. }
  72. DBSTRINGHEADER;
  73. DECLARE_STANDARD_TYPES(DBSTRINGHEADER);
  74. /***************************** Private Functions *****************************/
  75. /* Module Prototypes
  76. ********************/
  77. PRIVATE_CODE COMPARISONRESULT StringSearchCmp(PCVOID, PCVOID);
  78. PRIVATE_CODE COMPARISONRESULT StringSortCmp(PCVOID, PCVOID);
  79. PRIVATE_CODE BOOL UnlockString(PSTRING);
  80. PRIVATE_CODE BOOL FreeStringWalker(PVOID, PVOID);
  81. PRIVATE_CODE void FreeHashBucket(HLIST);
  82. PRIVATE_CODE TWINRESULT WriteHashBucket(HCACHEDFILE, HLIST, PLONG, PDWORD);
  83. PRIVATE_CODE TWINRESULT WriteString(HCACHEDFILE, HNODE, PSTRING, PDWORD);
  84. PRIVATE_CODE TWINRESULT ReadString(HCACHEDFILE, HSTRINGTABLE, HHANDLETRANS, LPTSTR, DWORD);
  85. PRIVATE_CODE TWINRESULT SlowReadString(HCACHEDFILE, LPTSTR, DWORD);
  86. #ifdef VSTF
  87. PRIVATE_CODE BOOL IsValidPCNEWSTRINGTABLE(PCNEWSTRINGTABLE);
  88. PRIVATE_CODE BOOL IsValidPCSTRING(PCSTRING);
  89. PRIVATE_CODE BOOL IsValidPCSTRINGTABLE(PCSTRINGTABLE);
  90. #endif
  91. /*
  92. ** StringSearchCmp()
  93. **
  94. **
  95. **
  96. ** Arguments:
  97. **
  98. ** Returns:
  99. **
  100. ** Side Effects: none
  101. */
  102. PRIVATE_CODE COMPARISONRESULT StringSearchCmp(PCVOID pcszPath, PCVOID pcstring)
  103. {
  104. ASSERT(IS_VALID_STRING_PTR(pcszPath, CSTR));
  105. ASSERT(IS_VALID_STRUCT_PTR(pcstring, CSTRING));
  106. return(MapIntToComparisonResult(lstrcmp((LPCTSTR)pcszPath,
  107. (LPCTSTR)&(((PCSTRING)pcstring)->string))));
  108. }
  109. /*
  110. ** StringSortCmp()
  111. **
  112. **
  113. **
  114. ** Arguments:
  115. **
  116. ** Returns:
  117. **
  118. ** Side Effects: none
  119. */
  120. PRIVATE_CODE COMPARISONRESULT StringSortCmp(PCVOID pcstring1, PCVOID pcstring2)
  121. {
  122. ASSERT(IS_VALID_STRUCT_PTR(pcstring1, CSTRING));
  123. ASSERT(IS_VALID_STRUCT_PTR(pcstring2, CSTRING));
  124. return(MapIntToComparisonResult(lstrcmp((LPCTSTR)&(((PCSTRING)pcstring1)->string),
  125. (LPCTSTR)&(((PCSTRING)pcstring2)->string))));
  126. }
  127. /*
  128. ** UnlockString()
  129. **
  130. ** Decrements a string's lock count.
  131. **
  132. ** Arguments:
  133. **
  134. ** Returns: void
  135. **
  136. ** Side Effects: none
  137. */
  138. PRIVATE_CODE BOOL UnlockString(PSTRING pstring)
  139. {
  140. ASSERT(IS_VALID_STRUCT_PTR(pstring, CSTRING));
  141. /* Is the lock count going to underflow? */
  142. if (EVAL(pstring->ulcLock > 0))
  143. pstring->ulcLock--;
  144. return(pstring->ulcLock > 0);
  145. }
  146. /*
  147. ** FreeStringWalker()
  148. **
  149. **
  150. **
  151. ** Arguments:
  152. **
  153. ** Returns:
  154. **
  155. ** Side Effects: none
  156. */
  157. #pragma warning(disable:4100) /* "unreferenced formal parameter" warning */
  158. PRIVATE_CODE BOOL FreeStringWalker(PVOID pstring, PVOID pvUnused)
  159. {
  160. ASSERT(IS_VALID_STRUCT_PTR(pstring, CSTRING));
  161. ASSERT(! pvUnused);
  162. FreeMemory(pstring);
  163. return(TRUE);
  164. }
  165. #pragma warning(default:4100) /* "unreferenced formal parameter" warning */
  166. /*
  167. ** FreeHashBucket()
  168. **
  169. ** Frees the strings in a hash bucket, and the hash bucket's string list.
  170. **
  171. ** Arguments: hlistHashBucket - handle to hash bucket's list of strings
  172. **
  173. ** Returns: void
  174. **
  175. ** Side Effects: none
  176. **
  177. ** N.b., this function ignores the lock counts of the strings in the hash
  178. ** bucket. All strings in the hash bucket are freed.
  179. */
  180. PRIVATE_CODE void FreeHashBucket(HLIST hlistHashBucket)
  181. {
  182. ASSERT(! hlistHashBucket || IS_VALID_HANDLE(hlistHashBucket, LIST));
  183. /* Are there any strings in this hash bucket to delete? */
  184. if (hlistHashBucket)
  185. {
  186. /* Yes. Delete all strings in list. */
  187. EVAL(WalkList(hlistHashBucket, &FreeStringWalker, NULL));
  188. /* Delete hash bucket string list. */
  189. DestroyList(hlistHashBucket);
  190. }
  191. return;
  192. }
  193. /*
  194. ** MyGetStringLen()
  195. **
  196. ** Retrieves the length of a string in a string table.
  197. **
  198. ** Arguments: pcstring - pointer to string whose length is to be
  199. ** determined
  200. **
  201. ** Returns: Length of string in bytes, not including null terminator.
  202. **
  203. ** Side Effects: none
  204. */
  205. PRIVATE_CODE int MyGetStringLen(PCSTRING pcstring)
  206. {
  207. ASSERT(IS_VALID_STRUCT_PTR(pcstring, CSTRING));
  208. return(lstrlen(pcstring->string) * sizeof(TCHAR));
  209. }
  210. /*
  211. ** WriteHashBucket()
  212. **
  213. **
  214. **
  215. ** Arguments:
  216. **
  217. ** Returns: TWINRESULT
  218. **
  219. ** Side Effects: none
  220. */
  221. PRIVATE_CODE TWINRESULT WriteHashBucket(HCACHEDFILE hcf,
  222. HLIST hlistHashBucket,
  223. PLONG plcStrings,
  224. PDWORD pdwcbMaxStringLen)
  225. {
  226. TWINRESULT tr = TR_SUCCESS;
  227. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  228. ASSERT(! hlistHashBucket || IS_VALID_HANDLE(hlistHashBucket, LIST));
  229. ASSERT(IS_VALID_WRITE_PTR(plcStrings, LONG));
  230. ASSERT(IS_VALID_WRITE_PTR(pdwcbMaxStringLen, DWORD));
  231. /* Any strings in this hash bucket? */
  232. *plcStrings = 0;
  233. *pdwcbMaxStringLen = 0;
  234. if (hlistHashBucket)
  235. {
  236. BOOL bContinue;
  237. HNODE hnode;
  238. /* Yes. Walk hash bucket, saving each string. */
  239. for (bContinue = GetFirstNode(hlistHashBucket, &hnode);
  240. bContinue;
  241. bContinue = GetNextNode(hnode, &hnode))
  242. {
  243. PSTRING pstring;
  244. pstring = (PSTRING)GetNodeData(hnode);
  245. ASSERT(IS_VALID_STRUCT_PTR(pstring, CSTRING));
  246. /*
  247. * As a sanity check, don't save any string with a lock count of 0. A
  248. * 0 lock count implies that the string has not been referenced since
  249. * it was restored from the database, or something is broken.
  250. */
  251. if (pstring->ulcLock > 0)
  252. {
  253. DWORD dwcbStringLen;
  254. tr = WriteString(hcf, hnode, pstring, &dwcbStringLen);
  255. if (tr == TR_SUCCESS)
  256. {
  257. if (dwcbStringLen > *pdwcbMaxStringLen)
  258. *pdwcbMaxStringLen = dwcbStringLen;
  259. ASSERT(*plcStrings < LONG_MAX);
  260. (*plcStrings)++;
  261. }
  262. else
  263. break;
  264. }
  265. else
  266. ERROR_OUT((TEXT("WriteHashBucket(): String \"%s\" has 0 lock count and will not be saved."),
  267. pstring->string));
  268. }
  269. }
  270. return(tr);
  271. }
  272. /*
  273. ** WriteString()
  274. **
  275. **
  276. **
  277. ** Arguments:
  278. **
  279. ** Returns: TWINRESULT
  280. **
  281. ** Side Effects: none
  282. */
  283. PRIVATE_CODE TWINRESULT WriteString(HCACHEDFILE hcf, HNODE hnodeOld,
  284. PSTRING pstring, PDWORD pdwcbStringLen)
  285. {
  286. TWINRESULT tr = TR_BRIEFCASE_WRITE_FAILED;
  287. DBSTRINGHEADER dbsh;
  288. /* (+ 1) for null terminator. */
  289. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  290. ASSERT(IS_VALID_HANDLE(hnodeOld, NODE));
  291. ASSERT(IS_VALID_STRUCT_PTR(pstring, CSTRING));
  292. ASSERT(IS_VALID_READ_BUFFER_PTR(pstring, STRING, sizeof(STRING) + MyGetStringLen(pstring) + sizeof(TCHAR) - sizeof(pstring->string)));
  293. ASSERT(IS_VALID_WRITE_PTR(pdwcbStringLen, DWORD));
  294. /* Create string header. */
  295. dbsh.hsOld = (HSTRING)hnodeOld;
  296. /* Save string header and string. */
  297. if (WriteToCachedFile(hcf, (PCVOID)&dbsh, sizeof(dbsh), NULL))
  298. {
  299. LPSTR pszAnsi;
  300. /* (+ 1) for null terminator. */
  301. *pdwcbStringLen = MyGetStringLen(pstring) + SIZEOF(TCHAR);
  302. // If its unicode, convert the string to ansi before writing it out
  303. #ifdef UNICODE
  304. {
  305. pszAnsi = LocalAlloc(LPTR, *pdwcbStringLen);
  306. if (NULL == pszAnsi)
  307. {
  308. return tr;
  309. }
  310. WideCharToMultiByte(CP_ACP, 0, pstring->string, -1, pszAnsi, *pdwcbStringLen, NULL, NULL);
  311. // We should always have a string at this point that can be converted losslessly
  312. #if (defined(DEBUG) || defined(DBG)) && defined(UNICODE)
  313. {
  314. WCHAR szUnicode[MAX_PATH*2];
  315. MultiByteToWideChar(CP_ACP, 0, pszAnsi, -1, szUnicode, ARRAYSIZE(szUnicode));
  316. ASSERT(0 == lstrcmp(szUnicode, pstring->string));
  317. }
  318. #endif
  319. if (WriteToCachedFile(hcf, (PCVOID) pszAnsi, lstrlenA(pszAnsi) + 1, NULL))
  320. tr = TR_SUCCESS;
  321. LocalFree(pszAnsi);
  322. }
  323. #else
  324. if (WriteToCachedFile(hcf, (PCVOID)&(pstring->string), (UINT)*pdwcbStringLen, NULL))
  325. tr = TR_SUCCESS;
  326. #endif
  327. }
  328. return(tr);
  329. }
  330. /*
  331. ** ReadString()
  332. **
  333. **
  334. **
  335. ** Arguments:
  336. **
  337. ** Returns: TWINRESULT
  338. **
  339. ** Side Effects: none
  340. */
  341. PRIVATE_CODE TWINRESULT ReadString(HCACHEDFILE hcf, HSTRINGTABLE hst,
  342. HHANDLETRANS hht, LPTSTR pszStringBuf,
  343. DWORD dwcbStringBufLen)
  344. {
  345. TWINRESULT tr;
  346. DBSTRINGHEADER dbsh;
  347. DWORD dwcbRead;
  348. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  349. ASSERT(IS_VALID_HANDLE(hst, STRINGTABLE));
  350. ASSERT(IS_VALID_HANDLE(hht, HANDLETRANS));
  351. ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszStringBuf, STR, (UINT)dwcbStringBufLen));
  352. if (ReadFromCachedFile(hcf, &dbsh, sizeof(dbsh), &dwcbRead) &&
  353. dwcbRead == sizeof(dbsh))
  354. {
  355. tr = SlowReadString(hcf, pszStringBuf, dwcbStringBufLen);
  356. if (tr == TR_SUCCESS)
  357. {
  358. HSTRING hsNew;
  359. if (AddString(pszStringBuf, hst, GetHashBucketIndex, &hsNew))
  360. {
  361. /*
  362. * We must undo the LockString() performed by AddString() to
  363. * maintain the correct string lock count. N.b., the lock count of
  364. * a string may be > 0 even after unlocking since the client may
  365. * already have added the string to the given string table.
  366. */
  367. UnlockString((PSTRING)GetNodeData((HNODE)hsNew));
  368. if (! AddHandleToHandleTranslator(hht, (HGENERIC)(dbsh.hsOld), (HGENERIC)hsNew))
  369. {
  370. DeleteNode((HNODE)hsNew);
  371. tr = TR_CORRUPT_BRIEFCASE;
  372. }
  373. }
  374. else
  375. tr = TR_OUT_OF_MEMORY;
  376. }
  377. }
  378. else
  379. tr = TR_CORRUPT_BRIEFCASE;
  380. return(tr);
  381. }
  382. /*
  383. ** SlowReadString()
  384. **
  385. **
  386. **
  387. ** Arguments:
  388. **
  389. ** Returns:
  390. **
  391. ** Side Effects: none
  392. */
  393. PRIVATE_CODE TWINRESULT SlowReadString(HCACHEDFILE hcf, LPTSTR pszStringBuf,
  394. DWORD dwcbStringBufLen)
  395. {
  396. TWINRESULT tr = TR_CORRUPT_BRIEFCASE;
  397. LPTSTR pszStringBufEnd;
  398. DWORD dwcbRead;
  399. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  400. ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszStringBuf, STR, (UINT)dwcbStringBufLen));
  401. pszStringBufEnd = pszStringBuf + dwcbStringBufLen;
  402. // The database strings are always written ANSI, so if we are running unicode,
  403. // we need to convert as we go
  404. #ifdef UNICODE
  405. {
  406. LPSTR pszAnsiEnd;
  407. LPSTR pszAnsiStart;
  408. LPSTR pszAnsi = LocalAlloc(LPTR, dwcbStringBufLen);
  409. pszAnsiStart = pszAnsi;
  410. pszAnsiEnd = pszAnsi + dwcbStringBufLen;
  411. if (NULL == pszAnsi)
  412. {
  413. return tr;
  414. }
  415. while (pszAnsi < pszAnsiEnd &&
  416. ReadFromCachedFile(hcf, pszAnsi, sizeof(*pszAnsi), &dwcbRead) &&
  417. dwcbRead == sizeof(*pszAnsi))
  418. {
  419. if (*pszAnsi)
  420. pszAnsi++;
  421. else
  422. {
  423. tr = TR_SUCCESS;
  424. break;
  425. }
  426. }
  427. if (tr == TR_SUCCESS)
  428. {
  429. MultiByteToWideChar(CP_ACP, 0, pszAnsiStart, -1, pszStringBuf, dwcbStringBufLen / sizeof(TCHAR));
  430. }
  431. LocalFree(pszAnsiStart);
  432. }
  433. #else
  434. while (pszStringBuf < pszStringBufEnd &&
  435. ReadFromCachedFile(hcf, pszStringBuf, sizeof(*pszStringBuf), &dwcbRead) &&
  436. dwcbRead == sizeof(*pszStringBuf))
  437. {
  438. if (*pszStringBuf)
  439. pszStringBuf++;
  440. else
  441. {
  442. tr = TR_SUCCESS;
  443. break;
  444. }
  445. }
  446. #endif
  447. return(tr);
  448. }
  449. #ifdef VSTF
  450. /*
  451. ** IsValidPCNEWSTRINGTABLE()
  452. **
  453. **
  454. **
  455. ** Arguments:
  456. **
  457. ** Returns:
  458. **
  459. ** Side Effects: none
  460. */
  461. PRIVATE_CODE BOOL IsValidPCNEWSTRINGTABLE(PCNEWSTRINGTABLE pcnst)
  462. {
  463. BOOL bResult;
  464. if (IS_VALID_READ_PTR(pcnst, CNEWSTRINGTABLE) &&
  465. EVAL(pcnst->hbc > 0))
  466. bResult = TRUE;
  467. else
  468. bResult = FALSE;
  469. return(bResult);
  470. }
  471. /*
  472. ** IsValidPCSTRING()
  473. **
  474. **
  475. **
  476. ** Arguments:
  477. **
  478. ** Returns:
  479. **
  480. ** Side Effects: none
  481. */
  482. PRIVATE_CODE BOOL IsValidPCSTRING(PCSTRING pcs)
  483. {
  484. BOOL bResult;
  485. if (IS_VALID_READ_PTR(pcs, CSTRING) &&
  486. IS_VALID_STRING_PTR(pcs->string, CSTR))
  487. bResult = TRUE;
  488. else
  489. bResult = FALSE;
  490. return(bResult);
  491. }
  492. /*
  493. ** IsValidStringWalker()
  494. **
  495. **
  496. **
  497. ** Arguments:
  498. **
  499. ** Returns:
  500. **
  501. ** Side Effects: none
  502. */
  503. #pragma warning(disable:4100) /* "unreferenced formal parameter" warning */
  504. PRIVATE_CODE BOOL IsValidStringWalker(PVOID pstring, PVOID pvUnused)
  505. {
  506. ASSERT(! pvUnused);
  507. return(IS_VALID_STRUCT_PTR(pstring, CSTRING));
  508. }
  509. #pragma warning(default:4100) /* "unreferenced formal parameter" warning */
  510. /*
  511. ** IsValidPCSTRINGTABLE()
  512. **
  513. **
  514. **
  515. ** Arguments:
  516. **
  517. ** Returns:
  518. **
  519. ** Side Effects: none
  520. */
  521. PRIVATE_CODE BOOL IsValidPCSTRINGTABLE(PCSTRINGTABLE pcst)
  522. {
  523. BOOL bResult = FALSE;
  524. if (IS_VALID_READ_PTR(pcst, CSTRINGTABLE) &&
  525. EVAL(pcst->hbc > 0) &&
  526. IS_VALID_READ_BUFFER_PTR(pcst->phlistHashBuckets, HLIST, pcst->hbc * sizeof((pcst->phlistHashBuckets)[0])))
  527. {
  528. HASHBUCKETCOUNT hbc;
  529. for (hbc = 0; hbc < pcst->hbc; hbc++)
  530. {
  531. HLIST hlistHashBucket;
  532. hlistHashBucket = (pcst->phlistHashBuckets)[hbc];
  533. if (hlistHashBucket)
  534. {
  535. if (! IS_VALID_HANDLE(hlistHashBucket, LIST) ||
  536. ! WalkList(hlistHashBucket, &IsValidStringWalker, NULL))
  537. break;
  538. }
  539. }
  540. if (hbc == pcst->hbc)
  541. bResult = TRUE;
  542. }
  543. return(bResult);
  544. }
  545. #endif
  546. /****************************** Public Functions *****************************/
  547. /*
  548. ** CreateStringTable()
  549. **
  550. ** Creates a new string table.
  551. **
  552. ** Arguments: pcnszt - pointer to NEWSTRINGTABLE descibing string table to
  553. ** be created
  554. **
  555. ** Returns: Handle to new string table if successful, or NULL if
  556. ** unsuccessful.
  557. **
  558. ** Side Effects: none
  559. */
  560. PUBLIC_CODE BOOL CreateStringTable(PCNEWSTRINGTABLE pcnszt,
  561. PHSTRINGTABLE phst)
  562. {
  563. PSTRINGTABLE pst;
  564. ASSERT(IS_VALID_STRUCT_PTR(pcnszt, CNEWSTRINGTABLE));
  565. ASSERT(IS_VALID_WRITE_PTR(phst, HSTRINGTABLE));
  566. /* Try to allocate new string table structure. */
  567. *phst = NULL;
  568. if (AllocateMemory(sizeof(*pst), &pst))
  569. {
  570. PHLIST phlistHashBuckets;
  571. /* Try to allocate hash bucket array. */
  572. #ifdef DBLCHECK
  573. ASSERT((double)(pcnszt->hbc) * (double)(sizeof(*phlistHashBuckets)) <= (double)SIZE_T_MAX);
  574. #endif
  575. if (AllocateMemory(pcnszt->hbc * sizeof(*phlistHashBuckets), (PVOID *)(&phlistHashBuckets)))
  576. {
  577. HASHBUCKETCOUNT bc;
  578. /* Successs! Initialize STRINGTABLE fields. */
  579. pst->phlistHashBuckets = phlistHashBuckets;
  580. pst->hbc = pcnszt->hbc;
  581. /* Initialize all hash buckets to NULL. */
  582. for (bc = 0; bc < pcnszt->hbc; bc++)
  583. phlistHashBuckets[bc] = NULL;
  584. *phst = (HSTRINGTABLE)pst;
  585. ASSERT(IS_VALID_HANDLE(*phst, STRINGTABLE));
  586. }
  587. else
  588. /* Free string table structure. */
  589. FreeMemory(pst);
  590. }
  591. return(*phst != NULL);
  592. }
  593. /*
  594. ** DestroyStringTable()
  595. **
  596. ** Destroys a string table.
  597. **
  598. ** Arguments: hst - handle to string table to be destroyed
  599. **
  600. ** Returns: void
  601. **
  602. ** Side Effects: none
  603. */
  604. PUBLIC_CODE void DestroyStringTable(HSTRINGTABLE hst)
  605. {
  606. HASHBUCKETCOUNT bc;
  607. ASSERT(IS_VALID_HANDLE(hst, STRINGTABLE));
  608. /* Traverse array of hash bucket heads, freeing hash bucket strings. */
  609. for (bc = 0; bc < ((PSTRINGTABLE)hst)->hbc; bc++)
  610. FreeHashBucket(((PSTRINGTABLE)hst)->phlistHashBuckets[bc]);
  611. /* Free array of hash buckets. */
  612. FreeMemory(((PSTRINGTABLE)hst)->phlistHashBuckets);
  613. /* Free string table structure. */
  614. FreeMemory((PSTRINGTABLE)hst);
  615. return;
  616. }
  617. /*
  618. ** AddString()
  619. **
  620. ** Adds a string to a string table.
  621. **
  622. ** Arguments: pcsz - pointer to string to be added
  623. ** hst - handle to string table that string is to be added to
  624. **
  625. ** Returns: Handle to new string if successful, or NULL if unsuccessful.
  626. **
  627. ** Side Effects: none
  628. */
  629. PUBLIC_CODE BOOL AddString(LPCTSTR pcsz, HSTRINGTABLE hst,
  630. STRINGTABLEHASHFUNC pfnHashFunc, PHSTRING phs)
  631. {
  632. BOOL bResult;
  633. HASHBUCKETCOUNT hbcNew;
  634. BOOL bFound;
  635. HNODE hnode;
  636. PHLIST phlistHashBucket;
  637. ASSERT(IS_VALID_STRING_PTR(pcsz, CSTR));
  638. ASSERT(IS_VALID_HANDLE(hst, STRINGTABLE));
  639. ASSERT(IS_VALID_CODE_PTR(pfnHashFunc, STRINGTABLEHASHFUNC));
  640. ASSERT(IS_VALID_WRITE_PTR(phs, HSTRING));
  641. /* Find appropriate hash bucket. */
  642. hbcNew = pfnHashFunc(pcsz, ((PSTRINGTABLE)hst)->hbc);
  643. ASSERT(hbcNew < ((PSTRINGTABLE)hst)->hbc);
  644. phlistHashBucket = &(((PSTRINGTABLE)hst)->phlistHashBuckets[hbcNew]);
  645. if (*phlistHashBucket)
  646. {
  647. /* Search the hash bucket for the string. */
  648. bFound = SearchSortedList(*phlistHashBucket, &StringSearchCmp, pcsz,
  649. &hnode);
  650. bResult = TRUE;
  651. }
  652. else
  653. {
  654. NEWLIST nl;
  655. /* Create a string list for this hash bucket. */
  656. bFound = FALSE;
  657. nl.dwFlags = NL_FL_SORTED_ADD;
  658. bResult = CreateList(&nl, phlistHashBucket);
  659. }
  660. /* Do we have a hash bucket for the string? */
  661. if (bResult)
  662. {
  663. /* Yes. Is the string already in the hash bucket? */
  664. if (bFound)
  665. {
  666. /* Yes. */
  667. LockString((HSTRING)hnode);
  668. *phs = (HSTRING)hnode;
  669. }
  670. else
  671. {
  672. /* No. Create it. */
  673. PSTRING pstringNew;
  674. /* (+ 1) for null terminator. */
  675. bResult = AllocateMemory(sizeof(*pstringNew) - sizeof(pstringNew->string)
  676. + (lstrlen(pcsz) + 1) * sizeof(TCHAR), &pstringNew);
  677. if (bResult)
  678. {
  679. HNODE hnodeNew;
  680. /* Set up STRING fields. */
  681. pstringNew->ulcLock = 1;
  682. lstrcpy(pstringNew->string, pcsz); // dynamically allocated above
  683. /* What's up with this string, Doc? */
  684. bResult = AddNode(*phlistHashBucket, StringSortCmp, pstringNew, &hnodeNew);
  685. /* Was the new string added to the hash bucket successfully? */
  686. if (bResult)
  687. /* Yes. */
  688. *phs = (HSTRING)hnodeNew;
  689. else
  690. /* No. */
  691. FreeMemory(pstringNew);
  692. }
  693. }
  694. }
  695. ASSERT(! bResult ||
  696. IS_VALID_HANDLE(*phs, STRING));
  697. return(bResult);
  698. }
  699. /*
  700. ** DeleteString()
  701. **
  702. ** Decrements a string's lock count. If the lock count goes to 0, the string
  703. ** is deleted from its string table.
  704. **
  705. ** Arguments: hs - handle to the string to be deleted
  706. **
  707. ** Returns: void
  708. **
  709. ** Side Effects: none
  710. */
  711. PUBLIC_CODE void DeleteString(HSTRING hs)
  712. {
  713. PSTRING pstring;
  714. ASSERT(IS_VALID_HANDLE(hs, STRING));
  715. pstring = (PSTRING)GetNodeData((HNODE)hs);
  716. /* Delete string completely? */
  717. if (! UnlockString(pstring))
  718. {
  719. /* Yes. Remove the string node from the hash bucket's list. */
  720. DeleteNode((HNODE)hs);
  721. FreeMemory(pstring);
  722. }
  723. return;
  724. }
  725. /*
  726. ** LockString()
  727. **
  728. ** Increments a string's lock count.
  729. **
  730. ** Arguments: hs - handle to string whose lock count is to be incremented
  731. **
  732. ** Returns: void
  733. **
  734. ** Side Effects: none
  735. */
  736. PUBLIC_CODE void LockString(HSTRING hs)
  737. {
  738. PSTRING pstring;
  739. ASSERT(IS_VALID_HANDLE(hs, STRING));
  740. /* Increment lock count. */
  741. pstring = (PSTRING)GetNodeData((HNODE)hs);
  742. ASSERT(pstring->ulcLock < ULONG_MAX);
  743. pstring->ulcLock++;
  744. return;
  745. }
  746. /*
  747. ** CompareStrings()
  748. **
  749. **
  750. **
  751. ** Arguments:
  752. **
  753. ** Returns:
  754. **
  755. ** Side Effects: none
  756. */
  757. PUBLIC_CODE COMPARISONRESULT CompareStringsI(HSTRING hs1, HSTRING hs2)
  758. {
  759. ASSERT(IS_VALID_HANDLE(hs1, STRING));
  760. ASSERT(IS_VALID_HANDLE(hs2, STRING));
  761. /* This comparison works across string tables. */
  762. return(MapIntToComparisonResult(lstrcmpi(((PCSTRING)GetNodeData((HNODE)hs1))->string,
  763. ((PCSTRING)GetNodeData((HNODE)hs2))->string)));
  764. }
  765. /*
  766. ** GetString()
  767. **
  768. ** Retrieves a pointer to a string in a string table.
  769. **
  770. ** Arguments: hs - handle to the string to be retrieved
  771. **
  772. ** Returns: Pointer to string.
  773. **
  774. ** Side Effects: none
  775. */
  776. PUBLIC_CODE LPCTSTR GetString(HSTRING hs)
  777. {
  778. PSTRING pstring;
  779. ASSERT(IS_VALID_HANDLE(hs, STRING));
  780. pstring = (PSTRING)GetNodeData((HNODE)hs);
  781. return((LPCTSTR)&(pstring->string));
  782. }
  783. /*
  784. ** WriteStringTable()
  785. **
  786. **
  787. **
  788. ** Arguments:
  789. **
  790. ** Returns: TWINRESULT
  791. **
  792. ** Side Effects: none
  793. */
  794. PUBLIC_CODE TWINRESULT WriteStringTable(HCACHEDFILE hcf, HSTRINGTABLE hst)
  795. {
  796. TWINRESULT tr = TR_BRIEFCASE_WRITE_FAILED;
  797. DWORD dwcbStringTableDBHeaderOffset;
  798. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  799. ASSERT(IS_VALID_HANDLE(hst, STRINGTABLE));
  800. /* Save initial file poisition. */
  801. dwcbStringTableDBHeaderOffset = GetCachedFilePointerPosition(hcf);
  802. if (dwcbStringTableDBHeaderOffset != INVALID_SEEK_POSITION)
  803. {
  804. STRINGTABLEDBHEADER stdbh;
  805. /* Leave space for the string table header. */
  806. ZeroMemory(&stdbh, sizeof(stdbh));
  807. if (WriteToCachedFile(hcf, (PCVOID)&stdbh, sizeof(stdbh), NULL))
  808. {
  809. HASHBUCKETCOUNT hbc;
  810. /* Save strings in each hash bucket. */
  811. stdbh.dwcbMaxStringLen = 0;
  812. stdbh.lcStrings = 0;
  813. tr = TR_SUCCESS;
  814. for (hbc = 0; hbc < ((PSTRINGTABLE)hst)->hbc; hbc++)
  815. {
  816. LONG lcStringsInHashBucket;
  817. DWORD dwcbStringLen;
  818. tr = WriteHashBucket(hcf,
  819. (((PSTRINGTABLE)hst)->phlistHashBuckets)[hbc],
  820. &lcStringsInHashBucket, &dwcbStringLen);
  821. if (tr == TR_SUCCESS)
  822. {
  823. /* Watch out for overflow. */
  824. ASSERT(stdbh.lcStrings <= LONG_MAX - lcStringsInHashBucket);
  825. stdbh.lcStrings += lcStringsInHashBucket;
  826. if (dwcbStringLen > stdbh.dwcbMaxStringLen)
  827. stdbh.dwcbMaxStringLen = dwcbStringLen;
  828. }
  829. else
  830. break;
  831. }
  832. if (tr == TR_SUCCESS)
  833. {
  834. /* Save string table header. */
  835. // The on-disk dwCBMaxString len always refers to ANSI chars,
  836. // whereas in memory it is for the TCHAR type, we adjust it
  837. // around the save
  838. stdbh.dwcbMaxStringLen /= sizeof(TCHAR);
  839. tr = WriteDBSegmentHeader(hcf, dwcbStringTableDBHeaderOffset,
  840. &stdbh, sizeof(stdbh));
  841. stdbh.dwcbMaxStringLen *= sizeof(TCHAR);
  842. TRACE_OUT((TEXT("WriteStringTable(): Wrote %ld strings."),
  843. stdbh.lcStrings));
  844. }
  845. }
  846. }
  847. return(tr);
  848. }
  849. /*
  850. ** ReadStringTable()
  851. **
  852. **
  853. **
  854. ** Arguments:
  855. **
  856. ** Returns: TWINRESULT
  857. **
  858. ** Side Effects: none
  859. */
  860. PUBLIC_CODE TWINRESULT ReadStringTable(HCACHEDFILE hcf, HSTRINGTABLE hst,
  861. PHHANDLETRANS phhtTrans)
  862. {
  863. TWINRESULT tr;
  864. STRINGTABLEDBHEADER stdbh;
  865. DWORD dwcbRead;
  866. ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  867. ASSERT(IS_VALID_HANDLE(hst, STRINGTABLE));
  868. ASSERT(IS_VALID_WRITE_PTR(phhtTrans, HHANDLETRANS));
  869. if (ReadFromCachedFile(hcf, &stdbh, sizeof(stdbh), &dwcbRead) &&
  870. dwcbRead == sizeof(stdbh))
  871. {
  872. LPTSTR pszStringBuf;
  873. // The string header will have the ANSI cb max, whereas inmemory
  874. // we need the cb max based on the current character size
  875. stdbh.dwcbMaxStringLen *= sizeof(TCHAR);
  876. if (AllocateMemory(stdbh.dwcbMaxStringLen, &pszStringBuf))
  877. {
  878. HHANDLETRANS hht;
  879. if (CreateHandleTranslator(stdbh.lcStrings, &hht))
  880. {
  881. LONG lcStrings;
  882. tr = TR_SUCCESS;
  883. TRACE_OUT((TEXT("ReadStringTable(): Reading %ld strings, maximum length %lu."),
  884. stdbh.lcStrings,
  885. stdbh.dwcbMaxStringLen));
  886. for (lcStrings = 0;
  887. lcStrings < stdbh.lcStrings && tr == TR_SUCCESS;
  888. lcStrings++)
  889. tr = ReadString(hcf, hst, hht, pszStringBuf, stdbh.dwcbMaxStringLen);
  890. if (tr == TR_SUCCESS)
  891. {
  892. PrepareForHandleTranslation(hht);
  893. *phhtTrans = hht;
  894. ASSERT(IS_VALID_HANDLE(hst, STRINGTABLE));
  895. ASSERT(IS_VALID_HANDLE(*phhtTrans, HANDLETRANS));
  896. }
  897. else
  898. DestroyHandleTranslator(hht);
  899. }
  900. else
  901. tr = TR_OUT_OF_MEMORY;
  902. FreeMemory(pszStringBuf);
  903. }
  904. else
  905. tr = TR_OUT_OF_MEMORY;
  906. }
  907. else
  908. tr = TR_CORRUPT_BRIEFCASE;
  909. return(tr);
  910. }
  911. #if defined(DEBUG) || defined (VSTF)
  912. /*
  913. ** IsValidHSTRING()
  914. **
  915. **
  916. **
  917. ** Arguments:
  918. **
  919. ** Returns:
  920. **
  921. ** Side Effects: none
  922. */
  923. PUBLIC_CODE BOOL IsValidHSTRING(HSTRING hs)
  924. {
  925. BOOL bResult;
  926. if (IS_VALID_HANDLE((HNODE)hs, NODE))
  927. bResult = IS_VALID_STRUCT_PTR((PSTRING)GetNodeData((HNODE)hs), CSTRING);
  928. else
  929. bResult = FALSE;
  930. return(bResult);
  931. }
  932. /*
  933. ** IsValidHSTRINGTABLE()
  934. **
  935. **
  936. **
  937. ** Arguments:
  938. **
  939. ** Returns:
  940. **
  941. ** Side Effects: none
  942. */
  943. PUBLIC_CODE BOOL IsValidHSTRINGTABLE(HSTRINGTABLE hst)
  944. {
  945. return(IS_VALID_STRUCT_PTR((PSTRINGTABLE)hst, CSTRINGTABLE));
  946. }
  947. #endif
  948. #ifdef DEBUG
  949. /*
  950. ** GetStringCount()
  951. **
  952. **
  953. **
  954. ** Arguments:
  955. **
  956. ** Returns:
  957. **
  958. ** Side Effects: none
  959. */
  960. PUBLIC_CODE ULONG GetStringCount(HSTRINGTABLE hst)
  961. {
  962. ULONG ulcStrings = 0;
  963. HASHBUCKETCOUNT hbc;
  964. ASSERT(IS_VALID_HANDLE(hst, STRINGTABLE));
  965. for (hbc = 0; hbc < ((PCSTRINGTABLE)hst)->hbc; hbc++)
  966. {
  967. HLIST hlistHashBucket;
  968. hlistHashBucket = (((PCSTRINGTABLE)hst)->phlistHashBuckets)[hbc];
  969. if (hlistHashBucket)
  970. {
  971. ASSERT(ulcStrings <= ULONG_MAX - GetNodeCount(hlistHashBucket));
  972. ulcStrings += GetNodeCount(hlistHashBucket);
  973. }
  974. }
  975. return(ulcStrings);
  976. }
  977. #endif