/*
 * hbytes - hex-stringrep efficient byteblocks for Tcl
 * Copyright 2006-2012 Ian Jackson
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program 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
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this library; if not, see <http://www.gnu.org/licenses/>.
 */


#include "hbytes.h"

#define COMPLEX(hb) ((HBytes_ComplexValue*)hb->begin_complex)
#define SIMPLE_LEN(hb) ((Byte*)(hb)->end_0 - (Byte*)(hb)->begin_complex)

/* enquirers */

int cht_hb_len(const HBytes_Value *hb) {
  if (HBYTES_ISEMPTY(hb)) return 0;
  else if (HBYTES_ISCOMPLEX(hb)) return COMPLEX(hb)->len;
  else return SIMPLE_LEN(hb);
}

Byte *cht_hb_data(const HBytes_Value *hb) {
  if (HBYTES_ISEMPTY(hb)) return 0;
  else if (HBYTES_ISCOMPLEX(hb)) return COMPLEX(hb)->dstart;
  else return hb->begin_complex;
}

int cht_hb_issentinel(const HBytes_Value *hb) {
  return HBYTES_ISSENTINEL(hb);
}

/* constructors */

void cht_hb_empty(HBytes_Value *returns) {
  returns->begin_complex= returns->end_0= 0;
}

void cht_hb_sentinel(HBytes_Value *returns) {
  returns->begin_complex= 0;
  returns->end_0= (void*)&cht_hbytes_type;
}

Byte *cht_hb_arrayspace(HBytes_Value *returns, int l) {
  if (!l) { cht_hb_empty(returns); return 0; }
  returns->begin_complex= TALLOC(l);
  returns->end_0= returns->begin_complex + l;
  return returns->begin_complex;
}
  
void cht_hb_array(HBytes_Value *returns, const Byte *array, int l) {
  memcpy(cht_hb_arrayspace(returns,l), array, l);
}

/* destructor */

void cht_hb_free(const HBytes_Value *frees) {
  if (HBYTES_ISCOMPLEX(frees)) {
    HBytes_ComplexValue *cx= COMPLEX(frees);
    TFREE(cx->dstart - cx->prespace);
  }
  TFREE(frees->begin_complex);
}

/* mutators */

static HBytes_ComplexValue *complex(HBytes_Value *hb) {
  HBytes_ComplexValue *cx;

  if (HBYTES_ISCOMPLEX(hb)) return hb->begin_complex;

  cx= TALLOC(sizeof(*cx));
  cx->dstart= hb->begin_complex;
  cx->len= cx->avail= SIMPLE_LEN(hb);
  cx->prespace= 0;

  hb->begin_complex= cx;
  hb->end_0= 0;

  return cx;
}

Byte *cht_hb_prepend(HBytes_Value *hb, int el) {
  HBytes_ComplexValue *cx;
  int new_prespace;
  Byte *old_block, *new_block, *new_dstart;

  cx= complex(hb);

  assert(el < INT_MAX/4 && cx->len < INT_MAX/2);
  
  if (cx->prespace < el) {
    new_prespace= el*2 + cx->len;
    old_block= cx->dstart - cx->prespace;
    new_block= Tcl_Realloc(old_block, new_prespace + cx->avail);
    new_dstart= new_block + new_prespace;
    memmove(new_dstart, new_block + cx->prespace, cx->len);
    cx->prespace= new_prespace;
    cx->dstart= new_dstart;
  }
  cx->dstart -= el;
  cx->prespace -= el;
  cx->len += el;
  cx->avail += el;
  return cx->dstart;
}

Byte *cht_hb_append(HBytes_Value *hb, int el) {
  HBytes_ComplexValue *cx;
  int new_len, new_avail;
  Byte *newpart, *new_block, *old_block;

  cx= complex(hb);
  assert(el < INT_MAX/4 && cx->len < INT_MAX/4);

  new_len= cx->len + el;
  if (new_len > cx->avail) {
    new_avail= new_len*2;
    old_block= cx->dstart - cx->prespace;
    new_block= Tcl_Realloc(old_block, cx->prespace + new_avail);
    cx->dstart= new_block + cx->prespace;
    cx->avail= new_avail;
  }
  newpart= cx->dstart + cx->len;
  cx->len= new_len;
  return newpart;
}

static HBytes_ComplexValue*
prechop(HBytes_Value *hb, int cl, const Byte **rv) {
  HBytes_ComplexValue *cx;

  if (cl<0) { *rv=0; return 0; }
  if (cl==0) { *rv= (const void*)&cht_hbytes_type; return 0; }
  
  cx= complex(hb);
  if (cl > cx->len) { *rv=0; return 0; }
  return cx;
}

const Byte *cht_hb_unprepend(HBytes_Value *hb, int pl) {
  const Byte *chopped;
  HBytes_ComplexValue *cx= prechop(hb,pl,&chopped);
  if (!cx) return chopped;

  chopped= cx->dstart;
  cx->dstart += pl;
  cx->prespace += pl;
  cx->len -= pl;
  cx->avail -= pl;
  return chopped;
}

const Byte *cht_hb_unappend(HBytes_Value *hb, int sl) {
  const Byte *chopped;
  HBytes_ComplexValue *cx= prechop(hb,sl,&chopped);
  if (!cx) return chopped;

  cx->len -= sl;
  return cx->dstart + cx->len;
}

void memxor(Byte *dest, const Byte *src, int l) {
  while (l--) *dest++ ^= *src++;
}
