gecko-dev/intl/uconv/nsNCRFallbackEncoderWrapper...

157 строки
5.3 KiB
C++

/* -*- 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 "nsNCRFallbackEncoderWrapper.h"
#include "mozilla/dom/EncodingUtils.h"
nsNCRFallbackEncoderWrapper::nsNCRFallbackEncoderWrapper(const nsACString& aEncoding)
: mEncoder(mozilla::dom::EncodingUtils::EncoderForEncoding(aEncoding))
{
}
nsNCRFallbackEncoderWrapper::~nsNCRFallbackEncoderWrapper()
{
}
bool
nsNCRFallbackEncoderWrapper::WriteNCR(nsACString& aBytes,
uint32_t& aDstWritten,
int32_t aUnmappable)
{
// To avoid potentially shrinking aBytes and then growing it back, use
// another string for number formatting.
nsAutoCString ncr("&#");
ncr.AppendInt(aUnmappable);
ncr.Append(';');
uint32_t ncrLen = ncr.Length();
uint32_t needed = aDstWritten + ncrLen;
if (needed > INT32_MAX) {
return false;
}
if (needed > aBytes.Length() && !aBytes.SetLength(needed,
mozilla::fallible_t())) {
return false;
}
memcpy(aBytes.BeginWriting() + aDstWritten,
ncr.BeginReading(),
ncrLen);
aDstWritten += ncrLen;
return true;
}
bool
nsNCRFallbackEncoderWrapper::Encode(const nsAString& aUtf16,
nsACString& aBytes)
{
// mozilla::dom::EncodingUtils::EncoderForEncoding fails during shutdown
if (!mEncoder) {
return false;
}
// nsIUnicodeEncoder uses int32_t for sizes :-(
if (aUtf16.Length() > INT32_MAX) {
return false;
}
const char16_t* src = aUtf16.BeginReading();
const char16_t* srcEnd = aUtf16.EndReading();
uint32_t dstWritten = 0;
for (;;) {
int32_t srcLen = srcEnd - src;
int32_t dstLen = 0;
nsresult rv = mEncoder->GetMaxLength(src, srcLen, &dstLen);
if (NS_FAILED(rv)) {
return false;
}
uint32_t needed = dstWritten + dstLen;
if (needed > INT32_MAX) {
return false;
}
// Behind the scenes SetLength() makes the underlying allocation not have
// slop, so we don't need to round up here.
if (needed > aBytes.Length() && !aBytes.SetLength(needed,
mozilla::fallible_t())) {
return false;
}
// We need to re-obtain the destination pointer on every iteration, because
// SetLength() invalidates it.
char* dst = aBytes.BeginWriting() + dstWritten;
dstLen = aBytes.Length() - dstWritten;
mEncoder->Reset();
rv = mEncoder->Convert(src, &srcLen, dst, &dstLen);
// Update state tracking
src += srcLen;
dstWritten += dstLen;
if (rv == NS_OK_UENC_MOREOUTPUT) {
MOZ_ASSERT_UNREACHABLE("GetMaxLength must have returned a bogus length.");
return false;
}
if (rv == NS_ERROR_UENC_NOMAPPING) {
int32_t unmappable;
// The unmappable code unit or the first half of an unmappable surrogate
// pair is consumed by the encoder.
MOZ_ASSERT(srcLen > 0, "Encoder should have consumed some input.");
char16_t codeUnit = src[-1];
// Let's see if it is a surrogate
size_t highBits = (codeUnit & 0xFC00);
if (highBits == 0xD800) {
// high surrogate
// Let's see if we actually have a surrogate pair.
char16_t next;
if (src < srcEnd && NS_IS_LOW_SURROGATE((next = *src))) {
src++; // consume the low surrogate
unmappable = SURROGATE_TO_UCS4(codeUnit, next);
} else {
// unpaired surrogate.
unmappable = 0xFFFD;
}
} else if (highBits == 0xDC00) {
// low surrogate
// This must be an unpaired surrogate.
unmappable = 0xFFFD;
} else {
// not a surrogate
unmappable = codeUnit;
}
// If we are encoding to ISO-2022-JP, we need to let the encoder to
// generate a transition to the ASCII state if not already there.
dst = aBytes.BeginWriting() + dstWritten;
dstLen = aBytes.Length() - dstWritten;
rv = mEncoder->Finish(dst, &dstLen);
dstWritten += dstLen;
if (rv != NS_OK) {
// Failures should be impossible if GetMaxLength works. Big5 is the
// only case where Finish() may return NS_ERROR_UENC_NOMAPPING but
// that should never happen right after Convert() has returned it.
MOZ_ASSERT_UNREACHABLE("Broken encoder.");
return false;
}
if (!WriteNCR(aBytes, dstWritten, unmappable)) {
return false;
}
continue;
}
if (!(rv == NS_OK || rv == NS_OK_UENC_MOREINPUT)) {
return false;
}
MOZ_ASSERT(src == srcEnd, "Converter did not consume all input.");
dst = aBytes.BeginWriting() + dstWritten;
dstLen = aBytes.Length() - dstWritten;
rv = mEncoder->Finish(dst, &dstLen);
dstWritten += dstLen;
if (rv == NS_OK_UENC_MOREOUTPUT) {
MOZ_ASSERT_UNREACHABLE("GetMaxLength must have returned a bogus length.");
return false;
}
if (rv == NS_ERROR_UENC_NOMAPPING) {
// Big5
if (!WriteNCR(aBytes, dstWritten, 0xFFFD)) {
return false;
}
}
return aBytes.SetLength(dstWritten, mozilla::fallible_t());
}
}