/*
  ==============================================================================

   This file is part of the JUCE library - "Jules' Utility Class Extensions"
   Copyright 2004-6 by Raw Material Software ltd.

  ------------------------------------------------------------------------------

   JUCE can be redistributed and/or modified under the terms of the
   GNU General Public License, as published by the Free Software Foundation;
   either version 2 of the License, or (at your option) any later version.

   JUCE is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with JUCE; if not, visit www.gnu.org/licenses or write to the
   Free Software Foundation, Inc., 59 Temple Place, Suite 330, 
   Boston, MA 02111-1307 USA

  ------------------------------------------------------------------------------

   If you'd like to release a closed-source product which uses JUCE, commercial
   licenses are also available: visit www.rawmaterialsoftware.com/juce for
   more information.

  ==============================================================================
*/

#ifdef _MSC_VER
  #pragma warning (disable: 4514)
  #pragma warning (push)
#endif

#include "../basics/juce_StandardHeader.h"
#include <wctype.h>
#ifdef _MSC_VER
 #include <float.h>
#endif

BEGIN_JUCE_NAMESPACE


#include "juce_String.h"
#include "../threads/juce_ScopedLock.h"

#ifdef _MSC_VER
  #pragma warning (pop)
#endif

//==============================================================================
#ifndef JUCE_WIN32
// some functions that are needed on the Mac + Linux but already there on win32
void juce_strupr (tchar* t)
{
    while (*t != 0)
    {
        *t = toupper (*t);
        ++t;
    }
}

void juce_strlwr (tchar* t)
{
    while (*t != 0)
    {
        *t = tolower (*t);
        ++t;
    }
}

#if JUCE_STRINGS_ARE_UNICODE

int juce_stricmp (const wchar_t* s1, const wchar_t* s2)
{
    for (;;)
    {
        if (*s1 != *s2)
        {
            const int diff = toupper (*s1) - toupper (*s2);

            if (diff != 0)
                return diff < 0 ? -1 : 1;
        }
        else if (*s1 == 0)
            break;

        ++s1;
        ++s2;
    }

    return 0;
}

int juce_strnicmp (const wchar_t* s1, const wchar_t* s2, int numChars)
{
    while (--numChars >= 0)
    {
        if (*s1 != *s2)
        {
            const int diff = toupper (*s1) - toupper (*s2);

            if (diff != 0)
                return diff < 0 ? -1 : 1;
        }
        else if (*s1 == 0)
            break;

        ++s1;
        ++s2;
    }

    return 0;
}

static inline int _wtoi (const wchar_t* t)
{
    int v = 0;

    while (String::isWhitespace (*t))
        ++t;

    const bool isNeg = *t == T('-');
    if (isNeg)
        ++t;

    for (;;)
    {
        const wchar_t c = *t++;

        if (c >= T('0') && c <= T('9'))
            v = v * 10 + (int) (c - T('0'));
        else
            break;
    }

    return isNeg ? -v : v;
}

#endif
#endif

//==============================================================================
static const tchar* const emptyCharString = T("\0\0\0\0JUCE");
static const int safeEmptyStringRefCount = 0x3fffffff;
String::InternalRefCountedStringHolder String::emptyString = { safeEmptyStringRefCount, 0, { 0 } };


//==============================================================================
void String::deleteInternal() throw()
{
    if (atomicDecrementAndReturn (text->refCount) == 0)
        juce_free (text);
}

void String::createInternal (const int numChars) throw()
{
    jassert (numChars > 0);

    text = (InternalRefCountedStringHolder*) juce_malloc (sizeof (InternalRefCountedStringHolder)
                                                           + numChars * sizeof (tchar));
    text->text[0] = 0;
    text->refCount = 1;
    text->allocatedNumChars = numChars;
}

void String::appendInternal (const tchar* const newText,
                             const int numExtraChars) throw()
{
    if (numExtraChars > 0)
    {
        const int oldLen = (int) juce_strlen (text->text);
        const int newTotalLen = oldLen + numExtraChars;

        if (text->refCount > 1)
        {
            // it's in use by other strings as well, so we need to make a private copy before messing with it..
            InternalRefCountedStringHolder* const old = text;

            text = (InternalRefCountedStringHolder*)
                        juce_malloc (sizeof (InternalRefCountedStringHolder)
                                   + newTotalLen * sizeof (tchar));
            text->refCount = 1;
            text->allocatedNumChars = newTotalLen;

            memcpy (text->text, old->text, oldLen * sizeof (tchar));
            memcpy (text->text + oldLen, newText, numExtraChars * sizeof (tchar));

            atomicDecrement (old->refCount);
        }
        else
        {
            // no other strings using it, so just expand it if needed..
            if (newTotalLen > text->allocatedNumChars)
            {
                text = (InternalRefCountedStringHolder*)
                            juce_realloc (text, sizeof (InternalRefCountedStringHolder)
                                               + newTotalLen * sizeof (tchar));

                text->allocatedNumChars = newTotalLen;
            }

            memcpy (text->text + oldLen, newText, numExtraChars * sizeof (tchar));
        }

        text->text [newTotalLen] = 0;
    }
}

void String::dupeInternalIfMultiplyReferenced() throw()
{
    if (text->refCount > 1)
    {
        InternalRefCountedStringHolder* const old = text;
        const int len = text->allocatedNumChars;

        text = (InternalRefCountedStringHolder*)
                    juce_malloc (sizeof (InternalRefCountedStringHolder)
                                   + len * sizeof (tchar));
        text->refCount = 1;
        text->allocatedNumChars = len;

        memcpy (text->text, old->text, (len + 1) * sizeof (tchar));

        atomicDecrement (old->refCount);
    }
}

//==============================================================================
const String String::empty;


//==============================================================================
char String::toUpperCase (const char c) throw()
{
    return (c >= 'a' && c <= 'z') ? (char) (c + ('A' - 'a'))
                                  : c;
}

juce_wchar String::toUpperCase (const juce_wchar c) throw()
{
    return (juce_wchar) toupper (c);
}

char String::toLowerCase (const char c) throw()
{
    return (c >= 'A' && c <= 'Z') ? (char) (c + ('a' - 'A'))
                                  : c;
}

juce_wchar String::toLowerCase (const juce_wchar c) throw()
{
    return (juce_wchar) tolower (c);
}

bool String::isWhitespace (const char c) throw()
{
    return c == T(' ') || (c <= 13 && c >= 9);
}

bool String::isWhitespace (const juce_wchar c) throw()
{
    return iswspace (c) != 0;
}

bool String::isLetter (const char c) throw()
{
    return (c >= 'a' && c <= 'z')
        || (c >= 'A' && c <= 'Z');
}

bool String::isLetter (const juce_wchar c) throw()
{
    return iswalpha (c) != 0;
}

