string.c
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 29k
Category:

Windows Kernel

Development Platform:

Visual C++

  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.       
  325.           if (WriteToCachedFile(hcf, (PCVOID)&(pstring->string), (UINT)*pdwcbStringLen, NULL))
  326.              tr = TR_SUCCESS;
  327.  
  328.      #endif
  329.    }
  330.    return(tr);
  331. }
  332. /*
  333. ** ReadString()
  334. **
  335. **
  336. **
  337. ** Arguments:
  338. **
  339. ** Returns:       TWINRESULT
  340. **
  341. ** Side Effects:  none
  342. */
  343. PRIVATE_CODE TWINRESULT ReadString(HCACHEDFILE hcf, HSTRINGTABLE hst,
  344.                                       HHANDLETRANS hht, LPTSTR pszStringBuf,
  345.                                       DWORD dwcbStringBufLen)
  346. {
  347.    TWINRESULT tr;
  348.    DBSTRINGHEADER dbsh;
  349.    DWORD dwcbRead;
  350.    ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  351.    ASSERT(IS_VALID_HANDLE(hst, STRINGTABLE));
  352.    ASSERT(IS_VALID_HANDLE(hht, HANDLETRANS));
  353.    ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszStringBuf, STR, (UINT)dwcbStringBufLen));
  354.    if (ReadFromCachedFile(hcf, &dbsh, sizeof(dbsh), &dwcbRead) &&
  355.        dwcbRead == sizeof(dbsh))
  356.    {
  357.       tr = SlowReadString(hcf, pszStringBuf, dwcbStringBufLen);
  358.       if (tr == TR_SUCCESS)
  359.       {
  360.          HSTRING hsNew;
  361.          if (AddString(pszStringBuf, hst, GetHashBucketIndex, &hsNew))
  362.          {
  363.             /*
  364.              * We must undo the LockString() performed by AddString() to
  365.              * maintain the correct string lock count.  N.b., the lock count of
  366.              * a string may be > 0 even after unlocking since the client may
  367.              * already have added the string to the given string table.
  368.              */
  369.             UnlockString((PSTRING)GetNodeData((HNODE)hsNew));
  370.             if (! AddHandleToHandleTranslator(hht, (HGENERIC)(dbsh.hsOld), (HGENERIC)hsNew))
  371.             {
  372.                DeleteNode((HNODE)hsNew);
  373.                tr = TR_CORRUPT_BRIEFCASE;
  374.             }
  375.          }
  376.          else
  377.             tr = TR_OUT_OF_MEMORY;
  378.       }
  379.    }
  380.    else
  381.       tr = TR_CORRUPT_BRIEFCASE;
  382.    return(tr);
  383. }
  384. /*
  385. ** SlowReadString()
  386. **
  387. **
  388. **
  389. ** Arguments:
  390. **
  391. ** Returns:
  392. **
  393. ** Side Effects:  none
  394. */
  395. PRIVATE_CODE TWINRESULT SlowReadString(HCACHEDFILE hcf, LPTSTR pszStringBuf,
  396.                                           DWORD dwcbStringBufLen)
  397. {
  398.    TWINRESULT tr = TR_CORRUPT_BRIEFCASE;
  399.    LPTSTR pszStringBufEnd;
  400.    DWORD dwcbRead;
  401.    ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  402.    ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszStringBuf, STR, (UINT)dwcbStringBufLen));
  403.    pszStringBufEnd = pszStringBuf + dwcbStringBufLen;
  404.    // The database strings are always written ANSI, so if we are running unicode,
  405.    // we need to convert as we go
  406.    #ifdef UNICODE
  407.    {
  408.         LPSTR pszAnsiEnd;
  409.         LPSTR pszAnsiStart;
  410.         LPSTR pszAnsi = LocalAlloc(LPTR, dwcbStringBufLen);
  411.         pszAnsiStart  = pszAnsi;
  412.         pszAnsiEnd    = pszAnsi + dwcbStringBufLen;
  413.             
  414.         if (NULL == pszAnsi)
  415.         {
  416.             return tr;
  417.         }
  418.         while (pszAnsi < pszAnsiEnd &&
  419.               ReadFromCachedFile(hcf, pszAnsi, sizeof(*pszAnsi), &dwcbRead) &&
  420.               dwcbRead == sizeof(*pszAnsi))
  421.         {
  422.             if (*pszAnsi)
  423.                 pszAnsi++;
  424.             else
  425.             {
  426.                 tr = TR_SUCCESS;
  427.                 break;
  428.             }
  429.         }
  430.        if (tr == TR_SUCCESS)
  431.        {
  432.             MultiByteToWideChar(CP_ACP, 0, pszAnsiStart, -1, pszStringBuf, dwcbStringBufLen / sizeof(TCHAR));
  433.        }
  434.        LocalFree(pszAnsiStart);
  435.     }
  436.     #else
  437.        while (pszStringBuf < pszStringBufEnd &&
  438.               ReadFromCachedFile(hcf, pszStringBuf, sizeof(*pszStringBuf), &dwcbRead) &&
  439.               dwcbRead == sizeof(*pszStringBuf))
  440.        {
  441.           if (*pszStringBuf)
  442.              pszStringBuf++;
  443.           else
  444.           {
  445.              tr = TR_SUCCESS;
  446.              break;
  447.           }
  448.        }
  449.     #endif
  450.    return(tr);
  451. }
  452. #ifdef VSTF
  453. /*
  454. ** IsValidPCNEWSTRINGTABLE()
  455. **
  456. **
  457. **
  458. ** Arguments:
  459. **
  460. ** Returns:
  461. **
  462. ** Side Effects:  none
  463. */
  464. PRIVATE_CODE BOOL IsValidPCNEWSTRINGTABLE(PCNEWSTRINGTABLE pcnst)
  465. {
  466.    BOOL bResult;
  467.    if (IS_VALID_READ_PTR(pcnst, CNEWSTRINGTABLE) &&
  468.        EVAL(pcnst->hbc > 0))
  469.       bResult = TRUE;
  470.    else
  471.       bResult = FALSE;
  472.    return(bResult);
  473. }
  474. /*
  475. ** IsValidPCSTRING()
  476. **
  477. **
  478. **
  479. ** Arguments:
  480. **
  481. ** Returns:
  482. **
  483. ** Side Effects:  none
  484. */
  485. PRIVATE_CODE BOOL IsValidPCSTRING(PCSTRING pcs)
  486. {
  487.    BOOL bResult;
  488.    if (IS_VALID_READ_PTR(pcs, CSTRING) &&
  489.        IS_VALID_STRING_PTR(pcs->string, CSTR))
  490.       bResult = TRUE;
  491.    else
  492.       bResult = FALSE;
  493.    return(bResult);
  494. }
  495. /*
  496. ** IsValidStringWalker()
  497. **
  498. **
  499. **
  500. ** Arguments:
  501. **
  502. ** Returns:
  503. **
  504. ** Side Effects:  none
  505. */
  506. #pragma warning(disable:4100) /* "unreferenced formal parameter" warning */
  507. PRIVATE_CODE BOOL IsValidStringWalker(PVOID pstring, PVOID pvUnused)
  508. {
  509.    ASSERT(! pvUnused);
  510.    return(IS_VALID_STRUCT_PTR(pstring, CSTRING));
  511. }
  512. #pragma warning(default:4100) /* "unreferenced formal parameter" warning */
  513. /*
  514. ** IsValidPCSTRINGTABLE()
  515. **
  516. **
  517. **
  518. ** Arguments:
  519. **
  520. ** Returns:
  521. **
  522. ** Side Effects:  none
  523. */
  524. PRIVATE_CODE BOOL IsValidPCSTRINGTABLE(PCSTRINGTABLE pcst)
  525. {
  526.    BOOL bResult = FALSE;
  527.    if (IS_VALID_READ_PTR(pcst, CSTRINGTABLE) &&
  528.        EVAL(pcst->hbc > 0) &&
  529.        IS_VALID_READ_BUFFER_PTR(pcst->phlistHashBuckets, HLIST, pcst->hbc * sizeof((pcst->phlistHashBuckets)[0])))
  530.    {
  531.       HASHBUCKETCOUNT hbc;
  532.       for (hbc = 0; hbc < pcst->hbc; hbc++)
  533.       {
  534.          HLIST hlistHashBucket;
  535.          hlistHashBucket = (pcst->phlistHashBuckets)[hbc];
  536.          if (hlistHashBucket)
  537.          {
  538.             if (! IS_VALID_HANDLE(hlistHashBucket, LIST) ||
  539.                 ! WalkList(hlistHashBucket, &IsValidStringWalker, NULL))
  540.                break;
  541.          }
  542.       }
  543.       if (hbc == pcst->hbc)
  544.          bResult = TRUE;
  545.    }
  546.    return(bResult);
  547. }
  548. #endif
  549. /****************************** Public Functions *****************************/
  550. /*
  551. ** CreateStringTable()
  552. **
  553. ** Creates a new string table.
  554. **
  555. ** Arguments:     pcnszt - pointer to NEWSTRINGTABLE descibing string table to
  556. **                          be created
  557. **
  558. ** Returns:       Handle to new string table if successful, or NULL if
  559. **                unsuccessful.
  560. **
  561. ** Side Effects:  none
  562. */
  563. PUBLIC_CODE BOOL CreateStringTable(PCNEWSTRINGTABLE pcnszt,
  564.                                      PHSTRINGTABLE phst)
  565. {
  566.    PSTRINGTABLE pst;
  567.    ASSERT(IS_VALID_STRUCT_PTR(pcnszt, CNEWSTRINGTABLE));
  568.    ASSERT(IS_VALID_WRITE_PTR(phst, HSTRINGTABLE));
  569.    /* Try to allocate new string table structure. */
  570.    *phst = NULL;
  571.    if (AllocateMemory(sizeof(*pst), &pst))
  572.    {
  573.       PHLIST phlistHashBuckets;
  574.       /* Try to allocate hash bucket array. */
  575. #ifdef DBLCHECK
  576.       ASSERT((double)(pcnszt->hbc) * (double)(sizeof(*phlistHashBuckets)) <= (double)SIZE_T_MAX);
  577. #endif
  578.       if (AllocateMemory(pcnszt->hbc * sizeof(*phlistHashBuckets), (PVOID *)(&phlistHashBuckets)))
  579.       {
  580.          HASHBUCKETCOUNT bc;
  581.          /* Successs!  Initialize STRINGTABLE fields. */
  582.          pst->phlistHashBuckets = phlistHashBuckets;
  583.          pst->hbc = pcnszt->hbc;
  584.          /* Initialize all hash buckets to NULL. */
  585.          for (bc = 0; bc < pcnszt->hbc; bc++)
  586.             phlistHashBuckets[bc] = NULL;
  587.          *phst = (HSTRINGTABLE)pst;
  588.          ASSERT(IS_VALID_HANDLE(*phst, STRINGTABLE));
  589.       }
  590.       else
  591.          /* Free string table structure. */
  592.          FreeMemory(pst);
  593.    }
  594.    return(*phst != NULL);
  595. }
  596. /*
  597. ** DestroyStringTable()
  598. **
  599. ** Destroys a string table.
  600. **
  601. ** Arguments:     hst - handle to string table to be destroyed
  602. **
  603. ** Returns:       void
  604. **
  605. ** Side Effects:  none
  606. */
  607. PUBLIC_CODE void DestroyStringTable(HSTRINGTABLE hst)
  608. {
  609.    HASHBUCKETCOUNT bc;
  610.    ASSERT(IS_VALID_HANDLE(hst, STRINGTABLE));
  611.    /* Traverse array of hash bucket heads, freeing hash bucket strings. */
  612.    for (bc = 0; bc < ((PSTRINGTABLE)hst)->hbc; bc++)
  613.       FreeHashBucket(((PSTRINGTABLE)hst)->phlistHashBuckets[bc]);
  614.    /* Free array of hash buckets. */
  615.    FreeMemory(((PSTRINGTABLE)hst)->phlistHashBuckets);
  616.    /* Free string table structure. */
  617.    FreeMemory((PSTRINGTABLE)hst);
  618.    return;
  619. }
  620. /*
  621. ** AddString()
  622. **
  623. ** Adds a string to a string table.
  624. **
  625. ** Arguments:     pcsz - pointer to string to be added
  626. **                hst - handle to string table that string is to be added to
  627. **
  628. ** Returns:       Handle to new string if successful, or NULL if unsuccessful.
  629. **
  630. ** Side Effects:  none
  631. */
  632. PUBLIC_CODE BOOL AddString(LPCTSTR pcsz, HSTRINGTABLE hst, 
  633.                            STRINGTABLEHASHFUNC pfnHashFunc, PHSTRING phs)
  634. {
  635.    BOOL bResult;
  636.    HASHBUCKETCOUNT hbcNew;
  637.    BOOL bFound;
  638.    HNODE hnode;
  639.    PHLIST phlistHashBucket;
  640.    ASSERT(IS_VALID_STRING_PTR(pcsz, CSTR));
  641.    ASSERT(IS_VALID_HANDLE(hst, STRINGTABLE));
  642.    ASSERT(IS_VALID_CODE_PTR(pfnHashFunc, STRINGTABLEHASHFUNC));
  643.    ASSERT(IS_VALID_WRITE_PTR(phs, HSTRING));
  644.    /* Find appropriate hash bucket. */
  645.    hbcNew = pfnHashFunc(pcsz, ((PSTRINGTABLE)hst)->hbc);
  646.    ASSERT(hbcNew < ((PSTRINGTABLE)hst)->hbc);
  647.    phlistHashBucket = &(((PSTRINGTABLE)hst)->phlistHashBuckets[hbcNew]);
  648.    if (*phlistHashBucket)
  649.    {
  650.       /* Search the hash bucket for the string. */
  651.       bFound = SearchSortedList(*phlistHashBucket, &StringSearchCmp, pcsz,
  652.                                 &hnode);
  653.       bResult = TRUE;
  654.    }
  655.    else
  656.    {
  657.       NEWLIST nl;
  658.       /* Create a string list for this hash bucket. */
  659.       bFound = FALSE;
  660.       nl.dwFlags = NL_FL_SORTED_ADD;
  661.       bResult = CreateList(&nl, phlistHashBucket);
  662.    }
  663.    /* Do we have a hash bucket for the string? */
  664.    if (bResult)
  665.    {
  666.       /* Yes.  Is the string already in the hash bucket? */
  667.       if (bFound)
  668.       {
  669.          /* Yes. */
  670.          LockString((HSTRING)hnode);
  671.          *phs = (HSTRING)hnode;
  672.       }
  673.       else
  674.       {
  675.          /* No.  Create it. */
  676.          PSTRING pstringNew;
  677.          /* (+ 1) for null terminator. */
  678.          bResult = AllocateMemory(sizeof(*pstringNew) - sizeof(pstringNew->string)
  679.                                   + (lstrlen(pcsz) + 1) * sizeof(TCHAR), &pstringNew);
  680.          if (bResult)
  681.          {
  682.             HNODE hnodeNew;
  683.             /* Set up STRING fields. */
  684.             pstringNew->ulcLock = 1;
  685.             lstrcpy(pstringNew->string, pcsz);
  686.             /* What's up with this string, Doc? */
  687.             bResult = AddNode(*phlistHashBucket, StringSortCmp, pstringNew, &hnodeNew);
  688.             /* Was the new string added to the hash bucket successfully? */
  689.             if (bResult)
  690.                /* Yes. */
  691.                *phs = (HSTRING)hnodeNew;
  692.             else
  693.                /* No. */
  694.                FreeMemory(pstringNew);
  695.          }
  696.       }
  697.    }
  698.    ASSERT(! bResult ||
  699.           IS_VALID_HANDLE(*phs, STRING));
  700.    return(bResult);
  701. }
  702. /*
  703. ** DeleteString()
  704. **
  705. ** Decrements a string's lock count.  If the lock count goes to 0, the string
  706. ** is deleted from its string table.
  707. **
  708. ** Arguments:     hs - handle to the string to be deleted
  709. **
  710. ** Returns:       void
  711. **
  712. ** Side Effects:  none
  713. */
  714. PUBLIC_CODE void DeleteString(HSTRING hs)
  715. {
  716.    PSTRING pstring;
  717.    ASSERT(IS_VALID_HANDLE(hs, STRING));
  718.    pstring = (PSTRING)GetNodeData((HNODE)hs);
  719.    /* Delete string completely? */
  720.    if (! UnlockString(pstring))
  721.    {
  722.       /* Yes.  Remove the string node from the hash bucket's list. */
  723.       DeleteNode((HNODE)hs);
  724.       FreeMemory(pstring);
  725.    }
  726.    return;
  727. }
  728. /*
  729. ** LockString()
  730. **
  731. ** Increments a string's lock count.
  732. **
  733. ** Arguments:     hs - handle to string whose lock count is to be incremented
  734. **
  735. ** Returns:       void
  736. **
  737. ** Side Effects:  none
  738. */
  739. PUBLIC_CODE void LockString(HSTRING hs)
  740. {
  741.    PSTRING pstring;
  742.    ASSERT(IS_VALID_HANDLE(hs, STRING));
  743.    /* Increment lock count. */
  744.    pstring = (PSTRING)GetNodeData((HNODE)hs);
  745.    ASSERT(pstring->ulcLock < ULONG_MAX);
  746.    pstring->ulcLock++;
  747.    return;
  748. }
  749. /*
  750. ** CompareStrings()
  751. **
  752. **
  753. **
  754. ** Arguments:
  755. **
  756. ** Returns:
  757. **
  758. ** Side Effects:  none
  759. */
  760. PUBLIC_CODE COMPARISONRESULT CompareStringsI(HSTRING hs1, HSTRING hs2)
  761. {
  762.    ASSERT(IS_VALID_HANDLE(hs1, STRING));
  763.    ASSERT(IS_VALID_HANDLE(hs2, STRING));
  764.    /* This comparison works across string tables. */
  765.    return(MapIntToComparisonResult(lstrcmpi(((PCSTRING)GetNodeData((HNODE)hs1))->string,
  766.                                             ((PCSTRING)GetNodeData((HNODE)hs2))->string)));
  767. }
  768. /*
  769. ** GetString()
  770. **
  771. ** Retrieves a pointer to a string in a string table.
  772. **
  773. ** Arguments:     hs - handle to the string to be retrieved
  774. **
  775. ** Returns:       Pointer to string.
  776. **
  777. ** Side Effects:  none
  778. */
  779. PUBLIC_CODE LPCTSTR GetString(HSTRING hs)
  780. {
  781.    PSTRING pstring;
  782.    ASSERT(IS_VALID_HANDLE(hs, STRING));
  783.    pstring = (PSTRING)GetNodeData((HNODE)hs);
  784.    return((LPCTSTR)&(pstring->string));
  785. }
  786. /*
  787. ** WriteStringTable()
  788. **
  789. **
  790. **
  791. ** Arguments:
  792. **
  793. ** Returns:       TWINRESULT
  794. **
  795. ** Side Effects:  none
  796. */
  797. PUBLIC_CODE TWINRESULT WriteStringTable(HCACHEDFILE hcf, HSTRINGTABLE hst)
  798. {
  799.    TWINRESULT tr = TR_BRIEFCASE_WRITE_FAILED;
  800.    DWORD dwcbStringTableDBHeaderOffset;
  801.    ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  802.    ASSERT(IS_VALID_HANDLE(hst, STRINGTABLE));
  803.    /* Save initial file poisition. */
  804.    dwcbStringTableDBHeaderOffset = GetCachedFilePointerPosition(hcf);
  805.    if (dwcbStringTableDBHeaderOffset != INVALID_SEEK_POSITION)
  806.    {
  807.       STRINGTABLEDBHEADER stdbh;
  808.       /* Leave space for the string table header. */
  809.       ZeroMemory(&stdbh, sizeof(stdbh));
  810.       if (WriteToCachedFile(hcf, (PCVOID)&stdbh, sizeof(stdbh), NULL))
  811.       {
  812.          HASHBUCKETCOUNT hbc;
  813.          /* Save strings in each hash bucket. */
  814.          stdbh.dwcbMaxStringLen = 0;
  815.          stdbh.lcStrings = 0;
  816.          tr = TR_SUCCESS;
  817.          for (hbc = 0; hbc < ((PSTRINGTABLE)hst)->hbc; hbc++)
  818.          {
  819.             LONG lcStringsInHashBucket;
  820.             DWORD dwcbStringLen;
  821.             tr = WriteHashBucket(hcf,
  822.                               (((PSTRINGTABLE)hst)->phlistHashBuckets)[hbc],
  823.                               &lcStringsInHashBucket, &dwcbStringLen);
  824.             if (tr == TR_SUCCESS)
  825.             {
  826.                /* Watch out for overflow. */
  827.                ASSERT(stdbh.lcStrings <= LONG_MAX - lcStringsInHashBucket);
  828.                stdbh.lcStrings += lcStringsInHashBucket;
  829.                if (dwcbStringLen > stdbh.dwcbMaxStringLen)
  830.                   stdbh.dwcbMaxStringLen = dwcbStringLen;
  831.             }
  832.             else
  833.                break;
  834.          }
  835.          if (tr == TR_SUCCESS)
  836.          {
  837.             /* Save string table header. */
  838.             // The on-disk dwCBMaxString len always refers to ANSI chars,
  839.             // whereas in memory it is for the TCHAR type, we adjust it
  840.             // around the save
  841.             stdbh.dwcbMaxStringLen /= sizeof(TCHAR);
  842.             tr = WriteDBSegmentHeader(hcf, dwcbStringTableDBHeaderOffset,
  843.                                       &stdbh, sizeof(stdbh));
  844.             
  845.             stdbh.dwcbMaxStringLen *= sizeof(TCHAR);
  846.             TRACE_OUT((TEXT("WriteStringTable(): Wrote %ld strings."),
  847.                        stdbh.lcStrings));
  848.          }
  849.       }
  850.    }
  851.    return(tr);
  852. }
  853. /*
  854. ** ReadStringTable()
  855. **
  856. **
  857. **
  858. ** Arguments:
  859. **
  860. ** Returns:       TWINRESULT
  861. **
  862. ** Side Effects:  none
  863. */
  864. PUBLIC_CODE TWINRESULT ReadStringTable(HCACHEDFILE hcf, HSTRINGTABLE hst,
  865.                                          PHHANDLETRANS phhtTrans)
  866. {
  867.    TWINRESULT tr;
  868.    STRINGTABLEDBHEADER stdbh;
  869.    DWORD dwcbRead;
  870.    ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
  871.    ASSERT(IS_VALID_HANDLE(hst, STRINGTABLE));
  872.    ASSERT(IS_VALID_WRITE_PTR(phhtTrans, HHANDLETRANS));
  873.    if (ReadFromCachedFile(hcf, &stdbh, sizeof(stdbh), &dwcbRead) &&
  874.        dwcbRead == sizeof(stdbh))
  875.    {
  876.       LPTSTR pszStringBuf;
  877.       // The string header will have the ANSI cb max, whereas inmemory
  878.       // we need the cb max based on the current character size
  879.       stdbh.dwcbMaxStringLen *= sizeof(TCHAR);
  880.       if (AllocateMemory(stdbh.dwcbMaxStringLen, &pszStringBuf))
  881.       {
  882.          HHANDLETRANS hht;
  883.          if (CreateHandleTranslator(stdbh.lcStrings, &hht))
  884.          {
  885.             LONG lcStrings;
  886.             tr = TR_SUCCESS;
  887.             TRACE_OUT((TEXT("ReadStringTable(): Reading %ld strings, maximum length %lu."),
  888.                        stdbh.lcStrings,
  889.                        stdbh.dwcbMaxStringLen));
  890.             for (lcStrings = 0;
  891.                  lcStrings < stdbh.lcStrings && tr == TR_SUCCESS;
  892.                  lcStrings++)
  893.                tr = ReadString(hcf, hst, hht, pszStringBuf, stdbh.dwcbMaxStringLen);
  894.             if (tr == TR_SUCCESS)
  895.             {
  896.                PrepareForHandleTranslation(hht);
  897.                *phhtTrans = hht;
  898.                ASSERT(IS_VALID_HANDLE(hst, STRINGTABLE));
  899.                ASSERT(IS_VALID_HANDLE(*phhtTrans, HANDLETRANS));
  900.             }
  901.             else
  902.                DestroyHandleTranslator(hht);
  903.          }
  904.          else
  905.             tr = TR_OUT_OF_MEMORY;
  906.          FreeMemory(pszStringBuf);
  907.       }
  908.       else
  909.          tr = TR_OUT_OF_MEMORY;
  910.    }
  911.    else
  912.       tr = TR_CORRUPT_BRIEFCASE;
  913.    return(tr);
  914. }
  915. #if defined(DEBUG) || defined (VSTF)
  916. /*
  917. ** IsValidHSTRING()
  918. **
  919. **
  920. **
  921. ** Arguments:
  922. **
  923. ** Returns:
  924. **
  925. ** Side Effects:  none
  926. */
  927. PUBLIC_CODE BOOL IsValidHSTRING(HSTRING hs)
  928. {
  929.    BOOL bResult;
  930.    if (IS_VALID_HANDLE((HNODE)hs, NODE))
  931.       bResult = IS_VALID_STRUCT_PTR((PSTRING)GetNodeData((HNODE)hs), CSTRING);
  932.    else
  933.       bResult = FALSE;
  934.    return(bResult);
  935. }
  936. /*
  937. ** IsValidHSTRINGTABLE()
  938. **
  939. **
  940. **
  941. ** Arguments:
  942. **
  943. ** Returns:
  944. **
  945. ** Side Effects:  none
  946. */
  947. PUBLIC_CODE BOOL IsValidHSTRINGTABLE(HSTRINGTABLE hst)
  948. {
  949.    return(IS_VALID_STRUCT_PTR((PSTRINGTABLE)hst, CSTRINGTABLE));
  950. }
  951. #endif
  952. #ifdef DEBUG
  953. /*
  954. ** GetStringCount()
  955. **
  956. **
  957. **
  958. ** Arguments:
  959. **
  960. ** Returns:
  961. **
  962. ** Side Effects:  none
  963. */
  964. PUBLIC_CODE ULONG GetStringCount(HSTRINGTABLE hst)
  965. {
  966.    ULONG ulcStrings = 0;
  967.    HASHBUCKETCOUNT hbc;
  968.    ASSERT(IS_VALID_HANDLE(hst, STRINGTABLE));
  969.    for (hbc = 0; hbc < ((PCSTRINGTABLE)hst)->hbc; hbc++)
  970.    {
  971.       HLIST hlistHashBucket;
  972.       hlistHashBucket = (((PCSTRINGTABLE)hst)->phlistHashBuckets)[hbc];
  973.       if (hlistHashBucket)
  974.       {
  975.          ASSERT(ulcStrings <= ULONG_MAX - GetNodeCount(hlistHashBucket));
  976.          ulcStrings += GetNodeCount(hlistHashBucket);
  977.       }
  978.    }
  979.    return(ulcStrings);
  980. }
  981. #endif