windows-nt/Source/XPSP1/NT/inetsrv/intlwb/thai2/sth/trie.h
2020-09-26 16:20:57 +08:00

276 lines
12 KiB
C

#ifndef TRIE_H
#define TRIE_H
#include <windows.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Abstract trie node structure. wch is a character to transition on; flags describe various things
about the compressed trie; lpbNode points to the first byte of the next node in this state, and
lpbDown points to the first byte referenced by the down pointer, if any */
typedef struct tagTAGDATA
{
DWORD cTag; // Count of tagged nodes below this node in the subtree
DWORD dwData; // Stored tagged data for this node
} TAGDATA;
#define MAXTAGS 1
#if MAXTAGS > 8
#error No more than 8 tags are allowed
#endif
typedef struct tagTRIESCAN
{
WCHAR wch; // Unicode character
WORD wFlags; // see below
WORD wMask; // which tags are valid
WORD __pad0; //
DWORD cWords; // Words in subtree (only valid if TRIE_NODE_COUNT is set)
DWORD cSkipWords; // Words in subtrees ignored when following a skip pointer
LPBYTE lpbNode; // Address of next byte within the compressed trie
LPBYTE lpbDown; // Address referenced by down pointer, if any
LPBYTE lpbRight; // Address referenced by right pointer, if any
LPBYTE lpbSRDown; // Last single-ref address referenced
TAGDATA aTags[MAXTAGS]; // The list of tag counts/data
} TRIESCAN, *PTRIESCAN, *LPTRIESCAN;
// Trie node flags, only the lower 16 bits of the flags are saved in the trie
#define TRIE_NODE_VALID 0x00000001 // wch is the last letter of a valid word
#define TRIE_NODE_END 0x00000002 // Last node in the state (no more alternatives to wch)
#define TRIE_NODE_COUNT 0x00000004 // The count of words in the subtree is stored in the node
#define TRIE_NODE_TAGGED 0x00000008 // The node has tagged data
#define TRIE_NODE_DOWN 0x00000010 // iDown is valid (word so far is a valid prefix)
#define TRIE_NODE_RIGHT 0x00000020 // iRight is valid (word connects to a substate)
#define TRIE_DOWN_INLINE 0x00000040 // iDown omitted, since it points to next node in memory
#define TRIE_DOWN_MULTI 0x00000080 // iDown is a second reference or worse
#define TRIE_DOWN_ABS 0x00000100 // iDown is an absolute immediate offset into the trie
#define TRIE_NODE_SKIP 0x00000200 // Either iRight is a skip pointer or EOS is a 'soft' EOS
#define TRIE_NODE_SKIP_COUNT 0x00000400 // cSkipWords is valid
/* Macro to access the data in the node, works for dawgs and tries */
#define DAWGDATA(pdawg) ((pdawg)->wch)
#define DAWGDOWNFLAG(pdawg) ((pdawg)->wFlags & TRIE_NODE_DOWN)
#define DAWGENDFLAG(pdawg) ((pdawg)->wFlags & TRIE_NODE_END)
#define DAWGWORDFLAG(pdawg) ((pdawg)->wFlags & TRIE_NODE_VALID)
/* Fixed-length part of the compressed trie header */
typedef struct tagTRIESTATS
{
WORD version; // Version of this particular compressed trie
WORD __pad0; //
BYTE wTagsMask; // Which tags are in use
BYTE wEnumMask; // Which tags have enumeration
BYTE wDataMask; // Which tags have stored data
BYTE cTagFields; // Total tags in use
WORD cMaxWord; // Number of characters in longest word
WORD cMaxState; // Number of nodes in longest state (max alternatives)
WORD cCharFlagsCodesMax; // Bytes in longest char/flags code
WORD cTagsCodesMax; // Bytes in longest tagged data code
WORD cMRPointersCodesMax; // Bytes in longest MR pointer code
WORD cSROffsetsCodesMax; // Bytes in longest Single-ref code
DWORD cWords; // Number of words in dictionary
DWORD cUniqueSROffsets; // Unique offsets in Single-ref segment
DWORD cUniqueCharFlags; // Unique char/flags pairs
DWORD cUniqueTags; // Unique tagged data values
DWORD cUniqueMRPointers; // Unique multi-ref pointers
DWORD cbHeader; // Bytes in header & tables
DWORD cbTrie; // Bytes in trie
} TRIESTATS, *PTRIESTATS, *LPTRIESTATS;
/* Primary unit of a node. Nodes usually contain a pointer too */
typedef struct tagCHARFLAGS {
wchar_t wch;
short wFlags;
} CHARFLAGS, *PCHARFLAGS, *LPCHARFLAGS;
/* Control structure used to decompress the trie */
typedef struct tagTRIECTRL
{
TRIESTATS *lpTrieStats; // Pointer to base of header segment
WORD *lpwCharFlagsCodes; // decoding table for Char/flags
WORD *lpwTagsCodes; // decoding table for tagged data
WORD *lpwMRPointersCodes; // decoding table for multiref pointers
WORD *lpwSROffsetsCodes; // decoding table for singleref offsets
CHARFLAGS *lpCharFlags; // table to convert codes to char/flags
DWORD *lpwTags; // table to convert codes to tagged data
DWORD *lpwMRPointers; // table to convert codes to multiref pointers
DWORD *lpwSROffsets; // table to convert codes to Singleref offsets
BYTE *lpbTrie; // Pointer to the trie.
} TRIECTRL, *PTRIECTRL, *LPTRIECTRL;
/* Useful Constants */
#define TRIE_MAX_DEPTH 128 // We'll fail on any words longer than this
// The prototypes below are plain C (this is required for use with C++)
/* Given a pointer to a mapped file or resource containing a compressed trie,
read the trie into memory, making all the allocations required */
TRIECTRL * WINAPI TrieInit(LPBYTE lpByte);
/* Free all the allocations associated with a trie */
void WINAPI TrieFree(LPTRIECTRL lpTrieCtrl);
void WINAPI TrieDecompressNode(LPTRIECTRL lpTrieCtrl, LPTRIESCAN lpTrieScan);
/* 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);
BOOL WINAPI
TrieSkipNextNode(LPTRIECTRL lpTrieCtrl, LPTRIESCAN lpTrieScan, WCHAR wch);
/* 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);
/* 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 distinguisha valid word from a valid prefix, caller must test
wFlags for fTrieNodeValid. */
BOOL WINAPI
TrieCheckWord(LPTRIECTRL lpTrieCtrl, LPTRIESCAN lpTrieScan, wchar_t * lpwszWord);
/* Walk the trie from pTrieNode, calling pfnTrieWord on every valid word. pvParam is passed through
to pfnTrieWord. If pfnTrieWord returns non-zero, the enumeration stops. lpwszWord must point to a
space of cwchTrieWordMax+1 wchar_t's. To walk the entire trie, set *pTrieScan to all zeros. Returns
the number of words traversed. pfnTrieWord may be null if all you want is the count of words. */
int WINAPI
TrieEnumerate(
LPTRIECTRL lpTrieCtrl, // Trie to enumerate
LPTRIESCAN lpTrieScan, // structure holding starting point, all-zero for whole trie
wchar_t *pwszWord, // buffer to hold words being enumerated
void *pvParam, // parameter to pass to pfnTrieWord
int (*pfnTrieWord)(wchar_t *pwszWord, void *pvParam)
);
int WINAPI
TrieWordToIndex(
TRIECTRL *ptc, // Trie in which to find word index
wchar_t *pwszWord // Word for which we're looking
);
BOOL WINAPI
TrieIndexToWord(
TRIECTRL *ptc, // Trie in which to find indexed word
DWORD nIndex, // Index for which we're looking
wchar_t *pwszWord, // Returned word
int cwc // Max characters in buffer (including NULL)
);
int WINAPI
TrieWordToTagIndex(
TRIECTRL *ptc, // Trie in which to find word index
wchar_t *pwszWord, // Word for which we're looking
int tag // Which tag to enumerate
);
BOOL WINAPI
TrieTagIndexToWord(
TRIECTRL *ptc, // Trie in which to find indexed word
DWORD nIndex, // Index for which we're looking
wchar_t *pwszWord, // Returned word
int cwc, // Max characters in buffer (including NULL)
int tag // Which tag to enumerate
);
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
);
int WINAPI
TriePrefixToRange(
TRIECTRL *ptc, // Trie in which to find prefix range
wchar_t *pwszWord, // Prefix for which we're looking
int *piStart // Start of range with this prefix
);
/**** Subroutines for traversing Directed Acyclic Word Graphs ****/
/* Abstract trie node structure. wch is a character to transition on; flags describe various things
about the compressed trie; iDown indexes the first node in the state wch transitions to. DAWG is a special
kind of trie: a "Directed Acyclic Word Graph," essentially an ending-compressed trie. */
typedef struct tagDAWGNODE
{
DWORD wch; // Unicode character
DWORD wFlags; // see below
DWORD cWords; // Words below this node in the subtree
DWORD cSkipWords; // Words below skipped nodes
DWORD iDown; // Offset of first node in next state
DWORD iRight; // Offset to first node in next substate
DWORD cTags[8]; // Count of tagged nodes below this node in the subtree
DWORD dwData[8]; // Stored tagged data for this node
} DAWGNODE, *PDAWGNODE, *LPDAWGNODE;
/* Given a trie and a pointer to a node in it, find the next node in that state.
This is equivalent to traversing a right pointer or finding the next alternative
letter at the same position. Returns a pointer to the new node, NULL if there is
no next node (i.e. if this is the end of a state).*/
DAWGNODE * WINAPI DawgGetNextNode(void *pTrie, DAWGNODE *pTrieNode);
/* From this node, find the first node in the state it points to. This is equivalent
to traversing a down pointer or extending the word one letter and finding the first
alternative. Returns a pointer to the first node in the new state, NULL if there is
no down pointer. To find the first state in the trie, use pTrieNode == NULL */
DAWGNODE * WINAPI DawgGetNextState(void *pTrie, DAWGNODE *pTrieNode);
/* Check the validity of a word or prefix. Starts from the root of pTrie looking for
pwszWord. If it finds it, it returns a pointer to the terminal node in pTrie Returns
NULL if there is no path through the trie that corresponds to pwszWord. To distinguish
a valid word from a valid prefix, caller must test wFlags for fTrieNodeValid. */
DAWGNODE * WINAPI DawgCheckWord(void *pTrie, wchar_t *pwszWord);
/* Walk the trie from pTrieNode, calling pfnTrieWord on every valid word. pvParam is passed through
to pfnTrieWord. If pfnTrieWord returns non-zero, the enumeration stops. pwszWord must point to a
space of cwchTrieWordMax+1 wchar_t's. To walk the entire trie, pass NULL for pTrieNode. Returns
the number of words traversed. pfnTrieWord may be null if all you want is the count of words. */
int WINAPI
DawgEnumerate(
void *pTrie, // Trie to enumerate
DAWGNODE *pTrieNodeStart, // point to enumerate from, NULL if all
wchar_t *pwszWord, // buffer to hold words being enumerated
void *pvParam, // parameter to pass to pfnTrieWord
int (*pfnTrieWord)(wchar_t *pwszWord, void *pvParam)
);
// end plain C Prototypes
#ifdef __cplusplus
}
#endif
#endif // TRIE_H