static unsigned rightTruncateToBuffer(const String& string, unsigned length, unsigned keepCount, UChar* buffer, bool shouldInsertEllipsis)
{
    ASSERT_WITH_SECURITY_IMPLICATION(keepCount < length);
    ASSERT_WITH_SECURITY_IMPLICATION(keepCount < STRING_BUFFER_SIZE);

#if PLATFORM(IOS)
    // FIXME: We should guard this code behind an editing behavior. Then we can remove the PLATFORM(IOS)-guard.
    // Or just turn it on for all platforms. It seems like good behavior everywhere. Might be better to generalize
    // it to handle all whitespace, not just "space".

    // Strip single character before ellipsis character, when that character is preceded by a space
    if (keepCount > 1 && string[keepCount - 1] != space && keepCount > 2 && string[keepCount - 2] == space)
        --keepCount;

    // Strip whitespace before the ellipsis character
    while (keepCount > 1 && string[keepCount - 1] == space)
        --keepCount;
#endif

    NonSharedCharacterBreakIterator it(StringView(string).substring(0, length));
    unsigned keepLength = textBreakAtOrPreceding(it, keepCount);
    unsigned truncatedLength = shouldInsertEllipsis ? keepLength + 1 : keepLength;

    StringView(string).substring(0, keepLength).getCharactersWithUpconvert(buffer);
    if (shouldInsertEllipsis)
        buffer[keepLength] = horizontalEllipsis;

    return truncatedLength;
}
static unsigned rightClipToCharacterBuffer(const String& string, unsigned length, unsigned keepCount, UChar* buffer, bool)
{
    ASSERT(keepCount < length);
    ASSERT(keepCount < STRING_BUFFER_SIZE);

    NonSharedCharacterBreakIterator it(StringView(string).substring(0, length));
    unsigned keepLength = textBreakAtOrPreceding(it, keepCount);
    StringView(string).substring(0, keepLength).getCharactersWithUpconvert(buffer);

    return keepLength;
}
static unsigned rightTruncateToBuffer(const String& string, unsigned length, unsigned keepCount, UChar* buffer)
{
    ASSERT(keepCount < length);
    ASSERT(keepCount < STRING_BUFFER_SIZE);
    
    TextBreakIterator* it = characterBreakIterator(string.characters(), length);
    unsigned keepLength = textBreakAtOrPreceding(it, keepCount);
    unsigned truncatedLength = keepLength + 1;
    
    memcpy(buffer, string.characters(), sizeof(UChar) * keepLength);
    buffer[keepLength] = horizontalEllipsis;
    
    return truncatedLength;
}
static unsigned rightTruncateToBuffer(const String& string, unsigned length, unsigned keepCount, UChar* buffer)
{
    ASSERT(keepCount < length);
    ASSERT(keepCount < STRING_BUFFER_SIZE);

    NonSharedCharacterBreakIterator it(string);
    unsigned keepLength = textBreakAtOrPreceding(it, keepCount);
    unsigned truncatedLength = keepLength + 1;

    string.copyTo(buffer, 0, keepLength);
    buffer[keepLength] = horizontalEllipsis;

    return truncatedLength;
}
static unsigned centerTruncateToBuffer(const String& string, unsigned length, unsigned keepCount, UChar* buffer)
{
    ASSERT(keepCount < length);
    ASSERT(keepCount < STRING_BUFFER_SIZE);
    
    unsigned omitStart = (keepCount + 1) / 2;
    TextBreakIterator* it = characterBreakIterator(string.characters(), length);
    unsigned omitEnd = boundedTextBreakFollowing(it, omitStart + (length - keepCount) - 1, length);
    omitStart = textBreakAtOrPreceding(it, omitStart);
    
    unsigned truncatedLength = omitStart + 1 + (length - omitEnd);
    ASSERT(truncatedLength <= length);

    memcpy(buffer, string.characters(), sizeof(UChar) * omitStart);
    buffer[omitStart] = horizontalEllipsis;
    memcpy(&buffer[omitStart + 1], &string.characters()[omitEnd], sizeof(UChar) * (length - omitEnd));
    
    return truncatedLength;
}
static unsigned centerTruncateToBuffer(const String& string, unsigned length, unsigned keepCount, UChar* buffer)
{
    ASSERT(keepCount < length);
    ASSERT(keepCount < STRING_BUFFER_SIZE);

    unsigned omitStart = (keepCount + 1) / 2;
    NonSharedCharacterBreakIterator it(string);
    unsigned omitEnd = boundedTextBreakFollowing(it, omitStart + (length - keepCount) - 1, length);
    omitStart = textBreakAtOrPreceding(it, omitStart);

    unsigned truncatedLength = omitStart + 1 + (length - omitEnd);
    ASSERT(truncatedLength <= length);

    string.copyTo(buffer, 0, omitStart);
    buffer[omitStart] = horizontalEllipsis;
    string.copyTo(&buffer[omitStart + 1], omitEnd, length - omitEnd);

    return truncatedLength;
}
static unsigned rightClipToWordBuffer(const String& string, unsigned length, unsigned keepCount, UChar* buffer, bool)
{
    ASSERT(keepCount < length);
    ASSERT(keepCount < STRING_BUFFER_SIZE);

    TextBreakIterator* it = wordBreakIterator(StringView(string).substring(0, length));
    unsigned keepLength = textBreakAtOrPreceding(it, keepCount);
    StringView(string).substring(0, keepLength).getCharactersWithUpconvert(buffer);

#if PLATFORM(IOS)
    // FIXME: We should guard this code behind an editing behavior. Then we can remove the PLATFORM(IOS)-guard.
    // Or just turn it on for all platforms. It seems like good behavior everywhere. Might be better to generalize
    // it to handle all whitespace, not just "space".

    // Motivated by <rdar://problem/7439327> truncation should not include a trailing space
    while (keepLength && string[keepLength - 1] == space)
        --keepLength;
#endif

    return keepLength;
}
static unsigned centerTruncateToBuffer(const String& string, unsigned length, unsigned keepCount, UChar* buffer, bool shouldInsertEllipsis)
{
    ASSERT_WITH_SECURITY_IMPLICATION(keepCount < length);
    ASSERT_WITH_SECURITY_IMPLICATION(keepCount < STRING_BUFFER_SIZE);
    
    unsigned omitStart = (keepCount + 1) / 2;
    NonSharedCharacterBreakIterator it(StringView(string).substring(0, length));
    unsigned omitEnd = boundedTextBreakFollowing(it, omitStart + (length - keepCount) - 1, length);
    omitStart = textBreakAtOrPreceding(it, omitStart);

#if PLATFORM(IOS)
    // FIXME: We should guard this code behind an editing behavior. Then we can remove the PLATFORM(IOS)-guard.
    // Or just turn it on for all platforms. It seems like good behavior everywhere. Might be better to generalize
    // it to handle all whitespace, not just "space".

    // Strip single character before ellipsis character, when that character is preceded by a space
    if (omitStart > 1 && string[omitStart - 1] != space && omitStart > 2 && string[omitStart - 2] == space)
        --omitStart;

    // Strip whitespace before and after the ellipsis character
    while (omitStart > 1 && string[omitStart - 1] == space)
        --omitStart;

    // Strip single character after ellipsis character, when that character is followed by a space
    if ((length - omitEnd) > 1 && string[omitEnd] != space && (length - omitEnd) > 2 && string[omitEnd + 1] == space)
        ++omitEnd;

    while ((length - omitEnd) > 1 && string[omitEnd] == space)
        ++omitEnd;
#endif

    unsigned truncatedLength = omitStart + shouldInsertEllipsis + (length - omitEnd);
    ASSERT(truncatedLength <= length);

    StringView(string).substring(0, omitStart).getCharactersWithUpconvert(buffer);
    if (shouldInsertEllipsis)
        buffer[omitStart++] = horizontalEllipsis;
    StringView(string).substring(omitEnd, length - omitEnd).getCharactersWithUpconvert(&buffer[omitStart]);
    return truncatedLength;
}