// ---------------------------------------------------------------------------
// - Buffer.cpp                                                              -
// - standard object library - character buffer class implementation         -
// ---------------------------------------------------------------------------
// - This program is free software;  you can redistribute it  and/or  modify -
// - it provided that this copyright notice is kept intact.                  -
// -                                                                         -
// - 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.  In no event shall -
// - the copyright holder be liable for any  direct, indirect, incidental or -
// - special damages arising in any way out of the use of this software.     -
// ---------------------------------------------------------------------------
// - copyright (c) 1999-2013 amaury darsch                                   -
// ---------------------------------------------------------------------------

#include "Byte.hpp"
#include "Ascii.hpp"
#include "Vector.hpp"
#include "Buffer.hpp"
#include "System.hpp"
#include "Utility.hpp"
#include "Unicode.hpp"
#include "Integer.hpp"
#include "Boolean.hpp"
#include "QuarkZone.hpp"
#include "Exception.hpp"
#include "ccnv.hpp"

namespace afnix {

  // -------------------------------------------------------------------------
  // - public section                                                        -
  // -------------------------------------------------------------------------

  // generate a random buffer

  Buffer Buffer::random (const long size) {
    // check for valid size
    if (size < 0) {
      throw Exception ("size-error", "invalid random number size");
    }
    // create a buffer by size
    Buffer result (size);
    // fill it with random bytes
    for (long i = 0; i < size; i++) {
      result.add ((char) Utility::byternd ());
    }
    // here it is
    return result;
  }
  
  // -------------------------------------------------------------------------
  // - class section                                                         -
  // -------------------------------------------------------------------------

  // create a new buffer class with a default size of 1024 characters

  Buffer::Buffer (void) {
    d_size = System::blocksz ();
    p_data = new char [d_size];
    d_blen = 0;
    d_rflg = true;
    d_emod = Encoding::BYTE;
  }

  // create a new buffer with a predefined size

  Buffer::Buffer (const long size) {
    d_size = (size <= 0) ? System::blocksz () : size;
    p_data = new char[d_size];
    d_blen = 0;
    d_rflg = true;
    d_emod = Encoding::BYTE;
  }

  // create a new buffer by mode

  Buffer::Buffer (const Encoding::t_emod emod) {
    d_size = System::blocksz ();
    p_data = new char[d_size];
    d_blen = 0;
    d_rflg = true;
    d_emod = emod;
  }

  // create a new buffer by size with a mode

  Buffer::Buffer (const long size, const Encoding::t_emod emod) {
    d_size = (size <= 0) ? System::blocksz () : size;
    p_data = new char[d_size];
    d_blen = 0;
    d_rflg = true;
    d_emod = emod;
  }

  // create a new buffer and initialize it with a c string

  Buffer::Buffer (const char* value) {
    d_size = System::blocksz ();
    p_data = new char[d_size];
    d_blen = 0;
    d_rflg = true;
    d_emod = Encoding::UTF8;
    add (value, Ascii::strlen (value));
  }

  // create a new buffer and initialize it with a string

  Buffer::Buffer (const String& value) {
    d_size = System::blocksz ();
    p_data = new char[d_size];
    d_blen = 0;
    d_rflg = true;
    d_emod = Encoding::UTF8;
    add (value);
  }

  // create a new buffer by size and content, no copy, no resize

  Buffer::Buffer (const long size, const long blen, char* data) {
    d_size = size;;
    d_blen = blen;
    p_data = data;
    d_rflg = false;
    d_emod = Encoding::BYTE;
  }

  // copy construct a buffer

  Buffer::Buffer (const Buffer& that) {
    that.rdlock ();
    try {
      d_size = that.d_size;
      d_blen = 0;
      d_rflg = that.d_rflg;
      d_emod = that.d_emod;
      p_data = new char[d_size];
      add (that.p_data, that.d_blen);
      that.unlock ();
    } catch (...) {
      that.unlock ();
      throw;
    }
  }

  // destroy this buffer
  
  Buffer::~Buffer (void) {
    delete [] p_data;
  }
  
  // return the class name

  String Buffer::repr (void) const {
    return "Buffer";
  }

  // assign a buffer to this one

  Buffer& Buffer::operator = (const Buffer& that) {
    if (this == &that) return *this;
    wrlock ();
    that.rdlock ();
    try {
      // clean the old data
      delete [] p_data;
      p_data = nilp;
      d_blen = 0;
      // add the new data
      d_size = that.d_size;
      d_rflg = that.d_rflg;
      d_emod = that.d_emod;
      p_data = new char[d_size];
      add (that.p_data, that.d_blen);
      unlock ();
      that.unlock ();
      return *this;
    } catch (...) {
      unlock ();
      that.unlock ();
      throw;
    }
  }

