/*
 *  MICO --- an Open Source CORBA implementation
 *  Copyright (c) 1997-2010 by The Mico Team
 *
 *  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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *  For more information, visit the MICO Home Page at
 *  http://www.mico.org/
 */

#ifdef FAST_PCH
#include "orb_pch.h"
#endif // FAST_PCH
#ifdef __COMO__
#pragma hdrstop
#endif // __COMO__

#ifndef FAST_PCH

#include <CORBA-SMALL.h>
#include <stdio.h>
#include <string.h>
#ifdef HAVE_ANSI_CPLUSPLUS_HEADERS
#include <iostream>
#include <iomanip>
#else
#include <iostream.h>
#include <iomanip.h>
#endif
#include <mico/template_impl.h>

#endif // FAST_PCH


using namespace std;

CORBA::Buffer::Buffer (void *b)
{
    // readonly buffer with given contents
    _len = _wptr = 0x7fffffff;
    _rptr = 0;
    _ralignbase = _walignbase = 0;
    _buf = (Octet *)b;
    _readonly = TRUE;
}

CORBA::Buffer::Buffer (ULong sz)
{
    // read/write buffer with given initial size
    if (sz < MINSIZE)
        sz = MINSIZE;
    _buf = alloc (sz);
    _len = sz;
    _rptr = _wptr = 0;
    _ralignbase = _walignbase = 0;
    _readonly = FALSE;
}

CORBA::Buffer::Buffer (const Buffer &b)
{
    _buf = alloc (b._len);
    memcpy (_buf, b._buf, b._len);
    _len = b._len;
    _rptr = b._rptr;
    _wptr = b._wptr;
    _ralignbase = b._ralignbase;
    _walignbase = b._walignbase;
    _readonly = FALSE;
}

CORBA::Buffer::~Buffer ()
{
    if (!_readonly)
        free (_buf);
}

CORBA::Buffer &
CORBA::Buffer::operator= (const Buffer &b)
{
    if (this != &b) {
	assert (!_readonly && !b._readonly);
	free (_buf);
	_buf = alloc (b._len);
	memcpy (_buf, b._buf, b._len);
	_len = b._len;
	_rptr = b._rptr;
	_wptr = b._wptr;
	_ralignbase = b._ralignbase;
	_walignbase = b._walignbase;
    }
    return *this;
}

CORBA::Boolean
CORBA::Buffer::operator== (const Buffer &b)
{
    assert (!_readonly && !b._readonly);
    return length() == b.length() && !memcmp (data(), b.data(), length());
}

CORBA::Octet *
CORBA::Buffer::alloc (ULong sz)
{
    Octet *b = (Octet *)::malloc (sz);
    assert (b);
    return b;
}

CORBA::Octet *
CORBA::Buffer::realloc (Octet *b, ULong osz, ULong nsz)
{
    Octet *nb = (Octet *)::realloc ((void *)b, nsz);
    assert (nb);
    return nb;
}

void
CORBA::Buffer::free (Octet *b)
{
    ::free ((void *)b);
}

void
CORBA::Buffer::reset (ULong sz)
{
    _rptr = 0;
    _ralignbase = _walignbase = 0;
    if (!_readonly) {
        _wptr = 0;
        if (sz < MINSIZE)
            sz = MINSIZE;
        if (_len < sz) {
	    free (_buf);
            _buf = alloc (sz);
            _len = sz;
        }
    }
}

void
CORBA::Buffer::doresize (ULong needed)
{
    assert (!_readonly);
    if (_wptr + needed > _len) {
        ULong nlen = (_len < RESIZE_THRESH)
            ? (2*_len)
            : (_len + RESIZE_INCREMENT);
        if (_wptr + needed > nlen)
            nlen = _wptr + needed;
	_buf = realloc (_buf, _len, nlen);
        _len = nlen;
    }
}

CORBA::Boolean
CORBA::Buffer::peek (void *b, ULong blen)
{
    if (_wptr - _rptr < blen)
        return FALSE;
    memcpy (b, &_buf[_rptr], blen);
    return TRUE;
}

CORBA::Boolean
CORBA::Buffer::peek (Octet &o)
{
    if (_wptr == _rptr)
        return FALSE;
    o = _buf[_rptr];
    return TRUE;
}

CORBA::Boolean
CORBA::Buffer::get (Octet &o)
{
    if (_wptr == _rptr)
        return FALSE;
    o = _buf[_rptr++];
    return TRUE;
}

CORBA::Boolean
CORBA::Buffer::get (void *b, ULong l)
{
    if (_wptr - _rptr < l)
	return FALSE;
    memcpy (b, &_buf[_rptr], l);
    _rptr += l;
    return TRUE;
}

CORBA::Boolean
CORBA::Buffer::get1 (void *p)
{
    if (_wptr == _rptr)
        return FALSE;
    *(Octet *)p = _buf[_rptr++];
    return TRUE;
}

