Shrink the listMarkerText function
authordarin@apple.com <darin@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 14 Sep 2013 20:11:05 +0000 (20:11 +0000)
committerdarin@apple.com <darin@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 14 Sep 2013 20:11:05 +0000 (20:11 +0000)
https://bugs.webkit.org/show_bug.cgi?id=121364

Reviewed by Andreas Kling.

Changed code to use StringBuilder, which is a better fit for what we are
doing here. But mainly, inlining was out of hand, so hit that with the
NEVER_INLINE stick. Might be worth revisiting this to optimize further for
speed by making StringBuilder better at this.

* rendering/RenderListMarker.cpp:
(WebCore::toRoman): NEVER_INLINE and use StringBuilder.
(WebCore::toAlphabeticOrNumeric): Use StringBuilder.
(WebCore::toSymbolic): NEVER_INLINE and use StringBuilder.
(WebCore::toAlphabetic): Ditto.
(WebCore::toNumeric): Ditto.
(WebCore::toHebrewUnder1000): NEVER_INLINE.
(WebCore::toHebrew): NEVER_INLINE and use StringBuilder.
(WebCore::toArmenianUnder10000): NEVER_INLINE.
(WebCore::toArmenian): NEVER_INLINE and use StringBuilder.
(WebCore::toGeorgian): NEVER_INLINE and use StringBuilder.
(WebCore::toCJKIdeographic): NEVER_INLINE and use StringBuilder.
(WebCore::listMarkerText): Use StringBuilder.

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

Source/WebCore/ChangeLog
Source/WebCore/rendering/RenderListMarker.cpp

index 39434e1..4689298 100644 (file)
@@ -1,3 +1,29 @@
+2013-09-14  Darin Adler  <darin@apple.com>
+
+        Shrink the listMarkerText function
+        https://bugs.webkit.org/show_bug.cgi?id=121364
+
+        Reviewed by Andreas Kling.
+
+        Changed code to use StringBuilder, which is a better fit for what we are
+        doing here. But mainly, inlining was out of hand, so hit that with the
+        NEVER_INLINE stick. Might be worth revisiting this to optimize further for
+        speed by making StringBuilder better at this.
+
+        * rendering/RenderListMarker.cpp:
+        (WebCore::toRoman): NEVER_INLINE and use StringBuilder.
+        (WebCore::toAlphabeticOrNumeric): Use StringBuilder.
+        (WebCore::toSymbolic): NEVER_INLINE and use StringBuilder.
+        (WebCore::toAlphabetic): Ditto.
+        (WebCore::toNumeric): Ditto.
+        (WebCore::toHebrewUnder1000): NEVER_INLINE.
+        (WebCore::toHebrew): NEVER_INLINE and use StringBuilder.
+        (WebCore::toArmenianUnder10000): NEVER_INLINE.
+        (WebCore::toArmenian): NEVER_INLINE and use StringBuilder.
+        (WebCore::toGeorgian): NEVER_INLINE and use StringBuilder.
+        (WebCore::toCJKIdeographic): NEVER_INLINE and use StringBuilder.
+        (WebCore::listMarkerText): Use StringBuilder.
+
 2013-09-14  Sam Weinig  <sam@webkit.org>
 
         [CTTE] Node subclasses should take a Document by reference in their 
index ae7c186..bc96be6 100644 (file)
@@ -45,7 +45,7 @@ const int cMarkerPadding = 7;
 
 enum SequenceType { NumericSequence, AlphabeticSequence };
 
-static String toRoman(int number, bool upper)
+static NEVER_INLINE void toRoman(StringBuilder& builder, int number, bool upper)
 {
     // FIXME: CSS3 describes how to make this work for much larger numbers,
     // using overbars and special characters. It also specifies the characters
@@ -78,19 +78,16 @@ static String toRoman(int number, bool upper)
     } while (number);
 
     ASSERT(length <= lettersSize);
-    return String(&letters[lettersSize - length], length);
+    builder.append(&letters[lettersSize - length], length);
 }
 
-// The typedef is needed because taking sizeof(number) in the const expression below doesn't work with some compilers.
-// This is likely the case because of the template.
-typedef int numberType;
-
 template <typename CharacterType>
