#include "stdafx.h"
#pragma hdrstop

#include "DZRaw.h"

#include <assert.h>
#include "dz_errs.h"

#undef _DZ_FILE_
#define _DZ_FILE_ DZ_DZRAW_CPP

/* DZRaw.cpp * Copyright (C)  2009 Russell Peters
* Permission is granted to any individual or institution to use, copy, or
* redistribute this software so long as all of the original files are included,
* that it is not sold for profit, and that this copyright notice is retained.
** distributed under LGPL license
** see license.txt for details

************************************************************************
 Copyright (C) 2009, 2010  by Russell J. Peters, Roger Aelbrecht

   This file is part of TZipMaster Version 1.9.

    TZipMaster is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    TZipMaster 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 Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with TZipMaster.  If not, see <http://www.gnu.org/licenses/>.

    contact: problems@delphizip.org (include ZipMaster in the subject).
    updates: http://www.delphizip.org
    DelphiZip maillist subscribe at http://www.freelists.org/list/delphizip 
************************************************************************/

dzraw_imp* __fastcall DZRawData::NewImp(unsigned siz)
{
    unsigned rawsize = sizeof(dzraw_imp) + siz - (8 * sizeof(char));
    if (rawsize & 63)
        rawsize = (rawsize | 63) + 1;
    if (rawsize > 0xFFF0)
        rawsize = 0xFFF0;

    dzraw_imp* _imp = (dzraw_imp*) (new char[rawsize]);
    if (! _imp)
        throw DZException(DZ_ERM_MEMORY);

    _imp->refs      = 1;
	_imp->capacity  = (WORD)((rawsize - sizeof(dzraw_imp)) + (8 * sizeof(char)));
	_imp->len       = 0;
    return _imp;
}

dzraw_imp* DZRawData::NewImp(const unsigned char* src, int Len, int Space)
{
    if (!src)
        Len = 0;
    if (Space >= 0 && Len > Space)
        Len = Space;

    int siz = (Space >= 0) ? Space : Len;

    if (!siz)
        return NULL;    // empty

    dzraw_imp* nimp = NewImp(siz); // make new
    if (src && Len)
    {
        memcpy(nimp->data, src, Len);
        nimp->len = (WORD)Len;
    }
    return nimp;
}

void __fastcall DZRawData::Release(void)
{
    if (imp)
    {
        if (!DecRefs())
        {
            void *_imp = imp;
            imp = NULL;
//            free(_imp);
            delete[] _imp;
        }
        imp = NULL;
    }
}

int __fastcall DZRawData::IncRefs(void)
{
    if (imp)
        return InterlockedIncrement(&(imp->refs));
    return 0;
}

int __fastcall DZRawData::DecRefs(void)
{
    if (imp && imp->refs)
        return InterlockedDecrement(&(imp->refs));
    return -1;
}

void __fastcall DZRawData::Append(const unsigned char* src, int Len)
{
    if (!src)
        Len = 0;

    // check something to append
    if (Len)
    {
        if (!imp)
            imp = NewImp(src, Len);
        else
        {
            unsigned nlen = imp->len + Len;
            if (nlen > 0xFFF0)
                return;

            // do we need a new one
            if (imp->refs > 1 || (WORD)nlen > imp->capacity)
            {
                // need new imp - make copy with enough space
                dzraw_imp* nimp = NewImp(imp->data, imp->len, nlen);
                Release();      // out with the old
                imp = nimp;     // in with the new
            }
            // append data
            unsigned char *bf = &imp->data[imp->len];
            memcpy(bf, src, Len);
            imp->len = (WORD)nlen;
        }
    }
}

void __fastcall DZRawData::Assign(const unsigned char* src, int Len)
{
    Release();
    imp = NewImp(src, Len);
}

unsigned char * __fastcall DZRawData::GetBuffer(unsigned size)
{
    Release();
    imp = NewImp(size);
	imp->len = (WORD)size;
    return imp->data;
}