CORBA::Boolean
CORBA::Buffer::get2 (void *p)
{
    // assert (((_rptr - _ralignbase) % 2) == 0);
    if (_rptr+2 > _wptr)
	return FALSE;
    // assume that pointers can be cast to long
    if (!((_rptr | (long)p)&1)) {
	*(CORBA::Short *)p = (CORBA::Short &)_buf[_rptr];
	_rptr += 2;
    } else {
	*((Octet * &)p)++ = _buf[_rptr++];
	*(Octet *)p = _buf[_rptr++];
    }
    return TRUE;
}

CORBA::Boolean
CORBA::Buffer::get4 (void *p)
{
    // assert (((_rptr - _ralignbase) % 4) == 0);
    if (_rptr+4 > _wptr)
	return FALSE;
    if (!((_rptr | (long)p)&3)) {
	*(CORBA::Long *)p = (CORBA::Long &)_buf[_rptr];
	_rptr += 4;
    } else {
	*((Octet * &)p)++ = _buf[_rptr++];
	*((Octet * &)p)++ = _buf[_rptr++];
	*((Octet * &)p)++ = _buf[_rptr++];
	*(Octet *)p = _buf[_rptr++];
    }
    return TRUE;
}

CORBA::Boolean
CORBA::Buffer::get8 (void *p)
{
    // assert (((_rptr - _ralignbase) % 8) == 0);
    if (_rptr+8 > _wptr)
	return FALSE;
    if (!((_rptr | (long)p)&7)) {
	*(CORBA::LongLong *)p = (CORBA::LongLong &)_buf[_rptr];
	_rptr += 8;
    } else {
	*((Octet * &)p)++ = _buf[_rptr++];
	*((Octet * &)p)++ = _buf[_rptr++];
	*((Octet * &)p)++ = _buf[_rptr++];
	*((Octet * &)p)++ = _buf[_rptr++];
	*((Octet * &)p)++ = _buf[_rptr++];
	*((Octet * &)p)++ = _buf[_rptr++];
	*((Octet * &)p)++ = _buf[_rptr++];
	*(Octet *)p = _buf[_rptr++];
    }
    return TRUE;
}

// get 16 bytes with 8 byte alignment
CORBA::Boolean
CORBA::Buffer::get16 (void *p)
{
    // assert (((_rptr - _ralignbase) % 8) == 0);
    if (_rptr+16 > _wptr)
	return FALSE;
    if (!((_rptr | (long)p)&7)) {
	*((CORBA::LongLong * &)p)++ = (CORBA::LongLong &)_buf[_rptr];
	_rptr += 8;
	*(CORBA::LongLong *)p = (CORBA::LongLong &)_buf[_rptr];
	_rptr += 8;
    } else {
	*((Octet * &)p)++ = _buf[_rptr++];
	*((Octet * &)p)++ = _buf[_rptr++];
	*((Octet * &)p)++ = _buf[_rptr++];
	*((Octet * &)p)++ = _buf[_rptr++];
	*((Octet * &)p)++ = _buf[_rptr++];
	*((Octet * &)p)++ = _buf[_rptr++];
	*((Octet * &)p)++ = _buf[_rptr++];
	*((Octet * &)p)++ = _buf[_rptr++];
	*((Octet * &)p)++ = _buf[_rptr++];
	*((Octet * &)p)++ = _buf[_rptr++];
	*((Octet * &)p)++ = _buf[_rptr++];
	*((Octet * &)p)++ = _buf[_rptr++];
	*((Octet * &)p)++ = _buf[_rptr++];
	*((Octet * &)p)++ = _buf[_rptr++];
	*((Octet * &)p)++ = _buf[_rptr++];
	*(Octet *)p = _buf[_rptr++];
    }
    return TRUE;
}

void
CORBA::Buffer::replace (const void *o, ULong blen)
{
    assert (!_readonly);
    reset (blen);
    memcpy (&_buf[_wptr], o, blen);
    _wptr += blen;
}

void
CORBA::Buffer::replace (Octet o)
{
    assert (!_readonly);
    reset (1);
    _buf[_wptr++] = o;
}

void
CORBA::Buffer::put (const void *o, ULong l)
{
    assert (!_readonly);
    resize (l);
    memcpy (&_buf[_wptr], o, l);
    _wptr += l;
}

void
CORBA::Buffer::put (Octet o)
{
    assert (!_readonly);
    resize (1);
    _buf[_wptr++] = o;
}

void
CORBA::Buffer::put1 (const void *p)
{
    assert (!_readonly);
    resize (1);
    _buf[_wptr++] = *(const Octet *)p;
}