-static inline String toAlphabeticOrNumeric(numberType number, const CharacterType* sequence, unsigned sequenceSize, SequenceType type)
+static inline void toAlphabeticOrNumeric(StringBuilder& builder, int number, const CharacterType* sequence, unsigned sequenceSize, SequenceType type)
 {
     ASSERT(sequenceSize >= 2);
 
-    const int lettersSize = sizeof(numberType) * 8 + 1; // Binary is the worst case; requires one character per bit plus a minus sign.
+    // Taking sizeof(number) in the expression below doesn't work with some compilers.
+    const int lettersSize = sizeof(int) * 8 + 1; // Binary is the worst case; requires one character per bit plus a minus sign.
 
     CharacterType letters[lettersSize];
 
@@ -119,11 +116,11 @@ static inline String toAlphabeticOrNumeric(numberType number, const CharacterTyp
         letters[lettersSize - ++length] = hyphenMinus;
 
     ASSERT(length <= lettersSize);
-    return String(&letters[lettersSize - length], length);
+    builder.append(&letters[lettersSize - length], length);
 }
 
 template <typename CharacterType>
-static String toSymbolic(int number, const CharacterType* symbols, unsigned symbolsSize)
+static NEVER_INLINE void toSymbolic(StringBuilder& builder, int number, const CharacterType* symbols, unsigned symbolsSize)
 {
     ASSERT(number > 0);
     ASSERT(symbolsSize >= 1);
@@ -131,45 +128,43 @@ static String toSymbolic(int number, const CharacterType* symbols, unsigned symb
     --numberShadow;
 
     // The asterisks list-style-type is the worst case; we show |numberShadow| asterisks.
-    StringBuilder letters;
-    letters.append(symbols[numberShadow % symbolsSize]);
+    builder.append(symbols[numberShadow % symbolsSize]);
     unsigned numSymbols = numberShadow / symbolsSize;
     while (numSymbols--)
-        letters.append(symbols[numberShadow % symbolsSize]);
-    return letters.toString();
+        builder.append(symbols[numberShadow % symbolsSize]);
 }
 
 template <typename CharacterType>
-static String toAlphabetic(int number, const CharacterType* alphabet, unsigned alphabetSize)
+static NEVER_INLINE void toAlphabetic(StringBuilder& builder, int number, const CharacterType* alphabet, unsigned alphabetSize)
 {
-    return toAlphabeticOrNumeric(number, alphabet, alphabetSize, AlphabeticSequence);
+    toAlphabeticOrNumeric(builder, number, alphabet, alphabetSize, AlphabeticSequence);
 }
 
 template <typename CharacterType>
-static String toNumeric(int number, const CharacterType* numerals, unsigned numeralsSize)
+static NEVER_INLINE void toNumeric(StringBuilder& builder, int number, const CharacterType* numerals, unsigned numeralsSize)
 {
-    return toAlphabeticOrNumeric(number, numerals, numeralsSize, NumericSequence);
+    toAlphabeticOrNumeric(builder, number, numerals, numeralsSize, NumericSequence);
 }
 
 template <typename CharacterType, size_t size>
-static inline String toAlphabetic(int number, const CharacterType(&alphabet)[size])
+static inline void toAlphabetic(StringBuilder& builder, int number, const CharacterType(&alphabet)[size])
 {
-    return toAlphabetic(number, alphabet, size);
+    toAlphabetic(builder, number, alphabet, size);
 }
 
 template <typename CharacterType, size_t size>
-static inline String toNumeric(int number, const CharacterType(&alphabet)[size])
+static inline void toNumeric(StringBuilder& builder, int number, const CharacterType(&alphabet)[size])
 {
-    return toNumeric(number, alphabet, size);
+    toNumeric(builder, number, alphabet, size);
 }
 
 template <typename CharacterType, size_t size>
-static inline String toSymbolic(int number, const CharacterType(&alphabet)[size])
+static inline void toSymbolic(StringBuilder& builder, int number, const CharacterType(&alphabet)[size])
 {    
-    return toSymbolic(number, alphabet, size);
+    toSymbolic(builder, number, alphabet, size);
 }
 
-static int toHebrewUnder1000(int number, UChar letters[5])
+static NEVER_INLINE int toHebrewUnder1000(int number, UChar letters[5])
 {
     // FIXME: CSS3 mentions various refinements not implemented here.
     // FIXME: Should take a look at Mozilla's HebrewToText function (in nsBulletFrame).
@@ -197,14 +192,15 @@ static int toHebrewUnder1000(int number, UChar letters[5])
     return length;
 }
 
-static String toHebrew(int number)
+static NEVER_INLINE void toHebrew(StringBuilder& builder, int number)
 {
     // FIXME: CSS3 mentions ways to make this work for much larger numbers.
     ASSERT(number >= 0 && number <= 999999);
 
     if (number == 0) {
         static const UChar hebrewZero[3] = { 0x05D0, 0x05E4, 0x05E1 };
-        return String(hebrewZero, 3);
+        builder.append(hebrewZero, 3);
+        return;
     }
 
     const int lettersSize = 11; // big enough for two 5-digit sequences plus a quote mark between
@@ -221,10 +217,10 @@ static String toHebrew(int number)
     length += toHebrewUnder1000(number, letters + length);
 
     ASSERT(length <= lettersSize);
-    return String(letters, length);
+    builder.append(letters, length);
 }
 
-static int toArmenianUnder10000(int number, bool upper, bool addCircumflex, UChar letters[9])
+static NEVER_INLINE int toArmenianUnder10000(int number, bool upper, bool addCircumflex, UChar letters[9])
 {
     ASSERT(number >= 0 && number < 10000);
     int length = 0;
@@ -264,7 +260,7 @@ static int toArmenianUnder10000(int number, bool upper, bool addCircumflex, UCha
     return length;
 }
 
-static String toArmenian(int number, bool upper)
+static NEVER_INLINE void toArmenian(StringBuilder& builder, int number, bool upper)
 {
     ASSERT(number >= 1 && number <= 99999999);
 
@@ -275,10 +271,10 @@ static String toArmenian(int number, bool upper)
     length += toArmenianUnder10000(number % 10000, upper, false, letters + length);
 
     ASSERT(length <= lettersSize);
-    return String(letters, length);
+    builder.append(letters, length);
 }
 
-static String toGeorgian(int number)
+static NEVER_INLINE void toGeorgian(StringBuilder& builder, int number)
 {
     ASSERT(number >= 1 && number <= 19999);
 
@@ -319,12 +315,12 @@ static String toGeorgian(int number)
     }
 
     ASSERT(length <= lettersSize);
-    return String(letters, length);
+    builder.append(letters, length);
 }
 
 // The table uses the order from the CSS3 specification:
 // first 3 group markers, then 3 digit markers, then ten digits.
-static String toCJKIdeographic(int number, const UChar table[16])
+static NEVER_INLINE void toCJKIdeographic(StringBuilder& builder, int number, const UChar table[16])
 {
     ASSERT(number >= 0);
 
@@ -336,8 +332,10 @@ static String toCJKIdeographic(int number, const UChar table[16])
         digit5, digit6, digit7, digit8, digit9
     };
 
-    if (number == 0)
-        return String(&table[digit0 - 1], 1);
+    if (number == 0) {
+        builder.append(table[digit0 - 1]);
+        return;
+    }
 
     const int groupLength = 8; // 4 digits, 3 digit markers, and a group marker
     const int bufferLength = 4 * groupLength;
@@ -401,7 +399,7 @@ static String toCJKIdeographic(int number, const UChar table[16])
     if (last == digit0)
         --length;
 
-    return String(characters, length);
+    builder.append(characters, length);
 }
 
 static EListStyleType effectiveListMarkerType(EListStyleType type, int value)
@@ -604,165 +602,198 @@ static UChar listMarkerSuffix(EListStyleType type, int value)
 
 String listMarkerText(EListStyleType type, int value)
 {
+    StringBuilder builder;
+
     // If the list-style-type, say hebrew, cannot represent |value| because it's outside
     // its ordinal range then we fallback to some list style that can represent |value|.
     switch (effectiveListMarkerType(type, value)) {
         case NoneListStyle:
-            return "";
+            return emptyString();
 
         case Asterisks: {
-            static const LChar asterisksSymbols[1] = {
-                0x2A
-            };
-            return toSymbolic(value, asterisksSymbols);
+            static const LChar asterisksSymbols[1] = { 0x2A };
+            toSymbolic(builder, value, asterisksSymbols);
+            break;
         }
         // We use the same characters for text security.
         // See RenderText::setInternalString.
         case Circle:
-            return String(&whiteBullet, 1);
+            builder.append(whiteBullet);
+            break;
         case Disc:
-            return String(&bullet, 1);
+            builder.append(bullet);
+            break;
         case Footnotes: {
-            static const UChar footnotesSymbols[4] = {
-                0x002A, 0x2051, 0x2020, 0x2021
-            };
-            return toSymbolic(value, footnotesSymbols);
+            static const UChar footnotesSymbols[4] = { 0x002A, 0x2051, 0x2020, 0x2021 };
+            toSymbolic(builder, value, footnotesSymbols);
+            break;
         }
         case Square:
             // The CSS 2.1 test suite uses U+25EE BLACK MEDIUM SMALL SQUARE
             // instead, but I think this looks better.
-            return String(&blackSquare, 1);
+            builder.append(blackSquare);
+            break;
 
         case DecimalListStyle:
-            return String::number(value);
+            builder.appendNumber(value);
+            break;
+
         case DecimalLeadingZero:
-            if (value < -9 || value > 9)
-                return String::number(value);
-            if (value < 0)
-                return "-0" + String::number(-value); // -01 to -09
-            return "0" + String::number(value); // 00 to 09
+            if (value < -9 || value > 9) {
+                builder.appendNumber(value);
+                break;
+            }
+            if (value < 0) {
+                builder.append("-0");
+                builder.appendNumber(-value); // -01 to -09
+                break;
+            }
+            builder.append('0');
+            builder.appendNumber(value); // 00 to 09
+            break;
 
         case ArabicIndic: {
             static const UChar arabicIndicNumerals[10] = {
                 0x0660, 0x0661, 0x0662, 0x0663, 0x0664, 0x0665, 0x0666, 0x0667, 0x0668, 0x0669
             };
-            return toNumeric(value, arabicIndicNumerals);
+            toNumeric(builder, value, arabicIndicNumerals);
+            break;
         }
+
         case BinaryListStyle: {
-            static const LChar binaryNumerals[2] = {
-                '0', '1'
-            };
-            return toNumeric(value, binaryNumerals);
+            static const LChar binaryNumerals[2] = { '0', '1' };
+            toNumeric(builder, value, binaryNumerals);
+            break;
         }
+
         case Bengali: {
             static const UChar bengaliNumerals[10] = {
                 0x09E6, 0x09E7, 0x09E8, 0x09E9, 0x09EA, 0x09EB, 0x09EC, 0x09ED, 0x09EE, 0x09EF
             };
-            return toNumeric(value, bengaliNumerals);
+            toNumeric(builder, value, bengaliNumerals);
+            break;
         }
+
         case Cambodian:
         case Khmer: {
             static const UChar khmerNumerals[10] = {
                 0x17E0, 0x17E1, 0x17E2, 0x17E3, 0x17E4, 0x17E5, 0x17E6, 0x17E7, 0x17E8, 0x17E9
             };
-            return toNumeric(value, khmerNumerals);
+            toNumeric(builder, value, khmerNumerals);
+            break;
         }
         case Devanagari: {
             static const UChar devanagariNumerals[10] = {
                 0x0966, 0x0967, 0x0968, 0x0969, 0x096A, 0x096B, 0x096C, 0x096D, 0x096E, 0x096F
             };
-            return toNumeric(value, devanagariNumerals);
+            toNumeric(builder, value, devanagariNumerals);
+            break;
         }
         case Gujarati: {
             static const UChar gujaratiNumerals[10] = {
                 0x0AE6, 0x0AE7, 0x0AE8, 0x0AE9, 0x0AEA, 0x0AEB, 0x0AEC, 0x0AED, 0x0AEE, 0x0AEF
             };
-            return toNumeric(value, gujaratiNumerals);
+            toNumeric(builder, value, gujaratiNumerals);
+            break;
         }
         case Gurmukhi: {
             static const UChar gurmukhiNumerals[10] = {
                 0x0A66, 0x0A67, 0x0A68, 0x0A69, 0x0A6A, 0x0A6B, 0x0A6C, 0x0A6D, 0x0A6E, 0x0A6F
             };
-            return toNumeric(value, gurmukhiNumerals);
+            toNumeric(builder, value, gurmukhiNumerals);
+            break;
         }
         case Kannada: {
             static const UChar kannadaNumerals[10] = {
                 0x0CE6, 0x0CE7, 0x0CE8, 0x0CE9, 0x0CEA, 0x0CEB, 0x0CEC, 0x0CED, 0x0CEE, 0x0CEF
             };
-            return toNumeric(value, kannadaNumerals);
+            toNumeric(builder, value, kannadaNumerals);
+            break;
         }
         case LowerHexadecimal: {
             static const LChar lowerHexadecimalNumerals[16] = {
                 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
             };
-            return toNumeric(value, lowerHexadecimalNumerals);
+            toNumeric(builder, value, lowerHexadecimalNumerals);
+            break;
         }
         case Lao: {
             static const UChar laoNumerals[10] = {
                 0x0ED0, 0x0ED1, 0x0ED2, 0x0ED3, 0x0ED4, 0x0ED5, 0x0ED6, 0x0ED7, 0x0ED8, 0x0ED9
             };
-            return toNumeric(value, laoNumerals);
+            toNumeric(builder, value, laoNumerals);
+            break;
         }
         case Malayalam: {
             static const UChar malayalamNumerals[10] = {
                 0x0D66, 0x0D67, 0x0D68, 0x0D69, 0x0D6A, 0x0D6B, 0x0D6C, 0x0D6D, 0x0D6E, 0x0D6F
             };
-            return toNumeric(value, malayalamNumerals);
+            toNumeric(builder, value, malayalamNumerals);
+            break;
         }
         case Mongolian: {
             static const UChar mongolianNumerals[10] = {
                 0x1810, 0x1811, 0x1812, 0x1813, 0x1814, 0x1815, 0x1816, 0x1817, 0x1818, 0x1819
             };
-            return toNumeric(value, mongolianNumerals);
+            toNumeric(builder, value, mongolianNumerals);
+            break;
         }
         case Myanmar: {
             static const UChar myanmarNumerals[10] = {
                 0x1040, 0x1041, 0x1042, 0x1043, 0x1044, 0x1045, 0x1046, 0x1047, 0x1048, 0x1049
             };
-            return toNumeric(value, myanmarNumerals);
+            toNumeric(builder, value, myanmarNumerals);
+            break;
         }
         case Octal: {
             static const LChar octalNumerals[8] = {
                 '0', '1', '2', '3', '4', '5', '6', '7'
             };
-            return toNumeric(value, octalNumerals);
+            toNumeric(builder, value, octalNumerals);
+            break;
         }
         case Oriya: {
             static const UChar oriyaNumerals[10] = {
                 0x0B66, 0x0B67, 0x0B68, 0x0B69, 0x0B6A, 0x0B6B, 0x0B6C, 0x0B6D, 0x0B6E, 0x0B6F
             };
-            return toNumeric(value, oriyaNumerals);
+            toNumeric(builder, value, oriyaNumerals);
+            break;
         }
         case Persian:
         case Urdu: {
             static const UChar urduNumerals[10] = {
                 0x06F0, 0x06F1, 0x06F2, 0x06F3, 0x06F4, 0x06F5, 0x06F6, 0x06F7, 0x06F8, 0x06F9
             };
-            return toNumeric(value, urduNumerals);
+            toNumeric(builder, value, urduNumerals);
+            break;
         }
         case Telugu: {
             static const UChar teluguNumerals[10] = {
                 0x0C66, 0x0C67, 0x0C68, 0x0C69, 0x0C6A, 0x0C6B, 0x0C6C, 0x0C6D, 0x0C6E, 0x0C6F
             };
-            return toNumeric(value, teluguNumerals);
+            toNumeric(builder, value, teluguNumerals);
+            break;
         }
         case Tibetan: {
             static const UChar tibetanNumerals[10] = {
                 0x0F20, 0x0F21, 0x0F22, 0x0F23, 0x0F24, 0x0F25, 0x0F26, 0x0F27, 0x0F28, 0x0F29
             };
-            return toNumeric(value, tibetanNumerals);
+            toNumeric(builder, value, tibetanNumerals);
+            break;
         }
         case Thai: {
             static const UChar thaiNumerals[10] = {
                 0x0E50, 0x0E51, 0x0E52, 0x0E53, 0x0E54, 0x0E55, 0x0E56, 0x0E57, 0x0E58, 0x0E59
             };
-            return toNumeric(value, thaiNumerals);
+            toNumeric(builder, value, thaiNumerals);
+            break;
         }
         case UpperHexadecimal: {
             static const LChar upperHexadecimalNumerals[16] = {
                 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
             };
-            return toNumeric(value, upperHexadecimalNumerals);
+            toNumeric(builder, value, upperHexadecimalNumerals);
+            break;
         }
 
         case LowerAlpha:
@@ -771,7 +802,8 @@ String listMarkerText(EListStyleType type, int value)
                 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
                 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
             };
-            return toAlphabetic(value, lowerLatinAlphabet);
+            toAlphabetic(builder, value, lowerLatinAlphabet);
+            break;
         }
         case UpperAlpha:
         case UpperLatin: {
@@ -779,7 +811,8 @@ String listMarkerText(EListStyleType type, int value)
                 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
                 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
             };
-            return toAlphabetic(value, upperLatinAlphabet);
+            toAlphabetic(builder, value, upperLatinAlphabet);
+            break;
         }
         case LowerGreek: {
             static const UChar lowerGreekAlphabet[24] = {
@@ -787,7 +820,8 @@ String listMarkerText(EListStyleType type, int value)
                 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0,
                 0x03C1, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9
             };
-            return toAlphabetic(value, lowerGreekAlphabet);
+            toAlphabetic(builder, value, lowerGreekAlphabet);
+            break;
         }
 
         case Hiragana: {
@@ -801,7 +835,8 @@ String listMarkerText(EListStyleType type, int value)
                 0x3080, 0x3081, 0x3082, 0x3084, 0x3086, 0x3088, 0x3089, 0x308A,
                 0x308B, 0x308C, 0x308D, 0x308F, 0x3090, 0x3091, 0x3092, 0x3093
             };
