//=============================================================================
// File:       dwstring.cpp
// Contents:   Definitions for DwString
// Maintainer: Doug Sauder <dwsauder@fwb.gulf.net>
// WWW:        http://www.fwb.gulf.net/~dwsauder/mimepp.html
//
// Copyright (c) 1996, 1997 Douglas W. Sauder
// All rights reserved.
//
// IN NO EVENT SHALL DOUGLAS W. SAUDER BE LIABLE TO ANY PARTY FOR DIRECT,
// INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF
// THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF DOUGLAS W. SAUDER
// HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// DOUGLAS W. SAUDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT
// NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
// PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS"
// BASIS, AND DOUGLAS W. SAUDER HAS NO OBLIGATION TO PROVIDE MAINTENANCE,
// SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
//
//=============================================================================

#define DW_IMPLEMENTATION

#include <mimelib/config.h>
#include <mimelib/debug.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <new>
#include <mimelib/string.h>

// mmap
#include <unistd.h>
#include <sys/mman.h>


#define DW_MIN(a,b) ((a) <= (b) ? (a) : (b))
#define DW_MAX(a,b) ((a) >= (b) ? (a) : (b))

/* In some locales (such as tr_TR.UTF-8, az_AZ) using tolower() can cause
   unexpected results. Keywords must be compared in a
   locale-independent manner */
static char dw_asciitolower( const char c )
{
  if ( c >= 'A' && c <= 'Z' )
    return c - 'A' + 'a';
  else
    return c;
}

static char dw_asciitoupper( const char c )
{
  if ( c >= 'a' && c <= 'z' )
    return c - 'a' + 'A';
  else
    return c;
}

static int dw_strasciicasecmp(const char* s1, size_t len1, const char* s2,
    size_t len2)
{
    assert(s1 != 0);
    assert(s2 != 0);
    size_t len = DW_MIN(len1, len2);
    for (size_t i=0; i < len; ++i) {
        int c1 = dw_asciitolower( s1[i] );
        int c2 = dw_asciitolower( s2[i] );

        if ( c1 < c2 )
            return -1;
        else if ( c1 > c2 )
            return 1;
    }
    if (len1 < len2) {
        return -1;
    }
    else if (len1 > len2) {
        return 1;
    }
    return 0;
}

#if 0
static int dw_strcasecmp(const char* s1, size_t len1, const char* s2,
    size_t len2)
{
    assert(s1 != 0);
    assert(s2 != 0);
    size_t len = DW_MIN(len1, len2);
    for (size_t i=0; i < len; ++i) {
        int c1 = tolower(s1[i]);
        int c2 = tolower(s2[i]);
        if (c1 < c2) {
            return -1;
        }
        else if (c1 > c2) {
            return 1;
        }
    }
    if (len1 < len2) {
        return -1;
    }
    else if (len1 > len2) {
        return 1;
    }
    return 0;
}
#endif


static int dw_strcmp(const char* s1, size_t len1, const char* s2, size_t len2)
{
    assert(s1 != 0);
    assert(s2 != 0);
    size_t len = DW_MIN(len1, len2);
    for (size_t i=0; i < len; ++i) {
        if (s1[i] < s2[i]) {
            return -1;
        }
        else if (s1[i] > s2[i]) {
            return 1;
        }
    }
    if (len1 < len2) {
        return -1;
    }
    else if (len1 > len2) {
        return 1;
    }
    return 0;
}


// Copy

inline void mem_copy(const char* src, size_t n, char* dest)
{
    assert(src != 0);
    assert(dest != 0);
    assert(src != dest);
    if (n == 0 || src == dest || !src || !dest) return;
    memmove(dest, src, n);
}

#if !defined(DW_USE_ANSI_STRING)


// Allocate buffer whose size is a power of 2

static char* mem_alloc(size_t* aSize)
{
    assert(aSize != 0);
    // minimum size is 32
    size_t size = 32;
    while (size < *aSize) {
        size <<= 1;
    }
    *aSize = 0;
    char* buf = new char[size];
    if (buf != 0)
        *aSize = size;
    return buf;
}


// Free buffer

inline void mem_free(char* buf)
{
    assert(buf != 0);
    if (buf && buf != DwString::sEmptyBuffer)
        delete [] buf;
}


inline DwStringRep* new_rep_reference(DwStringRep* rep)
{
    assert(rep != 0);
    ++rep->mRefCount;
    return rep;
}


inline void delete_rep_safely(DwStringRep* rep)
{
    assert(rep != 0);
#if defined(DW_DEBUG_VERSION) || defined(DW_DEVELOPMENT_VERSION)
    if (rep->mRefCount <= 0) {
        std::cerr << "Error: attempt to delete a DwStringRep "
            "with ref count <= 0" << std::endl;
        std::cerr << "(Possibly 'delete' was called twice for same object)"
            << std::endl;
        abort();
    }
#endif //  defined(DW_DEBUG_VERSION) || defined(DW_DEVELOPMENT_VERSION)
    --rep->mRefCount;
    if (rep->mRefCount == 0) {
        delete rep;
    }
}


//--------------------------------------------------------------------------


//DwStringRep* DwStringRep::theirPool = NULL;
//int DwStringRep::theirPoolCount = 0;


// DwStringRep takes ownership of the buffer passed as an argument

DwStringRep::DwStringRep(char* aBuf, size_t aSize)
{
    assert(aBuf != 0);
    mSize = aSize;
    mBuffer = aBuf;
    mRefCount = 1;
    mPageMod = 0;
}

DwStringRep::DwStringRep(FILE* aFile, size_t aSize)
{
    assert(aFile != 0);
    static int pagesize = -1;
    if (pagesize < 0)
	pagesize = getpagesize();
    assert(pagesize != 0);
    int tell = ftell(aFile);
    mPageMod = tell % pagesize;
    mSize = aSize;
    mRefCount = 1;

    mBuffer = (char *)mmap(0, aSize + mPageMod, PROT_READ, MAP_SHARED, fileno(aFile), tell - mPageMod) + mPageMod;
    ++mPageMod;
    if (mBuffer == MAP_FAILED) {
	mBuffer = 0;
	mSize = 0;
	mPageMod = 0;
    }
}


DwStringRep::~DwStringRep()
{
#if defined (DW_DEBUG_VERSION) || defined (DW_DEVELOPMENT_VERSION)
    if (mBuffer == 0) {
        std::cerr << "DwStringRep destructor called for bad DwStringRep object"
            << std::endl;
        std::cerr << "(Possibly 'delete' was called twice for same object)"
            << std::endl;
        abort();
    }
#endif //  defined (DW_DEBUG_VERSION) || defined (DW_DEVELOPMENT_VERSION)
    if (mPageMod) {
	--mPageMod;
	munmap(mBuffer - mPageMod, mSize + mPageMod);
    } else {
	mem_free(mBuffer);
    }
    //DEV_STMT(mBuffer = 0)
}


void DwStringRep::CheckInvariants() const
{
#if defined (DW_DEBUG_VERSION)
    assert(mBuffer != 0);
    assert(mSize > 0);
    assert(mRefCount > 0);
#endif // defined (DW_DEBUG_VERSION)
}


// Efficient memory management.  May be used at some point in the future.