  // return a clone of this object

  Object* Buffer::clone (void) const {
    return new Buffer (*this);
  }

  // reset this buffer but do not change the size
  
  void Buffer::reset (void) {
    wrlock ();
    try {
      d_blen = 0;
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // return the size of this buffer
  
  long Buffer::getsize (void) const {
    rdlock ();
    try {
      long result = d_size;
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // return the length of this buffer
  
  long Buffer::length (void) const {
    rdlock ();
    try {
      long result = d_blen;
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // set the buffer encoding mode

  void Buffer::setemod (const Encoding::t_emod emod) {
    wrlock ();
    try {
      d_emod = emod;
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // get the buffer encoding

  Encoding::t_emod Buffer::getemod (void) const {
    rdlock ();
    try {
      Encoding::t_emod result = d_emod;
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // set the resize flag

  void Buffer::setrflg (const bool rflg) {
    wrlock ();
    try {
      d_rflg = rflg;
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // return the resize flag

  bool Buffer::getrflg (void) const {
    rdlock ();
    try {
      bool result = d_rflg;
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // return true if the buffer is empty

  bool Buffer::empty (void) const {
    rdlock ();
    try {
      bool result = (d_blen == 0);
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // return true if the buffer is full

  bool Buffer::full (void) const {
    rdlock ();
    try {
      bool result = d_rflg ? false : (d_blen >= d_size);
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // add a character in this buffer
  
  long Buffer::add (const char value) {
    wrlock ();
    try {
      // first check if we are at the buffer end
      if (d_blen >= d_size) {
	if (d_rflg == true) {
	  long size = d_size * 2;
	  char* buf = new char[size];
	  for (long i = 0; i < d_blen; i++) buf[i] = p_data[i];
	  delete [] p_data;
	  d_size   = size;
	  p_data = buf;
	} else {
	  unlock ();
	  return 0;
	}
      }
      p_data[d_blen++] = value;
      unlock ();
      return 1;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // add a unicode character in the buffer

  long Buffer::add (const t_quad value) {
    wrlock ();
    char* cbuf = nilp;
    try {
      // get the character encoding
      cbuf = Unicode::encode (d_emod, value);
      long  size = Ascii::strlen (cbuf);
      // add the coding in the buffer
      long result = add (cbuf, size);
      // clean and unlock
      delete [] cbuf;
      unlock ();
      return result;
    } catch (...) {
      delete [] cbuf;
      unlock ();
      throw;
    }
  }

  // add a c-string in this buffer

  long Buffer::add (const char* s) {
    wrlock ();
    try {
      long size = Ascii::strlen (s);
      long result = add (s, size);
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // add a character buffer in this buffer
  
  long Buffer::add (const char* cbuf, const long size) {
    if ((cbuf == nilp) || (size == 0)) return 0;
    wrlock ();
    try {
      long result = 0;
      for (long i = 0; i < size; i++) {
	result += add (cbuf[i]);
	if (full () == true) break;
      }
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // add a string in this buffer
  
  long Buffer::add (const String& s) {
    wrlock ();
    char* cbuf = nilp;
    try {
      // encode the string
      cbuf = Unicode::encode (d_emod, s);
      long  size = Ascii::strlen (cbuf);
      // add the buffer
      long result = add (cbuf, size);
      // clean and unlock
      delete [] cbuf;
      unlock ();
      return result;
    } catch (...) {
      delete [] cbuf;
      unlock ();
      throw;
    }
  }

  // add a buffer object to this buffer

  long Buffer::add (const Buffer& buffer) {
    // do not add yourself
    if (this == &buffer) return 0;
    // lock and add
    wrlock ();
    buffer.rdlock ();
    try {
      long result = add (buffer.p_data, buffer.d_blen);
      buffer.unlock ();
      unlock ();
      return result;
    } catch (...) {
      buffer.unlock ();
      unlock ();
      throw;
    }
  }
  
  // read a character in this buffer
  
  char Buffer::read (void) {
    wrlock ();
    try {
      // check for no character
      if (d_blen == 0) {
	unlock ();
	return nilc;
      }
      // get value and shift
      char value = p_data[0];
      for (long i = 0; i < d_blen-1; i++) p_data[i] = p_data[i+1];
      d_blen--;
      unlock ();
      return value;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // get the next character but do not remove it

  char Buffer::get (void) const {
    rdlock ();
    try {
      char result = (d_blen == 0) ? nilc : p_data[0];
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // get a character by index

  char Buffer::get (const long index) const {
    rdlock ();
    try {
      if ((index < 0) || (index >= d_blen)) {
	throw Exception ("range-error", "out-of-bound buffer index");
      }
      char result = p_data[index];
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // pop the last character if any

  char Buffer::pop (void) {
    wrlock ();
    try {
      // check for nil
      if ((d_size == 0) || (d_blen == 0)) {
	unlock ();
	return nilc;
      }
      char result = p_data[--d_blen];
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // shift the buffer by a certain amount

  void Buffer::shl (const long asl) {
    wrlock ();
    try {
      // check for amount
      if (asl < d_blen) {
	// shift the old buffer
	long  blen = d_blen - asl;
	long  size = d_rflg ? d_size : blen;
	char* data = new char[size];
	for (long i = asl; i < d_blen; i++) data[i-asl] = p_data[i];
	// adjust indexes
	delete [] p_data;
	d_size = size;
	d_blen = blen;
	p_data = data;
      } else {
	d_blen = 0;
      }
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // read a line from this buffer

  String Buffer::readln (void) {
    wrlock ();
    try {
      // create a buffer to accumulate characters
      Buffer buf = d_emod;
      bool   flg = false;
      // read the character in the buffer
      while (empty () == false) {
	char c = read ();
	if (c == crlc) {
	  flg = true;
	  continue;
	}
	if (c == eolc) break;
	if (flg == true) {
	  buf.add (crlc);
	  flg = false;
	}
	buf.add (c);
      }
      unlock ();
      return buf.tostring ();
    } catch (...) {
      unlock ();
      throw;
    }
  }
  
  // pushback a character in this buffer
  
  long Buffer::pushback (const char value) {
    wrlock ();
    try {
      // check if we are full
      if (d_blen >= d_size) {
	if (d_rflg == true) {
	  long size = d_size * 2;
	  char* buf = new char[size];
	  for (long i = 0; i < d_blen; i++) buf[i] = p_data[i];
	  d_size = size;
	  delete [] p_data;
	  p_data = buf;
	} else {
	  unlock ();
	  return 0;
	}
      }
      // shift the buffer by one
      for (long i = d_blen; i > 0; i--) p_data[i] = p_data[i-1];
      p_data[0] = value;
      d_blen++;
      unlock ();
      return 1;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // pushback a unicode character in the buffer

  long Buffer::pushback (const t_quad value) {
    wrlock ();
    char* cbuf = nilp;
    try {
      // get the character encoding
      cbuf = Unicode::encode (d_emod, value);
      long size = Ascii::strlen (cbuf);
      // pushback the coding in the buffer
      pushback (cbuf, size);
      // clean and unlock
      delete [] cbuf;
      unlock ();
      return size;
    } catch (...) {
      delete [] cbuf;
      unlock ();
      throw;
    }
  }

  // pushback a character string to this buffer

  long Buffer::pushback (const char* s) {
    wrlock ();
    try {
      long   size = Ascii::strlen (s);
      long result = pushback (s, size);
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // pushback a buffer in this buffer
  
  long Buffer::pushback (const char* s, const long size) {
    if ((s == nilp) || (size == 0)) return 0;
    wrlock ();
    try {
      long    len = size - 1;
      long result = 0;
      for (long i = len; i >= 0; i--) {
	result += pushback (s[i]);
	if (full () == true) break;
      }
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }
  
  // pushback a string in this buffer
  
  long Buffer::pushback (const String& value) {
    wrlock ();
    char* cbuf = nilp;
    try {
      // encode the string
      cbuf = Unicode::encode (d_emod, value);
      long  size = Ascii::strlen (cbuf);
      // pushback the string
      long result = pushback (cbuf, size);
      // clean and unlock
      delete [] cbuf;
      unlock ();
      return result;
    } catch (...) {
      delete [] cbuf;
      unlock ();
      throw;
    }
  }

  // pushback a buffer in this buffer
  
  long Buffer::pushback (const Buffer& buffer) {
    // do not pushback yourself
    if (this == &buffer) return 0;
    // lock and push
    wrlock ();
    buffer.rdlock ();
    try {
      long result = pushback (buffer.p_data, buffer.d_blen);
      buffer.unlock ();
      unlock ();
      return result;
    } catch (...) {
      buffer.unlock ();
      unlock ();
      throw;
    }
  }

  // return the buffer content as a null terminated quad buffer

  t_quad* Buffer::toquad (void) const {
    rdlock ();
    try {
      t_quad* result = Unicode::decode (d_emod, p_data, d_blen);
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // return the buffer content as a string
  
  String Buffer::tostring (void) const {
    rdlock ();
    try {
      // decode the buffer
      t_quad* sbuf = Unicode::decode (d_emod, p_data, d_blen);
      // map the result string
      String result = sbuf;
      // clean and return
      delete [] sbuf;
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // format the buffer content as an octet string

  String Buffer::format (void) const {
    rdlock ();
    try {
      // format the string
      String result = Ascii::btos ((const t_byte*) p_data, d_blen);
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    } 
  }

  // map this buffer to an anonymous data structure

  long Buffer::tomap (void* data, const long size) const {
    rdlock ();
    try {
      // check for a null content
      if (d_blen == 0) {
	unlock ();
	return 0;
      }
      // process normal size
      long result = (size < d_blen) ? size : d_blen;
      char* ptr   = reinterpret_cast <char*> (data);
      // copy the buffer content
      for (long i = 0; i < result; i++) ptr[i] = p_data[i];
      // unlock and return
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // add a word in natural format to this buffer

  void Buffer::addnw (const t_word wval) {
    wrlock ();
    try {
      add ((char) ((wval >> 8) & 0x00FFU));
      add ((char) (wval & 0x00FFU));
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // add a quad in natural format to this buffer

  void Buffer::addnq (const t_quad qval) {
    wrlock ();
    try {
      add ((char) ((qval >> 24) & 0x000000FFU));
      add ((char) ((qval >> 16) & 0x000000FFU));
      add ((char) ((qval >> 8)  & 0x000000FFU));
      add ((char) (qval & 0x000000FFU));
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // add an octa in natural format to this buffer

  void Buffer::addno (const t_octa oval) {
    wrlock ();
    try {
      add ((char) ((oval >> 56) & 0x00000000000000FFU));
      add ((char) ((oval >> 48) & 0x00000000000000FFU));
      add ((char) ((oval >> 40) & 0x00000000000000FFU));
      add ((char) ((oval >> 32) & 0x00000000000000FFU));
      add ((char) ((oval >> 24) & 0x00000000000000FFU));
      add ((char) ((oval >> 16) & 0x00000000000000FFU));
      add ((char) ((oval >> 8)  & 0x00000000000000FFU));
      add ((char) (oval & 0x00000000000000FFU));
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // get a word in natural format from this buffer

  t_word Buffer::getnw (void) {
    wrlock ();
    try {
      // check valid buffer size
      if (d_blen < 2) {
	throw Exception ("buffer-error", "small buffer size with getnw");
      }
      // prepare result
      t_word result = 0x0000U;
      // read and shift
      result |= (t_byte) read ();
      result <<= 8;
      result |= (t_byte) read ();
      // unlock and return
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // get a quad in natural format from this buffer

  t_quad Buffer::getnq (void) {
    wrlock ();
    try {
      // check valid buffer size
      if (d_blen < 4) {
	throw Exception ("buffer-error", "small buffer size with getnq");
      }
      // prepare result
      t_quad result = nilq;
      // read and shift
      result |= (t_byte) read ();
      result <<= 8;
      result |= (t_byte) read ();
      result <<= 8;
      result |= (t_byte) read ();
      result <<= 8;
      result |= (t_byte) read ();
      // unlock and return
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // get an octa in natural format from this buffer

  t_octa Buffer::getno (void) {
    wrlock ();
    try {
      // check valid buffer size
      if (d_blen < 8) {
	throw Exception ("buffer-error", "small buffer size with getno");
      }
      // prepare result
      t_octa result = 0ULL;
      // read and shift
      result |= (t_byte) read ();
      result <<= 8;
      result |= (t_byte) read ();
      result <<= 8;
      result |= (t_byte) read ();
      result <<= 8;
      result |= (t_byte) read ();
      result <<= 8;
      result |= (t_byte) read ();
      result <<= 8;
      result |= (t_byte) read ();
      result <<= 8;
      result |= (t_byte) read ();
      result <<= 8;
      result |= (t_byte) read ();
      // unlock and return
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // get a word in host format from this buffer

  t_word Buffer::gethw (void) {
    wrlock ();
    try {
      // check valid buffer size
      if (d_blen < 2) {
	throw Exception ("buffer-error", "small buffer size with gethw");
      }
      // extract the character buffer
      t_byte buf[2];
      for (long i = 0; i < 2; i++) buf[i] = read ();
      // convert it in host format
      t_word result = c_wntoh (buf);
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // get a quad in host format from this buffer

  t_quad Buffer::gethq (void) {
    wrlock ();
    try {
      // check valid buffer size
      if (d_blen < 4) {
	throw Exception ("buffer-error", "small buffer size with gethq");
      }
      // extract the character buffer
      t_byte buf[4];
      for (long i = 0; i < 4; i++) buf[i] = read ();
      // convert it in host format
      t_quad result = c_qntoh (buf);
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // get an octa in host format from this buffer

  t_octa Buffer::getho (void) {
    wrlock ();
    try {
      // check valid buffer size
      if (d_blen < 8) {
	throw Exception ("buffer-error", "small buffer size with getho");
      }
      // extract the character buffer
      t_byte buf[8];
      for (long i = 0; i < 8; i++) buf[i] = read ();
      // convert it in host format
      t_octa result = c_ontoh (buf);
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }
  
  // copy a buffer upto a boundary string
  
  Buffer* Buffer::cpbnds (const String& bnds) {
    wrlock ();
    char* cbuf = Unicode::encode (Encoding::UTF8, bnds);
    long  clen = Ascii::strlen (cbuf);
    try {
      // reset matching flag
      bool mflg = false;
      long mpos = 0;
      // loop into the buffer
      for (mpos = 0; mpos < d_blen; mpos++) {
	// check for matching
	if (p_data[mpos] != cbuf[0]) continue;
	// try to match
	mflg = true;
	for (long l = 0; l < clen; l++) {
	  long j = mpos + l;
	  if ((j >= d_blen) || (p_data[j] != cbuf[l])) {
	    mflg = false;
	    break;
	  }
	}
	if (mflg == true) break;
      }
      // this a a double check
      if (mpos >= d_blen) {
	throw Exception ("internal-error", "inconsistent buffer copy position");
      }
      // we have a matching at position mpos - which is the length of the
      // new buffer to copy
      Buffer* result = new Buffer (mpos);
      result->setrflg (false);
      // copy to the new buffer
      for (long k = 0; k < mpos; k++) result->p_data[k] = p_data[k];
      result->d_blen = mpos;
      // clean boundary buffer
      delete [] cbuf;
      // update the buffer by shifting upto mpos position
      for (long k = mpos; k < d_blen; k++) {
	long l = k - mpos;
	p_data[l] = p_data[k];
      }
      d_blen -= mpos;
      // done
      unlock ();
      return result;
    } catch (...) {
      delete [] cbuf;
      unlock ();
      throw;
    }
  }

  // -------------------------------------------------------------------------
  // - object section                                                        -
  // -------------------------------------------------------------------------

  // the quark zone
  static const long QUARK_ZONE_LENGTH = 18;
  static QuarkZone  zone (QUARK_ZONE_LENGTH);
  
  // the object supported quarks
  static const long QUARK_ADD      = zone.intern ("add");
  static const long QUARK_GET      = zone.intern ("get");
  static const long QUARK_SHL      = zone.intern ("shl");
  static const long QUARK_READ     = zone.intern ("read");
  static const long QUARK_RESET    = zone.intern ("reset");
  static const long QUARK_GETHW    = zone.intern ("get-host-word");
  static const long QUARK_GETHQ    = zone.intern ("get-host-quad");
  static const long QUARK_GETHO    = zone.intern ("get-host-octa");
  static const long QUARK_LENGTH   = zone.intern ("length");
  static const long QUARK_ISFULL   = zone.intern ("full-p");
  static const long QUARK_EMPTYP   = zone.intern ("empty-p");
  static const long QUARK_FORMAT   = zone.intern ("format");
  static const long QUARK_CPBNDS   = zone.intern ("copy-boundary"); 
  static const long QUARK_GETSIZE  = zone.intern ("get-size");
  static const long QUARK_SETRFLG  = zone.intern ("set-resize");
  static const long QUARK_TOSTRING = zone.intern ("to-string");
  static const long QUARK_PUSHBACK = zone.intern ("pushback");
  static const long QUARK_ISRESIZE = zone.intern ("resize-p");

  // create a new object in a generic way

  Object* Buffer::mknew (Vector* argv) {
    long argc = (argv == nilp) ? 0 : argv->length ();
    // create an empty buffer with 0 arguments
    Buffer* result = new Buffer;
    // loop with literal objects
    for (long i = 0; i < argc; i++) {
      Object* obj = argv->get (i);
      // check for a literal
      Literal* lobj = dynamic_cast <Literal*> (obj);
      if (lobj != nilp) {
	result->add (lobj->tostring ());
	continue;
      }
      // check for a vector
      Vector* vobj = dynamic_cast <Vector*> (obj);
      if (vobj != nilp) {
	long vlen = vobj->length ();
	for (long j = 0; j < vlen; j++) {
	  result->add ((char) vobj->getbyte (j));
	}
	continue;
      }
      throw Exception ("type-error", "invalid object with buffer",
		       Object::repr (obj));
    }
    return result;
  }

  // return true if the given quark is defined

  bool Buffer::isquark (const long quark, const bool hflg) const {
    rdlock ();
    if (zone.exists (quark) == true) {
      unlock ();
      return true;
    }      
    bool result = hflg ? Object::isquark (quark, hflg) : false;
    unlock ();
    return result;
  }

  // apply this object with a set of arguments and a quark

  Object* Buffer::apply (Runnable* robj, Nameset* nset, const long quark,
			 Vector* argv) {
    // get the number of arguments
    long argc = (argv == nilp) ? 0 : argv->length ();

    // dispatch 0 argument
    if (argc == 0) {
      if (quark == QUARK_GET)      return new Byte    (get      ());
      if (quark == QUARK_READ)     return new Byte    (read     ());
      if (quark == QUARK_GETHW)    return new Integer (gethw    ());
      if (quark == QUARK_GETHQ)    return new Integer (gethq    ());
      if (quark == QUARK_GETHO)    return new Integer (getho    ());
      if (quark == QUARK_LENGTH)   return new Integer (length   ());
      if (quark == QUARK_ISFULL)   return new Boolean (full     ());
      if (quark == QUARK_EMPTYP)   return new Boolean (empty    ());
      if (quark == QUARK_FORMAT)   return new String  (format   ());
      if (quark == QUARK_GETSIZE)  return new Integer (getsize  ());
      if (quark == QUARK_TOSTRING) return new String  (tostring ());
      if (quark == QUARK_ISRESIZE) return new Boolean (getrflg  ());
      if (quark == QUARK_RESET) {
	reset ();
	return nilp;
      }
    }
    // dispatch 1 argument
    if (argc == 1) {
      if (quark == QUARK_GET) {
	long index = argv->getlong (0);
	return new Byte (get (index));
      }
      if (quark == QUARK_SETRFLG) {
	bool rflg = argv->getbool (0);
	setrflg (rflg);
	return nilp;
      }
      if (quark == QUARK_ADD) {
	Object* obj = argv->get (0);
	// check for a byte
	Byte* bobj = dynamic_cast<Byte*> (obj);
	if (bobj != nilp) {
	  long result = add ((char) bobj->tobyte ());
	  return new Integer (result);
	}
	// check for a literal
	Literal* lobj = dynamic_cast<Literal*> (obj);
	if (lobj != nilp) {
	  long result = add (lobj->tostring ());
	  return new Integer (result);
	}
	// check for a buffer
	Buffer* uobj = dynamic_cast<Buffer*> (obj);
	if (uobj != nilp) {
	  long result = add (*uobj);
	  return new Integer (result);
	}
	throw Exception ("type-error", "invalid object to add in buffer");
      }
      if (quark == QUARK_PUSHBACK) {
	Object* obj = argv->get (0);
	// check for a byte
	Byte* bobj = dynamic_cast<Byte*> (obj);
	if (bobj != nilp) {
	  long result = pushback ((char) bobj->tobyte ());
	  return new Integer (result);
	}
	// check for a literal
	Literal* lobj = dynamic_cast<Literal*> (obj);
	if (lobj != nilp) {
	  long result = pushback (lobj->tostring ());
	  return new Integer (result);
	}
	// check for a buffer
	Buffer* uobj = dynamic_cast<Buffer*> (obj);
	if (uobj != nilp) {
	  long result = pushback (*uobj);
	  return new Integer (result);
	}
	throw Exception ("type-error", "invalid object to pushback in buffer");
      }
      if (quark == QUARK_SHL) {
	long asl = argv->getlong (0);
	shl (asl);
	return nilp;
      }
      if (quark == QUARK_CPBNDS) {
	String bnds = argv->getstring (0);
	return cpbnds (bnds);
      }
    }
    // call the object method
    return Object::apply (robj, nset, quark, argv);
  }
}