-            return toAlphabetic(value, hiraganaAlphabet);
+            toAlphabetic(builder, value, hiraganaAlphabet);
+            break;
         }
         case HiraganaIroha: {
             // FIXME: This table comes from the CSS3 draft, and is probably
@@ -814,7 +849,8 @@ String listMarkerText(EListStyleType type, int value)
                 0x3053, 0x3048, 0x3066, 0x3042, 0x3055, 0x304D, 0x3086, 0x3081,
                 0x307F, 0x3057, 0x3091, 0x3072, 0x3082, 0x305B, 0x3059
             };
-            return toAlphabetic(value, hiraganaIrohaAlphabet);
+            toAlphabetic(builder, value, hiraganaIrohaAlphabet);
+            break;
         }
         case Katakana: {
             // FIXME: This table comes from the CSS3 draft, and is probably
@@ -827,7 +863,8 @@ String listMarkerText(EListStyleType type, int value)
                 0x30E0, 0x30E1, 0x30E2, 0x30E4, 0x30E6, 0x30E8, 0x30E9, 0x30EA,
                 0x30EB, 0x30EC, 0x30ED, 0x30EF, 0x30F0, 0x30F1, 0x30F2, 0x30F3
             };
-            return toAlphabetic(value, katakanaAlphabet);
+            toAlphabetic(builder, value, katakanaAlphabet);
+            break;
         }
         case KatakanaIroha: {
             // FIXME: This table comes from the CSS3 draft, and is probably
@@ -840,7 +877,8 @@ String listMarkerText(EListStyleType type, int value)
                 0x30B3, 0x30A8, 0x30C6, 0x30A2, 0x30B5, 0x30AD, 0x30E6, 0x30E1,
                 0x30DF, 0x30B7, 0x30F1, 0x30D2, 0x30E2, 0x30BB, 0x30B9
             };