#if 0
void* DwStringRep::operator new(size_t sz)
{
    void* rep;
    if (theirPoolCount > 0) {
        --theirPoolCount;
        rep = theirPool;
        theirPool = theirPool->mNext;
    }
    else {
        rep = new char[sz];
    }
    return rep;
}


void DwStringRep::operator delete(void* aRep, size_t)
{
    if (theirPoolCount < 200) {
        DwStringRep* rep = (DwStringRep*) aRep;
        ++theirPoolCount;
        rep->mNext = theirPool;
        theirPool = rep;
    }
    else {
        delete [] (char*) aRep;
    }
}
#endif


//--------------------------------------------------------------------------

const size_t DwString::kEmptyBufferSize = 4;
char DW_EXPORT DwString::sEmptyBuffer[]="    ";
DwStringRep* DwString::sEmptyRep = 0;

const size_t DwString::npos = (size_t) -1;

DwString::DwString()
{
    if (sEmptyRep == 0) {
        sEmptyBuffer[0] = 0;
        sEmptyRep = new DwStringRep(sEmptyBuffer, kEmptyBufferSize);
        assert(sEmptyRep != 0);
    }
    DBG_STMT(sEmptyRep->CheckInvariants())
    mRep = new_rep_reference(sEmptyRep);
    mStart = 0;
    mLength = 0;
}


DwString::DwString(const DwString& aStr, size_t aPos, size_t aLen)
{
    assert(aPos <= aStr.mLength);
    if (sEmptyRep == 0) {
        sEmptyBuffer[0] = 0;
        sEmptyRep = new DwStringRep(sEmptyBuffer, kEmptyBufferSize);
        assert(sEmptyRep != 0);
    }
    DBG_STMT(aStr.CheckInvariants())
    size_t pos = DW_MIN(aPos, aStr.mLength);
    size_t len = DW_MIN(aLen, aStr.mLength - pos);
    if (len > 0) {
        mRep = new_rep_reference(aStr.mRep);
        mStart = aStr.mStart + pos;
        mLength = len;
    }
    else /* if (len == 0) */ {
        mRep = new_rep_reference(sEmptyRep);
        mStart = 0;
        mLength = 0;
    }
}


DwString::DwString(const char* aBuf, size_t aLen)
{
    assert(aBuf != 0);
    assert(aLen != (size_t)-1);
    if (sEmptyRep == 0) {
        sEmptyBuffer[0] = 0;
        sEmptyRep = new DwStringRep(sEmptyBuffer, kEmptyBufferSize);
        assert(sEmptyRep != 0);
    }
    DBG_STMT(sEmptyRep->CheckInvariants())
    // Set valid values, in case an exception is thrown
    mRep = new_rep_reference(sEmptyRep);
    mStart = 0;
    mLength = 0;
    _replace(0, mLength, aBuf, aLen);
}


DwString::DwString(FILE* aFile , size_t aLen)
{
    assert(aFile != 0);
    assert(aLen != (size_t)-1);
    if (sEmptyRep == 0) {
        sEmptyBuffer[0] = 0;
        sEmptyRep = new DwStringRep(sEmptyBuffer, kEmptyBufferSize);
        assert(sEmptyRep != 0);
    }
    DBG_STMT(sEmptyRep->CheckInvariants())
    // Set valid values, in case an exception is thrown
    mRep = new DwStringRep(aFile, aLen);
    mStart = 0;
    mLength = aLen;
}


DwString::DwString(const char* aCstr)
{
    if (sEmptyRep == 0) {
        sEmptyBuffer[0] = 0;
        sEmptyRep = new DwStringRep(sEmptyBuffer, kEmptyBufferSize);
        assert(sEmptyRep != 0);
    }
    DBG_STMT(sEmptyRep->CheckInvariants())
    // Set valid values, in case an exception is thrown
    mRep = new_rep_reference(sEmptyRep);
    mStart = 0;
    mLength = 0;
    if ( aCstr ) {
        size_t len = strlen(aCstr);
        _replace(0, mLength, aCstr, len);
    }
}


DwString::DwString(size_t aLen, char aChar)
{
    assert(aLen != (size_t)-1);
    if (sEmptyRep == 0) {
        sEmptyBuffer[0] = 0;
        sEmptyRep = new DwStringRep(sEmptyBuffer, kEmptyBufferSize);
        assert(sEmptyRep != 0);
    }
    DBG_STMT(sEmptyRep->CheckInvariants())
    // Set valid values, in case an exception is thrown
    mRep = new_rep_reference(sEmptyRep);
    mStart = 0;
    mLength = 0;
    _replace(0, mLength, aLen, aChar);
}


DwString::DwString(char* aBuf, size_t aSize, size_t aStart, size_t aLen)
{
    assert(aBuf != 0);
    assert(aSize > 0);
    assert(aLen < aSize);
    assert(aStart < aSize - aLen);
    if (sEmptyRep == 0) {
        sEmptyBuffer[0] = 0;
        sEmptyRep = new DwStringRep(sEmptyBuffer, kEmptyBufferSize);
        assert(sEmptyRep != 0);
    }
    DBG_STMT(sEmptyRep->CheckInvariants())
    // Set valid values, in case an exception is thrown
    mRep = new_rep_reference(sEmptyRep);
    mStart = 0;
    mLength = 0;
    DwStringRep* rep = new DwStringRep(aBuf, aSize);
    assert(rep != 0);
    if (rep != 0) {
        mRep = rep;
        mStart = aStart;
        mLength = aLen;
    }
    else /* if (rep == 0) */ {
        delete [] aBuf;
    }
}


DwString::~DwString()
{
    assert(mRep != 0);
    delete_rep_safely(mRep);
    DEV_STMT(mRep = 0)
}


size_t DwString::max_size() const
{
    return ((size_t)-1) - 1;
}


void DwString::resize(size_t aLen, char aChar)
{
    // making string shorter?
    if (aLen < mLength) {
        mLength = aLen;
        if (mRep->mRefCount == 1) {
            mRep->mBuffer[mStart + aLen] = 0;
        }
    }
    // expanding string
    else if (aLen > mLength) {
        _replace(mLength, 0, aLen-mLength, aChar);
    }
}


void DwString::resize(size_t aLen)
{
    resize(aLen, 0);
}


void DwString::reserve(size_t aSize)
{
    if (mRep->mRefCount == 1 && aSize < mRep->mSize && mRep != sEmptyRep) {
        return;
    }
    size_t size = aSize + 1;
    char* newBuf = mem_alloc(&size);
    assert(newBuf != 0);
    if (newBuf != 0) {
        char* to = newBuf;
        const char* from = mRep->mBuffer + mStart;
        mem_copy(from, mLength, to);
        to[mLength] = 0;
        DwStringRep* rep= new DwStringRep(newBuf, size);
        assert(rep != 0);
        if (rep != 0) {
            delete_rep_safely(mRep);
            mRep = rep;
            mStart = 0;
        }
        else {
             mem_free(newBuf);
        }
    }
}


void DwString::clear()
{
    assign("");
}


DwString& DwString::append(const DwString& aStr)
{
    return append(aStr, 0, aStr.mLength);
}


