/***************************************************************************
                          SIMPLY POWERFUL TOOLKIT (SPTK)
                          CBuffer.cpp  -  description
                             -------------------
    begin                : January 3, 2003
    copyright            : (C) 2003-2006 by Alexey S.Parshin
    email                : alexeyp@gmail.com
 ***************************************************************************/

/***************************************************************************
   This library is free software; you can redistribute it and/or modify it 
   under the terms of the GNU Library General Public License as published by
   the Free Software Foundation; either version 2 of the License, or (at 
   your option) any later version.

   This library 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 Library
   General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; if not, write to the Free Software Foundation, 
   Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.

   Please report all bugs and problems to "alexeyp@gmail.com"
 ***************************************************************************/

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sptk3/CBuffer.h>
#include <sptk3/CException.h>

using namespace std;
using namespace sptk;

CBuffer::CBuffer(unsigned sz) {
   m_buffer = (char *)calloc(1,sz);
   if (m_buffer)
      m_size = sz;
   else  m_size = 0;
   m_bytes = 0;
}

CBuffer::CBuffer(const void *data,unsigned sz) {
   m_buffer = (char *)malloc(sz);
   if (m_buffer) {
      memcpy(m_buffer,data,sz);
      m_size = sz;
      m_bytes = sz;
   } else {
      m_size = m_bytes = 0;
   }
}

CBuffer::CBuffer(const char *str) {
   unsigned sz = strlen(str) + 1;
   m_buffer = (char *)malloc(sz);
   if (m_buffer) {
      memcpy(m_buffer,str,sz);
      m_size = sz;
      m_bytes = sz-1;
   } else {
      m_size = 0;
      m_bytes = 0;
   }
}

CBuffer::CBuffer(const string& str) {
   unsigned sz = str.length() + 1;
   m_buffer = (char *)malloc(sz);
   if (m_buffer) {
      if (sz > 1)
         memcpy(m_buffer,str.c_str(),sz);
      else m_buffer[0] = 0;
      m_size = sz;
      m_bytes = sz-1;
   } else {
      m_size = 0;
      m_bytes = 0;
   }
}

CBuffer::CBuffer(const CBuffer& buffer) {
   unsigned sz = buffer.bytes() + 1;
   m_buffer = (char *)malloc(sz);
   if (m_buffer) {
      memcpy(m_buffer,buffer.data(),sz);
      m_size = sz;
      m_bytes = sz-1;
   } else {
      m_size = 0;
      m_bytes = 0;
   }
}

CBuffer::~CBuffer() {
   if (m_buffer) {
      free(m_buffer);
      m_buffer = NULL;
   }
}

bool CBuffer::checkSize(unsigned sz) {
   if (sz > m_size) {
      //unsigned newSize = (sz / 16 + 1) * 16;
      unsigned newSize = (sz * 8 / 64 + 1) * 8;
      char *p = (char *)realloc(m_buffer,newSize + 1);
      if (!p)
         throw CException("Can't reallocate a buffer");
      m_buffer = p;
      m_size = newSize;
   }
   return true;
}

void CBuffer::set(const char *data,unsigned sz) {
   if ( checkSize(sz) ) {
      if (data)
         memcpy(m_buffer,data,sz);
      m_bytes = sz;
   }
}

bool CBuffer::append(char ch) {
   if ( checkSize(m_bytes + 1) ) {
      m_buffer[m_bytes] = ch;
      m_bytes++;
      return true;
   }
   return false;
}

bool CBuffer::append(const char *data,unsigned sz) {
   if ( checkSize(m_bytes + sz + 1) ) {
      if (data)
         memcpy(m_buffer+m_bytes,data,sz);
      m_bytes += sz;
      m_buffer[m_bytes] = 0;
      return true;
   }
   return false;
}

void CBuffer::fill(char c) {
   memset(m_buffer,c,m_size);
}

void CBuffer::reset(unsigned sz) {
   if (sz) {
      char *p = (char *)realloc(m_buffer,sz + 1);
      if (!p)
         throw CException("Can't reallocate a buffer");
      m_buffer = p;
      m_size = sz;
   }
   m_bytes = 0;
}

void CBuffer::loadFromFile(string fileName) {
   FILE *f = fopen(fileName.c_str(),"rb");
   if (!f)
      throw CException("Can't open file " + fileName + " for reading");
   fseek(f,0,SEEK_END);
   int size = ftell(f);
   fseek(f,0,SEEK_SET);
   reset(size+1);
   m_buffer[size] = 0;
   m_bytes = fread(m_buffer,1,size,f);
   fclose(f);
}

void CBuffer::saveToFile(string fileName) const {
   FILE *f = fopen(fileName.c_str(),"wb");
   if (!f)
      throw CException("Can't open file " + fileName + " for writing");
   fwrite(m_buffer,1,bytes(),f);
   fclose(f);
}

CBuffer& CBuffer::operator = (const CBuffer& b) {
   m_bytes = 0;
   append(b.data(),b.m_bytes); 
   return *this;
}

CBuffer& CBuffer::operator = (const std::string& str) {
   m_bytes = 0;
   append(str.c_str(),str.length()); 
   return *this;
}

CBuffer& CBuffer::operator = (const char *str) {
   m_bytes = 0;
   append(str,strlen(str)); 
   return *this;
}
