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.

902 lines
26 KiB

  1. #include "private.h"
  2. #include "trie.h"
  3. #include "memmgr.h"
  4. #define fNLGNewMemory(pp,cb) ((*pp) = (TRIECTRL*)ExternAlloc(cb))
  5. #define NLGFreeMemory ExternFree
  6. /******************************Public*Routine******************************\
  7. * TrieInit
  8. *
  9. * Given a pointer to a resource or mapped file of a mapped file this
  10. * function allocates and initializes the trie structure.
  11. *
  12. * Returns NULL for failure, trie control structure pointer for success.
  13. *
  14. * History:
  15. * 16-Jun-1997 -by- Patrick Haluptzok patrickh
  16. * Wrote it.
  17. \**************************************************************************/
  18. TRIECTRL * WINAPI TrieInit(LPBYTE lpByte)
  19. {
  20. LPWORD lpwTables;
  21. TRIECTRL *lpTrieCtrl;
  22. LPTRIESTATS lpTrieStats;
  23. lpTrieStats = (LPTRIESTATS) lpByte;
  24. if (lpTrieStats == NULL)
  25. return(NULL);
  26. // Check the version number. This code currently only supports version 1 tries
  27. if (lpTrieStats->version > 1)
  28. return NULL;
  29. //
  30. // Allocate space for the control structure and the table of SR offsets
  31. //
  32. if (!fNLGNewMemory(&lpTrieCtrl, sizeof(TRIECTRL)))
  33. return NULL;
  34. //
  35. // Allocate space for the complete header, copy the fixed part and read in the rest
  36. //
  37. lpByte += lpTrieStats->cbHeader;
  38. lpTrieCtrl->lpTrieStats = lpTrieStats;
  39. //
  40. // Set up the table pointers (all these tables are inside the TRIECTRL allocation)
  41. //
  42. lpwTables = (LPWORD)(lpTrieStats+1);
  43. lpTrieCtrl->lpwCharFlagsCodes = lpwTables;
  44. lpwTables += lpTrieStats->cCharFlagsCodesMax;
  45. if (lpTrieStats->cCharFlagsCodesMax & 1) // Deal with possible data mis-alignment
  46. lpwTables++;
  47. lpTrieCtrl->lpwTagsCodes = lpwTables;
  48. lpwTables += lpTrieStats->cTagsCodesMax;
  49. if (lpTrieStats->cTagsCodesMax & 1) // Deal with possible data mis-alignment
  50. lpwTables++;
  51. lpTrieCtrl->lpwMRPointersCodes = lpwTables;
  52. lpwTables += lpTrieStats->cMRPointersCodesMax;
  53. if (lpTrieStats->cMRPointersCodesMax & 1) // Deal with possible data mis-alignment
  54. lpwTables++;
  55. lpTrieCtrl->lpwSROffsetsCodes = lpwTables;
  56. lpwTables += lpTrieStats->cSROffsetsCodesMax;
  57. if (lpTrieStats->cSROffsetsCodesMax & 1) // Deal with possible data mis-alignment
  58. lpwTables++;
  59. lpTrieCtrl->lpCharFlags = (LPCHARFLAGS)lpwTables;
  60. lpwTables = (LPWORD)(lpTrieCtrl->lpCharFlags + lpTrieStats->cUniqueCharFlags);
  61. lpTrieCtrl->lpwTags = (DWORD *)lpwTables;
  62. lpwTables += (2 * lpTrieStats->cUniqueTags);
  63. lpTrieCtrl->lpwMRPointers = (DWORD *) lpwTables;
  64. lpwTables += (2 * lpTrieStats->cUniqueMRPointers);
  65. lpTrieCtrl->lpwSROffsets = (DWORD *) lpwTables;
  66. lpwTables += (2 * lpTrieStats->cUniqueSROffsets);
  67. //
  68. // These tables should exactly fill the allocation
  69. //
  70. if ((LPBYTE)lpwTables - (LPBYTE)lpTrieStats != (int)lpTrieStats->cbHeader)
  71. {
  72. TrieFree(lpTrieCtrl);
  73. return NULL;
  74. }
  75. //
  76. // Init trie pointers
  77. //
  78. lpTrieCtrl->lpbTrie = (LPBYTE)lpByte;
  79. return lpTrieCtrl;
  80. }
  81. /******************************Public*Routine******************************\
  82. * TrieFree
  83. *
  84. * Free the resources allocated for the control structure.
  85. *
  86. * History:
  87. * 16-Jun-1997 -by- Patrick Haluptzok patrickh
  88. * Wrote it.
  89. \**************************************************************************/
  90. void WINAPI TrieFree(LPTRIECTRL lpTrieCtrl)
  91. {
  92. //
  93. // Finally free the control structure and all the tables. STILL MUST FREE THIS FOR ROM
  94. //
  95. NLGFreeMemory(lpTrieCtrl);
  96. }
  97. /* Deompress a single symbol using base-256 huffman from a compressed data structure. piSymbol
  98. points to a space to hold the decompressed value, which is an index to a frequency-ordered
  99. table of symbols (0 is most frequent). pcCodes is a table of code lengths returned from
  100. HuffmanComputeTable. pbData is a pointer to memory that contains the encoded data. The
  101. return value is the number of bytes decoded. */
  102. int DecompressSymbol(WORD *piSymbol, WORD *pcCodes, unsigned char *pbData)
  103. {
  104. int cBytes = 0;
  105. WORD wCode = 0, wiSymbol = 0;
  106. /* At each stage in this loop, we're trying to see if we've got a length-n code.
  107. dwCode is which length-n code it would have to be. If there aren't that many length-n codes,
  108. we have to try n+1. To do that, we subtract the number of length-n codes and shift in
  109. the next byte. dwiSymbol is the symbol number of the first length-n code. */
  110. while (1)
  111. {
  112. wCode += *pbData++;
  113. ++cBytes;
  114. if (wCode < *pcCodes)
  115. {
  116. break;
  117. }
  118. wiSymbol += *pcCodes;
  119. wCode -= *pcCodes++;
  120. wCode <<= 8;
  121. }
  122. /* Now that dwCode is a valid number of a length-cBytes code, we can just add it to
  123. dwiSymbol, because we've already added the counts of the shorter codes to it. */
  124. wiSymbol += wCode;
  125. *piSymbol = wiSymbol;
  126. return cBytes;
  127. }
  128. DWORD Get3ByteAddress(BYTE *pb)
  129. {
  130. return ((((pb[0] << 8) | pb[1]) << 8) | pb[2]) & 0x00ffffff;
  131. }
  132. void WINAPI TrieDecompressNode(LPTRIECTRL lpTrieCtrl, LPTRIESCAN lpTrieScan)
  133. {
  134. TRIESTATS *lpTrieStats;
  135. DWORD wOffset;
  136. DWORD wOffset2;
  137. WORD wCode;
  138. DWORD dwCode;
  139. BYTE wMask;
  140. BYTE bMask;
  141. int iTag;
  142. lpTrieStats = lpTrieCtrl->lpTrieStats;
  143. /* If this is an initial call, use the first byte in the trie */
  144. if (lpTrieScan->wFlags == 0)
  145. {
  146. lpTrieScan->lpbSRDown = 0;
  147. lpTrieScan->lpbNode = lpTrieCtrl->lpbTrie;
  148. }
  149. /* Decompress the char/flags */
  150. lpTrieScan->lpbNode += DecompressSymbol(&wCode, lpTrieCtrl->lpwCharFlagsCodes, lpTrieScan->lpbNode);
  151. lpTrieScan->wch = lpTrieCtrl->lpCharFlags[wCode].wch;
  152. lpTrieScan->wFlags = lpTrieCtrl->lpCharFlags[wCode].wFlags;
  153. // Decompress skip enumeration
  154. if (lpTrieScan->wFlags & TRIE_NODE_SKIP_COUNT)
  155. {
  156. // Values greater than 127 are really 15 or 21 bit values.
  157. dwCode = (DWORD) *lpTrieScan->lpbNode++;
  158. if (dwCode >= 0x00c0)
  159. {
  160. dwCode = ((dwCode & 0x003f) << 15);
  161. dwCode |= ((((DWORD) *lpTrieScan->lpbNode++) & 0x007f) << 8);
  162. dwCode |= (((DWORD) *lpTrieScan->lpbNode++) & 0x00ff);
  163. }
  164. else if (dwCode >= 0x0080)
  165. dwCode = ((dwCode & 0x007f) << 8) | (((DWORD) *lpTrieScan->lpbNode++) & 0x00ff);
  166. lpTrieScan->cSkipWords = dwCode;
  167. }
  168. /* Code to decompress enumeration goes here */
  169. if (lpTrieScan->wFlags & TRIE_NODE_COUNT)
  170. {
  171. // Values greater than 127 are really 15 or 21 bit values.
  172. dwCode = (DWORD) *lpTrieScan->lpbNode++;
  173. if (dwCode >= 0x00c0)
  174. {
  175. dwCode = ((dwCode & 0x003f) << 15);
  176. dwCode |= ((((DWORD) *lpTrieScan->lpbNode++) & 0x007f) << 8);
  177. dwCode |= (((DWORD) *lpTrieScan->lpbNode++) & 0x00ff);
  178. }
  179. else if (dwCode >= 0x0080)
  180. dwCode = ((dwCode & 0x007f) << 8) | (((DWORD) *lpTrieScan->lpbNode++) & 0x00ff);
  181. lpTrieScan->cWords = dwCode;
  182. // Decompress the tagged enumeration counts
  183. wMask = 1;
  184. for (iTag = 0; iTag < MAXTAGS; iTag++)
  185. {
  186. if (lpTrieCtrl->lpTrieStats->wEnumMask & wMask)
  187. {
  188. // Values greater than 127 are really 15 or 21 bit values.
  189. dwCode = (DWORD) *lpTrieScan->lpbNode++;
  190. if (dwCode >= 0x00c0)
  191. {
  192. dwCode = ((dwCode & 0x003f) << 15);
  193. dwCode |= ((((DWORD) *lpTrieScan->lpbNode++) & 0x007f) << 8);
  194. dwCode |= (((DWORD) *lpTrieScan->lpbNode++) & 0x00ff);
  195. }
  196. else if (dwCode >= 0x0080)
  197. dwCode = ((dwCode & 0x007f) << 8) | (((DWORD) *lpTrieScan->lpbNode++) & 0x00ff);
  198. lpTrieScan->aTags[iTag].cTag = dwCode;
  199. }
  200. else
  201. lpTrieScan->aTags[iTag].cTag = 0;
  202. wMask <<= 1;
  203. }
  204. }
  205. else
  206. lpTrieScan->cWords = 0;
  207. // Any tagged data for this node follows the counts
  208. lpTrieScan->wMask = 0;
  209. if (lpTrieScan->wFlags & TRIE_NODE_TAGGED)
  210. {
  211. // If there is only one tagged field, the mask byte won't be stored
  212. if (lpTrieCtrl->lpTrieStats->cTagFields == 1)
  213. bMask = lpTrieCtrl->lpTrieStats->wDataMask;
  214. else
  215. bMask = *lpTrieScan->lpbNode++;
  216. // Now that we know which elements are stored here, pull them in their proper place
  217. wMask = 1;
  218. for (iTag = 0; bMask && (iTag < MAXTAGS); iTag++)
  219. {
  220. if (lpTrieCtrl->lpTrieStats->wDataMask & bMask & wMask)
  221. {
  222. lpTrieScan->lpbNode += DecompressSymbol(&wCode, lpTrieCtrl->lpwTagsCodes, lpTrieScan->lpbNode);
  223. lpTrieScan->aTags[iTag].dwData = lpTrieCtrl->lpwTags[wCode];
  224. lpTrieScan->wMask |= wMask;
  225. }
  226. bMask &= ~wMask;
  227. wMask <<= 1;
  228. }
  229. }
  230. // There are two flavors of right pointers: Multiref and Skip.
  231. if (lpTrieScan->wFlags & TRIE_NODE_RIGHT)
  232. {
  233. if (lpTrieScan->wFlags & TRIE_NODE_SKIP)
  234. {
  235. lpTrieScan->lpbNode += DecompressSymbol(&wCode,lpTrieCtrl->lpwSROffsetsCodes,lpTrieScan->lpbNode);
  236. wOffset2 = lpTrieCtrl->lpwSROffsets[wCode]; // Only add this after entire node is decompressed
  237. }
  238. else
  239. {
  240. /* Multiref: The down pointer is encoded directly */
  241. lpTrieScan->lpbNode += DecompressSymbol(&wCode, lpTrieCtrl->lpwMRPointersCodes, lpTrieScan->lpbNode);
  242. lpTrieScan->lpbRight = lpTrieCtrl->lpbTrie + lpTrieCtrl->lpwMRPointers[wCode];
  243. }
  244. }
  245. else
  246. lpTrieScan->lpbRight = NULL;
  247. // There are 4 kinds of down pointer: Absolute, Inline, Multiref, and Singleref Offset.
  248. // Each requires different decompression
  249. if (lpTrieScan->wFlags & TRIE_DOWN_ABS)
  250. {
  251. // Immediate. The next 3 bytes are the absolute offset from the base of the trie.
  252. lpTrieScan->lpbDown = lpTrieCtrl->lpbTrie + Get3ByteAddress(lpTrieScan->lpbNode);
  253. lpTrieScan->lpbNode += 3;
  254. }
  255. else if (lpTrieScan->wFlags & TRIE_DOWN_INLINE)
  256. {
  257. /* Inline: The down pointer points to the next sequential byte (so it isn't stored) */
  258. lpTrieScan->lpbSRDown = lpTrieScan->lpbDown = lpTrieScan->lpbNode;
  259. }
  260. else if (lpTrieScan->wFlags & TRIE_DOWN_MULTI)
  261. {
  262. /* Multiref: The down pointer is encoded directly */
  263. lpTrieScan->lpbNode += DecompressSymbol(&wCode,lpTrieCtrl->lpwMRPointersCodes,
  264. lpTrieScan->lpbNode);
  265. lpTrieScan->lpbDown = lpTrieCtrl->lpbTrie + lpTrieCtrl->lpwMRPointers[wCode];
  266. }
  267. else if (lpTrieScan->wFlags & TRIE_NODE_DOWN)
  268. {
  269. /* SR Offset. The down pointer is encoded as an offset from the LAST downpointer
  270. into this singleref segment. So we have to keep the old one around so we can add to it */
  271. lpTrieScan->lpbNode += DecompressSymbol(&wCode,lpTrieCtrl->lpwSROffsetsCodes,
  272. lpTrieScan->lpbNode);
  273. if (lpTrieScan->lpbSRDown == 0)
  274. {
  275. lpTrieScan->lpbSRDown = lpTrieScan->lpbNode; // We offset from the end of the first node when going into a new state.
  276. }
  277. wOffset = lpTrieCtrl->lpwSROffsets[wCode];
  278. lpTrieScan->lpbSRDown += wOffset;
  279. lpTrieScan->lpbDown = lpTrieScan->lpbSRDown;
  280. }
  281. else
  282. lpTrieScan->lpbDown = NULL;
  283. // We couldn't deal with this until now, since skip pointers are always delta encoded from the end of node
  284. if ((lpTrieScan->wFlags & (TRIE_NODE_RIGHT | TRIE_NODE_SKIP)) == (TRIE_NODE_RIGHT | TRIE_NODE_SKIP))
  285. lpTrieScan->lpbRight = lpTrieScan->lpbNode + wOffset2;
  286. } // TrieDecompressNode
  287. /* Given a compressed trie and a pointer to a decompresed node from it, find and decompress
  288. the next node in the same state. lpTrieScan is a user-allocated structure that holds the
  289. decompressed node and into which the new node is copied.
  290. This is equivalent to traversing a right pointer or finding the next alternative
  291. letter at the same position. If there is no next node (i.e.this is the end of the state)
  292. then TrieGetNextNode returns FALSE. To scan from the beginning of the trie, set the lpTrieScan
  293. structure to zero */
  294. BOOL WINAPI TrieGetNextNode(LPTRIECTRL lpTrieCtrl, LPTRIESCAN lpTrieScan)
  295. {
  296. // Are we at EOS?
  297. if (lpTrieScan->wFlags & TRIE_NODE_END)
  298. {
  299. // Is this is a hard EOS?
  300. if (!(lpTrieScan->wFlags & TRIE_NODE_SKIP))
  301. {
  302. // If we can follow a right pointer, do so, else fail
  303. if (lpTrieScan->wFlags & TRIE_NODE_RIGHT)
  304. lpTrieScan->lpbNode = lpTrieScan->lpbRight;
  305. else
  306. return FALSE;
  307. }
  308. // Either we're at a soft EOS or we've followed a right pointer.
  309. // Both these require us to reset the SRDown for proper decompression
  310. lpTrieScan->lpbSRDown = 0;
  311. }
  312. // Decompress the node at return success
  313. TrieDecompressNode(lpTrieCtrl, lpTrieScan);
  314. return TRUE;
  315. }
  316. BOOL WINAPI TrieSkipNextNode(LPTRIECTRL lpTrieCtrl, LPTRIESCAN lpTrieScan, WCHAR wch)
  317. {
  318. // If this is the last node in the normal or skip state, quit here
  319. if (lpTrieScan->wFlags & TRIE_NODE_END)
  320. return FALSE;
  321. // If there isn't a right pointer or if the target letter is alphabetically less then
  322. // the current letter scan right normally. Otherwise, follow the skip pointer.
  323. if (!(lpTrieScan->wFlags & TRIE_NODE_RIGHT) || (wch < lpTrieScan->wch))
  324. return TrieGetNextNode(lpTrieCtrl, lpTrieScan);
  325. lpTrieScan->lpbSRDown = 0;
  326. lpTrieScan->lpbNode = lpTrieScan->lpbRight;
  327. TrieDecompressNode(lpTrieCtrl, lpTrieScan);
  328. return TRUE;
  329. }
  330. /* Follow the down pointer to the next state. This is equivalent to accepting the character
  331. in this node and advancing to the next character position. Returns FALSE if there is no
  332. down pointer. This also decompresses the first node in the state, so all the values in
  333. lpTrieScan will be good. */
  334. BOOL WINAPI TrieGetNextState(LPTRIECTRL lpTrieCtrl, LPTRIESCAN lpTrieScan)
  335. {
  336. /* Flags can't normally be zero; that always means "top node" */
  337. if (lpTrieScan->wFlags == 0)
  338. {
  339. TrieDecompressNode(lpTrieCtrl, lpTrieScan);
  340. return TRUE;
  341. }
  342. if (!(lpTrieScan->wFlags & TRIE_NODE_DOWN))
  343. return FALSE;
  344. lpTrieScan->lpbSRDown = 0;
  345. lpTrieScan->lpbNode = lpTrieScan->lpbDown;
  346. TrieDecompressNode(lpTrieCtrl, lpTrieScan);
  347. return TRUE;
  348. } // TrieGetNextState
  349. /* Check the validity of a word or prefix. Starts from the root of pTrie looking for
  350. pwszWord. If it finds it, it returns TRUE and the user-provided lpTrieScan structure
  351. contains the final node in the word. If there is no path, TrieCheckWord returns FALSE
  352. To distinguish a valid word from a valid prefix, caller must test
  353. wFlags for TRIE_NODE_VALID. */
  354. BOOL WINAPI TrieCheckWord(LPTRIECTRL lpTrieCtrl, LPTRIESCAN lpTrieScan, wchar_t far* lpwszWord)
  355. {
  356. // validate params
  357. if (lpTrieCtrl == NULL || lpTrieScan == NULL || lpwszWord == NULL)
  358. return FALSE;
  359. /* Start at the root of the trie and loop through all the letters in the word */
  360. memset(lpTrieScan,0,sizeof(*lpTrieScan));
  361. while (*lpwszWord)
  362. {
  363. /* Each new letter means we need to go to a new state. If there is none,
  364. the word is not in this trie */
  365. if (!TrieGetNextState(lpTrieCtrl, lpTrieScan))
  366. return FALSE;
  367. /* Now we walk across the state looking for this character. If we don't find
  368. it, this word is not in this trie */
  369. while (lpTrieScan->wch != *lpwszWord)
  370. {
  371. if (!TrieSkipNextNode(lpTrieCtrl, lpTrieScan, *lpwszWord))
  372. return FALSE;
  373. }
  374. ++lpwszWord;
  375. }
  376. return TRUE;
  377. } // TrieCheckWord
  378. // Find the index to the word in the trie.
  379. DWORD CountWords(TRIECTRL *ptc, TRIESCAN *pts)
  380. {
  381. TRIESCAN ts = *pts;
  382. DWORD cWords = 0;
  383. if (!TrieGetNextState(ptc, &ts))
  384. return cWords;
  385. do
  386. {
  387. if (ts.wFlags & TRIE_NODE_VALID)
  388. cWords++;
  389. cWords += CountWords(ptc, &ts);
  390. } while (TrieGetNextNode(ptc, &ts));
  391. return cWords;
  392. }
  393. int WINAPI TrieWordToIndex(TRIECTRL *ptc, wchar_t *pwszWord)
  394. {
  395. TRIESCAN ts;
  396. int ich = 0;
  397. int index = 0;
  398. BOOL bValid;
  399. memset(&ts, 0, sizeof(TRIESCAN));
  400. if (!TrieGetNextState(ptc, &ts))
  401. return FALSE;
  402. do
  403. {
  404. bValid = ts.wFlags & TRIE_NODE_VALID;
  405. // Scan to the right until we find a matching character. !!!WARNING!!! The state may not be alphabetized.
  406. // If the character doesn't match, add the subtree count to the enumeration total and slide to the right.
  407. if (ts.wch == pwszWord[ich])
  408. {
  409. ich++;
  410. // If we reached the end of word at a valid state, return the index
  411. if ((pwszWord[ich] == L'\0') && ts.wFlags & TRIE_NODE_VALID)
  412. return index;
  413. // Try going down a level
  414. if (!TrieGetNextState(ptc, &ts))
  415. return -1;
  416. }
  417. else
  418. {
  419. // Now, follow the skip pointer if exist and the alphabetic character is greater then
  420. // the pivot point. Otherwise, goto the next node. Add the sub tree count. If it's cached
  421. // use it, otherwise compute it recursively.
  422. if ((ts.wFlags & TRIE_NODE_SKIP_COUNT) && (pwszWord[ich] > ts.wch))
  423. {
  424. index += ts.cSkipWords;
  425. // This can't fail if TRIE_NODE_SKIP_COUNT is set
  426. TrieSkipNextNode(ptc, &ts, pwszWord[ich]);
  427. }
  428. else
  429. {
  430. index += (ts.wFlags & TRIE_NODE_COUNT) ? ts.cWords : CountWords(ptc, &ts);
  431. if (!TrieGetNextNode(ptc, &ts))
  432. return -1;
  433. }
  434. }
  435. // If the node we just visited was valid, increment the index
  436. if (bValid)
  437. index++;
  438. } while (TRUE);
  439. }
  440. // Given an index into the trie, return the word.
  441. BOOL WINAPI TrieIndexToWord(TRIECTRL *ptc, DWORD nIndex, wchar_t *pwszWord, int cwc)
  442. {
  443. TRIESCAN ts;
  444. int ich = 0;
  445. DWORD cWords;
  446. DWORD cSkips;
  447. memset(&ts, 0, sizeof(TRIESCAN));
  448. if (!TrieGetNextState(ptc, &ts))
  449. return FALSE;
  450. do
  451. {
  452. // If we're at the end of the buffer, fail
  453. if (ich + 1 >= cwc)
  454. return FALSE;
  455. // Remember this node's character
  456. pwszWord[ich] = ts.wch;
  457. // If we're on a valid word AND we've reached the index we're looking for, exit the loop
  458. if (ts.wFlags & TRIE_NODE_VALID)
  459. {
  460. if (!nIndex)
  461. break;
  462. nIndex--;
  463. }
  464. // Get the count of words in this subtree.
  465. cWords = (ts.wFlags & TRIE_NODE_COUNT) ? ts.cWords : CountWords(ptc, &ts);
  466. cSkips = (ts.wFlags & TRIE_NODE_SKIP_COUNT) ? ts.cSkipWords : 0x7fffffff;
  467. // Scan to the right until the word count of the subtree would be greater than or equal to the index
  468. // we're looking for. Descend that trie and repeat. !!!WARNING!!! The state may not be alphabetized.
  469. // If we can use a skip count, do so.
  470. if (nIndex < cWords)
  471. {
  472. if (!TrieGetNextState(ptc, &ts))
  473. return FALSE;
  474. ich++; // Advance the character position
  475. }
  476. else
  477. {
  478. if (nIndex >= cSkips)
  479. {
  480. nIndex -= cSkips;
  481. ts.lpbSRDown = 0;
  482. ts.lpbNode = ts.lpbRight;
  483. TrieDecompressNode(ptc, &ts);
  484. }
  485. else
  486. {
  487. nIndex -= cWords;
  488. if (!TrieGetNextNode(ptc, &ts))
  489. return FALSE;
  490. }
  491. }
  492. } while (TRUE);
  493. pwszWord[++ich] = L'\0'; // Null terminate the string
  494. return ts.wFlags & TRIE_NODE_VALID; // Return validity
  495. }
  496. int WINAPI TriePrefixToRange(TRIECTRL *ptc, wchar_t *pwszWord, int *piStart)
  497. {
  498. TRIESCAN ts;
  499. int ich = 0;
  500. int cnt;
  501. BOOL bValid;
  502. memset(&ts, 0, sizeof(TRIESCAN));
  503. *piStart = 0;
  504. if (!TrieGetNextState(ptc, &ts))
  505. return 0;
  506. // Deal with special case of empty string
  507. if (pwszWord && !*pwszWord)
  508. return ptc->lpTrieStats->cWords;
  509. do
  510. {
  511. // Get the count of words below this prefix
  512. cnt = (ts.wFlags & TRIE_NODE_COUNT) ? ts.cWords : CountWords(ptc, &ts);
  513. // If the node we just arrived at is valid, increment the count
  514. bValid = ts.wFlags & TRIE_NODE_VALID;
  515. // Scan to the right until we find a matching character. !!!WARNING!!! The state may not be alphabetized.
  516. // If the character doesn't match, add the subtree count to the enumeration total and slide to the right.
  517. if (ts.wch == pwszWord[ich])
  518. {
  519. ich++;
  520. // If we reached the end of prefix, return the count remaining below
  521. if (pwszWord[ich] == L'\0')
  522. {
  523. if (bValid)
  524. cnt++;
  525. return cnt;
  526. }
  527. // Try going down a level
  528. if (!TrieGetNextState(ptc, &ts))
  529. return 0;
  530. }
  531. else
  532. {
  533. // Add the sub tree count.
  534. *piStart += cnt;
  535. // Try the next letter in this state
  536. if (!TrieGetNextNode(ptc, &ts))
  537. return 0;
  538. }
  539. if (bValid)
  540. (*piStart)++;
  541. } while (TRUE);
  542. }
  543. // TAGS
  544. // Find the index to the word in the trie.
  545. DWORD CountTags(TRIECTRL *ptc, TRIESCAN *pts, DWORD wMask, int iTag)
  546. {
  547. TRIESCAN ts = *pts;
  548. DWORD cTags = 0;
  549. if (!TrieGetNextState(ptc, &ts))
  550. return cTags;
  551. do
  552. {
  553. if (ts.wFlags & wMask)
  554. cTags++;
  555. cTags += CountTags(ptc, &ts, wMask, iTag);
  556. } while (TrieGetNextNode(ptc, &ts));
  557. return cTags;
  558. }
  559. int WINAPI TrieWordToTagIndex(TRIECTRL *ptc, wchar_t *pwszWord, int iTag)
  560. {
  561. TRIESCAN ts;
  562. int ich = 0;
  563. int index = 0;
  564. BOOL bValid;
  565. DWORD wMask = 1 << iTag;
  566. memset(&ts, 0, sizeof(TRIESCAN));
  567. if (!TrieGetNextState(ptc, &ts))
  568. return FALSE;
  569. do
  570. {
  571. bValid = ts.wFlags & wMask;
  572. // Scan to the right until we find a matching character. !!!WARNING!!! The state may not be alphabetized.
  573. // If the character doesn't match, add the subtree count to the enumeration total and slide to the right.
  574. if (ts.wch == pwszWord[ich])
  575. {
  576. ich++;
  577. // If we reached the end of word at a valid state, return the index
  578. if ((pwszWord[ich] == L'\0') && ts.wFlags & wMask)
  579. return index;
  580. // Try going down a level
  581. if (!TrieGetNextState(ptc, &ts))
  582. return -1;
  583. }
  584. else
  585. {
  586. // Add the sub tree count. If it's cached use it, otherwise compute it recursively.
  587. index += (ts.wFlags & TRIE_NODE_COUNT) ? ts.aTags[iTag].cTag : CountTags(ptc, &ts, wMask, iTag);
  588. if (!TrieGetNextNode(ptc, &ts))
  589. return -1;
  590. }
  591. // If the node we just visited was valid, increment the index
  592. if (bValid)
  593. index++;
  594. } while (TRUE);
  595. }
  596. // Given an index into the trie, return the word.
  597. BOOL WINAPI TrieTagIndexToWord(TRIECTRL *ptc, DWORD nIndex, wchar_t *pwszWord, int cwc, int iTag)
  598. {
  599. TRIESCAN ts;
  600. int ich = 0;
  601. DWORD cTags;
  602. DWORD wMask = 1 << iTag;
  603. memset(&ts, 0, sizeof(TRIESCAN));
  604. if (!TrieGetNextState(ptc, &ts))
  605. return FALSE;
  606. do
  607. {
  608. // If we're at the end of the buffer, fail
  609. if (ich + 1 >= cwc)
  610. return FALSE;
  611. // Remember this node's character
  612. pwszWord[ich] = ts.wch;
  613. // If we're on a valid word AND we've reached the index we're looking for, exit the loop
  614. if (ts.wFlags & wMask)
  615. {
  616. if (!nIndex)
  617. break;
  618. nIndex--;
  619. }
  620. // Get the count of words in this subtree.
  621. cTags = (ts.wFlags & TRIE_NODE_COUNT) ? ts.aTags[iTag].cTag : CountTags(ptc, &ts, wMask, iTag);
  622. // Scan to the right until the word count of the subtree would be greater than or equal to the index
  623. // we're looking for. Descend that trie and repeat. !!!WARNING!!! The state may not be alphabetized.
  624. if (nIndex < cTags)
  625. {
  626. if (!TrieGetNextState(ptc, &ts))
  627. return FALSE;
  628. ich++; // Advance the character position
  629. }
  630. else
  631. {
  632. nIndex -= cTags;
  633. if (!TrieGetNextNode(ptc, &ts))
  634. return FALSE;
  635. }
  636. } while (TRUE);
  637. pwszWord[++ich] = L'\0'; // Null terminate the string
  638. return ts.wFlags & wMask; // Return validity
  639. }
  640. BOOL WINAPI
  641. TrieGetTagsFromWord(
  642. TRIECTRL *ptc, // Trie in which to find word
  643. wchar_t *pwszWord, // Word for which we're looking
  644. DWORD *pdw, // Returned values
  645. BYTE *pbValid // Mask for valid return values
  646. )
  647. {
  648. TRIESCAN ts;
  649. int iTag;
  650. WORD wMask;
  651. BYTE bMask = ptc->lpTrieStats->wTagsMask;
  652. if (!TrieCheckWord(ptc, &ts, pwszWord))
  653. return FALSE;
  654. if (ts.wFlags & TRIE_NODE_TAGGED)
  655. {
  656. wMask = 1;
  657. for (iTag = 0; bMask && (iTag < MAXTAGS); iTag++)
  658. {
  659. if (ts.wMask & wMask)
  660. {
  661. pdw[iTag] = ts.aTags[iTag].dwData;
  662. bMask |= wMask;
  663. }
  664. wMask <<= 1;
  665. }
  666. }
  667. *pbValid = (BYTE) wMask;
  668. return TRUE;
  669. }