DwString& DwString::append(const DwString& aStr, size_t aPos,
    size_t aLen)
{
    assert(aPos <= aStr.mLength);
    size_t pos = DW_MIN(aPos, aStr.mLength);
    size_t len = DW_MIN(aLen, aStr.mLength - pos);
    if (&aStr == this) {
        DwString temp(aStr);
        _replace(mLength, 0, &temp.mRep->mBuffer[temp.mStart+pos], len);
    }
    else {
        _replace(mLength, 0, &aStr.mRep->mBuffer[aStr.mStart+pos], len);
    }
    return *this;
}


DwString& DwString::append(const char* aBuf, size_t aLen)
{
    assert(aBuf != 0);
    if (aBuf != 0) {
        _replace(mLength, 0, aBuf, aLen);
    }
    return *this;
}


DwString& DwString::append(const char* aCstr)
{
    assert(aCstr != 0);
    size_t len = (aCstr) ? strlen(aCstr) : 0;
    _replace(mLength, 0, aCstr, len);
    return *this;
}


DwString& DwString::append(size_t aLen, char aChar)
{
    _replace(mLength, 0, aLen, aChar);
    return *this;
}


DwString& DwString::assign(const DwString& aStr)
{
    if (this != &aStr) {
        assign(aStr, 0, aStr.mLength);
    }
    return *this;
}


DwString& DwString::assign(const DwString& aStr, size_t aPos, size_t aLen)
{
    assert(aPos <= aStr.mLength);
    size_t pos = DW_MIN(aPos, aStr.mLength);
    size_t len = DW_MIN(aLen, aStr.mLength - pos);
    if (mRep == aStr.mRep) {
        mStart = aStr.mStart + pos;
        mLength = len;
    }
    else {
        delete_rep_safely(mRep);
        mRep = new_rep_reference(aStr.mRep);
        mStart = aStr.mStart + pos;
        mLength = len;
    }
    return *this;
}


DwString& DwString::assign(const char* aBuf, size_t aLen)
{
    assert(aBuf != 0);
    assert(aLen != (size_t)-1);
    _replace(0, mLength, aBuf, aLen);
    return *this;
}


DwString& DwString::assign(const char* aCstr)
{
    assert(aCstr != 0);
    size_t len = (aCstr) ? strlen(aCstr) : 0;
    _replace(0, mLength, aCstr, len);
    return *this;
}


DwString& DwString::assign(size_t aLen, char aChar)
{
    assert(aLen != (size_t)-1);
    _replace(0, mLength, aLen, aChar);
    return *this;
}


DwString& DwString::insert(size_t aPos, const DwString& aStr)
{
    return insert(aPos, aStr, 0, aStr.mLength);
}


DwString& DwString::insert(size_t aPos1, const DwString& aStr,
    size_t aPos2, size_t aLen2)
{
    assert(aPos1 <= mLength);
    assert(aPos2 <= aStr.mLength);
    size_t pos2 = DW_MIN(aPos2, aStr.mLength);
    size_t len2 = DW_MIN(aLen2, aStr.mLength - pos2);
    if (&aStr == this) {
        DwString temp(aStr);
        _replace(aPos1, 0, &temp.mRep->mBuffer[temp.mStart+pos2], len2);
    }
    else {
        _replace(aPos1, 0, &aStr.mRep->mBuffer[aStr.mStart+pos2], len2);
    }
    return *this;
}


DwString& DwString::insert(size_t aPos, const char* aBuf, size_t aLen)
{
    assert(aBuf != 0);
    _replace(aPos, 0, aBuf, aLen);
    return *this;
}


DwString& DwString::insert(size_t aPos, const char* aCstr)
{
    assert(aCstr != 0);
    size_t len = (aCstr) ? strlen(aCstr) : 0;
    _replace(aPos, 0, aCstr, len);
    return *this;
}


DwString& DwString::insert(size_t aPos, size_t aLen, char aChar)
{
    _replace(aPos, 0, aLen, aChar);
    return *this;
}


DwString& DwString::erase(size_t aPos, size_t aLen)
{
    assert(aPos <= mLength);
    size_t pos = DW_MIN(aPos, mLength);
    size_t len = DW_MIN(aLen, mLength - pos);
    _replace(pos, len, "", 0);
    return *this;
}


DwString& DwString::replace(size_t aPos1, size_t aLen1, const DwString& aStr)
{
    return replace(aPos1, aLen1, aStr, 0, aStr.mLength);
}


DwString& DwString::replace(size_t aPos1, size_t aLen1, const DwString& aStr,
    size_t aPos2, size_t aLen2)
{
    assert(aPos2 <= aStr.mLength);
    size_t pos2 = DW_MIN(aPos2, aStr.mLength);
    size_t len2 = DW_MIN(aLen2, aStr.mLength - pos2);
    if (&aStr == this) {
        DwString temp(aStr);
        _replace(aPos1, aLen1, &temp.mRep->mBuffer[temp.mStart+pos2], len2);
    }
    else {
        _replace(aPos1, aLen1, &aStr.mRep->mBuffer[aStr.mStart+pos2], len2);
    }
    return *this;
}


DwString& DwString::replace(size_t aPos1, size_t aLen1, const char* aBuf,
    size_t aLen2)
{
    _replace(aPos1, aLen1, aBuf, aLen2);
    return *this;
}


DwString& DwString::replace(size_t aPos1, size_t aLen1, const char* aCstr)
{
    size_t len2 = (aCstr) ? strlen(aCstr) : 0;
    _replace(aPos1, aLen1, aCstr, len2);
    return *this;
}


DwString& DwString::replace(size_t aPos1, size_t aLen1, size_t aLen2,
    char aChar)
{
    _replace(aPos1, aLen1, aLen2, aChar);
    return *this;
}


size_t DwString::copy(char* aBuf, size_t aLen, size_t aPos) const
{
    assert(aPos <= mLength);
    assert(aBuf != 0);
    size_t pos = DW_MIN(aPos, mLength);
    size_t len = DW_MIN(aLen, mLength - pos);
    char* to = aBuf;
    const char* from = mRep->mBuffer + mStart + pos;
    mem_copy(from, len, to);
    return len;
}


void DwString::swap(DwString& aStr)
{
    DwStringRep* rep = mRep;
    mRep = aStr.mRep;
    aStr.mRep = rep;
    size_t n = mStart;
    mStart = aStr.mStart;
    aStr.mStart = n;
    n = mLength;
    mLength = aStr.mLength;
    aStr.mLength = n;
}


size_t DwString::find(const DwString& aStr, size_t aPos) const
{
    return find(&aStr.mRep->mBuffer[aStr.mStart], aPos, aStr.mLength);
}


size_t DwString::find(const char* aBuf, size_t aPos, size_t aLen) const
{
    assert(aBuf != 0);
    if (aBuf == 0) return (size_t)-1;
    if (aLen > mLength) return (size_t)-1;
    if (aPos > mLength-aLen) return (size_t)-1;
    if (aLen == 0) return aPos;
    const char* buf = mRep->mBuffer + mStart;
    for (size_t i=aPos; i <= mLength-aLen; ++i) {
        size_t k = i;
        size_t j = 0;
        while (j < aLen && aBuf[j] == buf[k]) {
            ++j; ++k;
        }
        if (j == aLen) return i;
    }
    return (size_t)-1;
}


size_t DwString::find(const char* aCstr, size_t aPos) const
{
    assert(aCstr != 0);
    if (aCstr == 0) return (size_t)-1;
    size_t len = strlen(aCstr);
    return find(aCstr, aPos, len);
}