bool String::isDigit (const char c) throw()
{
    return (c >= '0' && c <= '9');
}

bool String::isDigit (const juce_wchar c) throw()
{
    return isdigit (c) != 0;
}

bool String::isLetterOrDigit (const char c) throw()
{
    return (c >= 'a' && c <= 'z')
        || (c >= 'A' && c <= 'Z')
        || (c >= '0' && c <= '9');
}

bool String::isLetterOrDigit (const juce_wchar c) throw()
{
    return iswalnum (c) != 0;
}

//==============================================================================
String::String() throw()
    : text (&emptyString)
{
}

String::String (const String& other) throw()
    : text (other.text)
{
    atomicIncrement (text->refCount);
}

String::String (const int numChars,
                const int /*dummyVariable*/) throw()
{
    createInternal (numChars);
}

String::String (const char* const t) throw()
{
    if (t != 0 && *t != 0)
    {
        const int len = (int) strlen (t);
        createInternal (len);

#if JUCE_STRINGS_ARE_UNICODE
        mbstowcs (text->text, t, len + 1);
#else
        memcpy (text->text, t, len + 1);
#endif
    }
    else
    {
        text = &emptyString;
        emptyString.refCount = safeEmptyStringRefCount;
    }
}

String::String (const juce_wchar* const t) throw()
{
    if (t != 0 && *t != 0)
    {
        const int len = (int) wcslen (t);
        createInternal (len);

#if JUCE_STRINGS_ARE_UNICODE
        memcpy (text->text, t, (len + 1) * sizeof (tchar));
#else
        wcstombs (text->text, t, len + 1);
#endif
    }
    else
    {
        text = &emptyString;
        emptyString.refCount = safeEmptyStringRefCount;
    }
}

String::String (const char* const t,
                const int maxChars) throw()
{
    int i;
    for (i = 0; i < maxChars; ++i)
        if (t[i] == 0)
            break;

    if (i > 0)
    {
        createInternal (i);

#if JUCE_STRINGS_ARE_UNICODE
        mbstowcs (text->text, t, i);
#else
        memcpy (text->text, t, i);
#endif

        text->text [i] = 0;
    }
    else
    {
        text = &emptyString;
        emptyString.refCount = safeEmptyStringRefCount;
    }
}

String::String (const juce_wchar* const t,
                const int maxChars) throw()
{
    int i;
    for (i = 0; i < maxChars; ++i)
        if (t[i] == 0)
            break;

    if (i > 0)
    {
        createInternal (i);

#if JUCE_STRINGS_ARE_UNICODE
        memcpy (text->text, t, i * sizeof (tchar));
#else
        wcstombs (text->text, t, i);
#endif
        text->text [i] = 0;
    }
    else
    {
        text = &emptyString;
        emptyString.refCount = safeEmptyStringRefCount;
    }
}

const String String::charToString (const tchar character) throw()
{
    tchar temp[2];
    temp[0] = character;
    temp[1] = 0;

    return String (temp);
}

// pass in a pointer to the END of a buffer..
static tchar* intToCharString (tchar* t, const int n)
{
    int v = abs (n);
    *--t = 0;

    do
    {
        *--t = (tchar)(T('0') + (v % 10));
        v /= 10;

    } while (v > 0);

    if (n < 0)
        *--t = T('-');

    return t;
}

String::String (const int number) throw()
{
    tchar buffer [64];
    tchar* const end = buffer + 64;

    const tchar* const t = intToCharString (end, number);

    const int numChars = (int) (end - t);
    createInternal (numChars - 1);
    memcpy (text->text, t, numChars * sizeof (tchar));
}


String::String (const int64 number) throw()
{
    tchar buffer [64];
    tchar* const end = buffer + 64;
    tchar* t = end;

    *--t = 0;
    int64 v = (number >= 0) ? number : -number;

    do
    {
        *--t = (tchar)(T('0') + (int)(v % 10));
        v /= 10;

    } while (v > 0);

    if (number < 0)
        *--t = T('-');

    const int len = (int) (((char*)end) - ((char*)t));
    createInternal (len - 1);
    memcpy (text->text, t, len);
}


// a double-to-string routine that actually uses the number of dec. places you asked for
// without resorting to exponent notation if the number's too big or small (which is what printf does).
void String::doubleToStringWithDecPlaces (double n, int numDecPlaces) throw()
{
    const int bufSize = 80;
    tchar buffer [bufSize];
    int len;
    tchar* t;

    if (numDecPlaces > 0 && isfinite (n))
    {
        double digitMultiple = pow (10.0, numDecPlaces);

        const bool negative = (n < 0);
        if (negative)
            n = -n;

        t = buffer + bufSize;
        *--t = (tchar)0;

        for (;;)
        {
            const double v = n * digitMultiple;

            if (numDecPlaces < 0 && v < 1.0)
                break;
            else if (numDecPlaces == 0)
                *--t = T('.');

            *--t = (tchar) (T('0') + (int) fmod (v, 10.0));

            digitMultiple *= 0.1;
            --numDecPlaces;
        }

        if (negative)
            *--t = T('-');

        len = (int) ((buffer + bufSize) - t);
    }
    else
    {
        len = juce_sprintf (buffer, sizeof (buffer) / sizeof (tchar), T("%.9g"), n) + 1;
        t = buffer;
    }

    if (len > 1)
    {
        jassert (len < (int) (sizeof (buffer) / sizeof (tchar)));

        createInternal (len - 1);
        memcpy (text->text, t, len * sizeof (tchar));
    }
    else
    {
        jassert (*t == 0);
        text = &emptyString;
        emptyString.refCount = safeEmptyStringRefCount;
    }
}

String::String (const float number,
                const int numberOfDecimalPlaces) throw()
{
    doubleToStringWithDecPlaces ((double) number,
                                 numberOfDecimalPlaces);
}

String::String (const double number,
                const int numberOfDecimalPlaces) throw()
{
    doubleToStringWithDecPlaces (number,
                                 numberOfDecimalPlaces);
}

String::~String() throw()
{
    if (atomicDecrementAndReturn (text->refCount) == 0)
        juce_free (text);
}

//==============================================================================
void String::preallocateStorage (const int numChars) throw()
{
    if (numChars > text->allocatedNumChars)
    {
        dupeInternalIfMultiplyReferenced();

        text = (InternalRefCountedStringHolder*) juce_realloc (text, sizeof (InternalRefCountedStringHolder)
                                                                       + numChars * sizeof (tchar));
        text->allocatedNumChars = numChars;
    }
}

