2011-02-14 Darin Adler <darin@apple.com>
authordarin@apple.com <darin@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 14 Feb 2011 21:23:45 +0000 (21:23 +0000)
committerdarin@apple.com <darin@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 14 Feb 2011 21:23:45 +0000 (21:23 +0000)
        Reviewed by Alexey Proskuryakov.

        Add built-in decoder for UTF-8 for improved performance
        https://bugs.webkit.org/show_bug.cgi?id=53898

        Covered by existing tests; not adding new tests at this time.

        This patch now handles errors in the same way the existing codecs do,
        and so passes our tests. The first version failed some tests because
        of incorrect error handling. The second version was missing one line
        of code that made it decode incomplete sequences at the end of the
        buffer twice.

        * platform/text/TextCodecICU.cpp:
        (WebCore::create): Renamed from newTextCodecICU, made a static member
        function, and added a call to adoptPtr.
        (WebCore::TextCodecICU::registerEncodingNames): Renamed from
        registerExtendedEncodingNames since this class is no longer used for
        base codecs. Removed aliases for UTF-8; now handled by TextCodecUTF8.
        (WebCore::TextCodecICU::registerCodecs): Renamed.
        (WebCore::fallbackForGBK): Renamed to conform to our current style.

        * platform/text/TextCodecICU.h: Updated for above changes. Changed
        indentation. Made most functions private, including virtual function
        overrides. Marked ICUConverterWrapper noncopyable.

        * platform/text/TextCodecUTF8.cpp:
        (WebCore::TextCodecUTF8::registerEncodingNames): Added the UTF-8 aliases
        that were formerly added by TextCodecICU.
        (WebCore::nonASCIISequenceLength): Fixed bug where this would return 4 for
        bytes F5-FF instead of failing.
        (WebCore::decodeNonASCIISequence): Tweaked coding style.
        (WebCore::appendCharacter): Added. Makes it easier to share code between
        the partial-character handling and main loop.
        (WebCore::TextCodecUTF8::decode): Fixed buffer size computation for case
        where there is a partial sequence. Fixed partial sequence handling so that
        goto is no longer needed, since compilers sometimes make poor code when
        goto is involved. Added a loop for partial sequences since we consume only
        one byte when a partial sequence is invalid. Fixed logic in main decoding
        loop so goto is not needed. Used early-exit style in both loops so the main
        flow is not nested inside if statements. Added correct error handling for
        flush when a partial sequence remains, which involved wrapping the function
        in yet another loop.

        * platform/text/TextCodecUTF8.h: Made virtual function overrides private.

        * platform/text/TextEncodingRegistry.cpp:
        (WebCore::buildBaseTextCodecMaps): Added calls to TextCodecUTF8. Removed
        calls to TextCodecICU. Added FIXMEs for other codecs that no longer need
        to be included here.
        (WebCore::extendTextCodecMaps): Updated for the name change of the
        TextCodecICU functions.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@78499 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebCore/ChangeLog
Source/WebCore/platform/text/TextCodecICU.cpp
Source/WebCore/platform/text/TextCodecICU.h
Source/WebCore/platform/text/TextCodecUTF8.cpp
Source/WebCore/platform/text/TextCodecUTF8.h
Source/WebCore/platform/text/TextEncodingRegistry.cpp