size_t DwString::find(char aChar, size_t aPos) const
{
    if (aPos >= mLength) return (size_t)-1;
    const char* buf = mRep->mBuffer + mStart;
    for (size_t i=aPos; i < mLength; ++i) {
        if (buf[i] == aChar) return i;
    }
    return (size_t)-1;
}


size_t DwString::rfind(const DwString& aStr, size_t aPos) const
{
    return rfind(&aStr.mRep->mBuffer[aStr.mStart], aPos, aStr.mLength);
}


size_t DwString::rfind(const char* aBuf, size_t aPos, size_t aLen) const
{
    assert(aBuf != 0);
    if (aBuf == 0) return (size_t)-1;
    if (aLen > mLength) return (size_t)-1;
    size_t pos = DW_MIN(aPos, mLength - aLen);
    if (aLen == 0) return pos;
    const char* buf = mRep->mBuffer + mStart;
    for (size_t i=0; i <= pos; ++i) {
        size_t k = pos - i;
        size_t j = 0;
        while (j < aLen && aBuf[j] == buf[k]) {
            ++j; ++k;
        }
        if (j == aLen) return pos - i;
    }
    return (size_t)-1;
}


size_t DwString::rfind(const char* aCstr, size_t aPos) const
{
    assert(aCstr != 0);
    size_t len = (aCstr) ? strlen(aCstr) : 0;
    return rfind(aCstr, aPos, len);
}


size_t DwString::rfind(char aChar, size_t aPos) const
{
    size_t pos = DW_MIN(aPos, mLength - 1);
    const char* buf = mRep->mBuffer + mStart;
    for (size_t i=0; i <= pos; ++i) {
        size_t k = pos - i;
        if (buf[k] == aChar) return k;
    }
    return (size_t)-1;
}


size_t DwString::find_first_of(const DwString& aStr, size_t aPos) const
{
    return find_first_of(&aStr.mRep->mBuffer[aStr.mStart], aPos, aStr.mLength);
}


size_t DwString::find_first_of(const char* aBuf, size_t aPos, size_t aLen) const
{
    assert(aBuf != 0);
    if (aBuf == 0) return (size_t)-1;
    if (aPos >= mLength) return (size_t)-1;
    if (aLen == 0) return aPos;
    char table[256];
    memset(table, 0, sizeof(table));
    for (size_t j=0; j < aLen; ++j) {
        table[aBuf[j]&0xff] = 1;
    }
    const char* buf = mRep->mBuffer + mStart;
    for (size_t i=aPos; i < mLength; ++i) {
        if (table[buf[i]&0xff]) return i;
    }
    return (size_t)-1;
}


size_t DwString::find_first_of(const char* aCstr, size_t aPos) const
{
    assert(aCstr != 0);
    if (aCstr == 0) return (size_t)-1;
    size_t len = strlen(aCstr);
    return find_first_of(aCstr, aPos, len);
}


size_t DwString::find_last_of(const DwString& aStr, size_t aPos) const
{
    return find_last_of(&aStr.mRep->mBuffer[aStr.mStart], aPos, aStr.mLength);
}


size_t DwString::find_last_of(const char* aBuf, size_t aPos, size_t aLen) const
{
    assert(aBuf != 0);
    if (aBuf == 0) return (size_t)-1;
    if (mLength == 0) return (size_t)-1;
    size_t pos = DW_MIN(aPos, mLength - 1);
    if (aLen == 0) return pos;
    char table[256];
    memset(table, 0, sizeof(table));
    for (size_t j=0; j < aLen; ++j) {
        table[aBuf[j]&0xff] = 1;
    }
    const char* buf = mRep->mBuffer + mStart;
    for (size_t k=0; k <= pos; ++k) {
        size_t i = pos - k;
        if (table[buf[i]&0xff]) return i;
    }
    return (size_t)-1;
}


size_t DwString::find_last_of(const char* aCstr, size_t aPos) const
{
    assert(aCstr != 0);
    if (aCstr == 0) return (size_t)-1;
    size_t len = strlen(aCstr);
    return find_last_of(aCstr, aPos, len);
}


size_t DwString::find_first_not_of(const DwString& aStr, size_t aPos) const
{
    return find_first_not_of(&aStr.mRep->mBuffer[aStr.mStart], aPos, aStr.mLength);
}


size_t DwString::find_first_not_of(const char* aBuf, size_t aPos, size_t aLen) const
{
    assert(aBuf != 0);
    if (aBuf == 0) return (size_t)-1;
    if (aPos >= mLength) return (size_t)-1;
    if (aLen == 0) return (size_t)-1;
    char table[256];
    memset(table, 1, sizeof(table));
    for (size_t j=0; j < aLen; ++j) {
        table[aBuf[j]&0xff] = 0;
    }
    const char* buf = mRep->mBuffer + mStart;
    for (size_t i=aPos; i < mLength; ++i) {
        if (table[buf[i]&0xff]) return i;
    }
    return (size_t)-1;
}


size_t DwString::find_first_not_of(const char* aCstr, size_t aPos) const
{
    assert(aCstr != 0);
    if (aCstr == 0) return (size_t)-1;
    size_t len = strlen(aCstr);
    return find_first_not_of(aCstr, aPos, len);
}


size_t DwString::find_last_not_of(const DwString& aStr, size_t aPos) const
{
    return find_last_not_of(&aStr.mRep->mBuffer[aStr.mStart], aPos, aStr.mLength);
}


size_t DwString::find_last_not_of(const char* aBuf, size_t aPos, size_t aLen) const
{
    assert(aBuf != 0);
    if (aBuf == 0) return (size_t)-1;
    if (mLength == 0) return (size_t)-1;
    size_t pos = DW_MIN(aPos, mLength - 1);
    if (aLen == 0) return (size_t)-1;
    char table[256];
    memset(table, 1, sizeof(table));
    for (size_t j=0; j < aLen; ++j) {
        table[aBuf[j]&0xff] = 0;
    }
    const char* buf = mRep->mBuffer + mStart;
    for (size_t k=0; k <= pos; ++k) {
        size_t i = pos - k;
        if (table[buf[i]&0xff]) return i;
    }
    return (size_t)-1;
}


size_t DwString::find_last_not_of(const char* aCstr, size_t aPos) const
{
    assert(aCstr != 0);
    if (aCstr == 0) return (size_t)-1;
    size_t len = strlen(aCstr);
    return find_last_not_of(aCstr, aPos, len);
}


DwString DwString::substr(size_t aPos, size_t aLen) const
{
    assert(aPos <= mLength);
    size_t pos = DW_MIN(aPos, mLength);
    size_t len = DW_MIN(aLen, mLength - pos);
    return DwString(*this, pos, len);
}


int DwString::compare(const DwString& aStr) const
{
    return compare(0, mLength, aStr, 0, aStr.mLength);
}


int DwString::compare(size_t aPos1, size_t aLen1, const DwString& aStr) const
{
    return compare(aPos1, aLen1, aStr, 0, aStr.mLength);
}


