windows-nt/Source/XPSP1/NT/inetsrv/intlwb/thai2/sth/trie.c

907 lines
28 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
#if !defined(LANGUAGE_IDENTIFICATION)
# include "precomp.h"
#endif
#include "trie.h"
#ifdef LANGUAGE_IDENTIFICATION
# include "mymalloc.h"
#endif
#ifndef WINCE
# include "assert.h"
#else
# define assert(x)
#endif
#include "thwbplat.h"
/******************************Public*Routine******************************\
* TrieInit
*
* Given a pointer to a resource or mapped file of a mapped file this
* function allocates and initializes the trie structure.
*
* Returns NULL for failure, trie control structure pointer for success.
*
* History:
* 16-Jun-1997 -by- Patrick Haluptzok patrickh
* Wrote it.
\**************************************************************************/
TRIECTRL * WINAPI TrieInit(LPBYTE lpByte)
{
LPWORD lpwTables;
TRIECTRL *lpTrieCtrl;
LPTRIESTATS lpTrieStats;
lpTrieStats = (LPTRIESTATS) lpByte;
//MessageBoxW(0,L"Step#1",L"Trie.C",MB_OK);
if (lpTrieStats == NULL)
return(NULL);
// Check the version number. This code currently only supports version 1 tries
//MessageBoxW(0,L"Step#2",L"Trie.C",MB_OK);
if (lpTrieStats->version > 1)
return NULL;
//
// Allocate space for the control structure and the table of SR offsets
//
//MessageBoxW(0,L"Step#3",L"Trie.C",MB_OK);
if (!fNLGNewMemory(&lpTrieCtrl, sizeof(TRIECTRL)))
return NULL;
//
// Allocate space for the complete header, copy the fixed part and read in the rest
//
//MessageBoxW(0,L"Step#4",L"Trie.C",MB_OK);
lpByte += lpTrieStats->cbHeader;
lpTrieCtrl->lpTrieStats = lpTrieStats;
//
// Set up the table pointers (all these tables are inside the TRIECTRL allocation)
//
lpwTables = (LPWORD)(lpTrieStats+1);
lpTrieCtrl->lpwCharFlagsCodes = lpwTables;
lpwTables += lpTrieStats->cCharFlagsCodesMax;
if ((DWORD_PTR) lpwTables & 0x02) // Deal with possible data mis-alignment
lpwTables++;
lpTrieCtrl->lpwTagsCodes = lpwTables;
lpwTables += lpTrieStats->cTagsCodesMax;
if ((DWORD_PTR) lpwTables & 0x02) // Deal with possible data mis-alignment
lpwTables++;
lpTrieCtrl->lpwMRPointersCodes = lpwTables;
lpwTables += lpTrieStats->cMRPointersCodesMax;
if ((DWORD_PTR) lpwTables & 0x02) // Deal with possible data mis-alignment
lpwTables++;
lpTrieCtrl->lpwSROffsetsCodes = lpwTables;
lpwTables += lpTrieStats->cSROffsetsCodesMax;
if ((DWORD_PTR) lpwTables & 0x02) // Deal with possible data mis-alignment
lpwTables++;
lpTrieCtrl->lpCharFlags = (LPCHARFLAGS)lpwTables;
lpwTables = (LPWORD)(lpTrieCtrl->lpCharFlags + lpTrieStats->cUniqueCharFlags);
lpTrieCtrl->lpwTags = (DWORD *)lpwTables;
lpwTables += (2 * lpTrieStats->cUniqueTags);
lpTrieCtrl->lpwMRPointers = (DWORD *) lpwTables;
lpwTables += (2 * lpTrieStats->cUniqueMRPointers);
lpTrieCtrl->lpwSROffsets = (DWORD *) lpwTables;
lpwTables += (2 * lpTrieStats->cUniqueSROffsets);
//
// These tables should exactly fill the allocation
//
assert((LPBYTE)lpwTables == (LPBYTE)lpTrieStats + lpTrieStats->cbHeader);
//
// Init trie pointers
//
lpTrieCtrl->lpbTrie = (LPBYTE)lpByte;
return (void *)lpTrieCtrl;
}
/******************************Public*Routine******************************\
* TrieFree
*
* Free the resources allocated for the control structure.
*
* History:
* 16-Jun-1997 -by- Patrick Haluptzok patrickh
* Wrote it.
\**************************************************************************/
void WINAPI TrieFree(LPTRIECTRL lpTrieCtrl)
{
//
// Finally free the control structure and all the tables. STILL MUST FREE THIS FOR ROM
//
NLGFreeMemory(lpTrieCtrl);
}
/* Deompress a single symbol using base-256 huffman from a compressed data structure. piSymbol
points to a space to hold the decompressed value, which is an index to a frequency-ordered
table of symbols (0 is most frequent). pcCodes is a table of code lengths returned from
HuffmanComputeTable. pbData is a pointer to memory that contains the encoded data. The
return value is the number of bytes decoded. */
int DecompressSymbol(WORD *piSymbol, WORD *pcCodes, unsigned char *pbData)
{
int cBytes = 0;
WORD wCode = 0, wiSymbol = 0;
/* At each stage in this loop, we're trying to see if we've got a length-n code.
dwCode is which length-n code it would have to be. If there aren't that many length-n codes,
we have to try n+1. To do that, we subtract the number of length-n codes and shift in
the next byte. dwiSymbol is the symbol number of the first length-n code. */
while (1)
{
wCode += *pbData++;
++cBytes;
if (wCode < *pcCodes)
{
break;
}
wiSymbol += *pcCodes;
wCode -= *pcCodes++;
wCode <<= 8;
}
/* Now that dwCode is a valid number of a length-cBytes code, we can just add it to
dwiSymbol, because we've already added the counts of the shorter codes to it. */
wiSymbol += wCode;
*piSymbol = wiSymbol;
return cBytes;
}
DWORD Get3ByteAddress(BYTE *pb)
{
return ((((pb[0] << 8) | pb[1]) << 8) | pb[2]) & 0x00ffffff;
}
void WINAPI TrieDecompressNode(LPTRIECTRL lpTrieCtrl, LPTRIESCAN lpTrieScan)
{
TRIESTATS *lpTrieStats;
DWORD wOffset;
DWORD wOffset2;
WORD wCode;
DWORD dwCode;
BYTE wMask;
BYTE bMask;
int iTag;
lpTrieStats = lpTrieCtrl->lpTrieStats;
/* If this is an initial call, use the first byte in the first SR segment */
if (lpTrieScan->wFlags == 0)
{
lpTrieScan->lpbSRDown = 0;
lpTrieScan->lpbNode = lpTrieCtrl->lpbTrie;
}
/* Decompress the char/flags */
lpTrieScan->lpbNode += DecompressSymbol(&wCode, lpTrieCtrl->lpwCharFlagsCodes, lpTrieScan->lpbNode);
lpTrieScan->wch = lpTrieCtrl->lpCharFlags[wCode].wch;
lpTrieScan->wFlags = lpTrieCtrl->lpCharFlags[wCode].wFlags;
// Decompress skip enumeration
if (lpTrieScan->wFlags & TRIE_NODE_SKIP_COUNT)
{
// Values greater than 127 are really 15 or 21 bit values.
dwCode = (DWORD) *lpTrieScan->lpbNode++;
if (dwCode >= 0x00c0)
{
dwCode = ((dwCode & 0x003f) << 15);
dwCode |= ((((DWORD) *lpTrieScan->lpbNode++) & 0x007f) << 8);
dwCode |= (((DWORD) *lpTrieScan->lpbNode++) & 0x00ff);
}
else if (dwCode >= 0x0080)
dwCode = ((dwCode & 0x007f) << 8) | (((DWORD) *lpTrieScan->lpbNode++) & 0x00ff);
lpTrieScan->cSkipWords = dwCode;
}
/* Code to decompress enumeration goes here */
if (lpTrieScan->wFlags & TRIE_NODE_COUNT)
{
// Values greater than 127 are really 15 or 21 bit values.
dwCode = (DWORD) *lpTrieScan->lpbNode++;
if (dwCode >= 0x00c0)
{
dwCode = ((dwCode & 0x003f) << 15);
dwCode |= ((((DWORD) *lpTrieScan->lpbNode++) & 0x007f) << 8);
dwCode |= (((DWORD) *lpTrieScan->lpbNode++) & 0x00ff);
}
else if (dwCode >= 0x0080)
dwCode = ((dwCode & 0x007f) << 8) | (((DWORD) *lpTrieScan->lpbNode++) & 0x00ff);
lpTrieScan->cWords = dwCode;
// Decompress the tagged enumeration counts
wMask = 1;
for (iTag = 0; iTag < MAXTAGS; iTag++)
{
if (lpTrieCtrl->lpTrieStats->wEnumMask & wMask)
{
// Values greater than 127 are really 15 or 21 bit values.
dwCode = (DWORD) *lpTrieScan->lpbNode++;
if (dwCode >= 0x00c0)
{
dwCode = ((dwCode & 0x003f) << 15);
dwCode |= ((((DWORD) *lpTrieScan->lpbNode++) & 0x007f) << 8);
dwCode |= (((DWORD) *lpTrieScan->lpbNode++) & 0x00ff);
}
else if (dwCode >= 0x0080)
dwCode = ((dwCode & 0x007f) << 8) | (((DWORD) *lpTrieScan->lpbNode++) & 0x00ff);
lpTrieScan->aTags[iTag].cTag = dwCode;
}
else
lpTrieScan->aTags[iTag].cTag = 0;
wMask <<= 1;
}
}
else
lpTrieScan->cWords = 0;
// Any tagged data for this node follows the counts
lpTrieScan->wMask = 0;
if (lpTrieScan->wFlags & TRIE_NODE_TAGGED)
{
// If there is only one tagged field, the mask byte won't be stored
if (lpTrieCtrl->lpTrieStats->cTagFields == 1)
bMask = lpTrieCtrl->lpTrieStats->wDataMask;
else
bMask = *lpTrieScan->lpbNode++;
// Now that we know which elements are stored here, pull them in their proper place
wMask = 1;
for (iTag = 0; bMask && (iTag < MAXTAGS); iTag++)
{
if (lpTrieCtrl->lpTrieStats->wDataMask & bMask & wMask)
{
lpTrieScan->lpbNode += DecompressSymbol(&wCode, lpTrieCtrl->lpwTagsCodes, lpTrieScan->lpbNode);
lpTrieScan->aTags[iTag].dwData = lpTrieCtrl->lpwTags[wCode];
lpTrieScan->wMask |= wMask;
}
bMask &= ~wMask;
wMask <<= 1;
}
}
// There are two flavors of right pointers: Multiref and Skip.
if (lpTrieScan->wFlags & TRIE_NODE_RIGHT)
{
if (lpTrieScan->wFlags & TRIE_NODE_SKIP)
{
lpTrieScan->lpbNode += DecompressSymbol(&wCode,lpTrieCtrl->lpwSROffsetsCodes,lpTrieScan->lpbNode);
wOffset2 = lpTrieCtrl->lpwSROffsets[wCode]; // Only add this after entire node is decompressed
}
else
{
/* Multiref: The down pointer is encoded directly */
lpTrieScan->lpbNode += DecompressSymbol(&wCode, lpTrieCtrl->lpwMRPointersCodes, lpTrieScan->lpbNode);
lpTrieScan->lpbRight = lpTrieCtrl->lpbTrie + lpTrieCtrl->lpwMRPointers[wCode];
}
}
else
lpTrieScan->lpbRight = NULL;
// There are 4 kinds of down pointer: Absolute, Inline, Multiref, and Singleref Offset.
// Each requires different decompression
if (lpTrieScan->wFlags & TRIE_DOWN_ABS)
{
// Immediate. The next 3 bytes are the absolute offset from the base of the trie.
lpTrieScan->lpbDown = lpTrieCtrl->lpbTrie + Get3ByteAddress(lpTrieScan->lpbNode);
lpTrieScan->lpbNode += 3;
}
else if (lpTrieScan->wFlags & TRIE_DOWN_INLINE)
{
/* Inline: The down pointer points to the next sequential byte (so it isn't stored) */
assert(lpTrieScan->wFlags&TRIE_NODE_END);
lpTrieScan->lpbSRDown = lpTrieScan->lpbDown = lpTrieScan->lpbNode;
}
else if (lpTrieScan->wFlags & TRIE_DOWN_MULTI)
{
/* Multiref: The down pointer is encoded directly */
lpTrieScan->lpbNode += DecompressSymbol(&wCode,lpTrieCtrl->lpwMRPointersCodes,
lpTrieScan->lpbNode);
lpTrieScan->lpbDown = lpTrieCtrl->lpbTrie + lpTrieCtrl->lpwMRPointers[wCode];
}
else if (lpTrieScan->wFlags & TRIE_NODE_DOWN)
{
/* SR Offset. The down pointer is encoded as an offset from the LAST downpointer
into this singleref segment. So we have to keep the old one around so we can add to it */
lpTrieScan->lpbNode += DecompressSymbol(&wCode,lpTrieCtrl->lpwSROffsetsCodes,
lpTrieScan->lpbNode);
if (lpTrieScan->lpbSRDown == 0)
{
lpTrieScan->lpbSRDown = lpTrieScan->lpbNode; // We offset from the end of the first node when going into a new state.
}
wOffset = lpTrieCtrl->lpwSROffsets[wCode];
lpTrieScan->lpbSRDown += wOffset;
lpTrieScan->lpbDown = lpTrieScan->lpbSRDown;
}
else
lpTrieScan->lpbDown = NULL;
// We couldn't deal with this until now, since skip pointers are always delta encoded from the end of node
if ((lpTrieScan->wFlags & (TRIE_NODE_RIGHT | TRIE_NODE_SKIP)) == (TRIE_NODE_RIGHT | TRIE_NODE_SKIP))
lpTrieScan->lpbRight = lpTrieScan->lpbNode + wOffset2;
} // TrieDecompressNode
/* Given a compressed trie and a pointer to a decompresed node from it, find and decompress
the next node in the same state. lpTrieScan is a user-allocated structure that holds the
decompressed node and into which the new node is copied.
This is equivalent to traversing a right pointer or finding the next alternative
letter at the same position. If there is no next node (i.e.this is the end of the state)
then TrieGetNextNode returns FALSE. To scan from the beginning of the trie, set the lpTrieScan
structure to zero */
BOOL WINAPI TrieGetNextNode(LPTRIECTRL lpTrieCtrl, LPTRIESCAN lpTrieScan)
{
// Are we at EOS?
if (lpTrieScan->wFlags & TRIE_NODE_END)
{
// Is this is a hard EOS?
if (!(lpTrieScan->wFlags & TRIE_NODE_SKIP))
{
// If we can follow a right pointer, do so, else fail
if (lpTrieScan->wFlags & TRIE_NODE_RIGHT)
lpTrieScan->lpbNode = lpTrieScan->lpbRight;
else
return FALSE;
}
// Either we're at a soft EOS or we've followed a right pointer.
// Both these require us to reset the SRDown for proper decompression
lpTrieScan->lpbSRDown = 0;
}
// Decompress the node at return success
TrieDecompressNode(lpTrieCtrl, lpTrieScan);
return TRUE;
}
BOOL WINAPI TrieSkipNextNode(LPTRIECTRL lpTrieCtrl, LPTRIESCAN lpTrieScan, WCHAR wch)
{
// If this is the last node in the normal or skip state, quit here
if (lpTrieScan->wFlags & TRIE_NODE_END)
return FALSE;
// If there isn't a right pointer or if the target letter is alphabetically less then
// the current letter scan right normally. Otherwise, follow the skip pointer.
if (!(lpTrieScan->wFlags & TRIE_NODE_RIGHT) || (wch < lpTrieScan->wch))
return TrieGetNextNode(lpTrieCtrl, lpTrieScan);
lpTrieScan->lpbSRDown = 0;
lpTrieScan->lpbNode = lpTrieScan->lpbRight;
TrieDecompressNode(lpTrieCtrl, lpTrieScan);
return TRUE;
}
/* Follow the down pointer to the next state. This is equivalent to accepting the character
in this node and advancing to the next character position. Returns FALSE if there is no
down pointer. This also decompresses the first node in the state, so all the values in
lpTrieScan will be good. */
BOOL WINAPI TrieGetNextState(LPTRIECTRL lpTrieCtrl, LPTRIESCAN lpTrieScan)
{
/* Flags can't normally be zero; that always means "top node" */
if (lpTrieScan->wFlags == 0)
{
TrieDecompressNode(lpTrieCtrl, lpTrieScan);
return TRUE;
}
if (!(lpTrieScan->wFlags & TRIE_NODE_DOWN))
return FALSE;
lpTrieScan->lpbSRDown = 0;
lpTrieScan->lpbNode = lpTrieScan->lpbDown;
TrieDecompressNode(lpTrieCtrl, lpTrieScan);
return TRUE;
} // TrieGetNextState
/* Check the validity of a word or prefix. Starts from the root of pTrie looking for
pwszWord. If it finds it, it returns TRUE and the user-provided lpTrieScan structure
contains the final node in the word. If there is no path, TrieCheckWord returns FALSE
To distinguish a valid word from a valid prefix, caller must test
wFlags for TRIE_NODE_VALID. */
BOOL WINAPI TrieCheckWord(LPTRIECTRL lpTrieCtrl, LPTRIESCAN lpTrieScan, wchar_t far* lpwszWord)
{
/* Start at the root of the trie and loop through all the letters in the word */
memset(lpTrieScan,0,sizeof(*lpTrieScan));
while (*lpwszWord)
{
/* Each new letter means we need to go to a new state. If there is none,
the word is not in this trie */
if (!TrieGetNextState(lpTrieCtrl, lpTrieScan))
return FALSE;
/* Now we walk across the state looking for this character. If we don't find
it, this word is not in this trie */
while (lpTrieScan->wch != *lpwszWord)
{
if (!TrieSkipNextNode(lpTrieCtrl, lpTrieScan, *lpwszWord))
return FALSE;
}
++lpwszWord;
}
return TRUE;
} // TrieCheckWord
// Find the index to the word in the trie.
DWORD CountWords(TRIECTRL *ptc, TRIESCAN *pts)
{
TRIESCAN ts = *pts;
DWORD cWords = 0;
if (!TrieGetNextState(ptc, &ts))
return cWords;
do
{
if (ts.wFlags & TRIE_NODE_VALID)
cWords++;
cWords += CountWords(ptc, &ts);
} while (TrieGetNextNode(ptc, &ts));
return cWords;
}
int WINAPI TrieWordToIndex(TRIECTRL *ptc, wchar_t *pwszWord)
{
TRIESCAN ts;
int ich = 0;
int index = 0;
BOOL bValid;
memset(&ts, 0, sizeof(TRIESCAN));
if (!TrieGetNextState(ptc, &ts))
return FALSE;
do
{
bValid = ts.wFlags & TRIE_NODE_VALID;
// Scan to the right until we find a matching character. !!!WARNING!!! The state may not be alphabetized.
// If the character doesn't match, add the subtree count to the enumeration total and slide to the right.
if (ts.wch == pwszWord[ich])
{
ich++;
// If we reached the end of word at a valid state, return the index
if ((pwszWord[ich] == L'\0') && ts.wFlags & TRIE_NODE_VALID)
return index;
// Try going down a level
if (!TrieGetNextState(ptc, &ts))
return -1;
}
else
{
// Now, follow the skip pointer if exist and the alphabetic character is greater then
// the pivot point. Otherwise, goto the next node. Add the sub tree count. If it's cached
// use it, otherwise compute it recursively.
if ((ts.wFlags & TRIE_NODE_SKIP_COUNT) && (pwszWord[ich] > ts.wch))
{
index += ts.cSkipWords;
// This can't fail if TRIE_NODE_SKIP_COUNT is set
TrieSkipNextNode(ptc, &ts, pwszWord[ich]);
}
else
{
index += (ts.wFlags & TRIE_NODE_COUNT) ? ts.cWords : CountWords(ptc, &ts);
if (!TrieGetNextNode(ptc, &ts))
return -1;
}
}
// If the node we just visited was valid, increment the index
if (bValid)
index++;
} while (TRUE);
}
// Given an index into the trie, return the word.
BOOL WINAPI TrieIndexToWord(TRIECTRL *ptc, DWORD nIndex, wchar_t *pwszWord, int cwc)
{
TRIESCAN ts;
int ich = 0;
DWORD cWords;
DWORD cSkips;
memset(&ts, 0, sizeof(TRIESCAN));
if (!TrieGetNextState(ptc, &ts))
return FALSE;
do
{
// If we're at the end of the buffer, fail
if (ich + 1 >= cwc)
return FALSE;
// Remember this node's character
pwszWord[ich] = ts.wch;
// If we're on a valid word AND we've reached the index we're looking for, exit the loop
if (ts.wFlags & TRIE_NODE_VALID)
{
if (!nIndex)
break;
nIndex--;
}
// Get the count of words in this subtree.
cWords = (ts.wFlags & TRIE_NODE_COUNT) ? ts.cWords : CountWords(ptc, &ts);
cSkips = (ts.wFlags & TRIE_NODE_SKIP_COUNT) ? ts.cSkipWords : 0x7fffffff;
// Scan to the right until the word count of the subtree would be greater than or equal to the index
// we're looking for. Descend that trie and repeat. !!!WARNING!!! The state may not be alphabetized.
// If we can use a skip count, do so.
if (nIndex < cWords)
{
if (!TrieGetNextState(ptc, &ts))
return FALSE;
ich++; // Advance the character position
}
else
{
if (nIndex >= cSkips)
{
nIndex -= cSkips;
ts.lpbSRDown = 0;
ts.lpbNode = ts.lpbRight;
TrieDecompressNode(ptc, &ts);
}
else
{
nIndex -= cWords;
if (!TrieGetNextNode(ptc, &ts))
return FALSE;
}
}
} while (TRUE);
pwszWord[++ich] = L'\0'; // Null terminate the string
return ts.wFlags & TRIE_NODE_VALID; // Return validity
}
int WINAPI TriePrefixToRange(TRIECTRL *ptc, wchar_t *pwszWord, int *piStart)
{
TRIESCAN ts;
int ich = 0;
int cnt;
BOOL bValid;
memset(&ts, 0, sizeof(TRIESCAN));
*piStart = 0;
if (!TrieGetNextState(ptc, &ts))
return 0;
// Deal with special case of empty string
if (pwszWord && !*pwszWord)
return ptc->lpTrieStats->cWords;
do
{
// Get the count of words below this prefix
cnt = (ts.wFlags & TRIE_NODE_COUNT) ? ts.cWords : CountWords(ptc, &ts);
// If the node we just arrived at is valid, increment the count
bValid = ts.wFlags & TRIE_NODE_VALID;
// Scan to the right until we find a matching character. !!!WARNING!!! The state may not be alphabetized.
// If the character doesn't match, add the subtree count to the enumeration total and slide to the right.
if (ts.wch == pwszWord[ich])
{
ich++;
// If we reached the end of prefix, return the count remaining below
if (pwszWord[ich] == L'\0')
{
if (bValid)
cnt++;
return cnt;
}
// Try going down a level
if (!TrieGetNextState(ptc, &ts))
return 0;
}
else
{
// Add the sub tree count.
*piStart += cnt;
// Try the next letter in this state
if (!TrieGetNextNode(ptc, &ts))
return 0;
}
if (bValid)
(*piStart)++;
} while (TRUE);
}
// TAGS
// Find the index to the word in the trie.
DWORD CountTags(TRIECTRL *ptc, TRIESCAN *pts, DWORD wMask, int iTag)
{
TRIESCAN ts = *pts;
DWORD cTags = 0;
if (!TrieGetNextState(ptc, &ts))
return cTags;
do
{
if (ts.wFlags & wMask)
cTags++;
cTags += CountTags(ptc, &ts, wMask, iTag);
} while (TrieGetNextNode(ptc, &ts));
return cTags;
}
int WINAPI TrieWordToTagIndex(TRIECTRL *ptc, wchar_t *pwszWord, int iTag)
{
TRIESCAN ts;
int ich = 0;
int index = 0;
BOOL bValid;
DWORD wMask = 1 << iTag;
memset(&ts, 0, sizeof(TRIESCAN));
if (!TrieGetNextState(ptc, &ts))
return FALSE;
do
{
bValid = ts.wFlags & wMask;
// Scan to the right until we find a matching character. !!!WARNING!!! The state may not be alphabetized.
// If the character doesn't match, add the subtree count to the enumeration total and slide to the right.
if (ts.wch == pwszWord[ich])
{
ich++;
// If we reached the end of word at a valid state, return the index
if ((pwszWord[ich] == L'\0') && ts.wFlags & wMask)
return index;
// Try going down a level
if (!TrieGetNextState(ptc, &ts))
return -1;
}
else
{
// Add the sub tree count. If it's cached use it, otherwise compute it recursively.
index += (ts.wFlags & TRIE_NODE_COUNT) ? ts.aTags[iTag].cTag : CountTags(ptc, &ts, wMask, iTag);
if (!TrieGetNextNode(ptc, &ts))
return -1;
}
// If the node we just visited was valid, increment the index
if (bValid)
index++;
} while (TRUE);
}
// Given an index into the trie, return the word.
BOOL WINAPI TrieTagIndexToWord(TRIECTRL *ptc, DWORD nIndex, wchar_t *pwszWord, int cwc, int iTag)
{
TRIESCAN ts;
int ich = 0;
DWORD cTags;
DWORD wMask = 1 << iTag;
memset(&ts, 0, sizeof(TRIESCAN));
if (!TrieGetNextState(ptc, &ts))
return FALSE;
do
{
// If we're at the end of the buffer, fail
if (ich + 1 >= cwc)
return FALSE;
// Remember this node's character
pwszWord[ich] = ts.wch;
// If we're on a valid word AND we've reached the index we're looking for, exit the loop
if (ts.wFlags & wMask)
{
if (!nIndex)
break;
nIndex--;
}
// Get the count of words in this subtree.
cTags = (ts.wFlags & TRIE_NODE_COUNT) ? ts.aTags[iTag].cTag : CountTags(ptc, &ts, wMask, iTag);
// Scan to the right until the word count of the subtree would be greater than or equal to the index
// we're looking for. Descend that trie and repeat. !!!WARNING!!! The state may not be alphabetized.
if (nIndex < cTags)
{
if (!TrieGetNextState(ptc, &ts))
return FALSE;
ich++; // Advance the character position
}
else
{
nIndex -= cTags;
if (!TrieGetNextNode(ptc, &ts))
return FALSE;
}
} while (TRUE);
pwszWord[++ich] = L'\0'; // Null terminate the string
return ts.wFlags & wMask; // Return validity
}
BOOL WINAPI
TrieGetTagsFromWord(
TRIECTRL *ptc, // Trie in which to find word
wchar_t *pwszWord, // Word for which we're looking
DWORD *pdw, // Returned values
BYTE *pbValid // Mask for valid return values
)
{
TRIESCAN ts;
int iTag;
WORD wMask;
BYTE bMask = ptc->lpTrieStats->wTagsMask;
if (!TrieCheckWord(ptc, &ts, pwszWord))
return FALSE;
if (ts.wFlags & TRIE_NODE_TAGGED)
{
wMask = 1;
for (iTag = 0; bMask && (iTag < MAXTAGS); iTag++)
{
if (ts.wMask & wMask)
{
pdw[iTag] = ts.aTags[iTag].dwData;
bMask |= wMask;
}
wMask <<= 1;
}
}
*pbValid = (BYTE) wMask;
return TRUE;
}