diff options
Diffstat (limited to 'deps/icu-small/source/i18n/uspoof_impl.cpp')
-rw-r--r-- | deps/icu-small/source/i18n/uspoof_impl.cpp | 868 |
1 files changed, 868 insertions, 0 deletions
diff --git a/deps/icu-small/source/i18n/uspoof_impl.cpp b/deps/icu-small/source/i18n/uspoof_impl.cpp new file mode 100644 index 0000000000..111a1489ff --- /dev/null +++ b/deps/icu-small/source/i18n/uspoof_impl.cpp @@ -0,0 +1,868 @@ +/* +********************************************************************** +* Copyright (C) 2008-2016, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +*/ + +#include "unicode/utypes.h" +#include "unicode/uspoof.h" +#include "unicode/uchar.h" +#include "unicode/uniset.h" +#include "unicode/utf16.h" +#include "utrie2.h" +#include "cmemory.h" +#include "cstring.h" +#include "identifier_info.h" +#include "scriptset.h" +#include "umutex.h" +#include "udataswp.h" +#include "uassert.h" +#include "uspoof_impl.h" + +#if !UCONFIG_NO_NORMALIZATION + + +U_NAMESPACE_BEGIN + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SpoofImpl) + +SpoofImpl::SpoofImpl(SpoofData *data, UErrorCode &status) : + fMagic(0), fChecks(USPOOF_ALL_CHECKS), fSpoofData(data), fAllowedCharsSet(NULL) , + fAllowedLocales(NULL), fCachedIdentifierInfo(NULL) { + if (U_FAILURE(status)) { + return; + } + fRestrictionLevel = USPOOF_HIGHLY_RESTRICTIVE; + + UnicodeSet *allowedCharsSet = new UnicodeSet(0, 0x10ffff); + allowedCharsSet->freeze(); + fAllowedCharsSet = allowedCharsSet; + fAllowedLocales = uprv_strdup(""); + if (fAllowedCharsSet == NULL || fAllowedLocales == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + fMagic = USPOOF_MAGIC; +} + + +SpoofImpl::SpoofImpl() : + fMagic(USPOOF_MAGIC), fChecks(USPOOF_ALL_CHECKS), fSpoofData(NULL), fAllowedCharsSet(NULL) , + fAllowedLocales(NULL), fCachedIdentifierInfo(NULL) { + UnicodeSet *allowedCharsSet = new UnicodeSet(0, 0x10ffff); + allowedCharsSet->freeze(); + fAllowedCharsSet = allowedCharsSet; + fAllowedLocales = uprv_strdup(""); + fRestrictionLevel = USPOOF_HIGHLY_RESTRICTIVE; +} + + +// Copy Constructor, used by the user level clone() function. +SpoofImpl::SpoofImpl(const SpoofImpl &src, UErrorCode &status) : + fMagic(0), fChecks(USPOOF_ALL_CHECKS), fSpoofData(NULL), fAllowedCharsSet(NULL) , + fAllowedLocales(NULL), fCachedIdentifierInfo(NULL) { + if (U_FAILURE(status)) { + return; + } + fMagic = src.fMagic; + fChecks = src.fChecks; + if (src.fSpoofData != NULL) { + fSpoofData = src.fSpoofData->addReference(); + } + fAllowedCharsSet = static_cast<const UnicodeSet *>(src.fAllowedCharsSet->clone()); + if (fAllowedCharsSet == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + } + fAllowedLocales = uprv_strdup(src.fAllowedLocales); + fRestrictionLevel = src.fRestrictionLevel; +} + +SpoofImpl::~SpoofImpl() { + fMagic = 0; // head off application errors by preventing use of + // of deleted objects. + if (fSpoofData != NULL) { + fSpoofData->removeReference(); // Will delete if refCount goes to zero. + } + delete fAllowedCharsSet; + uprv_free((void *)fAllowedLocales); + delete fCachedIdentifierInfo; +} + +// +// Incoming parameter check on Status and the SpoofChecker object +// received from the C API. +// +const SpoofImpl *SpoofImpl::validateThis(const USpoofChecker *sc, UErrorCode &status) { + if (U_FAILURE(status)) { + return NULL; + } + if (sc == NULL) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return NULL; + } + SpoofImpl *This = (SpoofImpl *)sc; + if (This->fMagic != USPOOF_MAGIC || + This->fSpoofData == NULL) { + status = U_INVALID_FORMAT_ERROR; + return NULL; + } + if (!SpoofData::validateDataVersion(This->fSpoofData->fRawData, status)) { + return NULL; + } + return This; +} + +SpoofImpl *SpoofImpl::validateThis(USpoofChecker *sc, UErrorCode &status) { + return const_cast<SpoofImpl *> + (SpoofImpl::validateThis(const_cast<const USpoofChecker *>(sc), status)); +} + + + +//-------------------------------------------------------------------------------------- +// +// confusableLookup() This is the heart of the confusable skeleton generation +// implementation. +// +// Given a source character, produce the corresponding +// replacement character(s), appending them to the dest string. +// +//--------------------------------------------------------------------------------------- +int32_t SpoofImpl::confusableLookup(UChar32 inChar, int32_t tableMask, UnicodeString &dest) const { + + // Binary search the spoof data key table for the inChar + int32_t *low = fSpoofData->fCFUKeys; + int32_t *mid = NULL; + int32_t *limit = low + fSpoofData->fRawData->fCFUKeysSize; + UChar32 midc; + do { + int32_t delta = ((int32_t)(limit-low))/2; + mid = low + delta; + midc = *mid & 0x1fffff; + if (inChar == midc) { + goto foundChar; + } else if (inChar < midc) { + limit = mid; + } else { + low = mid; + } + } while (low < limit-1); + mid = low; + midc = *mid & 0x1fffff; + if (inChar != midc) { + // Char not found. It maps to itself. + int i = 0; + dest.append(inChar); + return i; + } + foundChar: + int32_t keyFlags = *mid & 0xff000000; + if ((keyFlags & tableMask) == 0) { + // We found the right key char, but the entry doesn't pertain to the + // table we need. See if there is an adjacent key that does + if (keyFlags & USPOOF_KEY_MULTIPLE_VALUES) { + int32_t *altMid; + for (altMid = mid-1; (*altMid&0x00ffffff) == inChar; altMid--) { + keyFlags = *altMid & 0xff000000; + if (keyFlags & tableMask) { + mid = altMid; + goto foundKey; + } + } + for (altMid = mid+1; (*altMid&0x00ffffff) == inChar; altMid++) { + keyFlags = *altMid & 0xff000000; + if (keyFlags & tableMask) { + mid = altMid; + goto foundKey; + } + } + } + // No key entry for this char & table. + // The input char maps to itself. + int i = 0; + dest.append(inChar); + return i; + } + + foundKey: + int32_t stringLen = USPOOF_KEY_LENGTH_FIELD(keyFlags) + 1; + int32_t keyTableIndex = (int32_t)(mid - fSpoofData->fCFUKeys); + + // Value is either a UChar (for strings of length 1) or + // an index into the string table (for longer strings) + uint16_t value = fSpoofData->fCFUValues[keyTableIndex]; + if (stringLen == 1) { + dest.append((UChar)value); + return 1; + } + + // String length of 4 from the above lookup is used for all strings of length >= 4. + // For these, get the real length from the string lengths table, + // which maps string table indexes to lengths. + // All strings of the same length are stored contiguously in the string table. + // 'value' from the lookup above is the starting index for the desired string. + + int32_t ix; + if (stringLen == 4) { + int32_t stringLengthsLimit = fSpoofData->fRawData->fCFUStringLengthsSize; + for (ix = 0; ix < stringLengthsLimit; ix++) { + if (fSpoofData->fCFUStringLengths[ix].fLastString >= value) { + stringLen = fSpoofData->fCFUStringLengths[ix].fStrLength; + break; + } + } + U_ASSERT(ix < stringLengthsLimit); + } + + U_ASSERT(value + stringLen <= fSpoofData->fRawData->fCFUStringTableLen); + UChar *src = &fSpoofData->fCFUStrings[value]; + dest.append(src, stringLen); + return stringLen; +} + + +//--------------------------------------------------------------------------------------- +// +// wholeScriptCheck() +// +// Input text is already normalized to NFD +// Return the set of scripts, each of which can represent something that is +// confusable with the input text. The script of the input text +// is included; input consisting of characters from a single script will +// always produce a result consisting of a set containing that script. +// +//--------------------------------------------------------------------------------------- +void SpoofImpl::wholeScriptCheck( + const UnicodeString &text, ScriptSet *result, UErrorCode &status) const { + + UTrie2 *table = + (fChecks & USPOOF_ANY_CASE) ? fSpoofData->fAnyCaseTrie : fSpoofData->fLowerCaseTrie; + result->setAll(); + int32_t length = text.length(); + for (int32_t inputIdx=0; inputIdx < length;) { + UChar32 c = text.char32At(inputIdx); + inputIdx += U16_LENGTH(c); + uint32_t index = utrie2_get32(table, c); + if (index == 0) { + // No confusables in another script for this char. + // TODO: we should change the data to have sets with just the single script + // bit for the script of this char. Gets rid of this special case. + // Until then, grab the script from the char and intersect it with the set. + UScriptCode cpScript = uscript_getScript(c, &status); + U_ASSERT(cpScript > USCRIPT_INHERITED); + result->intersect(cpScript, status); + } else if (index == 1) { + // Script == Common or Inherited. Nothing to do. + } else { + result->intersect(fSpoofData->fScriptSets[index]); + } + } +} + + +void SpoofImpl::setAllowedLocales(const char *localesList, UErrorCode &status) { + UnicodeSet allowedChars; + UnicodeSet *tmpSet = NULL; + const char *locStart = localesList; + const char *locEnd = NULL; + const char *localesListEnd = localesList + uprv_strlen(localesList); + int32_t localeListCount = 0; // Number of locales provided by caller. + + // Loop runs once per locale from the localesList, a comma separated list of locales. + do { + locEnd = uprv_strchr(locStart, ','); + if (locEnd == NULL) { + locEnd = localesListEnd; + } + while (*locStart == ' ') { + locStart++; + } + const char *trimmedEnd = locEnd-1; + while (trimmedEnd > locStart && *trimmedEnd == ' ') { + trimmedEnd--; + } + if (trimmedEnd <= locStart) { + break; + } + const char *locale = uprv_strndup(locStart, (int32_t)(trimmedEnd + 1 - locStart)); + localeListCount++; + + // We have one locale from the locales list. + // Add the script chars for this locale to the accumulating set of allowed chars. + // If the locale is no good, we will be notified back via status. + addScriptChars(locale, &allowedChars, status); + uprv_free((void *)locale); + if (U_FAILURE(status)) { + break; + } + locStart = locEnd + 1; + } while (locStart < localesListEnd); + + // If our caller provided an empty list of locales, we disable the allowed characters checking + if (localeListCount == 0) { + uprv_free((void *)fAllowedLocales); + fAllowedLocales = uprv_strdup(""); + tmpSet = new UnicodeSet(0, 0x10ffff); + if (fAllowedLocales == NULL || tmpSet == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + tmpSet->freeze(); + delete fAllowedCharsSet; + fAllowedCharsSet = tmpSet; + fChecks &= ~USPOOF_CHAR_LIMIT; + return; + } + + + // Add all common and inherited characters to the set of allowed chars. + UnicodeSet tempSet; + tempSet.applyIntPropertyValue(UCHAR_SCRIPT, USCRIPT_COMMON, status); + allowedChars.addAll(tempSet); + tempSet.applyIntPropertyValue(UCHAR_SCRIPT, USCRIPT_INHERITED, status); + allowedChars.addAll(tempSet); + + // If anything went wrong, we bail out without changing + // the state of the spoof checker. + if (U_FAILURE(status)) { + return; + } + + // Store the updated spoof checker state. + tmpSet = static_cast<UnicodeSet *>(allowedChars.clone()); + const char *tmpLocalesList = uprv_strdup(localesList); + if (tmpSet == NULL || tmpLocalesList == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + uprv_free((void *)fAllowedLocales); + fAllowedLocales = tmpLocalesList; + tmpSet->freeze(); + delete fAllowedCharsSet; + fAllowedCharsSet = tmpSet; + fChecks |= USPOOF_CHAR_LIMIT; +} + + +const char * SpoofImpl::getAllowedLocales(UErrorCode &/*status*/) { + return fAllowedLocales; +} + + +// Given a locale (a language), add all the characters from all of the scripts used with that language +// to the allowedChars UnicodeSet + +void SpoofImpl::addScriptChars(const char *locale, UnicodeSet *allowedChars, UErrorCode &status) { + UScriptCode scripts[30]; + + int32_t numScripts = uscript_getCode(locale, scripts, UPRV_LENGTHOF(scripts), &status); + if (U_FAILURE(status)) { + return; + } + if (status == U_USING_DEFAULT_WARNING) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + UnicodeSet tmpSet; + int32_t i; + for (i=0; i<numScripts; i++) { + tmpSet.applyIntPropertyValue(UCHAR_SCRIPT, scripts[i], status); + allowedChars->addAll(tmpSet); + } +} + + +// Convert a text format hex number. Utility function used by builder code. Static. +// Input: UChar *string text. Output: a UChar32 +// Input has been pre-checked, and will have no non-hex chars. +// The number must fall in the code point range of 0..0x10ffff +// Static Function. +UChar32 SpoofImpl::ScanHex(const UChar *s, int32_t start, int32_t limit, UErrorCode &status) { + if (U_FAILURE(status)) { + return 0; + } + U_ASSERT(limit-start > 0); + uint32_t val = 0; + int i; + for (i=start; i<limit; i++) { + int digitVal = s[i] - 0x30; + if (digitVal>9) { + digitVal = 0xa + (s[i] - 0x41); // Upper Case 'A' + } + if (digitVal>15) { + digitVal = 0xa + (s[i] - 0x61); // Lower Case 'a' + } + U_ASSERT(digitVal <= 0xf); + val <<= 4; + val += digitVal; + } + if (val > 0x10ffff) { + status = U_PARSE_ERROR; + val = 0; + } + return (UChar32)val; +} + +// IdentifierInfo Cache. IdentifierInfo objects are somewhat expensive to create. +// Maintain a one-element cache, which is sufficient to avoid repeatedly +// creating new ones unless we get multi-thread concurrency in spoof +// check operations, which should be statistically uncommon. + +// These functions are used in place of new & delete of an IdentifierInfo. +// They will recycle the IdentifierInfo when possible. +// They are logically const, and used within const functions that must be thread safe. +IdentifierInfo *SpoofImpl::getIdentifierInfo(UErrorCode &status) const { + IdentifierInfo *returnIdInfo = NULL; + if (U_FAILURE(status)) { + return returnIdInfo; + } + SpoofImpl *nonConstThis = const_cast<SpoofImpl *>(this); + { + Mutex m; + returnIdInfo = nonConstThis->fCachedIdentifierInfo; + nonConstThis->fCachedIdentifierInfo = NULL; + } + if (returnIdInfo == NULL) { + returnIdInfo = new IdentifierInfo(status); + if (U_SUCCESS(status) && returnIdInfo == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + } + if (U_FAILURE(status) && returnIdInfo != NULL) { + delete returnIdInfo; + returnIdInfo = NULL; + } + } + return returnIdInfo; +} + + +void SpoofImpl::releaseIdentifierInfo(IdentifierInfo *idInfo) const { + if (idInfo != NULL) { + SpoofImpl *nonConstThis = const_cast<SpoofImpl *>(this); + { + Mutex m; + if (nonConstThis->fCachedIdentifierInfo == NULL) { + nonConstThis->fCachedIdentifierInfo = idInfo; + idInfo = NULL; + } + } + delete idInfo; + } +} + + + + +//---------------------------------------------------------------------------------------------- +// +// class SpoofData Implementation +// +//---------------------------------------------------------------------------------------------- + + +UBool SpoofData::validateDataVersion(const SpoofDataHeader *rawData, UErrorCode &status) { + if (U_FAILURE(status) || + rawData == NULL || + rawData->fMagic != USPOOF_MAGIC || + rawData->fFormatVersion[0] > 1 || + rawData->fFormatVersion[1] > 0) { + status = U_INVALID_FORMAT_ERROR; + return FALSE; + } + return TRUE; +} + +static UBool U_CALLCONV +spoofDataIsAcceptable(void *context, + const char * /* type */, const char * /*name*/, + const UDataInfo *pInfo) { + if( + pInfo->size >= 20 && + pInfo->isBigEndian == U_IS_BIG_ENDIAN && + pInfo->charsetFamily == U_CHARSET_FAMILY && + pInfo->dataFormat[0] == 0x43 && // dataFormat="Cfu " + pInfo->dataFormat[1] == 0x66 && + pInfo->dataFormat[2] == 0x75 && + pInfo->dataFormat[3] == 0x20 && + pInfo->formatVersion[0] == 1 + ) { + UVersionInfo *version = static_cast<UVersionInfo *>(context); + if(version != NULL) { + uprv_memcpy(version, pInfo->dataVersion, 4); + } + return TRUE; + } else { + return FALSE; + } +} + +// +// SpoofData::getDefault() - return a wrapper around the spoof data that is +// baked into the default ICU data. +// +// Called once, from the initOnce() function in uspoof_impl.cpp; the resulting +// SpoofData is shared by all spoof checkers using the default data. +// +SpoofData *SpoofData::getDefault(UErrorCode &status) { + UDataMemory *udm = udata_openChoice(NULL, "cfu", "confusables", + spoofDataIsAcceptable, + NULL, // context, would receive dataVersion if supplied. + &status); + if (U_FAILURE(status)) { + return NULL; + } + SpoofData *This = new SpoofData(udm, status); + if (U_FAILURE(status)) { + delete This; + return NULL; + } + if (This == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + } + return This; +} + +SpoofData::SpoofData(UDataMemory *udm, UErrorCode &status) +{ + reset(); + if (U_FAILURE(status)) { + return; + } + fUDM = udm; + // fRawData is non-const because it may be constructed by the data builder. + fRawData = reinterpret_cast<SpoofDataHeader *>( + const_cast<void *>(udata_getMemory(udm))); + validateDataVersion(fRawData, status); + initPtrs(status); +} + + +SpoofData::SpoofData(const void *data, int32_t length, UErrorCode &status) +{ + reset(); + if (U_FAILURE(status)) { + return; + } + if ((size_t)length < sizeof(SpoofDataHeader)) { + status = U_INVALID_FORMAT_ERROR; + return; + } + void *ncData = const_cast<void *>(data); + fRawData = static_cast<SpoofDataHeader *>(ncData); + if (length < fRawData->fLength) { + status = U_INVALID_FORMAT_ERROR; + return; + } + validateDataVersion(fRawData, status); + initPtrs(status); +} + + +// Spoof Data constructor for use from data builder. +// Initializes a new, empty data area that will be populated later. +SpoofData::SpoofData(UErrorCode &status) { + reset(); + if (U_FAILURE(status)) { + return; + } + fDataOwned = true; + + // The spoof header should already be sized to be a multiple of 16 bytes. + // Just in case it's not, round it up. + uint32_t initialSize = (sizeof(SpoofDataHeader) + 15) & ~15; + U_ASSERT(initialSize == sizeof(SpoofDataHeader)); + + fRawData = static_cast<SpoofDataHeader *>(uprv_malloc(initialSize)); + fMemLimit = initialSize; + if (fRawData == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + uprv_memset(fRawData, 0, initialSize); + + fRawData->fMagic = USPOOF_MAGIC; + fRawData->fFormatVersion[0] = 1; + fRawData->fFormatVersion[1] = 0; + fRawData->fFormatVersion[2] = 0; + fRawData->fFormatVersion[3] = 0; + initPtrs(status); +} + +// reset() - initialize all fields. +// Should be updated if any new fields are added. +// Called by constructors to put things in a known initial state. +void SpoofData::reset() { + fRawData = NULL; + fDataOwned = FALSE; + fUDM = NULL; + fMemLimit = 0; + fRefCount = 1; + fCFUKeys = NULL; + fCFUValues = NULL; + fCFUStringLengths = NULL; + fCFUStrings = NULL; + fAnyCaseTrie = NULL; + fLowerCaseTrie = NULL; + fScriptSets = NULL; +} + + +// SpoofData::initPtrs() +// Initialize the pointers to the various sections of the raw data. +// +// This function is used both during the Trie building process (multiple +// times, as the individual data sections are added), and +// during the opening of a Spoof Checker from prebuilt data. +// +// The pointers for non-existent data sections (identified by an offset of 0) +// are set to NULL. +// +// Note: During building the data, adding each new data section +// reallocs the raw data area, which likely relocates it, which +// in turn requires reinitializing all of the pointers into it, hence +// multiple calls to this function during building. +// +void SpoofData::initPtrs(UErrorCode &status) { + fCFUKeys = NULL; + fCFUValues = NULL; + fCFUStringLengths = NULL; + fCFUStrings = NULL; + if (U_FAILURE(status)) { + return; + } + if (fRawData->fCFUKeys != 0) { + fCFUKeys = (int32_t *)((char *)fRawData + fRawData->fCFUKeys); + } + if (fRawData->fCFUStringIndex != 0) { + fCFUValues = (uint16_t *)((char *)fRawData + fRawData->fCFUStringIndex); + } + if (fRawData->fCFUStringLengths != 0) { + fCFUStringLengths = (SpoofStringLengthsElement *)((char *)fRawData + fRawData->fCFUStringLengths); + } + if (fRawData->fCFUStringTable != 0) { + fCFUStrings = (UChar *)((char *)fRawData + fRawData->fCFUStringTable); + } + + if (fAnyCaseTrie == NULL && fRawData->fAnyCaseTrie != 0) { + fAnyCaseTrie = utrie2_openFromSerialized(UTRIE2_16_VALUE_BITS, + (char *)fRawData + fRawData->fAnyCaseTrie, fRawData->fAnyCaseTrieLength, NULL, &status); + } + if (fLowerCaseTrie == NULL && fRawData->fLowerCaseTrie != 0) { + fLowerCaseTrie = utrie2_openFromSerialized(UTRIE2_16_VALUE_BITS, + (char *)fRawData + fRawData->fLowerCaseTrie, fRawData->fLowerCaseTrieLength, NULL, &status); + } + + if (fRawData->fScriptSets != 0) { + fScriptSets = (ScriptSet *)((char *)fRawData + fRawData->fScriptSets); + } +} + + +SpoofData::~SpoofData() { + utrie2_close(fAnyCaseTrie); + fAnyCaseTrie = NULL; + utrie2_close(fLowerCaseTrie); + fLowerCaseTrie = NULL; + if (fDataOwned) { + uprv_free(fRawData); + } + fRawData = NULL; + if (fUDM != NULL) { + udata_close(fUDM); + } + fUDM = NULL; +} + + +void SpoofData::removeReference() { + if (umtx_atomic_dec(&fRefCount) == 0) { + delete this; + } +} + + +SpoofData *SpoofData::addReference() { + umtx_atomic_inc(&fRefCount); + return this; +} + + +void *SpoofData::reserveSpace(int32_t numBytes, UErrorCode &status) { + if (U_FAILURE(status)) { + return NULL; + } + if (!fDataOwned) { + U_ASSERT(FALSE); + status = U_INTERNAL_PROGRAM_ERROR; + return NULL; + } + + numBytes = (numBytes + 15) & ~15; // Round up to a multiple of 16 + uint32_t returnOffset = fMemLimit; + fMemLimit += numBytes; + fRawData = static_cast<SpoofDataHeader *>(uprv_realloc(fRawData, fMemLimit)); + fRawData->fLength = fMemLimit; + uprv_memset((char *)fRawData + returnOffset, 0, numBytes); + initPtrs(status); + return (char *)fRawData + returnOffset; +} + + +U_NAMESPACE_END + +U_NAMESPACE_USE + +//----------------------------------------------------------------------------- +// +// uspoof_swap - byte swap and char encoding swap of spoof data +// +//----------------------------------------------------------------------------- +U_CAPI int32_t U_EXPORT2 +uspoof_swap(const UDataSwapper *ds, const void *inData, int32_t length, void *outData, + UErrorCode *status) { + + if (status == NULL || U_FAILURE(*status)) { + return 0; + } + if(ds==NULL || inData==NULL || length<-1 || (length>0 && outData==NULL)) { + *status=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + // + // Check that the data header is for spoof data. + // (Header contents are defined in gencfu.cpp) + // + const UDataInfo *pInfo = (const UDataInfo *)((const char *)inData+4); + if(!( pInfo->dataFormat[0]==0x43 && /* dataFormat="Cfu " */ + pInfo->dataFormat[1]==0x66 && + pInfo->dataFormat[2]==0x75 && + pInfo->dataFormat[3]==0x20 && + pInfo->formatVersion[0]==1 )) { + udata_printError(ds, "uspoof_swap(): data format %02x.%02x.%02x.%02x " + "(format version %02x %02x %02x %02x) is not recognized\n", + pInfo->dataFormat[0], pInfo->dataFormat[1], + pInfo->dataFormat[2], pInfo->dataFormat[3], + pInfo->formatVersion[0], pInfo->formatVersion[1], + pInfo->formatVersion[2], pInfo->formatVersion[3]); + *status=U_UNSUPPORTED_ERROR; + return 0; + } + + // + // Swap the data header. (This is the generic ICU Data Header, not the uspoof Specific + // header). This swap also conveniently gets us + // the size of the ICU d.h., which lets us locate the start + // of the uspoof specific data. + // + int32_t headerSize=udata_swapDataHeader(ds, inData, length, outData, status); + + + // + // Get the Spoof Data Header, and check that it appears to be OK. + // + // + const uint8_t *inBytes =(const uint8_t *)inData+headerSize; + SpoofDataHeader *spoofDH = (SpoofDataHeader *)inBytes; + if (ds->readUInt32(spoofDH->fMagic) != USPOOF_MAGIC || + ds->readUInt32(spoofDH->fLength) < sizeof(SpoofDataHeader)) + { + udata_printError(ds, "uspoof_swap(): Spoof Data header is invalid.\n"); + *status=U_UNSUPPORTED_ERROR; + return 0; + } + + // + // Prefight operation? Just return the size + // + int32_t spoofDataLength = ds->readUInt32(spoofDH->fLength); + int32_t totalSize = headerSize + spoofDataLength; + if (length < 0) { + return totalSize; + } + + // + // Check that length passed in is consistent with length from Spoof data header. + // + if (length < totalSize) { + udata_printError(ds, "uspoof_swap(): too few bytes (%d after ICU Data header) for spoof data.\n", + spoofDataLength); + *status=U_INDEX_OUTOFBOUNDS_ERROR; + return 0; + } + + + // + // Swap the Data. Do the data itself first, then the Spoof Data Header, because + // we need to reference the header to locate the data, and an + // inplace swap of the header leaves it unusable. + // + uint8_t *outBytes = (uint8_t *)outData + headerSize; + SpoofDataHeader *outputDH = (SpoofDataHeader *)outBytes; + + int32_t sectionStart; + int32_t sectionLength; + + // + // If not swapping in place, zero out the output buffer before starting. + // Gaps may exist between the individual sections, and these must be zeroed in + // the output buffer. The simplest way to do that is to just zero the whole thing. + // + if (inBytes != outBytes) { + uprv_memset(outBytes, 0, spoofDataLength); + } + + // Confusables Keys Section (fCFUKeys) + sectionStart = ds->readUInt32(spoofDH->fCFUKeys); + sectionLength = ds->readUInt32(spoofDH->fCFUKeysSize) * 4; + ds->swapArray32(ds, inBytes+sectionStart, sectionLength, outBytes+sectionStart, status); + + // String Index Section + sectionStart = ds->readUInt32(spoofDH->fCFUStringIndex); + sectionLength = ds->readUInt32(spoofDH->fCFUStringIndexSize) * 2; + ds->swapArray16(ds, inBytes+sectionStart, sectionLength, outBytes+sectionStart, status); + + // String Table Section + sectionStart = ds->readUInt32(spoofDH->fCFUStringTable); + sectionLength = ds->readUInt32(spoofDH->fCFUStringTableLen) * 2; + ds->swapArray16(ds, inBytes+sectionStart, sectionLength, outBytes+sectionStart, status); + + // String Lengths Section + sectionStart = ds->readUInt32(spoofDH->fCFUStringLengths); + sectionLength = ds->readUInt32(spoofDH->fCFUStringLengthsSize) * 4; + ds->swapArray16(ds, inBytes+sectionStart, sectionLength, outBytes+sectionStart, status); + + // Any Case Trie + sectionStart = ds->readUInt32(spoofDH->fAnyCaseTrie); + sectionLength = ds->readUInt32(spoofDH->fAnyCaseTrieLength); + utrie2_swap(ds, inBytes+sectionStart, sectionLength, outBytes+sectionStart, status); + + // Lower Case Trie + sectionStart = ds->readUInt32(spoofDH->fLowerCaseTrie); + sectionLength = ds->readUInt32(spoofDH->fLowerCaseTrieLength); + utrie2_swap(ds, inBytes+sectionStart, sectionLength, outBytes+sectionStart, status); + + // Script Sets. The data is an array of int32_t + sectionStart = ds->readUInt32(spoofDH->fScriptSets); + sectionLength = ds->readUInt32(spoofDH->fScriptSetsLength) * sizeof(ScriptSet); + ds->swapArray32(ds, inBytes+sectionStart, sectionLength, outBytes+sectionStart, status); + + // And, last, swap the header itself. + // int32_t fMagic // swap this + // uint8_t fFormatVersion[4] // Do not swap this, just copy + // int32_t fLength and all the rest // Swap the rest, all is 32 bit stuff. + // + uint32_t magic = ds->readUInt32(spoofDH->fMagic); + ds->writeUInt32((uint32_t *)&outputDH->fMagic, magic); + + if (outputDH->fFormatVersion != spoofDH->fFormatVersion) { + uprv_memcpy(outputDH->fFormatVersion, spoofDH->fFormatVersion, sizeof(spoofDH->fFormatVersion)); + } + // swap starting at fLength + ds->swapArray32(ds, &spoofDH->fLength, sizeof(SpoofDataHeader)-8 /* minus magic and fFormatVersion[4] */, &outputDH->fLength, status); + + return totalSize; +} + +#endif |