int DwString::compare(size_t aPos1, size_t aLen1, const DwString& aStr,
        size_t aPos2, size_t aLen2) const
{
    assert(aPos1 <= mLength);
    assert(aPos2 <= aStr.mLength);
    size_t pos1 = DW_MIN(aPos1, mLength);
    const char* buf1 = mRep->mBuffer + mStart + pos1;
    size_t len1 = DW_MIN(aLen1, mLength - pos1);
    size_t pos2 = DW_MIN(aPos2, aStr.mLength);
    const char* buf2 = aStr.mRep->mBuffer + aStr.mStart + pos2;
    size_t len2 = DW_MIN(aLen2, aStr.mLength - pos2);
    size_t len = DW_MIN(len1, len2);
    int r = strncmp(buf1, buf2, len);
    if (r == 0) {
        if (len1 < len2)
            r = -1;
        else if (len1 > len2) {
            r = 1;
        }
    }
    return r;
}


int DwString::compare(const char* aCstr) const
{
    assert(aCstr != 0);
    size_t len = (aCstr) ? strlen(aCstr) : 0;
    return compare(0, mLength, aCstr, len);
}


int DwString::compare(size_t aPos1, size_t aLen1, const char* aBuf,
    size_t aLen2) const
{
    assert(aBuf != 0);
    assert(aPos1 <= mLength);
    if (aBuf == 0) {
        return (aLen1 > 0) ? 1 : 0;
    }
    size_t pos1 = DW_MIN(aPos1, mLength);
    const char* buf1 = mRep->mBuffer + mStart + pos1;
    size_t len1 = DW_MIN(aLen1, mLength - pos1);
    const char* buf2 = aBuf;
    size_t len2 = aLen2;
    size_t len = DW_MIN(len1, len2);
    int r = strncmp(buf1, buf2, len);
    if (r == 0) {
        if (len1 < len2)
            r = -1;
        else if (len1 > len2) {
            r = 1;
        }
    }
    return r;
}


const char* DwString::ClassName() const
{
    return "DwString";
}


int DwString::ObjectId() const
{
    return (int) (long) this;
}


void DwString::ConvertToLowerCase()
{
    if (mRep->mRefCount > 1) {
        _copy();
    }
    char* buf = mRep->mBuffer + mStart;
    for (size_t i=0; i < mLength; ++i) {
        buf[i] = (char) dw_asciitolower(buf[i]);
    }
}


void DwString::ConvertToUpperCase()
{
    if (mRep->mRefCount > 1) {
        _copy();
    }
    char* buf = mRep->mBuffer + mStart;
    for (size_t i=0; i < mLength; ++i) {
        buf[i] = (char) dw_asciitoupper(buf[i]);
    }
}


void DwString::Trim()
{
    const char* buf = mRep->mBuffer + mStart;
    size_t i = 0;
    while (mLength > 0) {
        if (isspace(buf[i])) {
            ++mStart;
            --mLength;
            ++i;
        }
        else {
            break;
        }
    }
    buf = mRep->mBuffer + mStart;
    i = mLength - 1;
    while (mLength > 0) {
        if (isspace(buf[i])) {
            --mLength;
            --i;
        }
        else {
            break;
        }
    }
    if (mLength == 0) {
        assign("");
    }
}


void DwString::WriteTo(std::ostream& aStrm) const
{
    const char* buf = mRep->mBuffer + mStart;
    for (size_t i=0; i < mLength; ++i) {
        aStrm << buf[i];
    }
}


void DwString::TakeBuffer(char* aBuf, size_t aSize, size_t aStart, size_t aLen)
{
    assert(aBuf != 0);
    DwStringRep* rep = new DwStringRep(aBuf, aSize);
    assert(rep != 0);
    if (rep) {
        delete_rep_safely(mRep);
        mRep = rep;
        mStart = aStart;
        mLength = aLen;
    }
}


void DwString::ReleaseBuffer(char** aBuf, size_t* aSize, size_t* aStart,
    size_t* aLen)
{
    assert(aBuf != 0);
    assert(aSize != 0);
    assert(aStart != 0);
    assert(aLen != 0);
    if (mRep->mRefCount == 1) {
        *aBuf = mRep->mBuffer;
        *aSize = mRep->mSize;
    }
    else {
        size_t size = mRep->mSize;
        char* buf = new char [size];
        assert(buf != 0);
        if (buf != 0) {
            mem_copy(mRep->mBuffer, size, buf);
            *aBuf = buf;
            *aSize = size;
        }
        else {
            // If not throwing an exception, recover as best we can
            *aBuf = 0;
            *aSize = 0;
            *aStart = mStart = 0;
            *aLen = mLength = 0;
            return;
        }
    }
    *aStart = mStart;
    *aLen = mLength;
    mRep = new_rep_reference(sEmptyRep);
    mStart = 0;
    mLength = 0;
}


void DwString::CopyTo(DwString* aStr) const
{
    assert(aStr != 0);
    if (!aStr) return;
    size_t len = mLength;
    size_t size = len + 1;
    char* buf = mem_alloc(&size);
    assert(buf != 0);
    if (buf != 0) {
        mem_copy(mRep->mBuffer+mStart, len, buf);
        buf[len] = 0;
        DwStringRep* rep = new DwStringRep(buf, size);
        assert(rep != 0);
        if (rep != 0) {
            aStr->mRep = rep;
            delete_rep_safely(aStr->mRep);
            aStr->mStart = 0;
            aStr->mLength = len;
        }
    }
}


void DwString::_copy()
{
    if (mRep->mRefCount > 1) {
        size_t size = mLength + 1;
        char* newBuf = mem_alloc(&size);
        assert(newBuf != 0);
        if (newBuf != 0) {
            char* to = newBuf;
            const char* from = mRep->mBuffer + mStart;
            mem_copy(from, mLength, to);
            to[mLength] = 0;
            DwStringRep* rep = new DwStringRep(newBuf, size);
            assert(rep != 0);
            if (rep != 0) {
                delete_rep_safely(mRep);
                mRep = rep;
                mStart = 0;
            }
            else /* if (rep == 0) */ {
                mem_free(newBuf);
                mLength = 0;
            }
        }
        else /* if (newBuf == 0) */ {
            mLength = 0;
        }
    }
}