void __fastcall DZRawData::SetLength(unsigned Len)
{
    if (!Len)
        Release();
	else
    {
        dzraw_imp* nimp;
        // do we need a new one
        if (!imp || imp->refs > 1 || (WORD)Len > imp->capacity)
        {
            // need new imp - make copy with enough space
            if (!imp)
                nimp = NewImp(Len);
            else
                nimp = NewImp(imp->data, imp->len, Len);
            Release();      // out with the old
            imp = nimp;     // in with the new
        }
        imp->len = (WORD)Len;
    }
}

__fastcall DZRawData::DZRawData(const DZRawData& other)
{
    imp = NULL;
    if (!other.IsEmpty())
    {
        imp = other.imp;
        IncRefs();
    }
}

__fastcall DZRawData::DZRawData(unsigned size)
{
    imp = NULL;
    if (size)
        imp = NewImp(size);
}

__fastcall DZRawData::DZRawData(const unsigned char* str, unsigned len)
{
    imp = NULL;
    if (str && len)
    {
        imp = NewImp(str, len);
    }
}

unsigned __fastcall DZRawData::Capacity(void) const
{
    if (imp)
        return imp->capacity;
    return NULL;
}

unsigned __fastcall DZRawData::Length(void) const
{
    if (imp)
        return imp->len;
    return 0;
}

const unsigned char* DZRawData::begin(void) const
{
    if (imp)
        return imp->data;
    return NULL;
}

const unsigned char* DZRawData::end(void) const
{
    if (imp)
        return imp->data + imp->len;
    return NULL;
}

const unsigned char* DZRawData::Find(WORD tag) const
{
    if (Length() < sizeof(XWord))
        return NULL;
    const unsigned char *p = begin();
    const unsigned char *q;
    const unsigned char *e = end();
    XWord tg, sz;
    while (p + 3 < e)
    {
        q = p;
        tg.b[0] = *p++;
        tg.b[1] = *p++;
        if (tg.w == tag)
            return q;   // found
        sz.b[0] = *p++;
        sz.b[1] = *p++;
        p += sz.w;
    }
    return NULL;
}

DZRawData& __fastcall DZRawData::operator =(const DZRawData& other)
{
    if (this != &other)
    {
        Release();
        if (!other.IsEmpty())
        {
            imp = other.imp;
            IncRefs();
        }
    }
    return *this;
}

DZRawData __fastcall DZRawData::operator +(const DZRawData& other)
{
    DZRawData res(*this);
    res.Append(other.begin(), other.Length());
    return res;
}

DZRawData& __fastcall DZRawData::operator +=(const DZRawData& other)
{
    Append(other.begin(), other.Length());
    return *this;
}

DZRawData& __fastcall DZRawData::operator +=(unsigned char ch)
{
    Append(&ch, 1);
    return *this;
}

DZRawData& __fastcall DZRawData::operator +=(WORD w)
{
    Append((const unsigned char*)&w, sizeof(WORD));
    return *this;
}

//unsigned char __fastcall DZRawData::operator [](unsigned idx) const
//{
//    if (!imp || idx >= Length())
//        return 0;
//    return *(imp + idx);
//}

WORD  __fastcall DZRawData::operator [](unsigned idx) const
{
    unsigned widx = idx * sizeof(WORD);
    if (!imp || widx >= Length() - (sizeof(WORD)-1))
        return 0;
    return ((WORD*)idx)[idx];
}

DZRawData __fastcall DZRawData::operator -(WORD tag)
{
    DZRawData res(Capacity());
    if (imp)
    {
        const unsigned char *p = begin();
        const unsigned char *e = end();
        XWord tg, sz;
        while (p + 3 < e)
        {
            tg.b[0] = *p++;
            tg.b[1] = *p++;
            sz.b[0] = *p++;
            sz.b[1] = *p++;

            if (tg.w != tag)
            {
                res += tg.w;
                res += sz.w;

                while (p < e && sz.w-- > 0)
                    res += *p++;
            }
            else
                p += sz.w;
        }

        while (p < e)
            res += *p++;
    }
    return res;
}


DZRawData& __fastcall DZRawData::operator -=(WORD tag)
{
    if (Find(tag))
    {
        DZRawData tmp = (*this) - tag;
        (*this) = tmp;
    }
    return *this;
}