//==============================================================================
#if JUCE_STRINGS_ARE_UNICODE
String::operator const char*() const throw()
{
    if (isEmpty())
    {
        return (const char*) emptyCharString;
    }
    else
    {
        String* const mutableThis = const_cast <String*> (this);

        mutableThis->dupeInternalIfMultiplyReferenced();
        int len = (int) juce_strlen (text->text) + 1;
        mutableThis->text = (InternalRefCountedStringHolder*)
                                juce_realloc (text, sizeof (InternalRefCountedStringHolder)
                                                      + (len * sizeof (juce_wchar) + len));
        char* otherCopy = (char*)(text->text + len);
        --len;
        wcstombs (otherCopy, text->text, len);
        otherCopy[len] = 0;
        return otherCopy;
    }
}

#else

String::operator const juce_wchar*() const throw()
{
    if (isEmpty())
    {
        return (const juce_wchar*) emptyCharString;
    }
    else
    {
        String* const mutableThis = const_cast <String*> (this);

        mutableThis->dupeInternalIfMultiplyReferenced();
        int len = (int) juce_strlen (text->text) + 1;
        mutableThis->text = (InternalRefCountedStringHolder*)
                                juce_realloc (text, sizeof (InternalRefCountedStringHolder)
                                                  + (len * sizeof (juce_wchar) + len));

        juce_wchar* otherCopy = (juce_wchar*)(text->text + len);
        --len;
        mbstowcs (otherCopy, text->text, len);
        otherCopy[len] = 0;
        return otherCopy;
    }
}

#endif

void String::copyToBuffer (char* const destBuffer,
                           const int maxCharsToCopy) const throw()
{
    const int len = jmin (maxCharsToCopy, length());

#if JUCE_STRINGS_ARE_UNICODE
    wcstombs (destBuffer, text->text, len);
#else
    memcpy (destBuffer, text->text, len * sizeof (tchar));
#endif

    destBuffer[len] = 0;
}

void String::copyToBuffer (juce_wchar* const destBuffer,
                           const int maxCharsToCopy) const throw()
{
    const int len = jmin (maxCharsToCopy, length());

#if JUCE_STRINGS_ARE_UNICODE
    memcpy (destBuffer, text->text, len * sizeof (juce_wchar));
#else
    mbstowcs (destBuffer, text->text, len);
#endif

    destBuffer[len] = 0;
}

//==============================================================================
int String::length() const throw()
{
    return (int) juce_strlen (text->text);
}

int String::hashCode() const throw()
{
    const tchar* t = text->text;
    int result = 0;

    while (*t != (tchar) 0)
        result = 31 * result + *t++;

    return result;
}

int64 String::hashCode64() const throw()
{
    const tchar* t = text->text;
    int64 result = 0;

    while (*t != (tchar) 0)
        result = 101 * result + *t++;

    return result;
}

//==============================================================================
const String& String::operator= (const tchar* const otherText) throw()
{
    if (otherText != 0 && *otherText != 0)
    {
        const int otherLen = (int) juce_strlen (otherText);

        if (otherLen > 0)
        {
            // avoid resizing the memory block if the string is
            // shrinking..
            if (text->refCount > 1
                || otherLen > text->allocatedNumChars
                || otherLen <= (text->allocatedNumChars >> 1))
            {
                deleteInternal();
                createInternal (otherLen);
            }

            memcpy (text->text, otherText, (otherLen + 1) * sizeof (tchar));

            return *this;
        }
    }

    deleteInternal();
    text = &emptyString;
    emptyString.refCount = safeEmptyStringRefCount;

    return *this;
}

const String& String::operator= (const String& other) throw()
{
    if (this != &other)
    {
        atomicIncrement (other.text->refCount);

        if (atomicDecrementAndReturn (text->refCount) == 0)
            juce_free (text);

        text = other.text;
    }

    return *this;
}

void String::setFromSubstring (const String& other,
                               int startChar,
                               int endChar) throw()
{
    const int otherLen = other.length();

    if (endChar > otherLen)
        endChar = otherLen;

    if (startChar < 0)
        startChar = 0;

    const int num = endChar - startChar;

    if (num < 0)
    {
        deleteInternal();
        text = &emptyString;
    }
    else
    {
        if (num > text->allocatedNumChars)
        {
            deleteInternal();
            createInternal (num);
        }
        else
        {
            dupeInternalIfMultiplyReferenced();
        }

        memcpy (text->text, other.text->text + startChar, num);
        text->text [num] = 0;
    }
}


//==============================================================================
bool String::operator== (const String& other) const throw()
{
    return text == other.text
            || juce_strcmp (text->text, other.text->text) == 0;
}

bool String::operator== (const tchar* const t) const throw()
{
    return juce_strcmp (text->text, t) == 0;
}

bool String::equalsIgnoreCase (const tchar* t) const throw()
{
    return juce_stricmp (text->text, t) == 0;
}

bool String::equalsIgnoreCase (const String& other) const throw()
{
    return text == other.text
            || juce_stricmp (text->text, other.text->text) == 0;
}

bool String::operator!= (const String& other) const throw()
{
    return text != other.text
            && juce_strcmp (text->text, other.text->text) != 0;
}

bool String::operator!= (const tchar* const t) const throw()
{
    return juce_strcmp (text->text, t) != 0;
}

bool String::operator> (const String& other) const throw()
{
    return compare (other) > 0;
}

bool String::operator< (const tchar* const other) const throw()
{
    return compare (other) < 0;
}

bool String::operator>= (const String& other) const throw()
{
    return compare (other) >= 0;
}

bool String::operator<= (const tchar* const other) const throw()
{
    return compare (other) <= 0;
}

int String::compare (const tchar* const other) const throw()
{
    return juce_strcmp (text->text, other);
}

int String::compareIgnoreCase (const tchar* const other) const throw()
{
    return juce_stricmp (text->text, other);
}

int String::compareLexicographically (const tchar* other) const throw()
{
    if (other == 0)
        return isEmpty();

    const tchar* s1 = text->text;
    while (*s1 != 0 && ! isLetterOrDigit (*s1))
        ++s1;

#ifdef __BORLANDC__
    // workaround for a moronic borland compiler bug
    const tchar* other2 = other;
    while (*other2 != 0 && ! isLetterOrDigit (*other2))
        ++other2;
#else
    while (*other != 0 && ! isLetterOrDigit (*other))
        ++other;
#endif

    return juce_stricmp (s1, other);
}

//==============================================================================
const String String::operator+ (const String& other) const throw()
{
    if (*(other.text->text) == 0)
        return *this;

    if (isEmpty())
        return other;

    const int len       = (int) juce_strlen (text->text);
    const int otherLen  = (int) juce_strlen (other.text->text);

    String result (len + otherLen, (int) 0);
    memcpy (result.text->text, text->text, len * sizeof (tchar));
    memcpy (result.text->text + len, other.text->text, otherLen * sizeof (tchar));
    result.text->text [len + otherLen] = 0;

    return result;
}