-            return toAlphabetic(value, katakanaIrohaAlphabet);
+            toAlphabetic(builder, value, katakanaIrohaAlphabet);
+            break;
         }
 
         case Afar:
@@ -850,7 +888,8 @@ String listMarkerText(EListStyleType type, int value)
                 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1260, 0x1270, 0x1290,
                 0x12A0, 0x12A8, 0x12C8, 0x12D0, 0x12E8, 0x12F0, 0x1308, 0x1338, 0x1348
             };
-            return toAlphabetic(value, ethiopicHalehameAaErAlphabet);
+            toAlphabetic(builder, value, ethiopicHalehameAaErAlphabet);
+            break;
         }
         case Amharic:
         case EthiopicHalehameAmEt: {
@@ -860,7 +899,8 @@ String listMarkerText(EListStyleType type, int value)
                 0x12C8, 0x12D0, 0x12D8, 0x12E0, 0x12E8, 0x12F0, 0x1300, 0x1308, 0x1320,
                 0x1328, 0x1330, 0x1338, 0x1340, 0x1348, 0x1350
             };
-            return toAlphabetic(value, ethiopicHalehameAmEtAlphabet);
+            toAlphabetic(builder, value, ethiopicHalehameAmEtAlphabet);
+            break;
         }
         case AmharicAbegede:
         case EthiopicAbegedeAmEt: {
@@ -870,21 +910,24 @@ String listMarkerText(EListStyleType type, int value)
                 0x1298, 0x1220, 0x12D0, 0x1348, 0x1338, 0x1240, 0x1228, 0x1230, 0x1238,
                 0x1270, 0x1278, 0x1280, 0x1340, 0x1330, 0x1350
             };
