#include "stdafx.h"
#include "ZString.h"
#include "ZTrace.h"

//
//  Keep a list of 256 byte buffers to recycle.
//

enum { 
    cbQuanta    = 256,
    cBOB        = 0xA5,
    cEOB        = 0xA6
};

static char* g_pListOfFreeBuffers;

//
//
//

inline static void CheckBuffer(const char* pBuffer,int cbBuffer = cbQuanta)
{
    ASSERT(pBuffer);
    ASSERT(cBOB == (255&*(pBuffer-1)));
    ASSERT(cEOB == (255&*(pBuffer+cbBuffer)));
}

//
//  
//

void ZString::recycle(char* p,int cb) 
{
    CheckBuffer(p,cb);
    if (cbQuanta == cb) {
        *(char**)p = g_pListOfFreeBuffers;
        g_pListOfFreeBuffers = p;
    } else {
        delete (p-1);
    }
}

//
//  
//

ZString::~ZString() 
{
    if (m_sBuffer) {
        char* p = m_sBuffer;
        m_sBuffer = 0;
        recycle(p,m_cbBufferMax);
    }
}

//
//  Upsize the string buffer to at least the given size.
//

void ZString::resize(int cbNew)
{
    char* sOld      = m_sBuffer;
    int cbOld       = m_cbBufferMax;

    // upsize to 256 byte quanta
    cbNew = (cbNew+255)&~255;

    char* pNew = g_pListOfFreeBuffers;
    if ((cbQuanta == cbNew) && pNew) {
        g_pListOfFreeBuffers = *(char**)pNew;
    } else {
        pNew = new char[cbNew+2];
        ++pNew;
        *(pNew-1)           = (char)cBOB;
        *(pNew+cbNew)       = (char)cEOB;
    }
    memset(pNew,0,cbNew);
    CheckBuffer(pNew,cbNew);
    
    m_cbBufferMax   = cbNew;
    m_sBuffer       = pNew;

    if (sOld) {
        ASSERT(cbOld <= cbNew);
        memcpy(m_sBuffer,sOld,cbOld);

        recycle(sOld,cbOld);
    }
}

//
//  Remove leading blanks from a string.
//

void ZString::trimLeft() 
{
    char* s2 = m_sBuffer;
    while (isspace(*s2)) ++s2;
    if (m_sBuffer < s2) {
        ::memmove(m_sBuffer,s2,::strlen(s2)+1);
    }
}

//
//  Remove trailing blanks from a string.
//

void ZString::trimRight() 
{
    char* s1 = m_sBuffer;
    char* s2 = m_sBuffer + strlen(m_sBuffer);
    while (s1 < s2) {
        --s2;
        if (!isspace(*s2)) break;
        *s2 = 0;
    }
}

//
//  Remove leading and trailing blanks from a string.
//

void ZString::trim() 
{
    trimRight();
    trimLeft();
}

//
//  To work with BSTR in and out parameters.
//

void ZString::cloneFrom(BSTR bs) 
{
    _bstr_t s(bs);
    strcpy(s);
}

void ZString::cloneTo(BSTR* pbs) 
{
    // Don't use _bstr_t here as the Microsoft code is a bit buggy.

    const char* sBuffer = getBuffer();
    int cbBuffer = strlen(sBuffer) + 1;

    // Determine the length required to hold the "wide" string.
    int nWide = ::MultiByteToWideChar(
        CP_ACP,     // code page
        0,          // flags
        sBuffer,    // multi-byte string
        cbBuffer,   // length of multi-byte string
        0,          // no wide string to convert into (yet)
        0           // length of wide string not known (yet)
        );

    // Allocate the properly sized "wide" string.
    BSTR bs = ::SysAllocStringLen(0,nWide);
    ASSERT(bs);

    // Copy and convert the multi-byte string into a wide string.
    ::MultiByteToWideChar(
        CP_ACP,     // code page
        0,          // flags
        sBuffer,    // multi-byte string
        cbBuffer,   // length of multi-byte string
        bs,         // wide string to convert into
        nWide       // length of wide string
        );

    *pbs = bs;
}
