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.

899 lines
28 KiB

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