-            return toAlphabetic(value, ethiopicAbegedeAmEtAlphabet);
+            toAlphabetic(builder, value, ethiopicAbegedeAmEtAlphabet);
+            break;
         }
         case CjkEarthlyBranch: {
             static const UChar cjkEarthlyBranchAlphabet[12] = {
                 0x5B50, 0x4E11, 0x5BC5, 0x536F, 0x8FB0, 0x5DF3, 0x5348, 0x672A, 0x7533,
                 0x9149, 0x620C, 0x4EA5
             };
-            return toAlphabetic(value, cjkEarthlyBranchAlphabet);
+            toAlphabetic(builder, value, cjkEarthlyBranchAlphabet);
+            break;
         }
         case CjkHeavenlyStem: {
             static const UChar cjkHeavenlyStemAlphabet[10] = {
                 0x7532, 0x4E59, 0x4E19, 0x4E01, 0x620A, 0x5DF1, 0x5E9A, 0x8F9B, 0x58EC,
                 0x7678
             };
-            return toAlphabetic(value, cjkHeavenlyStemAlphabet);
+            toAlphabetic(builder, value, cjkHeavenlyStemAlphabet);
+            break;
         }
         case Ethiopic:
         case EthiopicHalehameGez: {
@@ -893,7 +936,8 @@ String listMarkerText(EListStyleType type, int value)
                 0x1270, 0x1280, 0x1290, 0x12A0, 0x12A8, 0x12C8, 0x12D0, 0x12D8, 0x12E8,
                 0x12F0, 0x1308, 0x1320, 0x1330, 0x1338, 0x1340, 0x1348, 0x1350
             };