const String String::operator+ (const tchar* const textToAppend) const throw()
{
    if (textToAppend == 0 || *textToAppend == 0)
        return *this;

    const int len       = (int) juce_strlen (text->text);
    const int otherLen  = (int) juce_strlen (textToAppend);

    String result (len + otherLen, (int) 0);
    memcpy (result.text->text, text->text, len * sizeof (tchar));
    memcpy (result.text->text + len, textToAppend, otherLen * sizeof (tchar));
    result.text->text [len + otherLen] = 0;

    return result;
}

const String String::operator+ (const tchar characterToAppend) const throw()
{
    if (characterToAppend == 0)
        return *this;

    const int len = (int) juce_strlen (text->text);
    String result ((int)(len + 1), (int) 0);

    memcpy (result.text->text, text->text, len * sizeof (tchar));
    result.text->text[len] = characterToAppend;
    result.text->text[len + 1] = 0;

    return result;
}

//==============================================================================
const String JUCE_API operator+ (const char* const string1,
                                 const String& string2) throw()
{
    return String (string1) + string2;
}

const String JUCE_API operator+ (const juce_wchar* const string1,
                                 const String& string2) throw()
{
    return String (string1) + string2;
}

//==============================================================================
const String& String::operator+= (const tchar* const t) throw()
{
    if (t != 0)
        appendInternal (t, (int) juce_strlen (t));

    return *this;
}

const String& String::operator+= (const String& other) throw()
{
    if (isEmpty())
        operator= (other);
    else
        appendInternal (other.text->text,
                        (int) juce_strlen (other.text->text));

    return *this;
}

const String& String::operator+= (const char ch) throw()
{
    char asString[2];
    asString[0] = ch;
    asString[1] = 0;

#if JUCE_STRINGS_ARE_UNICODE
    operator+= (String (asString));
#else
    appendInternal (asString, 1);
#endif

    return *this;
}

const String& String::operator+= (const juce_wchar ch) throw()
{
    juce_wchar asString[2];
    asString[0] = ch;
    asString[1] = 0;

#if JUCE_STRINGS_ARE_UNICODE
    appendInternal (asString, 1);
#else
    operator+= (String (asString));
#endif

    return *this;
}

void String::append (const tchar* const other,
                     const int howMany) throw()
{
    if (howMany > 0)
    {
        int i;
        for (i = 0; i < howMany; ++i)
            if (other[i] == 0)
                break;

        appendInternal (other, i);
    }
}

String& String::operator<< (const int number) throw()
{
    tchar buffer [64];
    tchar* const end = buffer + 64;
    const tchar* const t = intToCharString (end, number);
    appendInternal (t, (int) (end - t) - 1);

    return *this;
}

String& String::operator<< (const short number) throw()
{
    tchar buffer [64];
    tchar* const end = buffer + 64;
    const tchar* const t = intToCharString (end, (int)number);
    appendInternal (t, (int) (end - t) - 1);

    return *this;
}

String& String::operator<< (const double number) throw()
{
    operator+= (String (number));
    return *this;
}

String& String::operator<< (const float number) throw()
{
    operator+= (String (number));
    return *this;
}

String& String::operator<< (const char character) throw()
{
    operator+= (character);
    return *this;
}

String& String::operator<< (const juce_wchar character) throw()
{
    operator+= (character);
    return *this;
}

String& String::operator<< (const char* const t) throw()
{
#if JUCE_STRINGS_ARE_UNICODE
    operator+= (String (t));
#else
    operator+= (t);
#endif
    return *this;
}

String& String::operator<< (const juce_wchar* const t) throw()
{
#if JUCE_STRINGS_ARE_UNICODE
    operator+= (t);
#else
    operator+= (String (t));
#endif
    return *this;
}

String& String::operator<< (const String& t) throw()
{
    operator+= (t);
    return *this;
}

//==============================================================================
int String::indexOfChar (const tchar character) const throw()
{
    const tchar* t = text->text;

    for (;;)
    {
        if (*t == character)
            return (int) (t - text->text);

        if (*t++ == 0)
            return -1;
    }
}

int String::lastIndexOfChar (const tchar character) const throw()
{
    for (int i = (int) juce_strlen (text->text); --i >= 0;)
        if (text->text[i] == character)
            return i;

    return -1;
}

int String::indexOf (const tchar* const t) const throw()
{
    const tchar* const r = juce_strstr (text->text, t);
    return (r == 0) ? -1
                    : (int) (r - text->text);
}

int String::indexOfChar (const int startIndex,
                         const tchar character) const throw()
{
    if (startIndex >= (int) juce_strlen (text->text))
        return -1;

    const tchar* t = text->text + jmax (0, startIndex);

    for (;;)
    {
        if (*t == character)
            return (int) (t - text->text);

        if (*t++ == 0)
            return -1;
    }
}

int String::indexOf (const int startIndex,
                     const tchar* const other) const throw()
{
    if (other == 0 || startIndex >= (int) juce_strlen (text->text))
        return -1;

    const tchar* const found = juce_strstr (text->text + jmax (0, startIndex),
                                            other);

    return (found == 0) ? -1
                        : (int) (found - text->text);
}

int String::indexOfIgnoreCase (const tchar* const other) const throw()
{
    if (other != 0 && *other != 0)
    {
        const int len = (int) juce_strlen (other);
        const int end = (int) juce_strlen (text->text) - len;

        for (int i = 0; i <= end; ++i)
            if (juce_strnicmp (text->text + i, other, len) == 0)
                return i;
    }

    return -1;
}

int String::indexOfIgnoreCase (const int startIndex,
                               const tchar* const other) const throw()
{
    if (other != 0 && *other != 0)
    {
        const int len = (int) juce_strlen (other);
        const int end = length() - len;

        for (int i = jmax (0, startIndex); i <= end; ++i)
            if (juce_strnicmp (text->text + i, other, len) == 0)
                return i;
    }

    return -1;
}

int String::lastIndexOf (const tchar* const other) const throw()
{
    if (other != 0 && *other != 0)
    {
        const int len = (int) juce_strlen (other);
        int i = length() - len;

        if (i >= 0)
        {
            const tchar* n = text->text + i;

            while (i >= 0)
            {
                if (juce_strncmp (n--, other, len) == 0)
                    return i;

                --i;
            }
        }
    }

    return -1;
}

int String::lastIndexOfIgnoreCase (const tchar* const other) const throw()
{
    if (other != 0 && *other != 0)
    {
        const int len = (int) juce_strlen (other);
        int i = length() - len;

        if (i >= 0)
        {
            const tchar* n = text->text + i;

            while (i >= 0)
            {
                if (juce_strnicmp (n--, other, len) == 0)
                    return i;

                --i;
            }
        }
    }

    return -1;
}