index 311c98a..573a8c6 100644 (file)
@@ -1,3 +1,58 @@
+2011-02-14  Darin Adler  <darin@apple.com>
+
+        Reviewed by Alexey Proskuryakov.
+
+        Add built-in decoder for UTF-8 for improved performance
+        https://bugs.webkit.org/show_bug.cgi?id=53898
+
+        Covered by existing tests; not adding new tests at this time.
+
+        This patch now handles errors in the same way the existing codecs do,
+        and so passes our tests. The first version failed some tests because
+        of incorrect error handling. The second version was missing one line
+        of code that made it decode incomplete sequences at the end of the
+        buffer twice.
+
+        * platform/text/TextCodecICU.cpp:
+        (WebCore::create): Renamed from newTextCodecICU, made a static member
+        function, and added a call to adoptPtr.
+        (WebCore::TextCodecICU::registerEncodingNames): Renamed from
+        registerExtendedEncodingNames since this class is no longer used for
+        base codecs. Removed aliases for UTF-8; now handled by TextCodecUTF8.
+        (WebCore::TextCodecICU::registerCodecs): Renamed.
+        (WebCore::fallbackForGBK): Renamed to conform to our current style.
+
+        * platform/text/TextCodecICU.h: Updated for above changes. Changed
+        indentation. Made most functions private, including virtual function
+        overrides. Marked ICUConverterWrapper noncopyable.
+
+        * platform/text/TextCodecUTF8.cpp:
+        (WebCore::TextCodecUTF8::registerEncodingNames): Added the UTF-8 aliases
+        that were formerly added by TextCodecICU.
+        (WebCore::nonASCIISequenceLength): Fixed bug where this would return 4 for
+        bytes F5-FF instead of failing.
+        (WebCore::decodeNonASCIISequence): Tweaked coding style.
+        (WebCore::appendCharacter): Added. Makes it easier to share code between
+        the partial-character handling and main loop.
+        (WebCore::TextCodecUTF8::decode): Fixed buffer size computation for case
+        where there is a partial sequence. Fixed partial sequence handling so that
+        goto is no longer needed, since compilers sometimes make poor code when
+        goto is involved. Added a loop for partial sequences since we consume only
+        one byte when a partial sequence is invalid. Fixed logic in main decoding
+        loop so goto is not needed. Used early-exit style in both loops so the main
+        flow is not nested inside if statements. Added correct error handling for
+        flush when a partial sequence remains, which involved wrapping the function
+        in yet another loop.
+
+        * platform/text/TextCodecUTF8.h: Made virtual function overrides private.
+
+        * platform/text/TextEncodingRegistry.cpp:
+        (WebCore::buildBaseTextCodecMaps): Added calls to TextCodecUTF8. Removed
+        calls to TextCodecICU. Added FIXMEs for other codecs that no longer need
+        to be included here.
+        (WebCore::extendTextCodecMaps): Updated for the name change of the
+        TextCodecICU functions.
+
 2011-02-14  Adam Barth  <abarth@webkit.org>
 
         Reviewed by Eric Seidel.
index 92a158a..0d99196 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2004, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2004, 2006, 2007, 2008, 2011 Apple Inc. All rights reserved.
  * Copyright (C) 2006 Alexey Proskuryakov <ap@nypop.com>
  *
  * Redistribution and use in source and binary forms, with or without
 #include "config.h"
 #include "TextCodecICU.h"
 
-#include "PlatformString.h"
 #include "ThreadGlobalData.h"
 #include <unicode/ucnv.h>
 #include <unicode/ucnv_cb.h>
 #include <wtf/Assertions.h>
-#include <wtf/text/CString.h>
-#include <wtf/PassOwnPtr.h>
 #include <wtf/StringExtras.h>
 #include <wtf/Threading.h>
+#include <wtf/text/CString.h>
 #include <wtf/unicode/CharacterNames.h>
 
 using std::min;
@@ -55,22 +53,12 @@ static UConverter*& cachedConverterICU()
     return threadGlobalData().cachedConverterICU().converter;
 }
 
-static PassOwnPtr<TextCodec> newTextCodecICU(const TextEncoding& encoding, const void*)
-{
-    return new TextCodecICU(encoding);
-}
-
-void TextCodecICU::registerBaseEncodingNames(EncodingNameRegistrar registrar)
+PassOwnPtr<TextCodec> TextCodecICU::create(const TextEncoding& encoding, const void*)
 {
-    registrar("UTF-8", "UTF-8");
+    return adoptPtr(new TextCodecICU(encoding));
 }
 