-            return toAlphabetic(value, ethiopicHalehameGezAlphabet);
+            toAlphabetic(builder, value, ethiopicHalehameGezAlphabet);
+            break;
         }
         case EthiopicAbegede:
         case EthiopicAbegedeGez: {
@@ -902,21 +946,24 @@ String listMarkerText(EListStyleType type, int value)
                 0x12E8, 0x12A8, 0x1208, 0x1218, 0x1290, 0x1220, 0x12D0, 0x1348, 0x1338,
                 0x1240, 0x1228, 0x1230, 0x1270, 0x1280, 0x1340, 0x1330, 0x1350
             };
-            return toAlphabetic(value, ethiopicAbegedeGezAlphabet);
+            toAlphabetic(builder, value, ethiopicAbegedeGezAlphabet);
+            break;
         }
         case HangulConsonant: {
             static const UChar hangulConsonantAlphabet[14] = {
                 0x3131, 0x3134, 0x3137, 0x3139, 0x3141, 0x3142, 0x3145, 0x3147, 0x3148,
                 0x314A, 0x314B, 0x314C, 0x314D, 0x314E
             };
-            return toAlphabetic(value, hangulConsonantAlphabet);
+            toAlphabetic(builder, value, hangulConsonantAlphabet);
+            break;
         }
         case Hangul: {
             static const UChar hangulAlphabet[14] = {
                 0xAC00, 0xB098, 0xB2E4, 0xB77C, 0xB9C8, 0xBC14, 0xC0AC, 0xC544, 0xC790,
                 0xCC28, 0xCE74, 0xD0C0, 0xD30C, 0xD558
             };
-            return toAlphabetic(value, hangulAlphabet);
+            toAlphabetic(builder, value, hangulAlphabet);
+            break;
         }
         case Oromo:
         case EthiopicHalehameOmEt: {
@@ -925,7 +972,8 @@ String listMarkerText(EListStyleType type, int value)
                 0x1278, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12C8, 0x12E8, 0x12F0, 0x12F8,
                 0x1300, 0x1308, 0x1320, 0x1328, 0x1338, 0x1330, 0x1348
             };
