summaryrefslogtreecommitdiffstats
path: root/intl/uconv/util/nsUCSupport.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'intl/uconv/util/nsUCSupport.cpp')
-rw-r--r--intl/uconv/util/nsUCSupport.cpp621
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);
+}