void DwString::_replace(size_t aPos1, size_t aLen1, const char* aBuf, size_t aLen2)
{
    assert(aPos1 <= mLength);
    assert(aBuf != 0);
    size_t pos1 = DW_MIN(aPos1, mLength);
    size_t len1 = DW_MIN(aLen1, mLength - pos1);
    assert(mStart + mLength - len1 < ((size_t)-1) - aLen2);
    size_t len2 = DW_MIN(aLen2, ((size_t)-1) - (mStart + mLength - len1));
    size_t i;
    char* to;
    const char* from;
    size_t newLen = (mLength - len1) + len2;
    // Is new string empty?
    if (newLen == 0 || aBuf == 0) {
        if (mRep != sEmptyRep) {
            delete_rep_safely(mRep);
            mRep = new_rep_reference(sEmptyRep);
            mStart = 0;
            mLength = 0;
        }
    }
    // Is buffer shared?  Is buffer too small?
    else if (mRep->mRefCount > 1 || newLen >= mRep->mSize) {
        size_t size = newLen + 1;
        char* newBuf = mem_alloc(&size);
        assert(newBuf != 0);
        if (newBuf != 0) {
            to = newBuf;
            memcpy(to, mRep->mBuffer + mStart, pos1);
            to += pos1;
            memcpy(to, aBuf, len2);
            to += len2;
            memcpy(to, mRep->mBuffer + mStart + pos1 + len1, mLength - pos1 - len1);
            to += mLength - pos1 - len1;
            *to = 0;
            DwStringRep* rep = new DwStringRep(newBuf, size);
            assert(rep != 0);
            if (rep != 0) {
                delete_rep_safely(mRep);
                mRep = rep;
                mStart = 0;
                mLength = newLen;
            }
        }
    }
    // Is the replacement smaller than the replaced?
    else if (len2 < len1) {
        to = mRep->mBuffer + mStart + pos1;
        from = aBuf;
        for (i=0; i < len2; ++i) *to++ = *from++;
        from = mRep->mBuffer + mStart + pos1 + len1;
        for (i=0; i < mLength - pos1 - len1; ++i) *to++ = *from++;
        *to = 0;
        mLength = newLen;
    }
    // Is there enough room at end of buffer?
    else if (mStart + newLen < mRep->mSize) {
        to = mRep->mBuffer + mStart + newLen;
        from = mRep->mBuffer + mStart + mLength - 1;
        *to-- = 0;
        for (i=0; i < mLength-pos1-len1; ++i) *to-- = *from--;
        from = aBuf + (len2 - 1);
        for (i=0; i < len2; ++i) *to-- = *from--;
        mLength = newLen;
    }
    // Is there enough room at beginning of buffer?
    else if (len2 - len1 <= mStart) {
        to = mRep->mBuffer + mStart - (len2 - len1);
        from = mRep->mBuffer + mStart;
        for (i=0; i < pos1; ++i) *to++ = *from++;
        from = aBuf;
        for (i=0; i < len2; ++i) *to++ = *from++;
        mStart -= len2 - len1;
        mLength = newLen;
    }
    // There's enough room, but we must move characters.
    else {
        to = mRep->mBuffer + newLen;
        from = mRep->mBuffer + mStart + mLength - 1;
        *to-- = 0;
        for (i=0; i < mLength-pos1-len1; ++i) *to-- = *from--;
        to = mRep->mBuffer;
        from = mRep->mBuffer + mStart;
        for (i=0; i < pos1; ++i) *to++ = *from++;
        from = aBuf;
        for (i=0; i < len2; ++i) *to++ = *from++;
        mStart = 0;
        mLength = newLen;
    }
}


void DwString::_replace(size_t aPos1, size_t aLen1, size_t aLen2, char aChar)
{
    assert(aPos1 <= mLength);
    size_t pos1 = DW_MIN(aPos1, mLength);
    size_t len1 = DW_MIN(aLen1, mLength - pos1);
    assert(mStart + mLength - len1 < ((size_t)-1) - aLen2);
    size_t len2 = DW_MIN(aLen2, ((size_t)-1) - (mStart + mLength - len1));
    size_t i;
    char* to;
    const char* from;
    size_t newLen = mLength - len1 + len2;
    // Is new string empty?
    if (newLen == 0) {
        if (mRep != sEmptyRep) {
            delete_rep_safely(mRep);
            mRep = new_rep_reference(sEmptyRep);
            mStart = 0;
            mLength = 0;
        }
    }
    // Is buffer shared?  Is buffer too small?
    else if (mRep->mRefCount > 1 || newLen >= mRep->mSize) {
        size_t size = newLen + 1;
        char* newBuf = mem_alloc(&size);
        assert(newBuf != 0);
        if (newBuf != 0) {
            to = newBuf;
            from = mRep->mBuffer + mStart;
            for (i=0; i < pos1; ++i) *to++ = *from++;
            for (i=0; i < len2; ++i) *to++ = aChar;
            from = mRep->mBuffer + mStart + pos1 + len1;
            for (i=0; i < mLength - pos1 - len1; ++i) *to++ = *from++;
            *to = 0;
            DwStringRep* rep = new DwStringRep(newBuf, size);
            assert(rep != 0);
            if (rep != 0) {
                delete_rep_safely(mRep);
                mRep = rep;
                mStart = 0;
                mLength = newLen;
            }
        }
    }
    // Is the replacement smaller than the replaced?
    else if (len2 < len1) {
        to = mRep->mBuffer + mStart + pos1;
        for (i=0; i < len2; ++i) *to++ = aChar;
        from = mRep->mBuffer + mStart + pos1 + len1;
        for (i=0; i < mLength - pos1 - len1; ++i) *to++ = *from++;
        *to = 0;
        mLength = newLen;
    }
    // Is there enough room at end of buffer?
    else if (mStart + newLen < mRep->mSize) {
        to = mRep->mBuffer + mStart + newLen;
        from = mRep->mBuffer + mStart + mLength - 1;
        *to-- = 0;
        for (i=0; i < mLength-pos1-len1; ++i) *to-- = *from--;
        for (i=0; i < len2; ++i) *to-- = aChar;
        mLength = newLen;
    }
    // Is there enough room at beginning of buffer?
    else if (len2 - len1 <= mStart) {
        to = mRep->mBuffer + mStart - (len2 - len1);
        from = mRep->mBuffer + mStart;
        for (i=0; i < pos1; ++i) *to++ = *from++;
        for (i=0; i < len2; ++i) *to++ = aChar;
        mStart -= len2 - len1;
        mLength = newLen;
    }
    // There's enough room, but we must move characters.
    else {
        to = mRep->mBuffer + newLen;
        from = mRep->mBuffer + mStart + mLength - 1;
        *to-- = 0;
        for (i=0; i < mLength-pos1-len1; ++i) *to-- = *from--;
        to = mRep->mBuffer;
        from = mRep->mBuffer + mStart;
        for (i=0; i < pos1; ++i) *to++ = *from++;
        for (i=0; i < len2; ++i) *to++ = aChar;
        mStart = 0;
        mLength = newLen;
    }
}


#if defined (DW_DEBUG_VERSION)
void DwString::PrintDebugInfo(std::ostream& aStrm) const
{
    aStrm <<
    "----------------- Debug info for DwString class ----------------\n";
    aStrm << "Id:               " << ClassName() << ", " << ObjectId() << "\n";
    aStrm << "Rep:              " << (void*) mRep << "\n";
    aStrm << "Buffer:           " << (void*) mRep->mBuffer << "\n";
    aStrm << "Buffer size:      " << mRep->mSize << "\n";
    aStrm << "Start:            " << mStart << "\n";
    aStrm << "Length:           " << mLength << "\n";
    aStrm << "Contents:         ";
    for (size_t i=0; i < mLength && i < 64; ++i) {
        aStrm << mRep->mBuffer[mStart+i];
    }
    aStrm << std::endl;
}
#else
void DwString::PrintDebugInfo(std::ostream& ) const {}
#endif // defined (DW_DEBUG_VERSION)


void DwString::CheckInvariants() const
{
#if defined (DW_DEBUG_VERSION)
    assert(mRep != 0);
    mRep->CheckInvariants();
#endif // defined (DW_DEBUG_VERSION)
}


DwString operator + (const DwString& aStr1, const DwString& aStr2)
{
    DwString str(aStr1);
    str.append(aStr2);
    return str;
}


DwString operator + (const char* aCstr, const DwString& aStr2)
{
    DwString str(aCstr);
    str.append(aStr2);
    return str;
}


DwString operator + (char aChar, const DwString& aStr2)
{
    DwString str(1, aChar);
    str.append(aStr2);
    return str;
}