-            return toAlphabetic(value, ethiopicHalehameOmEtAlphabet);
+            toAlphabetic(builder, value, ethiopicHalehameOmEtAlphabet);
+            break;
         }
         case Sidama:
         case EthiopicHalehameSidEt: {
@@ -934,7 +982,8 @@ String listMarkerText(EListStyleType type, int value)
                 0x1270, 0x1278, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12C8, 0x12E8, 0x12F0,
                 0x12F8, 0x1300, 0x1308, 0x1320, 0x1328, 0x1338, 0x1330, 0x1348
             };
-            return toAlphabetic(value, ethiopicHalehameSidEtAlphabet);
+            toAlphabetic(builder, value, ethiopicHalehameSidEtAlphabet);
+            break;
         }
         case Somali:
         case EthiopicHalehameSoEt: {
@@ -943,7 +992,8 @@ String listMarkerText(EListStyleType type, int value)
                 0x1270, 0x1290, 0x12A0, 0x12A8, 0x12B8, 0x12C8, 0x12D0, 0x12E8, 0x12F0,
                 0x1300, 0x1308, 0x1338, 0x1348
             };
-            return toAlphabetic(value, ethiopicHalehameSoEtAlphabet);
+            toAlphabetic(builder, value, ethiopicHalehameSoEtAlphabet);
+            break;
         }
         case Tigre:
         case EthiopicHalehameTig: {
@@ -952,7 +1002,8 @@ String listMarkerText(EListStyleType type, int value)
                 0x1270, 0x1278, 0x1290, 0x12A0, 0x12A8, 0x12C8, 0x12D0, 0x12D8, 0x12E8,
                 0x12F0, 0x1300, 0x1308, 0x1320, 0x1328, 0x1338, 0x1330, 0x1348, 0x1350
             };
-            return toAlphabetic(value, ethiopicHalehameTigAlphabet);
+            toAlphabetic(builder, value, ethiopicHalehameTigAlphabet);
+            break;
         }
         case TigrinyaEr:
         case EthiopicHalehameTiEr: {
@@ -962,7 +1013,8 @@ String listMarkerText(EListStyleType type, int value)
                 0x12D0, 0x12D8, 0x12E0, 0x12E8, 0x12F0, 0x1300, 0x1308, 0x1320, 0x1328,
                 0x1330, 0x1338, 0x1348, 0x1350
             };