bool String::contains (const tchar* const other) const throw()
{
    return indexOf (other) >= 0;
}

bool String::containsChar (const tchar character) const throw()
{
    return indexOfChar (character) >= 0;
}

bool String::containsIgnoreCase (const tchar* const t) const throw()
{
    return indexOfIgnoreCase (t) >= 0;
}

bool String::containsWholeWord (const tchar* const word) const throw()
{
    if (word != 0 && *word != 0)
    {
        const int wordLen = (int) juce_strlen (word);
        const tchar* r = text->text + length() - wordLen;

        while (r >= text->text)
        {
            if (juce_strncmp (r, word, wordLen) == 0)
            {
                tchar prevChar = T(' ');

                if (r > text->text)
                    prevChar = *(r - 1);

                tchar nextChar = *(r + wordLen);

                if (!(isLetterOrDigit (nextChar) || isLetterOrDigit (prevChar)))
                    return true;
            }

            --r;
        }
    }

    return false;
}

bool String::containsWholeWordIgnoreCase (const tchar* const word) const throw()
{
    if (word == 0 || *word == 0)
        return false;

    const int wordLen = (int) juce_strlen (word);
    const tchar* r = text->text + length() - wordLen;

    while (r >= text->text)
    {
        if (juce_strnicmp (r, word, wordLen) == 0)
        {
            tchar prevChar = T(' ');

            if (r > text->text)
                prevChar = *(r - 1);

            const tchar nextChar = *(r + wordLen);

            if (!(isLetterOrDigit (nextChar) || isLetterOrDigit (prevChar)))
                return true;
        }

        --r;
    }

    return false;
}

//==============================================================================
static int indexOfMatch (const tchar* const wildcard, const tchar* const test, const bool ignoreCase)
{
    int start = 0;

    while (test [start] != 0)
    {
        int i = 0;

        for (;;)
        {
            const tchar wc = wildcard [i];
            const tchar c = test [i + start];

            if (wc == c
                 || (ignoreCase && String::toLowerCase (wc) == String::toLowerCase (c))
                 || (wc == T('?') && c != 0))
            {
                if (wc == 0)
                    return start;

                ++i;
            }
            else
            {
                if (wc == T('*') && (wildcard [i + 1] == 0
                                      || indexOfMatch (wildcard + i + 1,
                                                       test + start + i,
                                                       ignoreCase) >= 0))
                {
                    return start;
                }

                break;
            }
        }

        ++start;
    }

    return -1;
}

bool String::matchesWildcard (const tchar* wildcard, const bool ignoreCase) const throw()
{
    int i = 0;

    for (;;)
    {
        const tchar wc = wildcard [i];
        const tchar c = text->text [i];

        if (wc == c
             || (ignoreCase && String::toLowerCase (wc) == String::toLowerCase (c))
             || (wc == T('?') && c != 0))
        {
            if (wc == 0)
                return true;

            ++i;
        }
        else
        {
            return wc == T('*') && (wildcard [i + 1] == 0
                                     || indexOfMatch (wildcard + i + 1,
                                                      text->text + i,
                                                      ignoreCase) >= 0);
        }
    }
}

//==============================================================================
void String::printf (const tchar* const pf, ...) throw()
{
    va_list list;
    va_start (list, pf);

    vprintf (pf, list);
}

const String String::formatted (const tchar* const pf, ...) throw()
{
    va_list list;
    va_start (list, pf);

    String result;
    result.vprintf (pf, list);
    return result;
}

//==============================================================================
void String::vprintf (const tchar* const pf, va_list args) throw()
{
    tchar stackBuf [256];
    unsigned int bufSize = 256;
    tchar* buf = stackBuf;

    deleteInternal();

    for (;;)
    {
#ifdef __va_copy
        va_list list;
        __va_copy (list, args);
#else
        va_list list = args;
#endif

#if JUCE_STRINGS_ARE_UNICODE
#ifndef JUCE_WIN32
  #define _vsnwprintf vswprintf
#endif
        const int num = _vsnwprintf (buf, bufSize - 1, pf, list);
#else
        const int num = _vsnprintf (buf, bufSize - 1, pf, list);
#endif

        if (num > 0)
        {
            createInternal (num);
            memcpy (text->text, buf, (num + 1) * sizeof (tchar));
            break;
        }
        else if (num == 0)
        {
            text = &emptyString;
            emptyString.refCount = safeEmptyStringRefCount;
            break;
        }

        if (buf != stackBuf)
            juce_free (buf);

        bufSize += 256;
        buf = (tchar*) juce_malloc (bufSize * sizeof (tchar));
    }

    if (buf != stackBuf)
        juce_free (buf);
}

//==============================================================================
const String String::repeatedString (const tchar* const stringToRepeat,
                                     int numberOfTimesToRepeat) throw()
{
    const int len = (int) juce_strlen (stringToRepeat);
    String result ((int)(len * numberOfTimesToRepeat + 1), (int) 0);

    tchar* n = result.text->text;
    n[0] = 0;

    while (--numberOfTimesToRepeat >= 0)
    {
        juce_strcat (n, stringToRepeat);
        n += len;
    }

    return result;
}

//==============================================================================
const String String::replaceSection (int index,
                                     int numCharsToReplace,
                                     const tchar* const stringToInsert) const throw()
{
    if (index < 0)
    {
        // a negative index to replace from?
        jassertfalse
        index = 0;
    }

    if (numCharsToReplace < 0)
    {
        // replacing a negative number of characters?
        numCharsToReplace = 0;
        jassertfalse;
    }

    const int len = length();

    if (index + numCharsToReplace > len)
    {
        if (index > len)
        {
            // replacing beyond the end of the string?
            index = len;
            jassertfalse
        }

        numCharsToReplace = len - index;
    }

    const int newStringLen = (stringToInsert != 0) ? (int) juce_strlen (stringToInsert) : 0;
    const int newTotalLen = len + newStringLen - numCharsToReplace;

    String result (newTotalLen, (int) 0);

    memcpy (result.text->text,
            text->text,
            index * sizeof (tchar));

    if (newStringLen > 0)
        memcpy (result.text->text + index,
                stringToInsert,
                newStringLen * sizeof (tchar));

    const int endStringLen = newTotalLen - (index + newStringLen);

    if (endStringLen > 0)
        memcpy (result.text->text + (index + newStringLen),
                text->text + (index + numCharsToReplace),
                endStringLen * sizeof (tchar));

    result.text->text [newTotalLen] = 0;

    return result;
}

