diff options
Diffstat (limited to 'intl/uconv/util/nsUCSupport.cpp')
-rw-r--r-- | intl/uconv/util/nsUCSupport.cpp | 621 |
1 files changed, 621 insertions, 0 deletions
diff --git a/intl/uconv/util/nsUCSupport.cpp b/intl/uconv/util/nsUCSupport.cpp new file mode 100644 index 000000000..d6893f442 --- /dev/null +++ b/intl/uconv/util/nsUCSupport.cpp @@ -0,0 +1,621 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsUCSupport.h" +#include "nsUnicodeDecodeHelper.h" +#include "nsUnicodeEncodeHelper.h" +#include "mozilla/CheckedInt.h" +#include <algorithm> + +#define DEFAULT_BUFFER_CAPACITY 16 + +// XXX review the buffer growth limitation code + +//---------------------------------------------------------------------- +// Class nsBasicDecoderSupport [implementation] + +nsBasicDecoderSupport::nsBasicDecoderSupport() + : mErrBehavior(kOnError_Recover) +{ +} + +nsBasicDecoderSupport::~nsBasicDecoderSupport() +{ +} + +//---------------------------------------------------------------------- +// Interface nsISupports [implementation] + +#ifdef DEBUG +NS_IMPL_ISUPPORTS(nsBasicDecoderSupport, + nsIUnicodeDecoder, + nsIBasicDecoder) +#else +NS_IMPL_ISUPPORTS(nsBasicDecoderSupport, nsIUnicodeDecoder) +#endif + +//---------------------------------------------------------------------- +// Interface nsIUnicodeDecoder [implementation] + +void +nsBasicDecoderSupport::SetInputErrorBehavior(int32_t aBehavior) +{ + MOZ_ASSERT(aBehavior == kOnError_Recover || aBehavior == kOnError_Signal, + "Unknown behavior for SetInputErrorBehavior"); + mErrBehavior = aBehavior; +} + +char16_t +nsBasicDecoderSupport::GetCharacterForUnMapped() +{ + return char16_t(0xfffd); // Unicode REPLACEMENT CHARACTER +} + +//---------------------------------------------------------------------- +// Class nsBufferDecoderSupport [implementation] + +nsBufferDecoderSupport::nsBufferDecoderSupport(uint32_t aMaxLengthFactor) + : nsBasicDecoderSupport(), + mMaxLengthFactor(aMaxLengthFactor) +{ + mBufferCapacity = DEFAULT_BUFFER_CAPACITY; + mBuffer = new char[mBufferCapacity]; + + Reset(); +} + +nsBufferDecoderSupport::~nsBufferDecoderSupport() +{ + delete [] mBuffer; +} + +void nsBufferDecoderSupport::FillBuffer(const char ** aSrc, int32_t aSrcLength) +{ + int32_t bcr = std::min(mBufferCapacity - mBufferLength, aSrcLength); + memcpy(mBuffer + mBufferLength, *aSrc, bcr); + mBufferLength += bcr; + (*aSrc) += bcr; +} + +//---------------------------------------------------------------------- +// Subclassing of nsBasicDecoderSupport class [implementation] + +NS_IMETHODIMP nsBufferDecoderSupport::Convert(const char* aSrc, + int32_t* aSrcLength, + char16_t* aDest, + int32_t* aDestLength) +{ + // we do all operations using pointers internally + const char* src = aSrc; + const char* srcEnd = aSrc + *aSrcLength; + char16_t* dest = aDest; + char16_t* destEnd = aDest + *aDestLength; + + int32_t bcr, bcw; // byte counts for read & write; + nsresult res = NS_OK; + + // do we have some residual data from the last conversion? + if (mBufferLength > 0) { + if (dest == destEnd) { + res = NS_OK_UDEC_MOREOUTPUT; + } else { + for (;;) { + // we need new data to add to the buffer + if (src == srcEnd) { + res = NS_OK_UDEC_MOREINPUT; + break; + } + + // fill that buffer + int32_t buffLen = mBufferLength; // initial buffer length + FillBuffer(&src, srcEnd - src); + + // convert that buffer + bcr = mBufferLength; + bcw = destEnd - dest; + res = ConvertNoBuff(mBuffer, &bcr, dest, &bcw); + dest += bcw; + + // Detect invalid input character + if (res == NS_ERROR_ILLEGAL_INPUT && mErrBehavior == kOnError_Signal) { + break; + } + + if ((res == NS_OK_UDEC_MOREINPUT) && (bcw == 0)) { + res = NS_ERROR_UNEXPECTED; +#if defined(DEBUG_yokoyama) || defined(DEBUG_ftang) + NS_ERROR("This should not happen. Internal buffer may be corrupted."); +#endif + break; + } else { + if (bcr < buffLen) { + // we didn't convert that residual data - unfill the buffer + src -= mBufferLength - buffLen; + mBufferLength = buffLen; +#if defined(DEBUG_yokoyama) || defined(DEBUG_ftang) + NS_ERROR("This should not happen. Internal buffer may be corrupted."); +#endif + } else { + // the buffer and some extra data was converted - unget the rest + src -= mBufferLength - bcr; + mBufferLength = 0; + res = NS_OK; + } + break; + } + } + } + } + + if (res == NS_OK) { + bcr = srcEnd - src; + bcw = destEnd - dest; + res = ConvertNoBuff(src, &bcr, dest, &bcw); + src += bcr; + dest += bcw; + + // if we have partial input, store it in our internal buffer. + if (res == NS_OK_UDEC_MOREINPUT) { + bcr = srcEnd - src; + // make sure buffer is large enough + if (bcr > mBufferCapacity) { + // somehow we got into an error state and the buffer is growing out + // of control + res = NS_ERROR_UNEXPECTED; + } else { + FillBuffer(&src, bcr); + } + } + } + + *aSrcLength -= srcEnd - src; + *aDestLength -= destEnd - dest; + return res; +} + +NS_IMETHODIMP nsBufferDecoderSupport::Reset() +{ + mBufferLength = 0; + return NS_OK; +} + +NS_IMETHODIMP nsBufferDecoderSupport::GetMaxLength(const char* aSrc, + int32_t aSrcLength, + int32_t* aDestLength) +{ + NS_ASSERTION(mMaxLengthFactor != 0, "Must override GetMaxLength!"); + + mozilla::CheckedInt32 length = aSrcLength; + length *= mMaxLengthFactor; + + if (!length.isValid()) { + return NS_ERROR_OUT_OF_MEMORY; + } + + *aDestLength = length.value(); + return NS_OK; +} + +//---------------------------------------------------------------------- +// Class nsMultiTableDecoderSupport [implementation] + +nsMultiTableDecoderSupport::nsMultiTableDecoderSupport( + int32_t aTableCount, + const uRange* aRangeArray, + uScanClassID* aScanClassArray, + uMappingTable** aMappingTable, + uint32_t aMaxLengthFactor) +: nsBufferDecoderSupport(aMaxLengthFactor) +{ + mTableCount = aTableCount; + mRangeArray = aRangeArray; + mScanClassArray = aScanClassArray; + mMappingTable = aMappingTable; +} + +nsMultiTableDecoderSupport::~nsMultiTableDecoderSupport() +{ +} + +//---------------------------------------------------------------------- +// Subclassing of nsBufferDecoderSupport class [implementation] + +NS_IMETHODIMP nsMultiTableDecoderSupport::ConvertNoBuff(const char* aSrc, + int32_t* aSrcLength, + char16_t* aDest, + int32_t* aDestLength) +{ + return nsUnicodeDecodeHelper::ConvertByMultiTable(aSrc, aSrcLength, + aDest, aDestLength, + mTableCount, mRangeArray, + mScanClassArray, + mMappingTable, + mErrBehavior == kOnError_Signal); +} + +//---------------------------------------------------------------------- +// Class nsOneByteDecoderSupport [implementation] + +nsOneByteDecoderSupport::nsOneByteDecoderSupport( + uMappingTable* aMappingTable) + : nsBasicDecoderSupport() + , mMappingTable(aMappingTable) + , mFastTableCreated(false) + , mFastTableMutex("nsOneByteDecoderSupport mFastTableMutex") +{ +} + +nsOneByteDecoderSupport::~nsOneByteDecoderSupport() +{ +} + +//---------------------------------------------------------------------- +// Subclassing of nsBasicDecoderSupport class [implementation] + +NS_IMETHODIMP nsOneByteDecoderSupport::Convert(const char* aSrc, + int32_t* aSrcLength, + char16_t* aDest, + int32_t* aDestLength) +{ + if (!mFastTableCreated) { + // Probably better to make this non-lazy and get rid of the mutex + mozilla::MutexAutoLock autoLock(mFastTableMutex); + if (!mFastTableCreated) { + nsresult res = nsUnicodeDecodeHelper::CreateFastTable( + mMappingTable, mFastTable, ONE_BYTE_TABLE_SIZE); + if (NS_FAILED(res)) return res; + mFastTableCreated = true; + } + } + + return nsUnicodeDecodeHelper::ConvertByFastTable(aSrc, aSrcLength, + aDest, aDestLength, + mFastTable, + ONE_BYTE_TABLE_SIZE, + mErrBehavior == kOnError_Signal); +} + +NS_IMETHODIMP nsOneByteDecoderSupport::GetMaxLength(const char* aSrc, + int32_t aSrcLength, + int32_t* aDestLength) +{ + // single byte to Unicode converter + *aDestLength = aSrcLength; + return NS_OK_UDEC_EXACTLENGTH; +} + +NS_IMETHODIMP nsOneByteDecoderSupport::Reset() +{ + // nothing to reset, no internal state in this case + return NS_OK; +} + +//---------------------------------------------------------------------- +// Class nsBasicEncoder [implementation] +nsBasicEncoder::nsBasicEncoder() +{ +} + +nsBasicEncoder::~nsBasicEncoder() +{ +} + +//---------------------------------------------------------------------- +// Interface nsISupports [implementation] + +NS_IMPL_ADDREF(nsBasicEncoder) +NS_IMPL_RELEASE(nsBasicEncoder) +#ifdef DEBUG +NS_IMPL_QUERY_INTERFACE(nsBasicEncoder, + nsIUnicodeEncoder, + nsIBasicEncoder) +#else +NS_IMPL_QUERY_INTERFACE(nsBasicEncoder, + nsIUnicodeEncoder) +#endif +//---------------------------------------------------------------------- +// Class nsEncoderSupport [implementation] + +nsEncoderSupport::nsEncoderSupport(uint32_t aMaxLengthFactor) : + mMaxLengthFactor(aMaxLengthFactor) +{ + mBufferCapacity = DEFAULT_BUFFER_CAPACITY; + mBuffer = new char[mBufferCapacity]; + + mErrBehavior = kOnError_Signal; + mErrChar = 0; + + Reset(); +} + +nsEncoderSupport::~nsEncoderSupport() +{ + delete [] mBuffer; +} + +NS_IMETHODIMP nsEncoderSupport::ConvertNoBuff(const char16_t* aSrc, + int32_t* aSrcLength, + char* aDest, + int32_t* aDestLength) +{ + // we do all operations using pointers internally + const char16_t* src = aSrc; + const char16_t* srcEnd = aSrc + *aSrcLength; + char* dest = aDest; + char* destEnd = aDest + *aDestLength; + + int32_t bcr, bcw; // byte counts for read & write; + nsresult res; + + for (;;) { + bcr = srcEnd - src; + bcw = destEnd - dest; + res = ConvertNoBuffNoErr(src, &bcr, dest, &bcw); + src += bcr; + dest += bcw; + + if (res == NS_ERROR_UENC_NOMAPPING) { + if (mErrBehavior == kOnError_Replace) { + const char16_t buff[] = {mErrChar}; + bcr = 1; + bcw = destEnd - dest; + src--; // back the input: maybe the guy won't consume consume anything. + res = ConvertNoBuffNoErr(buff, &bcr, dest, &bcw); + src += bcr; + dest += bcw; + if (res != NS_OK) break; + } else if (mErrBehavior == kOnError_CallBack) { + bcw = destEnd - dest; + src--; + res = mErrEncoder->Convert(*src, dest, &bcw); + dest += bcw; + // if enought output space then the last char was used + if (res != NS_OK_UENC_MOREOUTPUT) src++; + if (res != NS_OK) break; + } else break; + } + else break; + } + + *aSrcLength -= srcEnd - src; + *aDestLength -= destEnd - dest; + return res; +} + +NS_IMETHODIMP nsEncoderSupport::FinishNoBuff(char* aDest, + int32_t* aDestLength) +{ + *aDestLength = 0; + return NS_OK; +} + +nsresult nsEncoderSupport::FlushBuffer(char** aDest, const char* aDestEnd) +{ + int32_t bcr, bcw; // byte counts for read & write; + nsresult res = NS_OK; + char* dest = *aDest; + + if (mBufferStart < mBufferEnd) { + bcr = mBufferEnd - mBufferStart; + bcw = aDestEnd - dest; + if (bcw < bcr) bcr = bcw; + memcpy(dest, mBufferStart, bcr); + dest += bcr; + mBufferStart += bcr; + + if (mBufferStart < mBufferEnd) res = NS_OK_UENC_MOREOUTPUT; + } + + *aDest = dest; + return res; +} + + +//---------------------------------------------------------------------- +// Interface nsIUnicodeEncoder [implementation] + +NS_IMETHODIMP nsEncoderSupport::Convert(const char16_t* aSrc, + int32_t* aSrcLength, + char* aDest, + int32_t* aDestLength) +{ + // we do all operations using pointers internally + const char16_t* src = aSrc; + const char16_t* srcEnd = aSrc + *aSrcLength; + char* dest = aDest; + char* destEnd = aDest + *aDestLength; + + int32_t bcr, bcw; // byte counts for read & write; + nsresult res; + + res = FlushBuffer(&dest, destEnd); + if (res == NS_OK_UENC_MOREOUTPUT) goto final; + + bcr = srcEnd - src; + bcw = destEnd - dest; + res = ConvertNoBuff(src, &bcr, dest, &bcw); + src += bcr; + dest += bcw; + if ((res == NS_OK_UENC_MOREOUTPUT) && (dest < destEnd)) { + // convert exactly one character into the internal buffer + // at this point, there should be at least a char in the input + for (;;) { + bcr = 1; + bcw = mBufferCapacity; + res = ConvertNoBuff(src, &bcr, mBuffer, &bcw); + + if (res == NS_OK_UENC_MOREOUTPUT) { + delete [] mBuffer; + mBufferCapacity *= 2; + mBuffer = new char [mBufferCapacity]; + } else { + src += bcr; + mBufferStart = mBufferEnd = mBuffer; + mBufferEnd += bcw; + break; + } + } + + res = FlushBuffer(&dest, destEnd); + } + +final: + *aSrcLength -= srcEnd - src; + *aDestLength -= destEnd - dest; + return res; +} + +NS_IMETHODIMP nsEncoderSupport::Finish(char* aDest, int32_t* aDestLength) +{ + // we do all operations using pointers internally + char* dest = aDest; + char* destEnd = aDest + *aDestLength; + + int32_t bcw; // byte count for write; + nsresult res; + + res = FlushBuffer(&dest, destEnd); + if (res == NS_OK_UENC_MOREOUTPUT) goto final; + + // do the finish into the internal buffer. + for (;;) { + bcw = mBufferCapacity; + res = FinishNoBuff(mBuffer, &bcw); + + if (res == NS_OK_UENC_MOREOUTPUT) { + delete [] mBuffer; + mBufferCapacity *= 2; + mBuffer = new char [mBufferCapacity]; + } else { + mBufferStart = mBufferEnd = mBuffer; + mBufferEnd += bcw; + break; + } + } + + res = FlushBuffer(&dest, destEnd); + +final: + *aDestLength -= destEnd - dest; + return res; +} + +NS_IMETHODIMP nsEncoderSupport::Reset() +{ + mBufferStart = mBufferEnd = mBuffer; + return NS_OK; +} + +NS_IMETHODIMP nsEncoderSupport::SetOutputErrorBehavior( + int32_t aBehavior, + nsIUnicharEncoder* aEncoder, + char16_t aChar) +{ + if (aBehavior == kOnError_CallBack && !aEncoder) + return NS_ERROR_NULL_POINTER; + + mErrEncoder = aEncoder; + mErrBehavior = aBehavior; + mErrChar = aChar; + return NS_OK; +} + +NS_IMETHODIMP +nsEncoderSupport::GetMaxLength(const char16_t* aSrc, + int32_t aSrcLength, + int32_t* aDestLength) +{ + mozilla::CheckedInt32 length = aSrcLength; + length *= mMaxLengthFactor; + + if (!length.isValid()) { + return NS_ERROR_OUT_OF_MEMORY; + } + + *aDestLength = length.value(); + return NS_OK; +} + + +//---------------------------------------------------------------------- +// Class nsTableEncoderSupport [implementation] + +nsTableEncoderSupport::nsTableEncoderSupport(uScanClassID aScanClass, + uShiftOutTable* aShiftOutTable, + uMappingTable* aMappingTable, + uint32_t aMaxLengthFactor) +: nsEncoderSupport(aMaxLengthFactor) +{ + mScanClass = aScanClass; + mShiftOutTable = aShiftOutTable, + mMappingTable = aMappingTable; +} + +nsTableEncoderSupport::nsTableEncoderSupport(uScanClassID aScanClass, + uMappingTable* aMappingTable, + uint32_t aMaxLengthFactor) +: nsEncoderSupport(aMaxLengthFactor) +{ + mScanClass = aScanClass; + mShiftOutTable = nullptr; + mMappingTable = aMappingTable; +} + +nsTableEncoderSupport::~nsTableEncoderSupport() +{ +} + +//---------------------------------------------------------------------- +// Subclassing of nsEncoderSupport class [implementation] + +NS_IMETHODIMP nsTableEncoderSupport::ConvertNoBuffNoErr( + const char16_t* aSrc, + int32_t* aSrcLength, + char* aDest, + int32_t* aDestLength) +{ + return nsUnicodeEncodeHelper::ConvertByTable(aSrc, aSrcLength, + aDest, aDestLength, + mScanClass, + mShiftOutTable, mMappingTable); +} + +//---------------------------------------------------------------------- +// Class nsMultiTableEncoderSupport [implementation] + +nsMultiTableEncoderSupport::nsMultiTableEncoderSupport( + int32_t aTableCount, + uScanClassID* aScanClassArray, + uShiftOutTable** aShiftOutTable, + uMappingTable** aMappingTable, + uint32_t aMaxLengthFactor) +: nsEncoderSupport(aMaxLengthFactor) +{ + mTableCount = aTableCount; + mScanClassArray = aScanClassArray; + mShiftOutTable = aShiftOutTable; + mMappingTable = aMappingTable; +} + +nsMultiTableEncoderSupport::~nsMultiTableEncoderSupport() +{ +} + +//---------------------------------------------------------------------- +// Subclassing of nsEncoderSupport class [implementation] + +NS_IMETHODIMP nsMultiTableEncoderSupport::ConvertNoBuffNoErr( + const char16_t* aSrc, + int32_t* aSrcLength, + char* aDest, + int32_t* aDestLength) +{ + return nsUnicodeEncodeHelper::ConvertByMultiTable(aSrc, aSrcLength, + aDest, aDestLength, + mTableCount, + mScanClassArray, + mShiftOutTable, + mMappingTable); +} |