DwString operator + (const DwString& aStr1, const char* aCstr)
{
    DwString str(aStr1);
    str.append(aCstr);
    return str;
}


DwString operator + (const DwString& aStr1, char aChar)
{
    DwString str(aStr1);
    str.append(1, aChar);
    return str;
}


DwBool operator == (const DwString& aStr1, const DwString& aStr2)
{
    const char* s1 = aStr1.data();
    size_t len1 = aStr1.length();
    const char* s2 = aStr2.data();
    size_t len2 = aStr2.length();
    int r = dw_strcmp(s1, len1, s2, len2);
    r = (r == 0) ? 1 : 0;
    return r;
}


DwBool operator == (const DwString& aStr1, const char* aCstr)
{
    assert(aCstr != 0);
    const char* s1 = aStr1.data();
    size_t len1 = aStr1.length();
    const char* s2 = aCstr;
    size_t len2 = (aCstr) ? strlen(aCstr) : 0;
    int r = dw_strcmp(s1, len1, s2, len2);
    r = (r == 0) ? 1 : 0;
    return r;
}


DwBool operator == (const char* aCstr, const DwString& aStr2)
{
    assert(aCstr != 0);
    const char* s1 = aCstr;
    size_t len1 = (aCstr) ? strlen(aCstr) : 0;
    const char* s2 = aStr2.data();
    size_t len2 = aStr2.length();
    int r = dw_strcmp(s1, len1, s2, len2);
    r = (r == 0) ? 1 : 0;
    return r;
}


DwBool operator != (const DwString& aStr1, const DwString& aStr2)
{
    const char* s1 = aStr1.data();
    size_t len1 = aStr1.length();
    const char* s2 = aStr2.data();
    size_t len2 = aStr2.length();
    int r = dw_strcmp(s1, len1, s2, len2);
    r = (r == 0) ? 0 : 1;
    return r;
}


DwBool operator != (const DwString& aStr1, const char* aCstr)
{
    assert(aCstr != 0);
    const char* s1 = aStr1.data();
    size_t len1 = aStr1.length();
    const char* s2 = aCstr;
    size_t len2 = (aCstr) ? strlen(aCstr) : 0;
    int r = dw_strcmp(s1, len1, s2, len2);
    r = (r == 0) ? 0 : 1;
    return r;
}


DwBool operator != (const char* aCstr, const DwString& aStr2)
{
    assert(aCstr != 0);
    const char* s1 = aCstr;
    size_t len1 = (aCstr) ? strlen(aCstr) : 0;
    const char* s2 = aStr2.data();
    size_t len2 = aStr2.length();
    int r = dw_strcmp(s1, len1, s2, len2);
    r = (r == 0) ? 0 : 1;
    return r;
}


DwBool operator < (const DwString& aStr1, const DwString& aStr2)
{
    const char* s1 = aStr1.data();
    size_t len1 = aStr1.length();
    const char* s2 = aStr2.data();
    size_t len2 = aStr2.length();
    int r = dw_strcmp(s1, len1, s2, len2);
    r = (r < 0) ? 1 : 0;
    return r;
}


DwBool operator < (const DwString& aStr1, const char* aCstr)
{
    assert(aCstr != 0);
    const char* s1 = aStr1.data();
    size_t len1 = aStr1.length();
    const char* s2 = aCstr;
    size_t len2 = (aCstr) ? strlen(aCstr) : 0;
    int r = dw_strcmp(s1, len1, s2, len2);
    r = (r < 0) ? 1 : 0;
    return r;
}


DwBool operator < (const char* aCstr, const DwString& aStr2)
{
    assert(aCstr != 0);
    const char* s1 = aCstr;
    size_t len1 = (aCstr) ? strlen(aCstr) : 0;
    const char* s2 = aStr2.data();
    size_t len2 = aStr2.length();
    int r = dw_strcmp(s1, len1, s2, len2);
    r = (r < 0) ? 1 : 0;
    return r;
}


DwBool operator > (const DwString& aStr1, const DwString& aStr2)
{
    const char* s1 = aStr1.data();
    size_t len1 = aStr1.length();
    const char* s2 = aStr2.data();
    size_t len2 = aStr2.length();
    int r = dw_strcmp(s1, len1, s2, len2);
    r = (r > 0) ? 1 : 0;
    return r;
}


DwBool operator > (const DwString& aStr1, const char* aCstr)
{
    assert(aCstr != 0);
    const char* s1 = aStr1.data();
    size_t len1 = aStr1.length();
    const char* s2 = aCstr;
    size_t len2 = (aCstr) ? strlen(aCstr) : 0;
    int r = dw_strcmp(s1, len1, s2, len2);
    r = (r > 0) ? 1 : 0;
    return r;
}


DwBool operator > (const char* aCstr, const DwString& aStr2)
{
    assert(aCstr != 0);
    const char* s1 = aCstr;
    size_t len1 = (aCstr) ? strlen(aCstr) : 0;
    const char* s2 = aStr2.data();
    size_t len2 = aStr2.length();
    int r = dw_strcmp(s1, len1, s2, len2);
    r = (r > 0) ? 1 : 0;
    return r;
}


DwBool operator <= (const DwString& aStr1, const DwString& aStr2)
{
    const char* s1 = aStr1.data();
    size_t len1 = aStr1.length();
    const char* s2 = aStr2.data();
    size_t len2 = aStr2.length();
    int r = dw_strcmp(s1, len1, s2, len2);
    r = (r <= 0) ? 1 : 0;
    return r;
}


DwBool operator <= (const DwString& aStr1, const char* aCstr)
{
    assert(aCstr != 0);
    const char* s1 = aStr1.data();
    size_t len1 = aStr1.length();
    const char* s2 = aCstr;
    size_t len2 = (aCstr) ? strlen(aCstr) : 0;
    int r = dw_strcmp(s1, len1, s2, len2);
    r = (r <= 0) ? 1 : 0;
    return r;
}


DwBool operator <= (const char* aCstr, const DwString& aStr2)
{
    assert(aCstr != 0);
    const char* s1 = aCstr;
    size_t len1 = (aCstr) ? strlen(aCstr) : 0;
    const char* s2 = aStr2.data();
    size_t len2 = aStr2.length();
    int r = dw_strcmp(s1, len1, s2, len2);
    r = (r <= 0) ? 1 : 0;
    return r;
}


DwBool operator >= (const DwString& aStr1, const DwString& aStr2)
{
    const char* s1 = aStr1.data();
    size_t len1 = aStr1.length();
    const char* s2 = aStr2.data();
    size_t len2 = aStr2.length();
    int r = dw_strcmp(s1, len1, s2, len2);
    r = (r >= 0) ? 1 : 0;
    return r;
}


DwBool operator >= (const DwString& aStr1, const char* aCstr)
{
    assert(aCstr != 0);
    const char* s1 = aStr1.data();
    size_t len1 = aStr1.length();
    const char* s2 = aCstr;
    size_t len2 = (aCstr) ? strlen(aCstr) : 0;
    int r = dw_strcmp(s1, len1, s2, len2);
    r = (r >= 0) ? 1 : 0;
    return r;
}