-void TextCodecICU::registerBaseCodecs(TextCodecRegistrar registrar)
-{
-    registrar("UTF-8", newTextCodecICU, 0);
-}
-
-void TextCodecICU::registerExtendedEncodingNames(EncodingNameRegistrar registrar)
+void TextCodecICU::registerEncodingNames(EncodingNameRegistrar registrar)
 {
     // We register Hebrew with logical ordering using a separate name.
     // Otherwise, this would share the same canonical name as the
@@ -143,9 +131,6 @@ void TextCodecICU::registerExtendedEncodingNames(EncodingNameRegistrar registrar
     registrar("csISO88598I", "ISO-8859-8-I");
     registrar("koi", "KOI8-R");
     registrar("logical", "ISO-8859-8-I");
-    registrar("unicode11utf8", "UTF-8");
-    registrar("unicode20utf8", "UTF-8");
-    registrar("x-unicode20utf8", "UTF-8");
     registrar("visual", "ISO-8859-8");
     registrar("winarabic", "windows-1256");
     registrar("winbaltic", "windows-1257");
@@ -163,7 +148,6 @@ void TextCodecICU::registerExtendedEncodingNames(EncodingNameRegistrar registrar
     registrar("x-euc", "EUC-JP");
     registrar("x-windows-949", "windows-949");
     registrar("x-uhc", "windows-949");
-    registrar("utf8", "UTF-8");
     registrar("shift-jis", "Shift_JIS");
 
     // These aliases are present in modern versions of ICU, but use different codecs, and have no standard names.
@@ -190,10 +174,10 @@ void TextCodecICU::registerExtendedEncodingNames(EncodingNameRegistrar registrar
     // and because older versions of ICU don't support ISO-8859-16 encoding at all.
 }
 
-void TextCodecICU::registerExtendedCodecs(TextCodecRegistrar registrar)
+void TextCodecICU::registerCodecs(TextCodecRegistrar registrar)
 {
     // See comment above in registerEncodingNames.
-    registrar("ISO-8859-8-I", newTextCodecICU, 0);
+    registrar("ISO-8859-8-I", create, 0);
 
     int32_t numEncodings = ucnv_countAvailable();
     for (int32_t i = 0; i < numEncodings; ++i) {
@@ -206,7 +190,7 @@ void TextCodecICU::registerExtendedCodecs(TextCodecRegistrar registrar)
             if (!U_SUCCESS(error) || !standardName)
                 continue;
         }
-        registrar(standardName, newTextCodecICU, 0);
+        registrar(standardName, create, 0);
     }
 }
 
@@ -300,6 +284,7 @@ public:
             ASSERT(err == U_ZERO_ERROR);
         }
     }
+
 private:
     UConverter* m_converter;
     bool m_shouldStopOnEncodingErrors;
@@ -354,28 +339,26 @@ String TextCodecICU::decode(const char* bytes, size_t length, bool flush, bool s
 }
 
 // We need to apply these fallbacks ourselves as they are not currently supported by ICU and
