//  This may look like C code, but it is really -*- C++ -*-

//  ------------------------------------------------------------------
//  The Goldware Library
//  Copyright (C) 1990-1999 Odinn Sorensen
//  Copyright (C) 1999-2000 Alexander S. Aganichev
//  ------------------------------------------------------------------
//  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 program; if not, write to the Free
//  Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
//  MA 02111-1307, USA
//  ------------------------------------------------------------------
//  $Id$
//  ------------------------------------------------------------------
//  String bag class.
//  ------------------------------------------------------------------

#include <gmemdbg.h>
#include <gstrall.h>
#include <gstrbags.h>


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

const int BLOCKSIZE = 4096;


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

GStrBag::GStrBag() {

  items = 0;
  bagsize = 0;
  bag = NULL;
  blocksize = BLOCKSIZE;
  currno = 0;
}


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

GStrBag::~GStrBag() {

  Reset();
}


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

void GStrBag::Reset() {

  if(bag != NULL)
    throw_release(bag);
}


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

int GStrBag::Add(const char* string) {

  return Add(string, strlen(string)+1);
}


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

int GStrBag::Add(const void* data, int length) {

  if(items == 0)
    bag = (char*)throw_malloc(blocksize);
  int currsize = bagsize + (items*sizeof(int));
  int currsizeblks = (currsize/blocksize) + 1;
  int newsizeblks = ((currsize+length+sizeof(int))/blocksize) + 1;
  if(newsizeblks != currsizeblks)
    bag = (char*)throw_realloc(bag, newsizeblks*blocksize);
  memmove(bag+bagsize+length, bag+bagsize, items*sizeof(int));
  memcpy(bag+bagsize, data, length);
  ((int*)(bag+bagsize+length))[items] = bagsize;
  bagsize += length;
  return items++;
}


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

void GStrBag::Change(int index, const char* string) {

  Change(index, string, strlen(string)+1);
}


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

void GStrBag::Change(int index, const void* data, int length) {

  int oldpos = Pos(index);
  int oldlen = bag ? strlen(bag+oldpos)+1 : 0;
  int lendiff = length - oldlen;
  int oldsize = bagsize+(items*sizeof(int));
  int movesize = oldsize - oldpos - oldlen;
  int currsizeblks = (oldsize/blocksize) + 1;
  int newsizeblks = ((oldsize+lendiff)/blocksize) + 1;
  if(lendiff > 0) {
    if(newsizeblks != currsizeblks)
      bag = (char*)throw_realloc(bag, oldsize+lendiff);
    memmove(bag+oldpos+length, bag+oldpos+oldlen, movesize);
  }
  else if(lendiff < 0) {
    memmove(bag+oldpos+length, bag+oldpos+oldlen, movesize);
    if(newsizeblks != currsizeblks)
      bag = (char*)throw_realloc(bag, oldsize+lendiff);
  }
  memcpy(bag+oldpos, data, length);
  bagsize += lendiff;
  for(int n=index+1; n<items; n++)
    Pos(n) += lendiff;
}


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

const char* GStrBag::operator[](int index) {

  return Index(index);
}


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

void GStrSet3::Put(const char* s1, const char* s2, const char* s3) {

  int len1 = strlen(s1 ? s1 : "")+1;
  int len2 = strlen(s2 ? s2 : "")+1;
  int len3 = strlen(s3 ? s3 : "")+1;
  cfg.size = len1 + len2 + len3;
  set = (char *)throw_xmalloc(cfg.size);
  cfg.pos2 = len1;
  cfg.pos3 = len1 + len2;
  memcpy(set, s1 ? s1 : "", len1);
  memcpy(set+cfg.pos2, s2 ? s2 : "", len2);
  memcpy(set+cfg.pos3, s3 ? s3 : "", len3);
}


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

void GStrSet3::Change(const char* s1, const char* s2, const char* s3) {
  
  char* oldset = set;
  Put(s1, s2, s3);
  throw_xfree(oldset);
}


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