DwBool operator >= (const char* aCstr, const DwString& aStr2)
{
    assert(aCstr != 0);
    const char* s1 = aCstr;
    size_t len1 = (aCstr) ? strlen(aCstr) : 0;
    const char* s2 = aStr2.data();
    size_t len2 = aStr2.length();
    int r = dw_strcmp(s1, len1, s2, len2);
    r = (r >= 0) ? 1 : 0;
    return r;
}


std::ostream& operator << (std::ostream& aOstrm, const DwString& aStr)
{
    const char* buf = aStr.data();
    for (size_t i=0; i < aStr.length(); ++i) {
        aOstrm << buf[i];
    }
    return aOstrm;
}


std::istream& getline(std::istream& aIstrm, DwString& aStr, char aDelim)
{
    aStr.clear();
    char ch;
    while (aIstrm.get(ch)) {
        if (ch == aDelim) break;
        if (aStr.length() < aStr.max_size()) {
            aStr.append(1, ch);
        }
    }
    return aIstrm;
}


std::istream& getline(std::istream& aIstrm, DwString& aStr)
{
    return getline(aIstrm, aStr, '\n');
}

#endif // !defined(DW_USE_ANSI_STRING)


int DwStrcasecmp(const DwString& aStr1, const DwString& aStr2)
{
    const char* s1 = aStr1.data();
    size_t len1 = aStr1.length();
    const char* s2 = aStr2.data();
    size_t len2 = aStr2.length();
    return dw_strasciicasecmp(s1, len1, s2, len2);
}


int DwStrcasecmp(const DwString& aStr, const char* aCstr)
{
    assert(aCstr != 0);
    const char* s1 = aStr.data();
    size_t len1 = aStr.length();
    const char* s2 = aCstr;
    size_t len2 = (aCstr) ? strlen(aCstr) : 0;
    return dw_strasciicasecmp(s1, len1, s2, len2);
}


int DwStrcasecmp(const char* aCstr, const DwString& aStr)
{
    assert(aCstr != 0);
    const char* s1 = aCstr;
    size_t len1 =  (aCstr) ? strlen(aCstr) : 0;
    const char* s2 = aStr.data();
    size_t len2 = aStr.length();
    return dw_strasciicasecmp(s1, len1, s2, len2);
}


int DwStrncasecmp(const DwString& aStr1, const DwString& aStr2, size_t n)
{
    const char* s1 = aStr1.data();
    size_t len1 = aStr1.length();
    len1 = DW_MIN(len1, n);
    const char* s2 = aStr2.data();
    size_t len2 = aStr2.length();
    len2 = DW_MIN(len2, n);
    return dw_strasciicasecmp(s1, len1, s2, len2);
}


int DwStrncasecmp(const DwString& aStr, const char* aCstr, size_t n)
{
    assert(aCstr != 0);
    const char* s1 = aStr.data();
    size_t len1 = aStr.length();
    len1 = DW_MIN(len1, n);
    const char* s2 = aCstr;
    size_t len2 = (aCstr) ? strlen(aCstr) : 0;
    len2 = DW_MIN(len2, n);
    return dw_strasciicasecmp(s1, len1, s2, len2);
}


int DwStrncasecmp(const char* aCstr, const DwString& aStr, size_t n)
{
    assert(aCstr != 0);
    const char* s1 = aCstr;
    size_t len1 = (aCstr) ? strlen(aCstr) : 0;
    len1 = DW_MIN(len1, n);
    const char* s2 = aStr.data();
    size_t len2 = aStr.length();
    len2 = DW_MIN(len2, n);
    return dw_strasciicasecmp(s1, len1, s2, len2);
}


int DwStrcmp(const DwString& aStr1, const DwString& aStr2)
{
    const char* s1 = aStr1.data();
    size_t len1 = aStr1.length();
    const char* s2 = aStr2.data();
    size_t len2 = aStr2.length();
    return dw_strcmp(s1, len1, s2, len2);
}


int DwStrcmp(const DwString& aStr, const char* aCstr)
{
    assert(aCstr != 0);
    const char* s1 = aStr.data();
    size_t len1 = aStr.length();
    const char* s2 = aCstr;
    size_t len2 = (aCstr) ? strlen(aCstr) : 0;
    return dw_strcmp(s1, len1, s2, len2);
}


int DwStrcmp(const char* aCstr, const DwString& aStr)
{
    assert(aCstr != 0);
    const char* s1 = aCstr;
    size_t len1 = (aCstr) ? strlen(aCstr) : 0;
    const char* s2 = aStr.data();
    size_t len2 = aStr.length();
    return dw_strcmp(s1, len1, s2, len2);
}


int DwStrncmp(const DwString& aStr1, const DwString& aStr2, size_t n)
{
    const char* s1 = aStr1.data();
    size_t len1 = aStr1.length();
    len1 = DW_MIN(len1, n);
    const char* s2 = aStr2.data();
    size_t len2 = aStr2.length();
    len2 = DW_MIN(len2, n);
    return dw_strcmp(s1, len1, s2, len2);
}


int DwStrncmp(const DwString& aStr, const char* aCstr, size_t n)
{
    assert(aCstr != 0);
    const char* s1 = aStr.data();
    size_t len1 = aStr.length();
    len1 = DW_MIN(len1, n);
    const char* s2 = aCstr;
    size_t len2 = (aCstr) ? strlen(aCstr) : 0;
    len2 = DW_MIN(len2, n);
    return dw_strcmp(s1, len1, s2, len2);
}


int DwStrncmp(const char* aCstr, const DwString& aStr, size_t n)
{
    assert(aCstr != 0);
    const char* s1 = aCstr;
    size_t len1 = (aCstr) ? strlen(aCstr) : 0;
    len1 = DW_MIN(len1, n);
    const char* s2 = aStr.data();
    size_t len2 = aStr.length();
    len2 = DW_MIN(len2, n);
    return dw_strcmp(s1, len1, s2, len2);
}


void DwStrcpy(DwString& aStrDest, const DwString& aStrSrc)
{
    aStrDest.assign(aStrSrc);
}


void DwStrcpy(DwString& aStrDest, const char* aCstrSrc)
{
    aStrDest.assign(aCstrSrc);
}


void DwStrcpy(char* aCstrDest, const DwString& aStrSrc)
{
    assert(aCstrDest != 0);
    const char* buf = aStrSrc.data();
    size_t len = aStrSrc.length();
    mem_copy(buf, len, aCstrDest);
    aCstrDest[len] = 0;
}


void DwStrncpy(DwString& aStrDest, const DwString& aStrSrc, size_t n)
{
    aStrDest.assign(aStrSrc, 0, n);
}


void DwStrncpy(DwString& aStrDest, const char* aCstrSrc, size_t n)
{
    aStrDest.assign(aCstrSrc, 0, n);
}


void DwStrncpy(char* aCstrDest, const DwString& aStrSrc, size_t n)
{
    assert(aCstrDest != 0);
    const char* buf = aStrSrc.data();
    size_t len = aStrSrc.length();
    len = DW_MIN(len, n);
    mem_copy(buf, len, aCstrDest);
    for (size_t i=len; i < n; ++i) {
        aCstrDest[i] = 0;
    }
}


char* DwStrdup(const DwString& aStr)
{
    size_t len = aStr.length();
    char* buf = new char[len+1];
    assert(buf != 0);
    if (buf != 0) {
        DwStrncpy(buf, aStr, len);
        buf[len] = 0;
    }
    return buf;
}