void
CORBA::Buffer::put2 (const void *p)
{
    // assert (((_wptr - _walignbase) % 2) == 0);
    assert (!_readonly && _wptr >= _walignbase);
    resize (2);
    CORBA::Octet *b = _buf + _wptr;
    if (!(((long)b | (long)p)&1)) {
	*((CORBA::Short *&)b)++ = *(const CORBA::Short *)p;
    } else {
	*b++ = *((const Octet * &)p)++;
	*b++ = *(const Octet *)p;
    }
    size_t t = b - _buf;
    assert(t < UINT_MAX);
    _wptr = (ULong)t;
}

void
CORBA::Buffer::put4 (const void *p)
{
   // assert (((_wptr - _walignbase) % 4) == 0);
    assert (!_readonly && _wptr >= _walignbase);
    resize (4);
    CORBA::Octet *b = _buf + _wptr;
    if (!(((long)b | (long)p)&3)) {
	*((CORBA::Long * &)b)++ = *(const CORBA::Long *)p;
    } else {
	*b++ = *((const Octet * &)p)++;
	*b++ = *((const Octet * &)p)++;
	*b++ = *((const Octet * &)p)++;
	*b++ = *(const Octet *)p;
    }
    size_t t = b - _buf;
    assert(t < UINT_MAX);
    _wptr = (ULong)t;
}

void
CORBA::Buffer::put8 (const void *p)
{
    // assert (((_wptr - _walignbase) % 8) == 0);
    assert (!_readonly && _wptr >= _walignbase);
    resize (8);
    CORBA::Octet *b = _buf + _wptr;
    if (!(((long)b | (long)p)&7)) {
	*((CORBA::LongLong *&)b)++ = *(const CORBA::LongLong *)p;
    } else {
	*b++ = *((const Octet * &)p)++;
	*b++ = *((const Octet * &)p)++;
	*b++ = *((const Octet * &)p)++;
	*b++ = *((const Octet * &)p)++;
	*b++ = *((const Octet * &)p)++;
	*b++ = *((const Octet * &)p)++;
	*b++ = *((const Octet * &)p)++;
	*b++ = *(const Octet *)p;
    }
    size_t t = b - _buf;
    assert(t < UINT_MAX);
    _wptr = (ULong)t;
}

// put 16 bytes with 8 byte alignment
void
CORBA::Buffer::put16 (const void *p)
{
    //  assert (((_wptr - _walignbase) % 8) == 0);
    assert (!_readonly && _wptr >= _walignbase);
    resize (16);
    CORBA::Octet *b = _buf + _wptr;
    if (!(((long)b | (long)p)&7)) {
	*((CORBA::LongLong *&)b)++ = *((const CORBA::LongLong * &)p)++;
	*((CORBA::LongLong *&)b)++ = *(const CORBA::LongLong *)p;
    } else {
	*b++ = *((const Octet * &)p)++;
	*b++ = *((const Octet * &)p)++;
	*b++ = *((const Octet * &)p)++;
	*b++ = *((const Octet * &)p)++;
	*b++ = *((const Octet * &)p)++;
	*b++ = *((const Octet * &)p)++;
	*b++ = *((const Octet * &)p)++;
	*b++ = *((const Octet * &)p)++;
	*b++ = *((const Octet * &)p)++;
	*b++ = *((const Octet * &)p)++;
	*b++ = *((const Octet * &)p)++;
	*b++ = *((const Octet * &)p)++;
	*b++ = *((const Octet * &)p)++;
	*b++ = *((const Octet * &)p)++;
	*b++ = *((const Octet * &)p)++;
	*b++ = *(const Octet *)p;
    }
    size_t t = b - _buf;
    assert(t < UINT_MAX);
    _wptr = (ULong)t;
}

void
CORBA::Buffer::dump (const char * desc, ostream &o) const
{
  CORBA::ULong i=_rptr, j;
  char temp[256];
  int l=0;

  while (i < _wptr) {
    sprintf (temp, "%10s  ", (i==_rptr) ? desc : "");
    o << temp;

    for (j=0; j<16 && i+j<_wptr; j++) {
      sprintf (temp, "%02x ", _buf[i+j]);
      o << temp;
    }

    for (; j<16; j++) {
      o << "   ";
    }

    o << " ";

    for (j=0; j<16 && i+j<_wptr; j++) {
      /*
       * Printable ISOLatin1 characters according to the Red Book
       */
      if ((_buf[i+j] >= 0040 && _buf[i+j] <= 0176) ||
	  (_buf[i+j] >= 0220 && _buf[i+j] != 0231 && _buf[i+j] != 0234)) {
	temp[j] = _buf[i+j];
      }
      else {
	temp[j] = '.';
      }
    }

    temp[j] = '\0';
    o << temp << endl;

    i += j;

    if (++l == 16 && i < _wptr) {
      o << endl;
      l = 0;
    }
  }
}