const String String::replace (const tchar* const stringToReplace,
                              const tchar* const stringToInsert,
                              const bool ignoreCase) const throw()
{
    int i = 0;
    String result (*this);

    while ((i = (ignoreCase ? result.indexOfIgnoreCase (i, stringToReplace)
                            : result.indexOf (i, stringToReplace))) >= 0)
    {
        result = result.replaceSection (i, (int) juce_strlen (stringToReplace), stringToInsert);
        i += (int) juce_strlen (stringToInsert);
    }

    return result;
}

const String String::replaceCharacter (const tchar charToReplace,
                                       const tchar charToInsert) const throw()
{
    const int index = indexOfChar (charToReplace);

    if (index < 0)
        return *this;

    String result (*this);
    result.dupeInternalIfMultiplyReferenced();

    tchar* t = result.text->text + index;

    while (*t != 0)
    {
        if (*t == charToReplace)
            *t = charToInsert;

        ++t;
    }

    return result;
}

const String String::replaceCharacters (const String& charactersToReplace,
                                        const tchar* const charactersToInsertInstead) const throw()
{
    String result (*this);
    result.dupeInternalIfMultiplyReferenced();

    tchar* t = result.text->text;
    const int len2 = (int) juce_strlen (charactersToInsertInstead);

    // the two strings passed in are supposed to be the same length!
    jassert (len2 == charactersToReplace.length());

    while (*t != 0)
    {
        const int index = charactersToReplace.indexOfChar (*t);

        if (index >= 0 && index < len2)
            *t = charactersToInsertInstead [index];

        ++t;
    }

    return result;
}

//==============================================================================
bool String::startsWith (const tchar* const other) const throw()
{
    return other != 0
            && juce_strncmp (text->text, other, juce_strlen (other)) == 0;
}

bool String::startsWithIgnoreCase (const tchar* const other) const throw()
{
    return other != 0
            && juce_strnicmp (text->text, other, juce_strlen (other)) == 0;
}

bool String::startsWithChar (const tchar character) const throw()
{
    return text->text[0] == character;
}

bool String::endsWithChar (const tchar character) const throw()
{
    return text->text[0] != 0
            && text->text [length() - 1] == character;
}

bool String::endsWith (const tchar* const other) const throw()
{
    if (other == 0)
        return false;

    const int thisLen = length();
    const int otherLen = (int) juce_strlen (other);

    return thisLen >= otherLen
            && juce_strcmp (text->text + thisLen - otherLen, other) == 0;
}

bool String::endsWithIgnoreCase (const tchar* const other) const throw()
{
    if (other == 0)
        return false;

    const int thisLen = length();
    const int otherLen = (int) juce_strlen (other);

    return thisLen >= otherLen
            && juce_stricmp (text->text + thisLen - otherLen, other) == 0;
}

//==============================================================================
const String String::toUpperCase() const throw()
{
    String result (*this);
    result.dupeInternalIfMultiplyReferenced();
    juce_strupr (result.text->text);
    return result;
}

const String String::toLowerCase() const throw()
{
    String result (*this);
    result.dupeInternalIfMultiplyReferenced();
    juce_strlwr (result.text->text);
    return result;
}

//==============================================================================
tchar& String::operator[] (const int index) throw()
{
    jassert (index >= 0 && index <= length());

    dupeInternalIfMultiplyReferenced();

    return text->text [index];
}

tchar String::getLastCharacter() const throw()
{
    return (isEmpty()) ? ((tchar) 0)
                       : text->text [juce_strlen (text->text) - 1];
}

const String String::substring (int start, int end) const throw()
{
    if (start < 0)
        start = 0;
    else if (end <= start)
        return empty;

    int len = 0;
    const tchar* const t = text->text;

    while (len <= end && t [len] != 0)
        ++len;

    if (end >= len)
    {
        if (start == 0)
            return *this;

        end = len;
    }

    return String (text->text + start,
                   end - start);
}

const String String::substring (const int start) const throw()
{
    if (start <= 0)
        return *this;

    const int len = (int) juce_strlen (text->text);

    if (start >= len)
        return empty;
    else
        return String (text->text + start,
                       len - start);
}

const String String::dropLastCharacters (const int numberToDrop) const throw()
{
    return String (text->text,
                   jmax (0, (int) juce_strlen (text->text) - numberToDrop));
}

const String String::fromFirstOccurrenceOf (const tchar* const sub,
                                            const bool includeSubString,
                                            const bool ignoreCase) const throw()
{
    const int i = (ignoreCase) ? indexOf (sub)
                               : indexOfIgnoreCase (sub);

    if (i < 0)
        return empty;
    else
        return substring ((includeSubString) ? i : i + (int) juce_strlen (sub));
}


const String String::fromLastOccurrenceOf (const tchar* const sub,
                                           const bool includeSubString,
                                           const bool ignoreCase) const throw()
{
    const int i = (ignoreCase) ? lastIndexOf (sub)
                               : lastIndexOfIgnoreCase (sub);

    if (i < 0)
        return *this;
    else
        return substring ((includeSubString) ? i : i + (int) juce_strlen (sub));
}

const String String::upToFirstOccurrenceOf (const tchar* const sub,
                                            const bool includeSubString,
                                            const bool ignoreCase) const throw()
{
    const int i = (ignoreCase) ? indexOfIgnoreCase (sub)
                               : indexOf (sub);

    if (i < 0)
        return *this;
    else
        return substring (0, (includeSubString) ? i + (int) juce_strlen (sub) : i);
}

const String String::upToLastOccurrenceOf (const tchar* const sub,
                                           const bool includeSubString,
                                           const bool ignoreCase) const throw()
{
    const int i = (ignoreCase) ? lastIndexOfIgnoreCase (sub)
                               : lastIndexOf (sub);
    if (i < 0)
        return *this;

    return substring (0, (includeSubString) ? i + (int) juce_strlen (sub) : i);
}

bool String::isQuotedString() const throw()
{
    const String trimmed (trimStart());

    return trimmed[0] == T('"')
        || trimmed[0] == T('\'');
}

const String String::unquoted() const throw()
{
    String s (*this);

    if (s[0] == T('"') || s[0] == T('\''))
        s = s.substring (1);

    if (s.endsWithChar (T('"')) || s.endsWithChar (T('\'')))
        s [s.length() - 1] = 0;

    return s;
}

const String String::quoted (const tchar quoteCharacter) const throw()
{
    if (isEmpty())
        return charToString (quoteCharacter) + quoteCharacter;

    String t (*this);

    if (! t.startsWithChar (quoteCharacter))
        t = charToString (quoteCharacter) + t;

    if (! t.endsWithChar (quoteCharacter))
        t += quoteCharacter;

    return t;
}