-// they were provided by the old TEC encoding path
-// Needed to fix <rdar://problem/4708689>
-static UChar getGbkEscape(UChar32 codePoint)
+// they were provided by the old TEC encoding path. Needed to fix <rdar://problem/4708689>.
+static UChar fallbackForGBK(UChar32 character)
 {
-    switch (codePoint) {
-        case 0x01F9:
-            return 0xE7C8;
-        case 0x1E3F:
-            return 0xE7C7;
-        case 0x22EF:
-            return 0x2026;
-        case 0x301C:
-            return 0xFF5E;
-        default:
-            return 0;
+    switch (character) {
+    case 0x01F9:
+        return 0xE7C8;
+    case 0x1E3F:
+        return 0xE7C7;
+    case 0x22EF:
+        return 0x2026;
+    case 0x301C:
+        return 0xFF5E;
     }
+    return 0;
 }
 
 // Invalid character handler when writing escaped entities for unrepresentable
 // characters. See the declaration of TextCodec::encode for more.
 static void urlEscapedEntityCallback(const void* context, UConverterFromUnicodeArgs* fromUArgs, const UChar* codeUnits, int32_t length,
-                                     UChar32 codePoint, UConverterCallbackReason reason, UErrorCode* err)
+    UChar32 codePoint, UConverterCallbackReason reason, UErrorCode* err)
 {
     if (reason == UCNV_UNASSIGNED) {
         *err = U_ZERO_ERROR;
@@ -389,10 +372,10 @@ static void urlEscapedEntityCallback(const void* context, UConverterFromUnicodeA
 
 // Substitutes special GBK characters, escaping all other unassigned entities.
 static void gbkCallbackEscape(const void* context, UConverterFromUnicodeArgs* fromUArgs, const UChar* codeUnits, int32_t length,
-                              UChar32 codePoint, UConverterCallbackReason reason, UErrorCode* err) 
+    UChar32 codePoint, UConverterCallbackReason reason, UErrorCode* err) 
 {
     UChar outChar;
-    if (reason == UCNV_UNASSIGNED && (outChar = getGbkEscape(codePoint))) {
+    if (reason == UCNV_UNASSIGNED && (outChar = fallbackForGBK(codePoint))) {
         const UChar* source = &outChar;
         *err = U_ZERO_ERROR;
         ucnv_cbFromUWriteUChars(fromUArgs, &source, source + 1, 0, err);
@@ -403,10 +386,10 @@ static void gbkCallbackEscape(const void* context, UConverterFromUnicodeArgs* fr
 
 // Combines both gbkUrlEscapedEntityCallback and GBK character substitution.
 static void gbkUrlEscapedEntityCallack(const void* context, UConverterFromUnicodeArgs* fromUArgs, const UChar* codeUnits, int32_t length,
-                                       UChar32 codePoint, UConverterCallbackReason reason, UErrorCode* err) 
+    UChar32 codePoint, UConverterCallbackReason reason, UErrorCode* err) 
 {
     if (reason == UCNV_UNASSIGNED) {
-        if (UChar outChar = getGbkEscape(codePoint)) {
+        if (UChar outChar = fallbackForGBK(codePoint)) {
             const UChar* source = &outChar;
             *err = U_ZERO_ERROR;
             ucnv_cbFromUWriteUChars(fromUArgs, &source, source + 1, 0, err);
@@ -419,10 +402,10 @@ static void gbkUrlEscapedEntityCallack(const void* context, UConverterFromUnicod
 }
 
 static void gbkCallbackSubstitute(const void* context, UConverterFromUnicodeArgs* fromUArgs, const UChar* codeUnits, int32_t length,
-                                  UChar32 codePoint, UConverterCallbackReason reason, UErrorCode* err) 
+    UChar32 codePoint, UConverterCallbackReason reason, UErrorCode* err) 
 {
     UChar outChar;
-    if (reason == UCNV_UNASSIGNED && (outChar = getGbkEscape(codePoint))) {
+    if (reason == UCNV_UNASSIGNED && (outChar = fallbackForGBK(codePoint))) {
         const UChar* source = &outChar;
         *err = U_ZERO_ERROR;
         ucnv_cbFromUWriteUChars(fromUArgs, &source, source + 1, 0, err);
@@ -486,5 +469,4 @@ CString TextCodecICU::encode(const UChar* characters, size_t length, Unencodable
     return CString(result.data(), size);
 }
 
-
 } // namespace WebCore
index bf517f7..f02c36d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2004, 2006, 2007 Apple Inc. All rights reserved.
+ * Copyright (C) 2004, 2006, 2007, 2011 Apple Inc. All rights reserved.
  * Copyright (C) 2006 Alexey Proskuryakov <ap@nypop.com>
  *
  * Redistribution and use in source and binary forms, with or without
@@ -29,7 +29,6 @@
 
 #include "TextCodec.h"
 #include "TextEncoding.h"
-
 #include <unicode/utypes.h>
 
 typedef struct UConverter UConverter;
@@ -38,19 +37,18 @@ namespace WebCore {
 
     class TextCodecICU : public TextCodec {
     public:
-        static void registerBaseEncodingNames(EncodingNameRegistrar);
-        static void registerBaseCodecs(TextCodecRegistrar);
+        static void registerEncodingNames(EncodingNameRegistrar);
+        static void registerCodecs(TextCodecRegistrar);
 
-        static void registerExtendedEncodingNames(EncodingNameRegistrar);
-        static void registerExtendedCodecs(TextCodecRegistrar);
+        virtual ~TextCodecICU();
 
+    private:
         TextCodecICU(const TextEncoding&);
-        virtual ~TextCodecICU();
+        static PassOwnPtr<TextCodec> create(const TextEncoding&, const void*);
 
         virtual String decode(const char*, size_t length, bool flush, bool stopOnError, bool& sawError);
         virtual CString encode(const UChar*, size_t length, UnencodableHandling);
 
-    private:
         void createICUConverter() const;
         void releaseICUConverter() const;
         bool needsGBKFallbacks() const { return m_needsGBKFallbacks; }
@@ -67,13 +65,12 @@ namespace WebCore {
     };
 
     struct ICUConverterWrapper {
-        ICUConverterWrapper()
-            : converter(0)
-        {
-        }
+        ICUConverterWrapper() : converter(0) { }
         ~ICUConverterWrapper();
 
         UConverter* converter;
+
+        WTF_MAKE_NONCOPYABLE(ICUConverterWrapper);
     };
 
 } // namespace WebCore
index 8944d68..81cf072 100644 (file)
 
 #include <wtf/text/CString.h>
 #include <wtf/text/StringBuffer.h>
-#include <wtf/unicode/UTF8.h>
+#include <wtf/unicode/CharacterNames.h>
 
 using namespace WTF::Unicode;
 using namespace std;
 
 namespace WebCore {
 
+const int nonCharacter = -1;
+
 // Assuming that a pointer is the size of a "machine word", then
 // uintptr_t is an integer type that is also a machine word.
 typedef uintptr_t MachineWord;
@@ -93,6 +95,15 @@ PassOwnPtr<TextCodec> TextCodecUTF8::create(const TextEncoding&, const void*)
 void TextCodecUTF8::registerEncodingNames(EncodingNameRegistrar registrar)
 {
     registrar("UTF-8", "UTF-8");
+
+    // Additional aliases that originally were present in the encoding
+    // table in WebKit on Macintosh, and subsequently added by
+    // TextCodecICU. Perhaps we can prove some are not used on the web
+    // and remove them.
+    registrar("unicode11utf8", "UTF-8");
+    registrar("unicode20utf8", "UTF-8");
+    registrar("utf8", "UTF-8");
+    registrar("x-unicode20utf8", "UTF-8");
 }
 
 void TextCodecUTF8::registerCodecs(TextCodecRegistrar registrar)
@@ -100,27 +111,38 @@ void TextCodecUTF8::registerCodecs(TextCodecRegistrar registrar)
     registrar("UTF-8", create, 0);
 }
 
-static inline int nonASCIISequenceLength(unsigned char firstByte)
+static inline int nonASCIISequenceLength(uint8_t firstByte)
 {
-    ASSERT(!isASCII(firstByte));
-    switch (firstByte >> 4) {
-    case 0xF:
-        return 4;
-    case 0xE:
-        return 3;
-    }
-    return 2;
+    static const uint8_t lengths[256] = {
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+        2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+        2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+        2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+        2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+        2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+        3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+        4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+    };
+    return lengths[firstByte];
 }
 
-static inline int decodeNonASCIISequence(const unsigned char* sequence, unsigned length)
+static inline int decodeNonASCIISequence(const uint8_t* sequence, unsigned length)
 {
     ASSERT(!isASCII(sequence[0]));
     if (length == 2) {
         ASSERT(sequence[0] <= 0xDF);
         if (sequence[0] < 0xC2)
-            return -1;
+            return nonCharacter;
         if (sequence[1] < 0x80 || sequence[1] > 0xBF)
-            return -1;
+            return nonCharacter;
         return ((sequence[0] << 6) + sequence[1]) - 0x00003080;
     }
     if (length == 3) {
@@ -128,18 +150,18 @@ static inline int decodeNonASCIISequence(const unsigned char* sequence, unsigned
         switch (sequence[0]) {
         case 0xE0:
             if (sequence[1] < 0xA0 || sequence[1] > 0xBF)
-                return -1;
+                return nonCharacter;
             break;
         case 0xED:
             if (sequence[1] < 0x80 || sequence[1] > 0x9F)
-                return -1;
+                return nonCharacter;
             break;
         default:
             if (sequence[1] < 0x80 || sequence[1] > 0xBF)
-                return -1;
+                return nonCharacter;
         }
         if (sequence[2] < 0x80 || sequence[2] > 0xBF)
-            return -1;
+            return nonCharacter;
         return ((sequence[0] << 12) + (sequence[1] << 6) + sequence[2]) - 0x000E2080;
     }
     ASSERT(length == 4);
@@ -147,109 +169,150 @@ static inline int decodeNonASCIISequence(const unsigned char* sequence, unsigned
     switch (sequence[0]) {
     case 0xF0:
         if (sequence[1] < 0x90 || sequence[1] > 0xBF)
-            return -1;
+            return nonCharacter;
         break;
     case 0xF4:
         if (sequence[1] < 0x80 || sequence[1] > 0x8F)
-            return -1;
+            return nonCharacter;
         break;
     default:
         if (sequence[1] < 0x80 || sequence[1] > 0xBF)
-            return -1;
+            return nonCharacter;
     }
     if (sequence[2] < 0x80 || sequence[2] > 0xBF)
-        return -1;
+        return nonCharacter;
     if (sequence[3] < 0x80 || sequence[3] > 0xBF)
-        return -1;
+        return nonCharacter;
     return ((sequence[0] << 18) + (sequence[1] << 12) + (sequence[2] << 6) + sequence[3]) - 0x03C82080;
 }
 
+static inline UChar* appendCharacter(UChar* destination, int character)
+{
+    ASSERT(character != nonCharacter);
+    ASSERT(!U_IS_SURROGATE(character));
+    if (U_IS_BMP(character))
+        *destination++ = character;
+    else {
+        *destination++ = U16_LEAD(character);
+        *destination++ = U16_TRAIL(character);
+    }
+    return destination;
+}
+
 String TextCodecUTF8::decode(const char* bytes, size_t length, bool flush, bool stopOnError, bool& sawError)
 {
-    StringBuffer buffer(length);
+    // Each input byte might turn into a character.
+    // That includes all bytes in the partial-sequence buffer because
+    // each byte in an invalid sequence will turn into a replacement character.
+    StringBuffer buffer(m_partialSequenceSize + length);
 
     const uint8_t* source = reinterpret_cast<const uint8_t*>(bytes);
     const uint8_t* end = source + length;
     const uint8_t* alignedEnd = alignToMachineWord(end);
     UChar* destination = buffer.characters();
 
-    int count;
-    int character;
-
-    if (m_partialSequenceSize) {
-        count = nonASCIISequenceLength(m_partialSequence[0]);
-        ASSERT(count > m_partialSequenceSize);
-        if (count - m_partialSequenceSize > end - source) {
-            memcpy(m_partialSequence + m_partialSequenceSize, source, end - source);
-            m_partialSequenceSize += end - source;
-            source = end;
-        } else {
+    do {
+        while (m_partialSequenceSize) {
+            int count = nonASCIISequenceLength(m_partialSequence[0]);
+            ASSERT(count > m_partialSequenceSize);
+            ASSERT(count >= 2);
+            ASSERT(count <= 4);
+            if (count - m_partialSequenceSize > end - source) {
+                if (!flush) {
+                    // We have an incomplete partial sequence, so put it all in the partial
+                    // sequence buffer, and break out of this loop so we can exit the function.
+                    memcpy(m_partialSequence + m_partialSequenceSize, source, end - source);
+                    m_partialSequenceSize += end - source;
+                    source = end;
+                    break;
+                }
+                // We have an incomplete partial sequence at the end of the buffer.
+                // That is an error.
+                sawError = true;
+                if (stopOnError) {
+                    source = end;
+                    break;
+                }
+                // Each error consumes one byte and generates one replacement character.
+                --m_partialSequenceSize;
+                memmove(m_partialSequence, m_partialSequence + 1, m_partialSequenceSize);
+                *destination++ = replacementCharacter;
+                continue;
+            }
             uint8_t completeSequence[U8_MAX_LENGTH];
             memcpy(completeSequence, m_partialSequence, m_partialSequenceSize);
             memcpy(completeSequence + m_partialSequenceSize, source, count - m_partialSequenceSize);
             source += count - m_partialSequenceSize;
+            int character = decodeNonASCIISequence(completeSequence, count);
+            if (character == nonCharacter) {
+                sawError = true;
+                if (stopOnError) {
+                    source = end;
+                    break;
+                }
+                // Each error consumes one byte and generates one replacement character.
+                memcpy(m_partialSequence, completeSequence + 1, count - 1);
+                m_partialSequenceSize = count - 1;
+                *destination++ = replacementCharacter;
+                continue;
+            }
             m_partialSequenceSize = 0;
-            character = decodeNonASCIISequence(completeSequence, count);
-            goto decodedNonASCII;
+            destination = appendCharacter(destination, character);
         }
-    }
 
-    while (source < end) {
-        if (isASCII(*source)) {
-            // Fast path for ASCII. Most UTF-8 text will be ASCII.
-            if (isAlignedToMachineWord(source)) {
-                while (source < alignedEnd) {
-                    MachineWord chunk = *reinterpret_cast_ptr<const MachineWord*>(source);
-                    if (chunk & NonASCIIMask<sizeof(MachineWord)>::value()) {
-                        if (isASCII(*source))
+        while (source < end) {
+            if (isASCII(*source)) {
+                // Fast path for ASCII. Most UTF-8 text will be ASCII.
+                if (isAlignedToMachineWord(source)) {
+                    while (source < alignedEnd) {
+                        MachineWord chunk = *reinterpret_cast_ptr<const MachineWord*>(source);
+                        if (chunk & NonASCIIMask<sizeof(MachineWord)>::value())
                             break;
-                        goto nonASCII;
+                        UCharByteFiller<sizeof(MachineWord)>::copy(destination, source);
+                        source += sizeof(MachineWord);
+                        destination += sizeof(MachineWord);
                     }
-                    UCharByteFiller<sizeof(MachineWord)>::copy(destination, source);
-                    source += sizeof(MachineWord);
-                    destination += sizeof(MachineWord);
+                    if (source == end)
+                        break;
+                    if (!isASCII(*source))
+                        continue;
                 }
-                if (source == end)
-                    break;
-            }
-            *destination++ = *source++;
-        } else {
-nonASCII:
-            count = nonASCIISequenceLength(*source);
-            ASSERT(count >= 2);
-            ASSERT(count <= 4);
-            if (count > end - source) {
-                ASSERT(end - source <= static_cast<ptrdiff_t>(sizeof(m_partialSequence)));
-                ASSERT(!m_partialSequenceSize);
-                m_partialSequenceSize = end - source;
-                memcpy(m_partialSequence, source, m_partialSequenceSize);
-                break;
+                *destination++ = *source++;
+                continue;
             }
-            character = decodeNonASCIISequence(source, count);
-            source += count;
-decodedNonASCII:
-            if (character < 0) {
-                if (stopOnError) {
-                    sawError = true;
+            int count = nonASCIISequenceLength(*source);
+            int character;
+            if (!count)
+                character = nonCharacter;
+            else {
+                ASSERT(count >= 2);
+                ASSERT(count <= 4);
+                if (count > end - source) {
+                    ASSERT(end - source <= static_cast<ptrdiff_t>(sizeof(m_partialSequence)));
+                    ASSERT(!m_partialSequenceSize);
+                    m_partialSequenceSize = end - source;
+                    memcpy(m_partialSequence, source, m_partialSequenceSize);
+                    source = end;
                     break;
                 }
-            } else {
-                ASSERT(!U_IS_SURROGATE(character));
-                if (U_IS_BMP(character))
-                    *destination++ = character;
-                else {
-                    *destination++ = U16_LEAD(character);
-                    *destination++ = U16_TRAIL(character);
-                }
+                character = decodeNonASCIISequence(source, count);
+            }
+            if (character == nonCharacter) {
+                sawError = true;
+                if (stopOnError)
+                    break;
+                // Each error consumes one byte and generates one replacement character.
+                ++source;
+                *destination++ = replacementCharacter;
+                continue;
             }
+            source += count;
+            destination = appendCharacter(destination, character);
         }
-    }
+    } while (flush && m_partialSequenceSize);
 
     buffer.shrink(destination - buffer.characters());
 
-    if (flush && m_partialSequenceSize)
-        sawError = true;
-
     return String::adopt(buffer);
 }
 
index f3b6b7a..2bbb31e 100644 (file)
@@ -35,13 +35,13 @@ public:
     static void registerEncodingNames(EncodingNameRegistrar);
     static void registerCodecs(TextCodecRegistrar);
 
-    virtual String decode(const char*, size_t length, bool flush, bool stopOnError, bool& sawError);
-    virtual CString encode(const UChar*, size_t length, UnencodableHandling);
-
 private:
     static PassOwnPtr<TextCodec> create(const TextEncoding&, const void*);
     TextCodecUTF8() : m_partialSequenceSize(0) { }
 
+    virtual String decode(const char*, size_t length, bool flush, bool stopOnError, bool& sawError);
+    virtual CString encode(const UChar*, size_t length, UnencodableHandling);
+
     int m_partialSequenceSize;
     char m_partialSequence[U8_MAX_LENGTH - 1];
     
index 1dc09ee..09e05bf 100644 (file)
@@ -58,6 +58,9 @@
 #include "TextCodecWinCE.h"
 #endif
 
+#include <wtf/CurrentTime.h>
+#include <wtf/text/CString.h>
+
 using namespace WTF;
 
 namespace WebCore {
@@ -220,28 +223,29 @@ static void buildBaseTextCodecMaps()
     TextCodecLatin1::registerEncodingNames(addToTextEncodingNameMap);
     TextCodecLatin1::registerCodecs(addToTextCodecMap);
 
+    TextCodecUTF8::registerEncodingNames(addToTextEncodingNameMap);
+    TextCodecUTF8::registerCodecs(addToTextCodecMap);
+
     TextCodecUTF16::registerEncodingNames(addToTextEncodingNameMap);
     TextCodecUTF16::registerCodecs(addToTextCodecMap);
 
     TextCodecUserDefined::registerEncodingNames(addToTextEncodingNameMap);
     TextCodecUserDefined::registerCodecs(addToTextCodecMap);
 
-#if USE(ICU_UNICODE)
-    TextCodecICU::registerBaseEncodingNames(addToTextEncodingNameMap);
-    TextCodecICU::registerBaseCodecs(addToTextCodecMap);
-#endif
-
 #if USE(GLIB_UNICODE)
+    // FIXME: This is not needed. The code above covers all the base codecs.
     TextCodecGtk::registerBaseEncodingNames(addToTextEncodingNameMap);
     TextCodecGtk::registerBaseCodecs(addToTextCodecMap);
 #endif
 
 #if USE(BREWMP_UNICODE)
+    // FIXME: This is not needed. The code above covers all the base codecs.
     TextCodecBrew::registerBaseEncodingNames(addToTextEncodingNameMap);
     TextCodecBrew::registerBaseCodecs(addToTextCodecMap);
 #endif
 
 #if OS(WINCE) && !PLATFORM(QT)
+    // FIXME: This is not needed. The code above covers all the base codecs.
     TextCodecWinCE::registerBaseEncodingNames(addToTextEncodingNameMap);
     TextCodecWinCE::registerBaseCodecs(addToTextCodecMap);
 #endif
@@ -303,8 +307,8 @@ bool shouldShowBackslashAsCurrencySymbolIn(const char* canonicalEncodingName)
 static void extendTextCodecMaps()
 {
 #if USE(ICU_UNICODE)
-    TextCodecICU::registerExtendedEncodingNames(addToTextEncodingNameMap);
-    TextCodecICU::registerExtendedCodecs(addToTextCodecMap);
+    TextCodecICU::registerEncodingNames(addToTextEncodingNameMap);
+    TextCodecICU::registerCodecs(addToTextCodecMap);
 #endif
 
 #if USE(QT4_UNICODE)