|
|
/*++
Copyright (c) 1991-2000, Microsoft Corporation All rights reserved.
Module Name:
string.c
Abstract:
This file contains functions that deal with characters and strings.
APIs found in this file: CompareStringW GetStringTypeExW GetStringTypeW
Revision History:
05-31-91 JulieB Created.
--*/
//
// Include Files.
//
#include "nls.h"
#include "nlssafe.h"
#include "jamo.h"
//
// Constant Declarations.
//
//
// State Table.
//
#define STATE_DW 1 // normal diacritic weight state
#define STATE_REVERSE_DW 2 // reverse diacritic weight state
#define STATE_CW 4 // case weight state
#define STATE_JAMO_WEIGHT 8 // jamo weight state
//
// Invalid weight value.
//
#define CMP_INVALID_WEIGHT 0xffffffff
#define CMP_INVALID_FAREAST 0xffff0000
#define CMP_INVALID_UW 0xffff
//
// Forward Declarations.
//
int LongCompareStringW( PLOC_HASH pHashN, DWORD dwCmpFlags, LPCWSTR lpString1, int cchCount1, LPCWSTR lpString2, int cchCount2, BOOL fModify);
int FindJamoDifference( PLOC_HASH pHashN, LPCWSTR* ppString1, int* ctr1, int cchCount1, DWORD* pWeight1, LPCWSTR* ppString2, int* ctr2, int cchCount2, DWORD* pWeight2, LPCWSTR* pLastJamo, WORD* uw1, WORD* uw2, int* pState, int* WhichJamo, BOOL fModify);
//-------------------------------------------------------------------------//
// INTERNAL MACROS //
//-------------------------------------------------------------------------//
////////////////////////////////////////////////////////////////////////////
//
// NOT_END_STRING
//
// Checks to see if the search has reached the end of the string.
// It returns TRUE if the counter is not at zero (counting backwards) and
// the null termination has not been reached (if -1 was passed in the count
// parameter.
//
// 11-04-92 JulieB Created.
////////////////////////////////////////////////////////////////////////////
#define NOT_END_STRING(ct, ptr, cchIn) \
((ct != 0) && (!((*(ptr) == 0) && (cchIn == -2))))
////////////////////////////////////////////////////////////////////////////
//
// AT_STRING_END
//
// Checks to see if the pointer is at the end of the string.
// It returns TRUE if the counter is zero or if the null termination
// has been reached (if -2 was passed in the count parameter).
//
// 11-04-92 JulieB Created.
////////////////////////////////////////////////////////////////////////////
#define AT_STRING_END(ct, ptr, cchIn) \
((ct == 0) || ((*(ptr) == 0) && (cchIn == -2)))
////////////////////////////////////////////////////////////////////////////
//
// REMOVE_STATE
//
// Removes the current state from the state table. This should only be
// called when the current state should not be entered for the remainder
// of the comparison. It decrements the counter going through the state
// table and decrements the number of states in the table.
//
// 11-04-92 JulieB Created.
////////////////////////////////////////////////////////////////////////////
#define REMOVE_STATE(value) (State &= ~value)
////////////////////////////////////////////////////////////////////////////
//
// POINTER_FIXUP
//
// Fixup the string pointers if expansion characters were found.
// Then, advance the string pointers and decrement the string counters.
//
// 11-04-92 JulieB Created.
////////////////////////////////////////////////////////////////////////////
#define POINTER_FIXUP() \
{ \ /* \
* Fixup the pointers (if necessary). \ */ \ if (pSave1 && (--cExpChar1 == 0)) \ { \ /* \
* Done using expansion temporary buffer. \ */ \ pString1 = pSave1; \ pSave1 = NULL; \ } \ \ if (pSave2 && (--cExpChar2 == 0)) \ { \ /* \
* Done using expansion temporary buffer. \ */ \ pString2 = pSave2; \ pSave2 = NULL; \ } \ \ /* \
* Advance the string pointers. \ */ \ pString1++; \ pString2++; \ }
////////////////////////////////////////////////////////////////////////////
//
// SCAN_LONGER_STRING
//
// Scans the longer string for diacritic, case, and special weights.
//
// 11-04-92 JulieB Created.
////////////////////////////////////////////////////////////////////////////
#define SCAN_LONGER_STRING( ct, \
ptr, \ cchIn, \ ret ) \ { \ /* \
* Search through the rest of the longer string to make sure \ * all characters are not to be ignored. If find a character that \ * should not be ignored, return the given return value immediately. \ * \ * The only exception to this is when a nonspace mark is found. If \ * another DW difference has been found earlier, then use that. \ */ \ while (NOT_END_STRING(ct, ptr, cchIn)) \ { \ Weight1 = GET_DWORD_WEIGHT(pHashN, *ptr); \ switch (GET_SCRIPT_MEMBER(&Weight1)) \ { \ case ( UNSORTABLE ): \ { \ break; \ } \ case ( NONSPACE_MARK ): \ { \ if ((!fIgnoreDiacritic) && (!WhichDiacritic)) \ { \ return (ret); \ } \ break; \ } \ case ( PUNCTUATION ) : \ case ( SYMBOL_1 ) : \ case ( SYMBOL_2 ) : \ case ( SYMBOL_3 ) : \ case ( SYMBOL_4 ) : \ case ( SYMBOL_5 ) : \ { \ if (!fIgnoreSymbol) \ { \ return (ret); \ } \ break; \ } \ case ( EXPANSION ) : \ case ( FAREAST_SPECIAL ) : \ case ( JAMO_SPECIAL ) : \ case ( EXTENSION_A ) : \ default : \ { \ return (ret); \ } \ } \ \ /* \
* Advance pointer and decrement counter. \ */ \ ptr++; \ ct--; \ } \ \ /* \
* Need to check diacritic, case, extra, and special weights for \ * final return value. Still could be equal if the longer part of \ * the string contained only characters to be ignored. \ * \ * NOTE: The following checks MUST REMAIN IN THIS ORDER: \ * Diacritic, Case, Extra, Punctuation. \ */ \ if (WhichDiacritic) \ { \ return (WhichDiacritic); \ } \ if (WhichCase) \ { \ return (WhichCase); \ } \ if (WhichExtra) \ { \ if (!fIgnoreDiacritic) \ { \ if (GET_WT_FOUR(&WhichExtra)) \ { \ return (GET_WT_FOUR(&WhichExtra)); \ } \ if (GET_WT_FIVE(&WhichExtra)) \ { \ return (GET_WT_FIVE(&WhichExtra)); \ } \ } \ if (GET_WT_SIX(&WhichExtra)) \ { \ return (GET_WT_SIX(&WhichExtra)); \ } \ if (GET_WT_SEVEN(&WhichExtra)) \ { \ return (GET_WT_SEVEN(&WhichExtra)); \ } \ } \ if (WhichJamo) \ { \ return (WhichJamo); \ } \ if (WhichPunct1) \ { \ return (WhichPunct1); \ } \ if (WhichPunct2) \ { \ return (WhichPunct2); \ } \ \ return (CSTR_EQUAL); \ }
////////////////////////////////////////////////////////////////////////////
//
// QUICK_SCAN_LONGER_STRING
//
// Scans the longer string for diacritic, case, and special weights.
// Assumes that both strings are null-terminated.
//
// 11-04-92 JulieB Created.
////////////////////////////////////////////////////////////////////////////
#define QUICK_SCAN_LONGER_STRING( ptr, \
ret ) \ { \ /* \
* Search through the rest of the longer string to make sure \ * all characters are not to be ignored. If find a character that \ * should not be ignored, return the given return value immediately. \ * \ * The only exception to this is when a nonspace mark is found. If \ * another DW difference has been found earlier, then use that. \ */ \ while (*ptr != 0) \ { \ switch (GET_SCRIPT_MEMBER(&(pHashN->pSortkey[*ptr]))) \ { \ case ( UNSORTABLE ): \ { \ break; \ } \ case ( NONSPACE_MARK ): \ { \ if (!WhichDiacritic) \ { \ return (ret); \ } \ break; \ } \ default : \ { \ return (ret); \ } \ } \ \ /* \
* Advance pointer. \ */ \ ptr++; \ } \ \ /* \
* Need to check diacritic, case, extra, and special weights for \ * final return value. Still could be equal if the longer part of \ * the string contained only unsortable characters. \ * \ * NOTE: The following checks MUST REMAIN IN THIS ORDER: \ * Diacritic, Case, Extra, Punctuation. \ */ \ if (WhichDiacritic) \ { \ return (WhichDiacritic); \ } \ if (WhichCase) \ { \ return (WhichCase); \ } \ if (WhichExtra) \ { \ if (GET_WT_FOUR(&WhichExtra)) \ { \ return (GET_WT_FOUR(&WhichExtra)); \ } \ if (GET_WT_FIVE(&WhichExtra)) \ { \ return (GET_WT_FIVE(&WhichExtra)); \ } \ if (GET_WT_SIX(&WhichExtra)) \ { \ return (GET_WT_SIX(&WhichExtra)); \ } \ if (GET_WT_SEVEN(&WhichExtra)) \ { \ return (GET_WT_SEVEN(&WhichExtra)); \ } \ } \ if (WhichJamo) \ { \ return (WhichJamo); \ } \ if (WhichPunct1) \ { \ return (WhichPunct1); \ } \ if (WhichPunct2) \ { \ return (WhichPunct2); \ } \ \ return (CSTR_EQUAL); \ }
////////////////////////////////////////////////////////////////////////////
//
// GET_FAREAST_WEIGHT
//
// Returns the weight for the far east special case in "wt". This currently
// includes the Cho-on, the Repeat, and the Kana characters.
//
// 08-19-93 JulieB Created.
////////////////////////////////////////////////////////////////////////////
#define GET_FAREAST_WEIGHT( wt, \
uw, \ mask, \ pBegin, \ pCur, \ ExtraWt, \ fModify ) \ { \ int ct; /* loop counter */ \ BYTE PrevSM; /* previous script member value */ \ BYTE PrevAW; /* previous alphanumeric value */ \ BYTE PrevCW; /* previous case value */ \ BYTE AW; /* alphanumeric value */ \ BYTE CW; /* case value */ \ DWORD PrevWt; /* previous weight */ \ \ \ /* \
* Get the alphanumeric weight and the case weight of the \ * current code point. \ */ \ AW = GET_ALPHA_NUMERIC(&wt); \ CW = GET_CASE(&wt); \ ExtraWt = (DWORD)0; \ \ /* \
* Special case Repeat and Cho-On. \ * AW = 0 => Repeat \ * AW = 1 => Cho-On \ * AW = 2+ => Kana \ */ \ if (AW <= MAX_SPECIAL_AW) \ { \ /* \
* If the script member of the previous character is \ * invalid, then give the special character an \ * invalid weight (highest possible weight) so that it \ * will sort AFTER everything else. \ */ \ ct = 1; \ PrevWt = CMP_INVALID_FAREAST; \ while ((pCur - ct) >= pBegin) \ { \ PrevWt = GET_DWORD_WEIGHT(pHashN, *(pCur - ct)); \ PrevWt &= mask; \ PrevSM = GET_SCRIPT_MEMBER(&PrevWt); \ if (PrevSM < FAREAST_SPECIAL) \ { \ if (PrevSM == EXPANSION) \ { \ PrevWt = CMP_INVALID_FAREAST; \ } \ else \ { \ /* \
* UNSORTABLE or NONSPACE_MARK. \ * \ * Just ignore these, since we only care about the \ * previous UW value. \ */ \ PrevWt = CMP_INVALID_FAREAST; \ ct++; \ continue; \ } \ } \ else if (PrevSM == FAREAST_SPECIAL) \ { \ PrevAW = GET_ALPHA_NUMERIC(&PrevWt); \ if (PrevAW <= MAX_SPECIAL_AW) \ { \ /* \
* Handle case where two special chars follow \ * each other. Keep going back in the string. \ */ \ PrevWt = CMP_INVALID_FAREAST; \ ct++; \ continue; \ } \ \ UNICODE_WT(&PrevWt) = \ MAKE_UNICODE_WT(KANA, PrevAW, fModify); \ \ /* \
* Only build weights 4, 5, 6, and 7 if the \ * previous character is KANA. \ * \ * Always: \ * 4W = previous CW & ISOLATE_SMALL \ * 6W = previous CW & ISOLATE_KANA \ * \ */ \ PrevCW = GET_CASE(&PrevWt); \ GET_WT_FOUR(&ExtraWt) = PrevCW & ISOLATE_SMALL; \ GET_WT_SIX(&ExtraWt) = PrevCW & ISOLATE_KANA; \ \ if (AW == AW_REPEAT) \ { \ /* \
* Repeat: \ * UW = previous UW \ * 5W = WT_FIVE_REPEAT \ * 7W = previous CW & ISOLATE_WIDTH \ */ \ uw = UNICODE_WT(&PrevWt); \ GET_WT_FIVE(&ExtraWt) = WT_FIVE_REPEAT; \ GET_WT_SEVEN(&ExtraWt) = PrevCW & ISOLATE_WIDTH; \ } \ else \ { \ /* \
* Cho-On: \ * UW = previous UW & CHO_ON_UW_MASK \ * 5W = WT_FIVE_CHO_ON \ * 7W = current CW & ISOLATE_WIDTH \ */ \ uw = UNICODE_WT(&PrevWt) & CHO_ON_UW_MASK; \ GET_WT_FIVE(&ExtraWt) = WT_FIVE_CHO_ON; \ GET_WT_SEVEN(&ExtraWt) = CW & ISOLATE_WIDTH; \ } \ } \ else \ { \ uw = GET_UNICODE_MOD(&PrevWt, fModify); \ } \ \ break; \ } \ } \ else \ { \ /* \
* Kana: \ * SM = KANA \ * AW = current AW \ * 4W = current CW & ISOLATE_SMALL \ * 5W = WT_FIVE_KANA \ * 6W = current CW & ISOLATE_KANA \ * 7W = current CW & ISOLATE_WIDTH \ */ \ uw = MAKE_UNICODE_WT(KANA, AW, fModify); \ GET_WT_FOUR(&ExtraWt) = CW & ISOLATE_SMALL; \ GET_WT_FIVE(&ExtraWt) = WT_FIVE_KANA; \ GET_WT_SIX(&ExtraWt) = CW & ISOLATE_KANA; \ GET_WT_SEVEN(&ExtraWt) = CW & ISOLATE_WIDTH; \ } \ \ /* \
* Get the weight for the far east special case and store it in wt. \ */ \ if ((AW > MAX_SPECIAL_AW) || (PrevWt != CMP_INVALID_FAREAST)) \ { \ /* \
* Always: \ * DW = current DW \ * CW = minimum CW \ */ \ UNICODE_WT(&wt) = uw; \ CASE_WT(&wt) = MIN_CW; \ } \ else \ { \ uw = CMP_INVALID_UW; \ wt = CMP_INVALID_FAREAST; \ ExtraWt = 0; \ } \ }
//-------------------------------------------------------------------------//
// API ROUTINES //
//-------------------------------------------------------------------------//
////////////////////////////////////////////////////////////////////////////
//
// CompareStringW
//
// Compares two wide character strings of the same locale according to the
// supplied locale handle.
//
// 05-31-91 JulieB Created.
////////////////////////////////////////////////////////////////////////////
int WINAPI CompareStringW( LCID Locale, DWORD dwCmpFlags, LPCWSTR lpString1, int cchCount1, LPCWSTR lpString2, int cchCount2) { register LPWSTR pString1; // ptr to go thru string 1
register LPWSTR pString2; // ptr to go thru string 2
PLOC_HASH pHashN; // ptr to LOC hash node
BOOL fIgnorePunct; // flag to ignore punctuation (not symbol)
BOOL fModify; // flag to use modified script member weights
DWORD State; // state table
DWORD Mask; // mask for weights
DWORD Weight1; // full weight of char - string 1
DWORD Weight2; // full weight of char - string 2
int JamoFlag = FALSE; LPCWSTR pLastJamo = lpString1;
int WhichDiacritic; // DW => 1 = str1 smaller, 3 = str2 smaller
int WhichCase; // CW => 1 = str1 smaller, 3 = str2 smaller
int WhichJamo; // XW for Jamo
int WhichPunct1; // SW => 1 = str1 smaller, 3 = str2 smaller
int WhichPunct2; // SW => 1 = str1 smaller, 3 = str2 smaller
LPWSTR pSave1; // ptr to saved pString1
LPWSTR pSave2; // ptr to saved pString2
int cExpChar1, cExpChar2; // ct of expansions in tmp
DWORD ExtraWt1, ExtraWt2; // extra weight values (for far east)
DWORD WhichExtra; // XW => wts 4, 5, 6, 7 (for far east)
//
// Invalid Parameter Check:
// - validate LCID
// - either string is null
//
VALIDATE_LANGUAGE(Locale, pHashN, 0, TRUE); if ((pHashN == NULL) || (lpString1 == NULL) || (lpString2 == NULL)) { SetLastError(ERROR_INVALID_PARAMETER); return (0); }
//
// Make sure the appropriate sorting tables are available. If not,
// return an error.
//
if ((pHashN->pSortkey == NULL) || (pHashN->IfIdeographFailure == TRUE)) { KdPrint(("NLSAPI: Appropriate Sorting Tables Not Loaded.\n")); SetLastError(ERROR_FILE_NOT_FOUND); return (0); }
//
// Call longer compare string if any of the following is true:
// - compression locale
// - either count is not -1
// - dwCmpFlags is not 0 or ignore case (see NOTE below)
// - locale is Korean - script member weight adjustment needed
//
// NOTE: If the value of NORM_IGNORECASE ever changes, this
// code should check for:
// ( (dwCmpFlags != 0) && (dwCmpFlags != NORM_IGNORECASE) )
// Since NORM_IGNORECASE is equal to 1, we can optimize this
// by checking for > 1.
//
dwCmpFlags &= (~LOCALE_USE_CP_ACP); fModify = IS_KOREAN(Locale); if ( (pHashN->IfCompression) || (cchCount1 > -1) || (cchCount2 > -1) || (dwCmpFlags > NORM_IGNORECASE) || (fModify == TRUE) ) { return (LongCompareStringW( pHashN, dwCmpFlags, lpString1, ((cchCount1 <= -1) ? -2 : cchCount1), lpString2, ((cchCount2 <= -1) ? -2 : cchCount2), fModify )); }
//
// Initialize string pointers.
//
pString1 = (LPWSTR)lpString1; pString2 = (LPWSTR)lpString2;
//
// Do a wchar by wchar compare.
//
while (TRUE) { //
// See if characters are equal.
// If characters are equal, increment pointers and continue
// string compare.
//
// NOTE: Loop is unrolled 8 times for performance.
//
if ((*pString1 != *pString2) || (*pString1 == 0)) { break; } pString1++; pString2++;
if ((*pString1 != *pString2) || (*pString1 == 0)) { break; } pString1++; pString2++;
if ((*pString1 != *pString2) || (*pString1 == 0)) { break; } pString1++; pString2++;
if ((*pString1 != *pString2) || (*pString1 == 0)) { break; } pString1++; pString2++;
if ((*pString1 != *pString2) || (*pString1 == 0)) { break; } pString1++; pString2++;
if ((*pString1 != *pString2) || (*pString1 == 0)) { break; } pString1++; pString2++;
if ((*pString1 != *pString2) || (*pString1 == 0)) { break; } pString1++; pString2++;
if ((*pString1 != *pString2) || (*pString1 == 0)) { break; } pString1++; pString2++; }
//
// If strings are both at null terminators, return equal.
//
if (*pString1 == *pString2) { return (CSTR_EQUAL); }
//
// Initialize flags, pointers, and counters.
//
fIgnorePunct = FALSE; WhichDiacritic = 0; WhichCase = 0; WhichJamo = 0; WhichPunct1 = 0; WhichPunct2 = 0; pSave1 = NULL; pSave2 = NULL; ExtraWt1 = (DWORD)0; WhichExtra = (DWORD)0;
//
// Switch on the different flag options. This will speed up
// the comparisons of two strings that are different.
//
// The only two possibilities in this optimized section are
// no flags and the ignore case flag.
//
if (dwCmpFlags == 0) { Mask = CMP_MASKOFF_NONE; } else { Mask = CMP_MASKOFF_CW; } State = (pHashN->IfReverseDW) ? STATE_REVERSE_DW : STATE_DW; State |= (STATE_CW | STATE_JAMO_WEIGHT);
//
// Compare each character's sortkey weight in the two strings.
//
while ((*pString1 != 0) && (*pString2 != 0)) { Weight1 = GET_DWORD_WEIGHT(pHashN, *pString1); Weight2 = GET_DWORD_WEIGHT(pHashN, *pString2); Weight1 &= Mask; Weight2 &= Mask;
if (Weight1 != Weight2) { BYTE sm1 = GET_SCRIPT_MEMBER(&Weight1); // script member 1
BYTE sm2 = GET_SCRIPT_MEMBER(&Weight2); // script member 2
WORD uw1 = GET_UNICODE_SM(&Weight1, sm1); // unicode weight 1
WORD uw2 = GET_UNICODE_SM(&Weight2, sm2); // unicode weight 2
BYTE dw1; // diacritic weight 1
BYTE dw2; // diacritic weight 2
BOOL fContinue; // flag to continue loop
DWORD Wt; // temp weight holder
WCHAR pTmpBuf1[MAX_TBL_EXPANSION]; // temp buffer for exp 1
WCHAR pTmpBuf2[MAX_TBL_EXPANSION]; // temp buffer for exp 2
//
// If Unicode Weights are different and no special cases,
// then we're done. Otherwise, we need to do extra checking.
//
// Must check ENTIRE string for any possibility of Unicode Weight
// differences. As soon as a Unicode Weight difference is found,
// then we're done. If no UW difference is found, then the
// first Diacritic Weight difference is used. If no DW difference
// is found, then use the first Case Difference. If no CW
// difference is found, then use the first Extra Weight
// difference. If no XW difference is found, then use the first
// Special Weight difference.
//
if ((uw1 != uw2) || (sm1 == FAREAST_SPECIAL) || (sm1 == EXTENSION_A)) { //
// Initialize the continue flag.
//
fContinue = FALSE;
//
// Check for Unsortable characters and skip them.
// This needs to be outside the switch statement. If EITHER
// character is unsortable, must skip it and start over.
//
if (sm1 == UNSORTABLE) { pString1++; fContinue = TRUE; } if (sm2 == UNSORTABLE) { pString2++; fContinue = TRUE; } if (fContinue) { continue; }
//
// Switch on the script member of string 1 and take care
// of any special cases.
//
switch (sm1) { case ( NONSPACE_MARK ) : { //
// Nonspace only - look at diacritic weight only.
//
if ((WhichDiacritic == 0) || (State & STATE_REVERSE_DW)) { WhichDiacritic = CSTR_GREATER_THAN;
//
// Remove state from state machine.
//
REMOVE_STATE(STATE_DW); }
//
// Adjust pointer and set flags.
//
pString1++; fContinue = TRUE;
break; } case ( PUNCTUATION ) : { //
// If the ignore punctuation flag is set, then skip
// over the punctuation.
//
if (fIgnorePunct) { pString1++; fContinue = TRUE; } else if (sm2 != PUNCTUATION) { //
// The character in the second string is
// NOT punctuation.
//
if (WhichPunct2) { //
// Set WP 2 to show that string 2 is smaller,
// since a punctuation char had already been
// found at an earlier position in string 2.
//
// Set the Ignore Punctuation flag so we just
// skip over any other punctuation chars in
// the string.
//
WhichPunct2 = CSTR_GREATER_THAN; fIgnorePunct = TRUE; } else { //
// Set WP 1 to show that string 2 is smaller,
// and that string 1 has had a punctuation
// char - since no punctuation chars have
// been found in string 2.
//
WhichPunct1 = CSTR_GREATER_THAN; }
//
// Advance pointer 1, and set flag to true.
//
pString1++; fContinue = TRUE; }
//
// Do NOT want to advance the pointer in string 1 if
// string 2 is also a punctuation char. This will
// be done later.
//
break; } case ( EXPANSION ) : { //
// Save pointer in pString1 so that it can be
// restored.
//
if (pSave1 == NULL) { pSave1 = pString1; } pString1 = pTmpBuf1;
//
// Expand character into temporary buffer.
//
pTmpBuf1[0] = GET_EXPANSION_1(&Weight1); pTmpBuf1[1] = GET_EXPANSION_2(&Weight1);
//
// Set cExpChar1 to the number of expansion characters
// stored.
//
cExpChar1 = MAX_TBL_EXPANSION;
fContinue = TRUE;
break; } case ( FAREAST_SPECIAL ) : { if (sm2 != EXPANSION) { //
// Get the weight for the far east special case
// and store it in Weight1.
//
GET_FAREAST_WEIGHT( Weight1, uw1, Mask, lpString1, pString1, ExtraWt1, FALSE );
if (sm2 != FAREAST_SPECIAL) { //
// The character in the second string is
// NOT a fareast special char.
//
// Set each of weights 4, 5, 6, and 7 to show
// that string 2 is smaller (if not already set).
//
if ((GET_WT_FOUR(&WhichExtra) == 0) && (GET_WT_FOUR(&ExtraWt1) != 0)) { GET_WT_FOUR(&WhichExtra) = CSTR_GREATER_THAN; } if ((GET_WT_FIVE(&WhichExtra) == 0) && (GET_WT_FIVE(&ExtraWt1) != 0)) { GET_WT_FIVE(&WhichExtra) = CSTR_GREATER_THAN; } if ((GET_WT_SIX(&WhichExtra) == 0) && (GET_WT_SIX(&ExtraWt1) != 0)) { GET_WT_SIX(&WhichExtra) = CSTR_GREATER_THAN; } if ((GET_WT_SEVEN(&WhichExtra) == 0) && (GET_WT_SEVEN(&ExtraWt1) != 0)) { GET_WT_SEVEN(&WhichExtra) = CSTR_GREATER_THAN; } } } break; } case ( JAMO_SPECIAL ) : { int ctr1; // dummy variables for FindJamoDifference
LPWSTR pStr1 = pString1; LPWSTR pStr2 = pString2;
//
// Set the JamoFlag so we don't handle it again.
//
JamoFlag = TRUE; fContinue = FindJamoDifference( pHashN, &pStr1, &ctr1, -2, &Weight1, &pStr2, &ctr1, -2, &Weight2, &pLastJamo, &uw1, &uw2, &State, &WhichJamo, fModify ); if (WhichJamo) { return (WhichJamo); }
pString1 = pStr1; pString2 = pStr2;
break; } case ( EXTENSION_A ) : { //
// Compare the weights.
//
if (Weight1 == Weight2) { //
// Adjust pointers and set flag.
//
pString1++; pString2++; fContinue = TRUE; } else { //
// Get the actual UW to compare.
//
if (sm2 == EXTENSION_A) { //
// Set the UW values to be the AW and DW since
// both strings contain an extension A char.
//
uw1 = MAKE_UNICODE_WT( GET_ALPHA_NUMERIC(&Weight1), GET_DIACRITIC(&Weight1), FALSE ); uw2 = MAKE_UNICODE_WT( GET_ALPHA_NUMERIC(&Weight2), GET_DIACRITIC(&Weight2), FALSE ); } else { //
// Only string1 contains an extension A char,
// so set the UW value to be the first UW
// value for extension A (default values):
// SM_EXT_A, AW_EXT_A
//
uw1 = MAKE_UNICODE_WT(SM_EXT_A, AW_EXT_A, fModify); } }
break; } case ( UNSORTABLE ) : { //
// Fill out the case statement so the compiler
// will use a jump table.
//
break; } }
//
// Switch on the script member of string 2 and take care
// of any special cases.
//
switch (sm2) { case ( NONSPACE_MARK ) : { //
// Nonspace only - look at diacritic weight only.
//
if ((WhichDiacritic == 0) || (State & STATE_REVERSE_DW)) { WhichDiacritic = CSTR_LESS_THAN;
//
// Remove state from state machine.
//
REMOVE_STATE(STATE_DW); }
//
// Adjust pointer and set flags.
//
pString2++; fContinue = TRUE;
break; } case ( PUNCTUATION ) : { //
// If the ignore punctuation flag is set, then skip
// over the punctuation.
//
if (fIgnorePunct) { //
// Pointer 2 will be advanced after if-else
// statement.
//
; } else if (sm1 != PUNCTUATION) { //
// The character in the first string is
// NOT punctuation.
//
if (WhichPunct1) { //
// Set WP 1 to show that string 1 is smaller,
// since a punctuation char had already
// been found at an earlier position in
// string 1.
//
// Set the Ignore Punctuation flag so we just
// skip over any other punctuation in the
// string.
//
WhichPunct1 = CSTR_LESS_THAN; fIgnorePunct = TRUE; } else { //
// Set WP 2 to show that string 1 is smaller,
// and that string 2 has had a punctuation
// char - since no punctuation chars have
// been found in string 1.
//
WhichPunct2 = CSTR_LESS_THAN; }
//
// Pointer 2 will be advanced after if-else
// statement.
//
} else { //
// Both code points are punctuation.
//
// See if either of the strings has encountered
// punctuation chars previous to this.
//
if (WhichPunct1) { //
// String 1 has had a punctuation char, so
// it should be the smaller string (since
// both have punctuation chars).
//
WhichPunct1 = CSTR_LESS_THAN; } else if (WhichPunct2) { //
// String 2 has had a punctuation char, so
// it should be the smaller string (since
// both have punctuation chars).
//
WhichPunct2 = CSTR_GREATER_THAN; } else { //
// Position is the same, so compare the
// special weights. Set WhichPunct1 to
// the smaller special weight.
//
WhichPunct1 = (((GET_ALPHA_NUMERIC(&Weight1) < GET_ALPHA_NUMERIC(&Weight2))) ? CSTR_LESS_THAN : CSTR_GREATER_THAN); }
//
// Set the Ignore Punctuation flag so we just
// skip over any other punctuation in the string.
//
fIgnorePunct = TRUE;
//
// Advance pointer 1. Pointer 2 will be
// advanced after if-else statement.
//
pString1++; }
//
// Advance pointer 2 and set flag to true.
//
pString2++; fContinue = TRUE;
break; } case ( EXPANSION ) : { //
// Save pointer in pString1 so that it can be
// restored.
//
if (pSave2 == NULL) { pSave2 = pString2; } pString2 = pTmpBuf2;
//
// Expand character into temporary buffer.
//
pTmpBuf2[0] = GET_EXPANSION_1(&Weight2); pTmpBuf2[1] = GET_EXPANSION_2(&Weight2);
//
// Set cExpChar2 to the number of expansion characters
// stored.
//
cExpChar2 = MAX_TBL_EXPANSION;
fContinue = TRUE;
break; } case ( FAREAST_SPECIAL ) : { if (sm1 != EXPANSION) { //
// Get the weight for the far east special case
// and store it in Weight2.
//
GET_FAREAST_WEIGHT( Weight2, uw2, Mask, lpString2, pString2, ExtraWt2, FALSE );
if (sm1 != FAREAST_SPECIAL) { //
// The character in the first string is
// NOT a fareast special char.
//
// Set each of weights 4, 5, 6, and 7 to show
// that string 1 is smaller (if not already set).
//
if ((GET_WT_FOUR(&WhichExtra) == 0) && (GET_WT_FOUR(&ExtraWt2) != 0)) { GET_WT_FOUR(&WhichExtra) = CSTR_LESS_THAN; } if ((GET_WT_FIVE(&WhichExtra) == 0) && (GET_WT_FIVE(&ExtraWt2) != 0)) { GET_WT_FIVE(&WhichExtra) = CSTR_LESS_THAN; } if ((GET_WT_SIX(&WhichExtra) == 0) && (GET_WT_SIX(&ExtraWt2) != 0)) { GET_WT_SIX(&WhichExtra) = CSTR_LESS_THAN; } if ((GET_WT_SEVEN(&WhichExtra) == 0) && (GET_WT_SEVEN(&ExtraWt2) != 0)) { GET_WT_SEVEN(&WhichExtra) = CSTR_LESS_THAN; } } else { //
// Characters in both strings are fareast
// special chars.
//
// Set each of weights 4, 5, 6, and 7
// appropriately (if not already set).
//
if ( (GET_WT_FOUR(&WhichExtra) == 0) && ( GET_WT_FOUR(&ExtraWt1) != GET_WT_FOUR(&ExtraWt2) ) ) { GET_WT_FOUR(&WhichExtra) = ( GET_WT_FOUR(&ExtraWt1) < GET_WT_FOUR(&ExtraWt2) ) ? CSTR_LESS_THAN : CSTR_GREATER_THAN; } if ( (GET_WT_FIVE(&WhichExtra) == 0) && ( GET_WT_FIVE(&ExtraWt1) != GET_WT_FIVE(&ExtraWt2) ) ) { GET_WT_FIVE(&WhichExtra) = ( GET_WT_FIVE(&ExtraWt1) < GET_WT_FIVE(&ExtraWt2) ) ? CSTR_LESS_THAN : CSTR_GREATER_THAN; } if ( (GET_WT_SIX(&WhichExtra) == 0) && ( GET_WT_SIX(&ExtraWt1) != GET_WT_SIX(&ExtraWt2) ) ) { GET_WT_SIX(&WhichExtra) = ( GET_WT_SIX(&ExtraWt1) < GET_WT_SIX(&ExtraWt2) ) ? CSTR_LESS_THAN : CSTR_GREATER_THAN; } if ( (GET_WT_SEVEN(&WhichExtra) == 0) && ( GET_WT_SEVEN(&ExtraWt1) != GET_WT_SEVEN(&ExtraWt2) ) ) { GET_WT_SEVEN(&WhichExtra) = ( GET_WT_SEVEN(&ExtraWt1) < GET_WT_SEVEN(&ExtraWt2) ) ? CSTR_LESS_THAN : CSTR_GREATER_THAN; } } } break; } case ( JAMO_SPECIAL ) : { if (!JamoFlag) { int ctr1, ctr2; // dummy variables for FindJamoDifference
LPWSTR pStr1 = pString1; LPWSTR pStr2 = pString2;
//
// Set the JamoFlag so we don't handle it again.
//
JamoFlag = TRUE; fContinue = FindJamoDifference( pHashN, &pStr1, &ctr1, -2, &Weight1, &pStr2, &ctr2, -2, &Weight2, &pLastJamo, &uw1, &uw2, &State, &WhichJamo, fModify ); if (WhichJamo) { return (WhichJamo); } pString1 = pStr1; pString2 = pStr2; } else { JamoFlag = FALSE; }
break; } case ( EXTENSION_A ) : { //
// If sm1 is an extension A character, then
// both sm1 and sm2 have been handled. We should
// only get here when either sm1 is not an
// extension A character or the two extension A
// characters are different.
//
if (sm1 != EXTENSION_A) { //
// Get the actual UW to compare.
//
// Only string2 contains an extension A char,
// so set the UW value to be the first UW
// value for extension A (default values):
// SM_EXT_A, AW_EXT_A
//
uw2 = MAKE_UNICODE_WT(SM_EXT_A, AW_EXT_A, fModify); }
//
// We should then fall through to the comparison
// of the Unicode weights.
//
break; } case ( UNSORTABLE ) : { //
// Fill out the case statement so the compiler
// will use a jump table.
//
break; } }
//
// See if the comparison should start again.
//
if (fContinue) { continue; }
//
// We're not supposed to drop down into the state table if
// unicode weights are different, so stop comparison and
// return result of unicode weight comparison.
//
if (uw1 != uw2) { return ((uw1 < uw2) ? CSTR_LESS_THAN : CSTR_GREATER_THAN); } }
//
// For each state in the state table, do the appropriate
// comparisons. (UW1 == UW2)
//
if (State & (STATE_DW | STATE_REVERSE_DW)) { //
// Get the diacritic weights.
//
dw1 = GET_DIACRITIC(&Weight1); dw2 = GET_DIACRITIC(&Weight2);
if (dw1 != dw2) { //
// Look ahead to see if diacritic follows a
// minimum diacritic weight. If so, get the
// diacritic weight of the nonspace mark.
//
while (*(pString1 + 1) != 0) { Wt = GET_DWORD_WEIGHT(pHashN, *(pString1 + 1)); if (GET_SCRIPT_MEMBER(&Wt) == NONSPACE_MARK) { dw1 += GET_DIACRITIC(&Wt); pString1++; } else { break; } }
while (*(pString2 + 1) != 0) { Wt = GET_DWORD_WEIGHT(pHashN, *(pString2 + 1)); if (GET_SCRIPT_MEMBER(&Wt) == NONSPACE_MARK) { dw2 += GET_DIACRITIC(&Wt); pString2++; } else { break; } }
//
// Save which string has the smaller diacritic
// weight if the diacritic weights are still
// different.
//
if (dw1 != dw2) { WhichDiacritic = (dw1 < dw2) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
//
// Remove state from state machine.
//
REMOVE_STATE(STATE_DW); } } } if (State & STATE_CW) { //
// Get the case weights.
//
if (GET_CASE(&Weight1) != GET_CASE(&Weight2)) { //
// Save which string has the smaller case weight.
//
WhichCase = (GET_CASE(&Weight1) < GET_CASE(&Weight2)) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
//
// Remove state from state machine.
//
REMOVE_STATE(STATE_CW); } } }
//
// Fixup the pointers.
//
POINTER_FIXUP(); }
//
// If the end of BOTH strings has been reached, then the unicode
// weights match exactly. Check the diacritic, case and special
// weights. If all are zero, then return success. Otherwise,
// return the result of the weight difference.
//
// NOTE: The following checks MUST REMAIN IN THIS ORDER:
// Diacritic, Case, Punctuation.
//
if (*pString1 == 0) { if (*pString2 == 0) { if (WhichDiacritic) { return (WhichDiacritic); } if (WhichCase) { return (WhichCase); } if (WhichExtra) { if (GET_WT_FOUR(&WhichExtra)) { return (GET_WT_FOUR(&WhichExtra)); } if (GET_WT_FIVE(&WhichExtra)) { return (GET_WT_FIVE(&WhichExtra)); } if (GET_WT_SIX(&WhichExtra)) { return (GET_WT_SIX(&WhichExtra)); } if (GET_WT_SEVEN(&WhichExtra)) { return (GET_WT_SEVEN(&WhichExtra)); } } if (WhichPunct1) { return (WhichPunct1); } if (WhichPunct2) { return (WhichPunct2); }
return (CSTR_EQUAL); } else { //
// String 2 is longer.
//
pString1 = pString2; } }
//
// Scan to the end of the longer string.
//
QUICK_SCAN_LONGER_STRING( pString1, ((*pString2 == 0) ? CSTR_GREATER_THAN : CSTR_LESS_THAN) ); }
////////////////////////////////////////////////////////////////////////////
//
// GetStringTypeExW
//
// Returns character type information about a particular Unicode string.
//
// 01-18-94 JulieB Created.
////////////////////////////////////////////////////////////////////////////
BOOL WINAPI GetStringTypeExW( LCID Locale, DWORD dwInfoType, LPCWSTR lpSrcStr, int cchSrc, LPWORD lpCharType) { PLOC_HASH pHashN; // ptr to LOC hash node
//
// Invalid Parameter Check:
// - Validate LCID
//
VALIDATE_LOCALE(Locale, pHashN, FALSE); if (pHashN == NULL) { SetLastError(ERROR_INVALID_PARAMETER); return (0); }
//
// Return the result of GetStringTypeW.
//
return (GetStringTypeW( dwInfoType, lpSrcStr, cchSrc, lpCharType )); }
////////////////////////////////////////////////////////////////////////////
//
// GetStringTypeW
//
// Returns character type information about a particular Unicode string.
//
// NOTE: The number of parameters is different from GetStringTypeA.
// The 16-bit OLE product shipped GetStringTypeA with the wrong
// parameters (ported from Chicago) and now we must support it.
//
// Use GetStringTypeEx to get the same set of parameters between
// the A and W version.
//
// 05-31-91 JulieB Created.
////////////////////////////////////////////////////////////////////////////
BOOL WINAPI GetStringTypeW( DWORD dwInfoType, LPCWSTR lpSrcStr, int cchSrc, LPWORD lpCharType) { int Ctr; // loop counter
//
// Invalid Parameter Check:
// - lpSrcStr NULL
// - cchSrc is 0
// - lpCharType NULL
// - same buffer - src and destination
// - (flags will be checked in switch statement below)
//
if ( (lpSrcStr == NULL) || (cchSrc == 0) || (lpCharType == NULL) || (lpSrcStr == lpCharType) ) { SetLastError(ERROR_INVALID_PARAMETER); return (FALSE); }
//
// If cchSrc is -1, then the source string is null terminated and we
// need to get the length of the source string. Add one to the
// length to include the null termination.
// (This will always be at least 1.)
//
if (cchSrc <= -1) { cchSrc = NlsStrLenW(lpSrcStr) + 1; }
//
// Make sure the ctype table is mapped in.
//
if (GetCTypeFileInfo()) { SetLastError(ERROR_FILE_NOT_FOUND); return (FALSE); }
//
// Return the appropriate information in the lpCharType parameter
// based on the dwInfoType parameter.
//
switch (dwInfoType) { case ( CT_CTYPE1 ) : { //
// Return the ctype 1 information for the string.
//
for (Ctr = 0; Ctr < cchSrc; Ctr++) { lpCharType[Ctr] = GET_CTYPE(lpSrcStr[Ctr], CType1); } break; } case ( CT_CTYPE2 ) : { //
// Return the ctype 2 information.
//
for (Ctr = 0; Ctr < cchSrc; Ctr++) { lpCharType[Ctr] = GET_CTYPE(lpSrcStr[Ctr], CType2); } break; } case ( CT_CTYPE3 ) : { //
// Return the ctype 3 information.
//
for (Ctr = 0; Ctr < cchSrc; Ctr++) { lpCharType[Ctr] = GET_CTYPE(lpSrcStr[Ctr], CType3); } break; } default : { //
// Invalid flag parameter, so return failure.
//
SetLastError(ERROR_INVALID_FLAGS); return (FALSE); } }
//
// Return success.
//
return (TRUE); }
//-------------------------------------------------------------------------//
// INTERNAL ROUTINES //
//-------------------------------------------------------------------------//
////////////////////////////////////////////////////////////////////////////
//
// LongCompareStringW
//
// Compares two wide character strings of the same locale according to the
// supplied locale handle.
//
// 05-31-91 JulieB Created.
////////////////////////////////////////////////////////////////////////////
int LongCompareStringW( PLOC_HASH pHashN, DWORD dwCmpFlags, LPCWSTR lpString1, int cchCount1, LPCWSTR lpString2, int cchCount2, BOOL fModify) { int ctr1 = cchCount1; // loop counter for string 1
int ctr2 = cchCount2; // loop counter for string 2
register LPWSTR pString1; // ptr to go thru string 1
register LPWSTR pString2; // ptr to go thru string 2
BOOL IfCompress; // if compression in locale
BOOL IfDblCompress1; // if double compression in string 1
BOOL IfDblCompress2; // if double compression in string 2
BOOL fEnd1; // if at end of string 1
BOOL fIgnorePunct; // flag to ignore punctuation (not symbol)
BOOL fIgnoreDiacritic; // flag to ignore diacritics
BOOL fIgnoreSymbol; // flag to ignore symbols
BOOL fStringSort; // flag to use string sort
DWORD State; // state table
DWORD Mask; // mask for weights
DWORD Weight1; // full weight of char - string 1
DWORD Weight2; // full weight of char - string 2
int JamoFlag = FALSE; LPCWSTR pLastJamo = lpString1;
int WhichDiacritic; // DW => 1 = str1 smaller, 3 = str2 smaller
int WhichCase; // CW => 1 = str1 smaller, 3 = str2 smaller
int WhichJamo; // XW for Jamo
int WhichPunct1; // SW => 1 = str1 smaller, 3 = str2 smaller
int WhichPunct2; // SW => 1 = str1 smaller, 3 = str2 smaller
LPWSTR pSave1; // ptr to saved pString1
LPWSTR pSave2; // ptr to saved pString2
int cExpChar1, cExpChar2; // ct of expansions in tmp
DWORD ExtraWt1, ExtraWt2; // extra weight values (for far east)
DWORD WhichExtra; // XW => wts 4, 5, 6, 7 (for far east)
//
// Initialize string pointers.
//
pString1 = (LPWSTR)lpString1; pString2 = (LPWSTR)lpString2;
//
// Invalid Flags Check:
// - invalid flags
//
if (dwCmpFlags & CS_INVALID_FLAG) { SetLastError(ERROR_INVALID_FLAGS); return (0); }
//
// See if we should stop on the null terminator regardless of the
// count values. The original count values are stored in ctr1 and ctr2
// above, so it's ok to set these here.
//
if (dwCmpFlags & NORM_STOP_ON_NULL) { cchCount1 = cchCount2 = -2; }
//
// Check if compression in the given locale. If not, then
// try a wchar by wchar compare. If strings are equal, this
// will be quick.
//
if ((IfCompress = pHashN->IfCompression) == FALSE) { //
// Compare each wide character in the two strings.
//
while ( NOT_END_STRING(ctr1, pString1, cchCount1) && NOT_END_STRING(ctr2, pString2, cchCount2) ) { //
// See if characters are equal.
//
if (*pString1 == *pString2) { //
// Characters are equal, so increment pointers,
// decrement counters, and continue string compare.
//
pString1++; pString2++; ctr1--; ctr2--; } else { //
// Difference was found. Fall into the sortkey
// check below.
//
break; } }
//
// If the end of BOTH strings has been reached, then the strings
// match exactly. Return success.
//
if ( AT_STRING_END(ctr1, pString1, cchCount1) && AT_STRING_END(ctr2, pString2, cchCount2) ) { return (CSTR_EQUAL); } }
//
// Initialize flags, pointers, and counters.
//
fIgnorePunct = dwCmpFlags & NORM_IGNORESYMBOLS; fIgnoreDiacritic = dwCmpFlags & NORM_IGNORENONSPACE; fIgnoreSymbol = fIgnorePunct; fStringSort = dwCmpFlags & SORT_STRINGSORT; WhichDiacritic = 0; WhichCase = 0; WhichJamo = 0; WhichPunct1 = 0; WhichPunct2 = 0; pSave1 = NULL; pSave2 = NULL; ExtraWt1 = (DWORD)0; WhichExtra = (DWORD)0;
//
// Set the weights to be invalid. This flags whether or not to
// recompute the weights next time through the loop. It also flags
// whether or not to start over (continue) in the loop.
//
Weight1 = CMP_INVALID_WEIGHT; Weight2 = CMP_INVALID_WEIGHT;
//
// Switch on the different flag options. This will speed up
// the comparisons of two strings that are different.
//
State = STATE_CW | STATE_JAMO_WEIGHT; switch (dwCmpFlags & (NORM_IGNORECASE | NORM_IGNORENONSPACE)) { case ( 0 ) : { Mask = CMP_MASKOFF_NONE; State |= (pHashN->IfReverseDW) ? STATE_REVERSE_DW : STATE_DW;
break; }
case ( NORM_IGNORECASE ) : { Mask = CMP_MASKOFF_CW; State |= (pHashN->IfReverseDW) ? STATE_REVERSE_DW : STATE_DW;
break; }
case ( NORM_IGNORENONSPACE ) : { Mask = CMP_MASKOFF_DW;
break; }
case ( NORM_IGNORECASE | NORM_IGNORENONSPACE ) : { Mask = CMP_MASKOFF_DW_CW;
break; } }
switch (dwCmpFlags & (NORM_IGNOREKANATYPE | NORM_IGNOREWIDTH)) { case ( 0 ) : { break; }
case ( NORM_IGNOREKANATYPE ) : { Mask &= CMP_MASKOFF_KANA;
break; }
case ( NORM_IGNOREWIDTH ) : { Mask &= CMP_MASKOFF_WIDTH;
if (dwCmpFlags & NORM_IGNORECASE) { REMOVE_STATE(STATE_CW); }
break; }
case ( NORM_IGNOREKANATYPE | NORM_IGNOREWIDTH ) : { Mask &= CMP_MASKOFF_KANA_WIDTH;
if (dwCmpFlags & NORM_IGNORECASE) { REMOVE_STATE(STATE_CW); }
break; } }
//
// Compare each character's sortkey weight in the two strings.
//
while ( NOT_END_STRING(ctr1, pString1, cchCount1) && NOT_END_STRING(ctr2, pString2, cchCount2) ) { if (Weight1 == CMP_INVALID_WEIGHT) { Weight1 = GET_DWORD_WEIGHT(pHashN, *pString1); Weight1 &= Mask; } if (Weight2 == CMP_INVALID_WEIGHT) { Weight2 = GET_DWORD_WEIGHT(pHashN, *pString2); Weight2 &= Mask; }
//
// If compression locale, then need to check for compression
// characters even if the weights are equal. If it's not a
// compression locale, then we don't need to check anything
// if the weights are equal.
//
if ( (IfCompress) && (GET_COMPRESSION(&Weight1) || GET_COMPRESSION(&Weight2)) ) { int ctr; // loop counter
PCOMPRESS_3 pComp3; // ptr to compress 3 table
PCOMPRESS_2 pComp2; // ptr to compress 2 table
int If1; // if compression found in string 1
int If2; // if compression found in string 2
int CompVal; // compression value
int IfEnd1; // if exists 1 more char in string 1
int IfEnd2; // if exists 1 more char in string 2
//
// Check for compression in the weights.
//
If1 = GET_COMPRESSION(&Weight1); If2 = GET_COMPRESSION(&Weight2); CompVal = ((If1 > If2) ? If1 : If2);
IfEnd1 = AT_STRING_END(ctr1 - 1, pString1 + 1, cchCount1); IfEnd2 = AT_STRING_END(ctr2 - 1, pString2 + 1, cchCount2);
if (pHashN->IfDblCompression == FALSE) { //
// NO double compression, so don't check for it.
//
switch (CompVal) { //
// Check for 3 characters compressing to 1.
//
case ( COMPRESS_3_MASK ) : { //
// Check character in string 1 and string 2.
//
if ( ((If1) && (!IfEnd1) && !AT_STRING_END(ctr1 - 2, pString1 + 2, cchCount1)) || ((If2) && (!IfEnd2) && !AT_STRING_END(ctr2 - 2, pString2 + 2, cchCount2)) ) { ctr = pHashN->pCompHdr->Num3; pComp3 = pHashN->pCompress3; for (; ctr > 0; ctr--, pComp3++) { //
// Check character in string 1.
//
if ( (If1) && (!IfEnd1) && !AT_STRING_END(ctr1 - 2, pString1 + 2, cchCount1) && (pComp3->UCP1 == *pString1) && (pComp3->UCP2 == *(pString1 + 1)) && (pComp3->UCP3 == *(pString1 + 2)) ) { //
// Found compression for string 1.
// Get new weight and mask it.
// Increment pointer and decrement counter.
//
Weight1 = MAKE_SORTKEY_DWORD(pComp3->Weights); Weight1 &= Mask; pString1 += 2; ctr1 -= 2;
//
// Set boolean for string 1 - search is
// complete.
//
If1 = 0;
//
// Break out of loop if both searches are
// done.
//
if (If2 == 0) { break; } }
//
// Check character in string 2.
//
if ( (If2) && (!IfEnd2) && !AT_STRING_END(ctr2 - 2, pString2 + 2, cchCount2) && (pComp3->UCP1 == *pString2) && (pComp3->UCP2 == *(pString2 + 1)) && (pComp3->UCP3 == *(pString2 + 2)) ) { //
// Found compression for string 2.
// Get new weight and mask it.
// Increment pointer and decrement counter.
//
Weight2 = MAKE_SORTKEY_DWORD(pComp3->Weights); Weight2 &= Mask; pString2 += 2; ctr2 -= 2;
//
// Set boolean for string 2 - search is
// complete.
//
If2 = 0;
//
// Break out of loop if both searches are
// done.
//
if (If1 == 0) { break; } } } if (ctr > 0) { break; } } //
// Fall through if not found.
//
}
//
// Check for 2 characters compressing to 1.
//
case ( COMPRESS_2_MASK ) : { //
// Check character in string 1 and string 2.
//
if ( ((If1) && (!IfEnd1)) || ((If2) && (!IfEnd2)) ) { ctr = pHashN->pCompHdr->Num2; pComp2 = pHashN->pCompress2; for (; ((ctr > 0) && (If1 || If2)); ctr--, pComp2++) { //
// Check character in string 1.
//
if ( (If1) && (!IfEnd1) && (pComp2->UCP1 == *pString1) && (pComp2->UCP2 == *(pString1 + 1)) ) { //
// Found compression for string 1.
// Get new weight and mask it.
// Increment pointer and decrement counter.
//
Weight1 = MAKE_SORTKEY_DWORD(pComp2->Weights); Weight1 &= Mask; pString1++; ctr1--;
//
// Set boolean for string 1 - search is
// complete.
//
If1 = 0;
//
// Break out of loop if both searches are
// done.
//
if (If2 == 0) { break; } }
//
// Check character in string 2.
//
if ( (If2) && (!IfEnd2) && (pComp2->UCP1 == *pString2) && (pComp2->UCP2 == *(pString2 + 1)) ) { //
// Found compression for string 2.
// Get new weight and mask it.
// Increment pointer and decrement counter.
//
Weight2 = MAKE_SORTKEY_DWORD(pComp2->Weights); Weight2 &= Mask; pString2++; ctr2--;
//
// Set boolean for string 2 - search is
// complete.
//
If2 = 0;
//
// Break out of loop if both searches are
// done.
//
if (If1 == 0) { break; } } } if (ctr > 0) { break; } } } } } else if (!IfEnd1 && !IfEnd2) { //
// Double Compression exists, so must check for it.
//
if (IfDblCompress1 = ((GET_DWORD_WEIGHT(pHashN, *pString1) & CMP_MASKOFF_CW) == (GET_DWORD_WEIGHT(pHashN, *(pString1 + 1)) & CMP_MASKOFF_CW))) { //
// Advance past the first code point to get to the
// compression character.
//
pString1++; ctr1--; IfEnd1 = AT_STRING_END(ctr1 - 1, pString1 + 1, cchCount1); }
if (IfDblCompress2 = ((GET_DWORD_WEIGHT(pHashN, *pString2) & CMP_MASKOFF_CW) == (GET_DWORD_WEIGHT(pHashN, *(pString2 + 1)) & CMP_MASKOFF_CW))) { //
// Advance past the first code point to get to the
// compression character.
//
pString2++; ctr2--; IfEnd2 = AT_STRING_END(ctr2 - 1, pString2 + 1, cchCount2); }
switch (CompVal) { //
// Check for 3 characters compressing to 1.
//
case ( COMPRESS_3_MASK ) : { //
// Check character in string 1.
//
if ( (If1) && (!IfEnd1) && !AT_STRING_END(ctr1 - 2, pString1 + 2, cchCount1) ) { ctr = pHashN->pCompHdr->Num3; pComp3 = pHashN->pCompress3; for (; ctr > 0; ctr--, pComp3++) { //
// Check character in string 1.
//
if ( (pComp3->UCP1 == *pString1) && (pComp3->UCP2 == *(pString1 + 1)) && (pComp3->UCP3 == *(pString1 + 2)) ) { //
// Found compression for string 1.
// Get new weight and mask it.
// Increment pointer and decrement counter.
//
Weight1 = MAKE_SORTKEY_DWORD(pComp3->Weights); Weight1 &= Mask; if (!IfDblCompress1) { pString1 += 2; ctr1 -= 2; }
//
// Set boolean for string 1 - search is
// complete.
//
If1 = 0;
break; } } }
//
// Check character in string 2.
//
if ( (If2) && (!IfEnd2) && !AT_STRING_END(ctr2 - 2, pString2 + 2, cchCount2) ) { ctr = pHashN->pCompHdr->Num3; pComp3 = pHashN->pCompress3; for (; ctr > 0; ctr--, pComp3++) { //
// Check character in string 2.
//
if ( (pComp3->UCP1 == *pString2) && (pComp3->UCP2 == *(pString2 + 1)) && (pComp3->UCP3 == *(pString2 + 2)) ) { //
// Found compression for string 2.
// Get new weight and mask it.
// Increment pointer and decrement counter.
//
Weight2 = MAKE_SORTKEY_DWORD(pComp3->Weights); Weight2 &= Mask; if (!IfDblCompress2) { pString2 += 2; ctr2 -= 2; }
//
// Set boolean for string 2 - search is
// complete.
//
If2 = 0;
break; } } }
//
// Fall through if not found.
//
if ((If1 == 0) && (If2 == 0)) { break; } }
//
// Check for 2 characters compressing to 1.
//
case ( COMPRESS_2_MASK ) : { //
// Check character in string 1.
//
if ((If1) && (!IfEnd1)) { ctr = pHashN->pCompHdr->Num2; pComp2 = pHashN->pCompress2; for (; ctr > 0; ctr--, pComp2++) { //
// Check character in string 1.
//
if ((pComp2->UCP1 == *pString1) && (pComp2->UCP2 == *(pString1 + 1))) { //
// Found compression for string 1.
// Get new weight and mask it.
// Increment pointer and decrement counter.
//
Weight1 = MAKE_SORTKEY_DWORD(pComp2->Weights); Weight1 &= Mask; if (!IfDblCompress1) { pString1++; ctr1--; }
//
// Set boolean for string 1 - search is
// complete.
//
If1 = 0;
break; } } }
//
// Check character in string 2.
//
if ((If2) && (!IfEnd2)) { ctr = pHashN->pCompHdr->Num2; pComp2 = pHashN->pCompress2; for (; ctr > 0; ctr--, pComp2++) { //
// Check character in string 2.
//
if ((pComp2->UCP1 == *pString2) && (pComp2->UCP2 == *(pString2 + 1))) { //
// Found compression for string 2.
// Get new weight and mask it.
// Increment pointer and decrement counter.
//
Weight2 = MAKE_SORTKEY_DWORD(pComp2->Weights); Weight2 &= Mask; if (!IfDblCompress2) { pString2++; ctr2--; }
//
// Set boolean for string 2 - search is
// complete.
//
If2 = 0;
break; } } } } }
//
// Reset the pointer back to the beginning of the double
// compression. Pointer fixup at the end will advance
// them correctly.
//
// If double compression, we advanced the pointer at
// the beginning of the switch statement. If double
// compression character was actually found, the pointer
// was NOT advanced. We now want to decrement the pointer
// to put it back to where it was.
//
// The next time through, the pointer will be pointing to
// the regular compression part of the string.
//
if (IfDblCompress1) { pString1--; ctr1++; } if (IfDblCompress2) { pString2--; ctr2++; } } }
//
// Check the weights again.
//
if ((Weight1 != Weight2) || (GET_SCRIPT_MEMBER(&Weight1) == EXTENSION_A)) { //
// Weights are still not equal, even after compression
// check, so compare the different weights.
//
BYTE sm1 = GET_SCRIPT_MEMBER(&Weight1); // script member 1
BYTE sm2 = GET_SCRIPT_MEMBER(&Weight2); // script member 2
WORD uw1 = GET_UNICODE_SM_MOD(&Weight1, sm1, fModify); // unicode weight 1
WORD uw2 = GET_UNICODE_SM_MOD(&Weight2, sm2, fModify); // unicode weight 2
BYTE dw1; // diacritic weight 1
BYTE dw2; // diacritic weight 2
DWORD Wt; // temp weight holder
WCHAR pTmpBuf1[MAX_TBL_EXPANSION]; // temp buffer for exp 1
WCHAR pTmpBuf2[MAX_TBL_EXPANSION]; // temp buffer for exp 2
//
// If Unicode Weights are different and no special cases,
// then we're done. Otherwise, we need to do extra checking.
//
// Must check ENTIRE string for any possibility of Unicode Weight
// differences. As soon as a Unicode Weight difference is found,
// then we're done. If no UW difference is found, then the
// first Diacritic Weight difference is used. If no DW difference
// is found, then use the first Case Difference. If no CW
// difference is found, then use the first Extra Weight
// difference. If no XW difference is found, then use the first
// Special Weight difference.
//
if ((uw1 != uw2) || ((sm1 <= SYMBOL_5) && (sm1 >= FAREAST_SPECIAL))) { //
// Check for Unsortable characters and skip them.
// This needs to be outside the switch statement. If EITHER
// character is unsortable, must skip it and start over.
//
if (sm1 == UNSORTABLE) { pString1++; ctr1--; Weight1 = CMP_INVALID_WEIGHT; } if (sm2 == UNSORTABLE) { pString2++; ctr2--; Weight2 = CMP_INVALID_WEIGHT; }
//
// Check for Ignore Nonspace and Ignore Symbol. If
// Ignore Nonspace is set and either character is a
// nonspace mark only, then we need to advance the
// pointer to skip over the character and continue.
// If Ignore Symbol is set and either character is a
// punctuation char, then we need to advance the
// pointer to skip over the character and continue.
//
// This step is necessary so that a string with a
// nonspace mark and a punctuation char following one
// another are properly ignored when one or both of
// the ignore flags is set.
//
if (fIgnoreDiacritic) { if (sm1 == NONSPACE_MARK) { pString1++; ctr1--; Weight1 = CMP_INVALID_WEIGHT; } if (sm2 == NONSPACE_MARK) { pString2++; ctr2--; Weight2 = CMP_INVALID_WEIGHT; } } if (fIgnoreSymbol) { if (sm1 == PUNCTUATION) { pString1++; ctr1--; Weight1 = CMP_INVALID_WEIGHT; } if (sm2 == PUNCTUATION) { pString2++; ctr2--; Weight2 = CMP_INVALID_WEIGHT; } } if ((Weight1 == CMP_INVALID_WEIGHT) || (Weight2 == CMP_INVALID_WEIGHT)) { continue; }
//
// Switch on the script member of string 1 and take care
// of any special cases.
//
switch (sm1) { case ( NONSPACE_MARK ) : { //
// Nonspace only - look at diacritic weight only.
//
if (!fIgnoreDiacritic) { if ((WhichDiacritic == 0) || (State & STATE_REVERSE_DW)) { WhichDiacritic = CSTR_GREATER_THAN;
//
// Remove state from state machine.
//
REMOVE_STATE(STATE_DW); } }
//
// Adjust pointer and counter and set flags.
//
pString1++; ctr1--; Weight1 = CMP_INVALID_WEIGHT;
break; } case ( SYMBOL_1 ) : case ( SYMBOL_2 ) : case ( SYMBOL_3 ) : case ( SYMBOL_4 ) : case ( SYMBOL_5 ) : { //
// If the ignore symbol flag is set, then skip over
// the symbol.
//
if (fIgnoreSymbol) { pString1++; ctr1--; Weight1 = CMP_INVALID_WEIGHT; }
break; } case ( PUNCTUATION ) : { //
// If the ignore punctuation flag is set, then skip
// over the punctuation char.
//
if (fIgnorePunct) { pString1++; ctr1--; Weight1 = CMP_INVALID_WEIGHT; } else if (!fStringSort) { //
// Use WORD sort method.
//
if (sm2 != PUNCTUATION) { //
// The character in the second string is
// NOT punctuation.
//
if (WhichPunct2) { //
// Set WP 2 to show that string 2 is
// smaller, since a punctuation char had
// already been found at an earlier
// position in string 2.
//
// Set the Ignore Punctuation flag so we
// just skip over any other punctuation
// chars in the string.
//
WhichPunct2 = CSTR_GREATER_THAN; fIgnorePunct = TRUE; } else { //
// Set WP 1 to show that string 2 is
// smaller, and that string 1 has had
// a punctuation char - since no
// punctuation chars have been found
// in string 2.
//
WhichPunct1 = CSTR_GREATER_THAN; }
//
// Advance pointer 1 and decrement counter 1.
//
pString1++; ctr1--; Weight1 = CMP_INVALID_WEIGHT; }
//
// Do NOT want to advance the pointer in string 1
// if string 2 is also a punctuation char. This
// will be done later.
//
}
break; } case ( EXPANSION ) : { //
// Save pointer in pString1 so that it can be
// restored.
//
if (pSave1 == NULL) { pSave1 = pString1; } pString1 = pTmpBuf1;
//
// Add one to counter so that subtraction doesn't end
// comparison prematurely.
//
ctr1++;
//
// Expand character into temporary buffer.
//
pTmpBuf1[0] = GET_EXPANSION_1(&Weight1); pTmpBuf1[1] = GET_EXPANSION_2(&Weight1);
//
// Set cExpChar1 to the number of expansion characters
// stored.
//
cExpChar1 = MAX_TBL_EXPANSION;
Weight1 = CMP_INVALID_WEIGHT;
break; } case ( FAREAST_SPECIAL ) : { if (sm2 != EXPANSION) { //
// Get the weight for the far east special case
// and store it in Weight1.
//
GET_FAREAST_WEIGHT( Weight1, uw1, Mask, lpString1, pString1, ExtraWt1, fModify );
if (sm2 != FAREAST_SPECIAL) { //
// The character in the second string is
// NOT a fareast special char.
//
// Set each of weights 4, 5, 6, and 7 to show
// that string 2 is smaller (if not already set).
//
if ((GET_WT_FOUR(&WhichExtra) == 0) && (GET_WT_FOUR(&ExtraWt1) != 0)) { GET_WT_FOUR(&WhichExtra) = CSTR_GREATER_THAN; } if ((GET_WT_FIVE(&WhichExtra) == 0) && (GET_WT_FIVE(&ExtraWt1) != 0)) { GET_WT_FIVE(&WhichExtra) = CSTR_GREATER_THAN; } if ((GET_WT_SIX(&WhichExtra) == 0) && (GET_WT_SIX(&ExtraWt1) != 0)) { GET_WT_SIX(&WhichExtra) = CSTR_GREATER_THAN; } if ((GET_WT_SEVEN(&WhichExtra) == 0) && (GET_WT_SEVEN(&ExtraWt1) != 0)) { GET_WT_SEVEN(&WhichExtra) = CSTR_GREATER_THAN; } } } break; } case ( JAMO_SPECIAL ) : { LPWSTR pStr1 = pString1; LPWSTR pStr2 = pString2;
//
// Set the JamoFlag so we don't handle it again.
//
JamoFlag = TRUE; FindJamoDifference( pHashN, &pStr1, &ctr1, cchCount1, &Weight1, &pStr2, &ctr2, cchCount2, &Weight2, &pLastJamo, &uw1, &uw2, &State, &WhichJamo, fModify );
if (WhichJamo) { return (WhichJamo); } pString1 = pStr1; pString2 = pStr2;
break; } case ( EXTENSION_A ) : { //
// Get the full weight in case DW got masked.
//
Weight1 = GET_DWORD_WEIGHT(pHashN, *pString1); if (sm2 == EXTENSION_A) { Weight2 = GET_DWORD_WEIGHT(pHashN, *pString2); }
//
// Compare the weights.
//
if (Weight1 == Weight2) { //
// Adjust pointers and counters and set flags.
//
pString1++; pString2++; ctr1--; ctr2--; Weight1 = CMP_INVALID_WEIGHT; Weight2 = CMP_INVALID_WEIGHT; } else { //
// Get the actual UW to compare.
//
if (sm2 == EXTENSION_A) { //
// Set the UW values to be the AW and DW since
// both strings contain an extension A char.
//
uw1 = MAKE_UNICODE_WT( GET_ALPHA_NUMERIC(&Weight1), GET_DIACRITIC(&Weight1), FALSE ); uw2 = MAKE_UNICODE_WT( GET_ALPHA_NUMERIC(&Weight2), GET_DIACRITIC(&Weight2), FALSE ); } else { //
// Only string1 contains an extension A char,
// so set the UW value to be the first UW
// value for extension A (default values):
// SM_EXT_A, AW_EXT_A
//
uw1 = MAKE_UNICODE_WT(SM_EXT_A, AW_EXT_A, fModify); } }
break; } case ( UNSORTABLE ) : { //
// Fill out the case statement so the compiler
// will use a jump table.
//
break; } }
//
// Switch on the script member of string 2 and take care
// of any special cases.
//
switch (sm2) { case ( NONSPACE_MARK ) : { //
// Nonspace only - look at diacritic weight only.
//
if (!fIgnoreDiacritic) { if ((WhichDiacritic == 0) || (State & STATE_REVERSE_DW))
{ WhichDiacritic = CSTR_LESS_THAN;
//
// Remove state from state machine.
//
REMOVE_STATE(STATE_DW); } }
//
// Adjust pointer and counter and set flags.
//
pString2++; ctr2--; Weight2 = CMP_INVALID_WEIGHT;
break; } case ( SYMBOL_1 ) : case ( SYMBOL_2 ) : case ( SYMBOL_3 ) : case ( SYMBOL_4 ) : case ( SYMBOL_5 ) : { //
// If the ignore symbol flag is set, then skip over
// the symbol.
//
if (fIgnoreSymbol) { pString2++; ctr2--; Weight2 = CMP_INVALID_WEIGHT; }
break; } case ( PUNCTUATION ) : { //
// If the ignore punctuation flag is set, then
// skip over the punctuation char.
//
if (fIgnorePunct) { //
// Advance pointer 2 and decrement counter 2.
//
pString2++; ctr2--; Weight2 = CMP_INVALID_WEIGHT; } else if (!fStringSort) { //
// Use WORD sort method.
//
if (sm1 != PUNCTUATION) { //
// The character in the first string is
// NOT punctuation.
//
if (WhichPunct1) { //
// Set WP 1 to show that string 1 is
// smaller, since a punctuation char had
// already been found at an earlier
// position in string 1.
//
// Set the Ignore Punctuation flag so we
// just skip over any other punctuation
// chars in the string.
//
WhichPunct1 = CSTR_LESS_THAN; fIgnorePunct = TRUE; } else { //
// Set WP 2 to show that string 1 is
// smaller, and that string 2 has had
// a punctuation char - since no
// punctuation chars have been found
// in string 1.
//
WhichPunct2 = CSTR_LESS_THAN; }
//
// Pointer 2 and counter 2 will be updated
// after if-else statement.
//
} else { //
// Both code points are punctuation chars.
//
// See if either of the strings has encountered
// punctuation chars previous to this.
//
if (WhichPunct1) { //
// String 1 has had a punctuation char, so
// it should be the smaller string (since
// both have punctuation chars).
//
WhichPunct1 = CSTR_LESS_THAN; } else if (WhichPunct2) { //
// String 2 has had a punctuation char, so
// it should be the smaller string (since
// both have punctuation chars).
//
WhichPunct2 = CSTR_GREATER_THAN; } else { BYTE aw1 = GET_ALPHA_NUMERIC(&Weight1); BYTE aw2 = GET_ALPHA_NUMERIC(&Weight2);
if (aw1 == aw2) { BYTE cw1 = GET_CASE(&Weight1); BYTE cw2 = GET_CASE(&Weight2); if (cw1 < cw2) { WhichPunct1 = CSTR_LESS_THAN; } else if (cw1 > cw2) { WhichPunct1 = CSTR_GREATER_THAN; } } else { //
// Position is the same, so compare the
// special weights. Set WhichPunct1 to
// the smaller special weight.
//
WhichPunct1 = (aw1 < aw2 ? CSTR_LESS_THAN : CSTR_GREATER_THAN); } }
//
// Set the Ignore Punctuation flag.
//
fIgnorePunct = TRUE;
//
// Advance pointer 1 and decrement counter 1.
// Pointer 2 and counter 2 will be updated
// after if-else statement.
//
pString1++; ctr1--; Weight1 = CMP_INVALID_WEIGHT; }
//
// Advance pointer 2 and decrement counter 2.
//
pString2++; ctr2--; Weight2 = CMP_INVALID_WEIGHT; }
break; } case ( EXPANSION ) : { //
// Save pointer in pString1 so that it can be restored.
//
if (pSave2 == NULL) { pSave2 = pString2; } pString2 = pTmpBuf2;
//
// Add one to counter so that subtraction doesn't end
// comparison prematurely.
//
ctr2++;
//
// Expand character into temporary buffer.
//
pTmpBuf2[0] = GET_EXPANSION_1(&Weight2); pTmpBuf2[1] = GET_EXPANSION_2(&Weight2);
//
// Set cExpChar2 to the number of expansion characters
// stored.
//
cExpChar2 = MAX_TBL_EXPANSION;
Weight2 = CMP_INVALID_WEIGHT;
break; } case ( FAREAST_SPECIAL ) : { if (sm1 != EXPANSION) { //
// Get the weight for the far east special case
// and store it in Weight2.
//
GET_FAREAST_WEIGHT( Weight2, uw2, Mask, lpString2, pString2, ExtraWt2, fModify );
if (sm1 != FAREAST_SPECIAL) { //
// The character in the first string is
// NOT a fareast special char.
//
// Set each of weights 4, 5, 6, and 7 to show
// that string 1 is smaller (if not already set).
//
if ((GET_WT_FOUR(&WhichExtra) == 0) && (GET_WT_FOUR(&ExtraWt2) != 0)) { GET_WT_FOUR(&WhichExtra) = CSTR_LESS_THAN; } if ((GET_WT_FIVE(&WhichExtra) == 0) && (GET_WT_FIVE(&ExtraWt2) != 0)) { GET_WT_FIVE(&WhichExtra) = CSTR_LESS_THAN; } if ((GET_WT_SIX(&WhichExtra) == 0) && (GET_WT_SIX(&ExtraWt2) != 0)) { GET_WT_SIX(&WhichExtra) = CSTR_LESS_THAN; } if ((GET_WT_SEVEN(&WhichExtra) == 0) && (GET_WT_SEVEN(&ExtraWt2) != 0)) { GET_WT_SEVEN(&WhichExtra) = CSTR_LESS_THAN; } } else { //
// Characters in both strings are fareast
// special chars.
//
// Set each of weights 4, 5, 6, and 7
// appropriately (if not already set).
//
if ( (GET_WT_FOUR(&WhichExtra) == 0) && ( GET_WT_FOUR(&ExtraWt1) != GET_WT_FOUR(&ExtraWt2) ) ) { GET_WT_FOUR(&WhichExtra) = ( GET_WT_FOUR(&ExtraWt1) < GET_WT_FOUR(&ExtraWt2) ) ? CSTR_LESS_THAN : CSTR_GREATER_THAN; } if ( (GET_WT_FIVE(&WhichExtra) == 0) && ( GET_WT_FIVE(&ExtraWt1) != GET_WT_FIVE(&ExtraWt2) ) ) { GET_WT_FIVE(&WhichExtra) = ( GET_WT_FIVE(&ExtraWt1) < GET_WT_FIVE(&ExtraWt2) ) ? CSTR_LESS_THAN : CSTR_GREATER_THAN; } if ( (GET_WT_SIX(&WhichExtra) == 0) && ( GET_WT_SIX(&ExtraWt1) != GET_WT_SIX(&ExtraWt2) ) ) { GET_WT_SIX(&WhichExtra) = ( GET_WT_SIX(&ExtraWt1) < GET_WT_SIX(&ExtraWt2) ) ? CSTR_LESS_THAN : CSTR_GREATER_THAN; } if ( (GET_WT_SEVEN(&WhichExtra) == 0) && ( GET_WT_SEVEN(&ExtraWt1) != GET_WT_SEVEN(&ExtraWt2) ) ) { GET_WT_SEVEN(&WhichExtra) = ( GET_WT_SEVEN(&ExtraWt1) < GET_WT_SEVEN(&ExtraWt2) ) ? CSTR_LESS_THAN : CSTR_GREATER_THAN; } } } break; } case ( JAMO_SPECIAL ) : { if (!JamoFlag) { LPWSTR pStr1 = pString1; LPWSTR pStr2 = pString2;
FindJamoDifference( pHashN, &pStr1, &ctr1, cchCount1, &Weight1, &pStr2, &ctr2, cchCount2, &Weight2, &pLastJamo, &uw1, &uw2, &State, &WhichJamo, fModify ); if (WhichJamo) { return (WhichJamo); } pString1 = pStr1; pString2 = pStr2; } else { //
// Reset the Jamo flag.
//
JamoFlag = FALSE; }
break; } case ( EXTENSION_A ) : { //
// If sm1 is an extension A character, then
// both sm1 and sm2 have been handled. We should
// only get here when either sm1 is not an
// extension A character or the two extension A
// characters are different.
//
if (sm1 != EXTENSION_A) { //
// Get the full weight in case DW got masked.
// Also, get the actual UW to compare.
//
// Only string2 contains an extension A char,
// so set the UW value to be the first UW
// value for extension A (default values):
// SM_EXT_A, AW_EXT_A
//
Weight2 = GET_DWORD_WEIGHT(pHashN, *pString2); uw2 = MAKE_UNICODE_WT(SM_EXT_A, AW_EXT_A, fModify); }
//
// We should then fall through to the comparison
// of the Unicode weights.
//
break; } case ( UNSORTABLE ) : { //
// Fill out the case statement so the compiler
// will use a jump table.
//
break; } }
//
// See if the comparison should start again.
//
if ((Weight1 == CMP_INVALID_WEIGHT) || (Weight2 == CMP_INVALID_WEIGHT)) { //
// Check to see if we're modifying the script value.
// If so, then we need to reset the fareast weight
// (if applicable) so that it doesn't get modified
// again.
//
if (fModify == TRUE) { if (sm1 == FAREAST_SPECIAL) { Weight1 = CMP_INVALID_WEIGHT; } else if (sm2 == FAREAST_SPECIAL) { Weight2 = CMP_INVALID_WEIGHT; } } continue; }
//
// We're not supposed to drop down into the state table if
// the unicode weights are different, so stop comparison
// and return result of unicode weight comparison.
//
if (uw1 != uw2) { return ((uw1 < uw2) ? CSTR_LESS_THAN : CSTR_GREATER_THAN); } }
//
// For each state in the state table, do the appropriate
// comparisons.
//
if (State & (STATE_DW | STATE_REVERSE_DW)) { //
// Get the diacritic weights.
//
dw1 = GET_DIACRITIC(&Weight1); dw2 = GET_DIACRITIC(&Weight2);
if (dw1 != dw2) { //
// Look ahead to see if diacritic follows a
// minimum diacritic weight. If so, get the
// diacritic weight of the nonspace mark.
//
while (!AT_STRING_END(ctr1 - 1, pString1 + 1, cchCount1)) { Wt = GET_DWORD_WEIGHT(pHashN, *(pString1 + 1)); if (GET_SCRIPT_MEMBER(&Wt) == NONSPACE_MARK) { dw1 += GET_DIACRITIC(&Wt); pString1++; ctr1--; } else { break; } }
while (!AT_STRING_END(ctr2 - 1, pString2 + 1, cchCount2)) { Wt = GET_DWORD_WEIGHT(pHashN, *(pString2 + 1)); if (GET_SCRIPT_MEMBER(&Wt) == NONSPACE_MARK) { dw2 += GET_DIACRITIC(&Wt); pString2++; ctr2--; } else { break; } }
//
// Save which string has the smaller diacritic
// weight if the diacritic weights are still
// different.
//
if (dw1 != dw2) { WhichDiacritic = (dw1 < dw2) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
//
// Remove state from state machine.
//
REMOVE_STATE(STATE_DW); } } } if (State & STATE_CW) { //
// Get the case weights.
//
if (GET_CASE(&Weight1) != GET_CASE(&Weight2)) { //
// Save which string has the smaller case weight.
//
WhichCase = (GET_CASE(&Weight1) < GET_CASE(&Weight2)) ? CSTR_LESS_THAN : CSTR_GREATER_THAN;
//
// Remove state from state machine.
//
REMOVE_STATE(STATE_CW); } } }
//
// Fixup the pointers and counters.
//
POINTER_FIXUP(); ctr1--; ctr2--;
//
// Reset the weights to be invalid.
//
Weight1 = CMP_INVALID_WEIGHT; Weight2 = CMP_INVALID_WEIGHT; }
//
// If the end of BOTH strings has been reached, then the unicode
// weights match exactly. Check the diacritic, case and special
// weights. If all are zero, then return success. Otherwise,
// return the result of the weight difference.
//
// NOTE: The following checks MUST REMAIN IN THIS ORDER:
// Diacritic, Case, Punctuation.
//
if (AT_STRING_END(ctr1, pString1, cchCount1)) { if (AT_STRING_END(ctr2, pString2, cchCount2)) { if (WhichDiacritic) { return (WhichDiacritic); } if (WhichCase) { return (WhichCase); } if (WhichExtra) { if (!fIgnoreDiacritic) { if (GET_WT_FOUR(&WhichExtra)) { return (GET_WT_FOUR(&WhichExtra)); } if (GET_WT_FIVE(&WhichExtra)) { return (GET_WT_FIVE(&WhichExtra)); } } if (GET_WT_SIX(&WhichExtra)) { return (GET_WT_SIX(&WhichExtra)); } if (GET_WT_SEVEN(&WhichExtra)) { return (GET_WT_SEVEN(&WhichExtra)); } } if (WhichPunct1) { return (WhichPunct1); } if (WhichPunct2) { return (WhichPunct2); }
return (CSTR_EQUAL); } else { //
// String 2 is longer.
//
pString1 = pString2; ctr1 = ctr2; cchCount1 = cchCount2; fEnd1 = CSTR_LESS_THAN; } } else { fEnd1 = CSTR_GREATER_THAN; }
//
// Scan to the end of the longer string.
//
SCAN_LONGER_STRING( ctr1, pString1, cchCount1, fEnd1 ); }
////////////////////////////////////////////////////////////////////////////
//
// FindJamoDifference
//
////////////////////////////////////////////////////////////////////////////
int FindJamoDifference( PLOC_HASH pHashN, LPCWSTR* ppString1, int* ctr1, int cchCount1, DWORD* pWeight1, LPCWSTR* ppString2, int* ctr2, int cchCount2, DWORD* pWeight2, LPCWSTR* pLastJamo, WORD* uw1, WORD* uw2, int* pState, int* WhichJamo, BOOL fModify) { int bRestart = 0; // if string compare should restart again
int oldHangulsFound1 = 0; // # of valid old Hangul Jamo compositions found
int oldHangulsFound2 = 0; // # of valid old Hangul Jamo compositions found
WORD UW; BYTE JamoWeight1[3]; // extra weight for first old Hangul composition
BYTE JamoWeight2[3]; // extra weight for second old Hangul composition
//
// Roll back to the first Jamo. We know that these Jamos in both strings
// should be equal, so we can decrement both strings at once.
//
while ((*ppString1 > *pLastJamo) && IsJamo(*(*ppString1 - 1))) { (*ppString1)--; (*ppString2)--; (*ctr1)++; (*ctr2)++; }
//
// Now we are at the beginning of two groups of Jamo characters.
// Compare Jamo unit (either a single Jamo or a valid old Hangul Jamo
// composition) until we run out Jamo units in either strings.
// We also exit when we reach the ends of either string.
//
// while (NOT_END_STRING(*ctr1, *ppString1, cchCount1) &&
// NOT_END_STRING(*ctr2, *ppString2, cchCount2))
//
for (;;) { if (IsJamo(**ppString1)) { if (IsLeadingJamo(**ppString1)) { if ((oldHangulsFound1 = MapOldHangulSortKey( pHashN, *ppString1, *ctr1, &UW, JamoWeight1, fModify )) > 0) { *uw1 = UW;
//
// Mark *pWeight1 so that it is not CMP_INVALID_WEIGHT.
// 0202 is the DW/CW.
//
*pWeight1 = ((DWORD)UW | 0x02020000);
//
// We always increment ppString1/ctr1 at the end of the
// loop, so we need to subtract 1 here.
//
*ppString1 += (oldHangulsFound1 - 1); *ctr1 -= (oldHangulsFound1 - 1); } } if (oldHangulsFound1 == 0) { //
// No valid old Hangul compositions are found. Get the UW
// for the Jamo instead.
//
*pWeight1 = GET_DWORD_WEIGHT(pHashN, **ppString1);
//
// The SMs in PSORTKEY for Jamos are not really SMs. They
// are all 4 (for JAMO_SPECIAL).
// Here we get the real Jamo Unicode weight. The actual SM
// is stored in DW.
//
*uw1 = MAKE_UNICODE_WT( GET_DIACRITIC(pWeight1), GET_ALPHA_NUMERIC(pWeight1), fModify ); ((PSORTKEY)pWeight1)->Diacritic = MIN_DW; } }
if (IsJamo(**ppString2)) { if (IsLeadingJamo(**ppString2)) { if ((oldHangulsFound2 = MapOldHangulSortKey( pHashN, *ppString2, *ctr2, &UW, JamoWeight2, fModify )) > 0) { *uw2 = UW; *pWeight2 = ((DWORD)UW | 0x02020000); *ppString2 += (oldHangulsFound2 - 1); *ctr2 -= (oldHangulsFound2 - 1); } } if (oldHangulsFound2 == 0) { *pWeight2 = GET_DWORD_WEIGHT(pHashN, **ppString2); *uw2 = MAKE_UNICODE_WT( GET_DIACRITIC(pWeight2), GET_ALPHA_NUMERIC(pWeight2), fModify ); ((PSORTKEY)pWeight2)->Diacritic = MIN_DW; } }
//
// See if either weight is invalid.
// A weight can be invalid when the character is not a Jamo.
//
if (*pWeight1 == CMP_INVALID_WEIGHT) { //
// The current character is not a Jamo. Set the Weight to
// be CMP_INVALID_WEIGHT, so that the string comparision can
// restart within the loop of CompareString().
//
*pWeight1 = CMP_INVALID_WEIGHT; bRestart = 1; goto FindJamoDifferenceExit; } if (*pWeight2 == CMP_INVALID_WEIGHT) { //
// The current character is not a Jamo. Set the Weight to
// be CMP_INVALID_WEIGHT, so that the string comparision can
// restart within the loop of CompareString().
//
*pWeight2 = CMP_INVALID_WEIGHT; bRestart = 1; goto FindJamoDifferenceExit; } if (*uw1 != *uw2) { //
// Found differences in Unicode weight. We can stop the
// processing now.
//
goto FindJamoDifferenceExit; }
//
// When we get here, we know that we have the same Unicode Weight.
// Check if we need to record the WhichJamo.
//
if ((*pState & STATE_JAMO_WEIGHT) && ((oldHangulsFound1 > 0) || (oldHangulsFound2 > 0))) { if ((oldHangulsFound1 > 0) && (oldHangulsFound2 > 0)) { *WhichJamo = (int)memcmp( JamoWeight1, JamoWeight2, sizeof(JamoWeight1) ) + 2; } else if (oldHangulsFound1 > 0) { *WhichJamo = CSTR_GREATER_THAN; } else { *WhichJamo = CSTR_LESS_THAN; } *pState &= ~STATE_JAMO_WEIGHT; oldHangulsFound1 = oldHangulsFound2 = 0; } (*ppString1)++; (*ctr1)--; (*ppString2)++; (*ctr2)--;
if (AT_STRING_END(*ctr1, *ppString1, cchCount1) || AT_STRING_END(*ctr2, *ppString2, cchCount2)) { break; } *pWeight1 = *pWeight2 = CMP_INVALID_WEIGHT; }
//
// If we drop out of the while loop because we reach the end of strings,
// decrement the pointers by one because loops in CompareString() will
// increase the pointers at the end of the loop.
//
// If we drop out of the while loop because the goto's in it, we are
// already off by one.
//
if (AT_STRING_END(*ctr1, *ppString1, cchCount1)) { (*ppString1)--; (*ctr1)++; } if (AT_STRING_END(*ctr2, *ppString2, cchCount2)) { (*ppString2)--; (*ctr2)++; }
FindJamoDifferenceExit: *pLastJamo = *ppString1; return (bRestart); }
|