-            return toAlphabetic(value, ethiopicHalehameTiErAlphabet);
+            toAlphabetic(builder, value, ethiopicHalehameTiErAlphabet);
+            break;
         }
         case TigrinyaErAbegede:
         case EthiopicAbegedeTiEr: {
@@ -972,7 +1024,8 @@ String listMarkerText(EListStyleType type, int value)
                 0x1298, 0x12D0, 0x1348, 0x1338, 0x1240, 0x1250, 0x1228, 0x1230, 0x1238,
                 0x1270, 0x1278, 0x1330, 0x1350
             };
-            return toAlphabetic(value, ethiopicAbegedeTiErAlphabet);
+            toAlphabetic(builder, value, ethiopicAbegedeTiErAlphabet);
+            break;
         }
         case TigrinyaEt:
         case EthiopicHalehameTiEt: {
@@ -982,7 +1035,8 @@ String listMarkerText(EListStyleType type, int value)
                 0x12B8, 0x12C8, 0x12D0, 0x12D8, 0x12E0, 0x12E8, 0x12F0, 0x1300, 0x1308,
                 0x1320, 0x1328, 0x1330, 0x1338, 0x1340, 0x1348, 0x1350
             };
-            return toAlphabetic(value, ethiopicHalehameTiEtAlphabet);
+            toAlphabetic(builder, value, ethiopicHalehameTiEtAlphabet);
+            break;
         }
         case TigrinyaEtAbegede:
         case EthiopicAbegedeTiEt: {
@@ -992,7 +1046,8 @@ String listMarkerText(EListStyleType type, int value)
                 0x1298, 0x1220, 0x12D0, 0x1348, 0x1338, 0x1240, 0x1250, 0x1228, 0x1230,
                 0x1238, 0x1270, 0x1278, 0x1280, 0x1340, 0x1330, 0x1350
             };
-            return toAlphabetic(value, ethiopicAbegedeTiEtAlphabet);
+            toAlphabetic(builder, value, ethiopicAbegedeTiEtAlphabet);
+            break;
         }
         case UpperGreek: {
             static const UChar upperGreekAlphabet[24] = {
@@ -1000,7 +1055,8 @@ String listMarkerText(EListStyleType type, int value)
                 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0, 0x03A1, 0x03A3,
                 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9
             };
-            return toAlphabetic(value, upperGreekAlphabet);
+            toAlphabetic(builder, value, upperGreekAlphabet);
+            break;
         }
         case LowerNorwegian: {
             static const LChar lowerNorwegianAlphabet[29] = {
@@ -1009,7 +1065,8 @@ String listMarkerText(EListStyleType type, int value)
                 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0xE6,
                 0xF8, 0xE5
             };
-            return toAlphabetic(value, lowerNorwegianAlphabet);
+            toAlphabetic(builder, value, lowerNorwegianAlphabet);
+            break;
         }
         case UpperNorwegian: {
             static const LChar upperNorwegianAlphabet[29] = {
@@ -1018,7 +1075,8 @@ String listMarkerText(EListStyleType type, int value)
                 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0xC6,
                 0xD8, 0xC5
             };
-            return toAlphabetic(value, upperNorwegianAlphabet);
+            toAlphabetic(builder, value, upperNorwegianAlphabet);
+            break;
         }
         case CJKIdeographic: {
             static const UChar traditionalChineseInformalTable[16] = {
@@ -1027,30 +1085,36 @@ String listMarkerText(EListStyleType type, int value)
                 0x96F6, 0x4E00, 0x4E8C, 0x4E09, 0x56DB,
                 0x4E94, 0x516D, 0x4E03, 0x516B, 0x4E5D
             };
-            return toCJKIdeographic(value, traditionalChineseInformalTable);
+            toCJKIdeographic(builder, value, traditionalChineseInformalTable);
+            break;
         }
 
         case LowerRoman:
-            return toRoman(value, false);
+            toRoman(builder, value, false);
+            break;
         case UpperRoman:
-            return toRoman(value, true);
+            toRoman(builder, value, true);
+            break;
 
         case Armenian:
         case UpperArmenian:
             // CSS3 says "armenian" means "lower-armenian".
             // But the CSS2.1 test suite contains uppercase test results for "armenian",
             // so we'll match the test suite.
-            return toArmenian(value, true);
+            toArmenian(builder, value, true);
+            break;
         case LowerArmenian:
-            return toArmenian(value, false);
+            toArmenian(builder, value, false);
+            break;
         case Georgian:
-            return toGeorgian(value);
+            toGeorgian(builder, value);
+            break;
         case Hebrew:
-            return toHebrew(value);
+            toHebrew(builder, value);
+            break;
     }
 
-    ASSERT_NOT_REACHED();
-    return "";
+    return builder.toString();
 }
 
 RenderListMarker::RenderListMarker(RenderListItem& listItem)