//==============================================================================
const String String::trim() const throw()
{
    if (isEmpty())
        return empty;

    int start = 0;

    while (isWhitespace (text->text [start]))
        ++start;

    const int len = (int) juce_strlen (text->text);
    int end = len - 1;

    while ((end >= start) && isWhitespace (text->text [end]))
        --end;

    ++end;

    if (end <= start)
        return empty;
    else if (start > 0 || end < len)
        return String (text->text + start, end - start);
    else
        return *this;
}

const String String::trimStart() const throw()
{
    if (isEmpty())
        return empty;

    const tchar* t = text->text;

    while (isWhitespace (*t))
        ++t;

    if (t == text->text)
        return *this;
    else
        return String (t);
}

const String String::trimEnd() const throw()
{
    if (isEmpty())
        return empty;

    const tchar* endT = text->text + (juce_strlen (text->text) - 1);

    while ((endT >= text->text) && isWhitespace (*endT))
        --endT;

    return String (text->text, (int) (++endT - text->text));
}

//==============================================================================
const String String::retainCharacters (const tchar* const charactersToRetain) const throw()
{
    const int len = length();

    if (charactersToRetain == 0 || len == 0)
        return empty;

    const int len2 = (int) juce_strlen (charactersToRetain);

    String result (len, (int) 0);
    tchar* n = result.text->text;

    for (int i = 0; i < len; ++i)
    {
        const tchar c = text->text[i];

        for (int j = len2; --j >= 0;)
        {
            if (charactersToRetain[j] == c)
            {
                *n++ = c;
                break;
            }
        }
    }

    *n = 0;

    return result;
}

const String String::initialSectionContainingOnly (const tchar* const charactersToRetain) const throw()
{
    if (charactersToRetain == 0)
        return empty;

    const int len = length();
    const int len2 = (int) juce_strlen (charactersToRetain);
    int i;

    for (i = 0; i < len; ++i)
    {
        const tchar c = text->text[i];
        bool ok = false;

        for (int j = len2; --j >= 0;)
        {
            if (charactersToRetain[j] == c)
            {
                ok = true;
                break;
            }
        }

        if (!ok)
            break;
    }

    return String (text->text, i);
}

const String String::initialSectionNotContaining (const tchar* const charactersNotToRetain) const throw()
{
    if (charactersNotToRetain == 0)
        return *this;

    const int len = length();
    const int len2 = (int) juce_strlen (charactersNotToRetain);

    for (int i = 0; i < len; ++i)
    {
        const tchar c = text->text[i];

        for (int j = len2; --j >= 0;)
            if (charactersNotToRetain[j] == c)
                return String (text->text, i);
    }

    return empty;
}

const String String::removeCharacters (const tchar* const charactersToRemove) const throw()
{
    if (charactersToRemove == 0 || isEmpty())
        return *this;

    const int len = length();
    const int len2 = (int) juce_strlen (charactersToRemove);

    String result (len, (int) 0);
    tchar* n = result.text->text;

    int i;
    for (i = 0; i < len; ++i)
    {
        tchar c = text->text[i];
        *n++ = c;

        for (int j = len2; --j >= 0;)
        {
            if (charactersToRemove[j] == c)
            {
                --n;
                break;
            }
        }
    }

    *n = 0;

    return result;
}

bool String::containsOnly (const tchar* const chars) const throw()
{
    const int len = (int) juce_strlen (chars);

    for (int i = length(); --i >= 0;)
    {
        bool ok = false;

        for (int j = len; --j >= 0;)
        {
            if (text->text[i] == chars[j])
            {
                ok = true;
                break;
            }
        }

        if (!ok)
            return false;
    }

    return true;
}

bool String::containsAnyOf (const tchar* const chars) const throw()
{
    const int len = (int) juce_strlen (chars);

    for (int i = length(); --i >= 0;)
        for (int j = len; --j >= 0;)
            if (text->text[i] == chars[j])
                return true;

    return false;
}

//==============================================================================
int String::getIntValue() const throw()
{
#if JUCE_STRINGS_ARE_UNICODE
    return _wtoi (text->text);
#else
    return atoi (text->text);
#endif
}

int String::getTrailingIntValue() const throw()
{
    int n = 0;
    int mult = 1;
    const tchar* t = text->text + length();

    while (--t >= text->text)
    {
        const tchar c = *t;

        if (! isDigit (c))
        {
            if (c == T('-'))
                n = -n;

            break;
        }

        n += mult * (c - T('0'));
        mult *= 10;
    }

    return n;
}

int64 String::getLargeIntValue() const throw()
{
#ifndef JUCE_WIN32
    int64 v = 0;
    const tchar* t = text->text;

    while (isWhitespace (*t))
        ++t;

    const bool isNeg = *t == T('-');
    if (isNeg)
        ++t;

    for (;;)
    {
        const tchar c = *t++;

        if (c >= T('0') && c <= T('9'))
            v = v * 10 + (int64) (c - T('0'));
        else
            break;
    }

    return isNeg ? -v : v;
#else
  #if JUCE_STRINGS_ARE_UNICODE
     return _wtoi64 (text->text);
  #else
     return _atoi64 (text->text);
  #endif
#endif
}

float String::getFloatValue() const throw()
{
#if JUCE_STRINGS_ARE_UNICODE
    wchar_t* endChar;
    return (float)wcstod (text->text, &endChar);
#else
    return (float)atof (text->text);
#endif
}

double String::getDoubleValue() const throw()
{
#if JUCE_STRINGS_ARE_UNICODE
    wchar_t* endChar;
    return wcstod (text->text, &endChar);
#else
    return atof (text->text);
#endif
}

static const tchar* const hexDigits = T("0123456789abcdef");

const String String::toHexString (const int number) throw()
{
    tchar buffer[32];
    tchar* const end = buffer + 32;
    tchar* t = end;
    *--t = 0;
    unsigned int v = (unsigned int)number;

    do
    {
        *--t = hexDigits[v & 15];
        v >>= 4;

    } while (v != 0);

    return String (t, (int) (((char*)end) - (char*)t) - 1);
}

const String String::toHexString (const int64 number) throw()
{
    tchar buffer[32];
    tchar* const end = buffer + 32;
    tchar* t = end;
    *--t = 0;
    uint64 v = (uint64) number;

    do
    {
        *--t = hexDigits [(int)(v & 15)];
        v >>= 4;

    } while (v != 0);

    return String (t, (int) (((char*)end) - (char*)t));
}

const String String::toHexString (const short number) throw()
{
    tchar buffer[32];
    tchar* const end = buffer + 32;
    tchar* t = end;
    *--t = 0;
    unsigned short v = (unsigned short)number;

    do
    {
        *--t = hexDigits [v & 15];
        v >>= 4;

    } while (v != 0);

    return String (t, (int) (((char*)end) - (char*)t));
}

const String String::toHexString (const unsigned char* data,
                                  const int size,
                                  const int groupSize) throw()
{
    if (size <= 0)
        return empty;

    int numChars = (size * 2) + 2;
    if (groupSize > 0)
        numChars += size / groupSize;

    String s (numChars, (int) 0);

    tchar* d = s.text->text;
    static const tchar* hexDigits = T("0123456789abcdef");

    for (int i = 0; i < size; ++i)
    {
        *d++ = hexDigits[(*data) >> 4];
        *d++ = hexDigits[(*data) & 0xf];
        ++data;

        if (groupSize > 0 && (i % groupSize) == 0)
            *d++ = T(' ');
    }

    if (groupSize > 0)
        --d;

    *d = 0;

    return s;
}

int String::getHexValue32() const throw()
{
    int result = 0;
    const tchar* c = text->text;

    for (;;)
    {
        const tchar digit = *c;

        if (digit == 0)
            break;

        if (digit >= T('0') && digit <= T('9'))
            result = (result << 4) | (digit - T('0'));
        else if (digit >= T('a') && digit <= T('f'))
            result = (result << 4) | (digit - (T('a') - 10));
        else if (digit >= T('A') && digit <= T('F'))
            result = (result << 4) | (digit - (T('A') - 10));

        ++c;
    }

    return result;
}

int64 String::getHexValue64() const throw()
{
    int64 result = 0;
    const tchar* c = text->text;

    for (;;)
    {
        const tchar digit = *c;

        if (digit == 0)
            break;

        if (digit >= T('0') && digit <= T('9'))
            result = (result << 4) | (digit - T('0'));
        else if (digit >= T('a') && digit <= T('f'))
            result = (result << 4) | (digit - (T('a') - 10));
        else if (digit >= T('A') && digit <= T('F'))
            result = (result << 4) | (digit - (T('A') - 10));

        ++c;
    }

    return result;
}

//==============================================================================
const String String::createStringFromData (const char* const data,
                                           const int size) throw()
{
    if (size <= 0 || data == 0)
    {
        return empty;
    }
    else if (size < 2)
    {
        return String::charToString (data[0]);
    }
    else if ((data[0] == (char)-2 && data[1] == (char)-1)
             || (data[0] == (char)-1 && data[1] == (char)-2))
    {
        // assume it's 16-bit unicode
        const bool bigEndian = (data[0] == (char)-2);
        const int numChars = size / 2 - 1;

        String result;
        result.preallocateStorage (numChars + 2);

        const uint16* const src = (const uint16*) (data + 2);

        if (bigEndian)
        {
            for (int i = 0; i < numChars; ++i)
                result += (juce_wchar) swapIfLittleEndian (src [i]);
        }
        else
        {
            for (int i = 0; i < numChars; ++i)
                result += (juce_wchar) swapIfBigEndian (src [i]);
        }

        return result;
    }
    else
    {
        // just assume it's ascii

#if JUCE_STRINGS_ARE_UNICODE && JUCE_LINUX
        // (workaround for strange behaviour of mbstowcs)
        int i;
        for (i = 0; i < size; ++i)
            if (data[i] == 0)
                break;

        String result;
        result.preallocateStorage (i + 2);

        for (int j = 0; j < i; ++j)
            result += (juce_wchar) (unsigned char) data[j];

        return result;
#else
        return String (data, size);
#endif
    }
}

//==============================================================================
const char* String::toUTF8() const
{
    if (isEmpty())
    {
        return (const char*) emptyCharString;
    }
    else
    {
        String* const mutableThis = const_cast <String*> (this);

        mutableThis->dupeInternalIfMultiplyReferenced();

        const int currentLen = (int) juce_strlen (text->text) + 1;
        const int utf8BytesNeeded = copyToUTF8 (0);

        mutableThis->text = (InternalRefCountedStringHolder*)
                                juce_realloc (text, sizeof (InternalRefCountedStringHolder)
                                                  + (currentLen * sizeof (juce_wchar) + utf8BytesNeeded));

        char* const otherCopy = (char*) (text->text + currentLen);
        copyToUTF8 ((uint8*) otherCopy);

        return otherCopy;
    }
}

int String::copyToUTF8 (uint8* buffer) const
{
#if JUCE_STRINGS_ARE_UNICODE
    int num = 0, index = 0;

    for (;;)
    {
        const uint32 c = (uint32) text->text [index++];

        if (c >= 0x80)
        {
            int numExtraBytes = 1;

            if (c >= 0x800)
            {
                ++numExtraBytes;

                if (c >= 0x10000)
                {
                    ++numExtraBytes;

                    if (c >= 0x200000)
                    {
                        ++numExtraBytes;

                        if (c >= 0x4000000)
                            ++numExtraBytes;
                    }
                }
            }

            if (buffer != 0)
            {
                buffer [num++] = (uint8) ((0xff << (7 - numExtraBytes)) | (c >> (numExtraBytes * 6)));

                while (--numExtraBytes >= 0)
                    buffer [num++] = (uint8) (0x80 | (0x3f & (c >> (numExtraBytes * 6))));
            }
            else
            {
                num += numExtraBytes + 1;
            }
        }
        else
        {
            if (buffer != 0)
                buffer [num] = (uint8) c;

            ++num;
        }

        if (c == 0)
            break;
    }

    return num;

#else
    if (buffer != 0)
        copyToBuffer (buffer, maxNumChars);

    return jmin (maxCharsToCopy, length());
#endif
}

const String String::fromUTF8 (const uint8* buffer, int bufferSizeBytes)
{
    if (buffer == 0)
        return String::empty;

    if (bufferSizeBytes < 0)
        bufferSizeBytes = INT_MAX;

    int numBytes;
    for (numBytes = 0; numBytes < bufferSizeBytes; ++numBytes)
        if (buffer [numBytes] == 0)
            break;

    String result (numBytes + 1, 0);
    tchar* dest = result.text->text;

    int i = 0;
    while (i < numBytes)
    {
        const uint8 c = buffer [i++];

        if ((c & 0x80) != 0)
        {
            int mask = 0x7f;
            int bit = 0x40;
            int numExtraValues = 0;

            while (bit != 0 && (c & bit) != 0)
            {
                bit >>= 1;
                mask >>= 1;
                ++numExtraValues;
            }

            int n = (c & mask);

            while (--numExtraValues >= 0 && i < bufferSizeBytes)
            {
                const uint8 c = buffer[i];

                if ((c & 0xc0) != 0x80)
                    break;

                n <<= 6;
                n |= (c & 0x3f);
                ++i;
            }

            *dest++ = (tchar) n;
        }
        else
        {
            *dest++ = (tchar) c;
        }
    }

    *dest = 0;
    return result;
}


END_JUCE_NAMESPACE
