// **********************************************************************
//
// Copyright (c) 2002
// IONA Technologies, Inc.
// Waltham, MA, USA
//
// All Rights Reserved
//
// **********************************************************************

#ifndef OCI_BUFFER_H
#define OCI_BUFFER_H

#include <OB/OCIBuffer_fwd.h>

namespace OCI
{

//
// The BufferImpl class (not reference counted)
//

//
// Under Win'95 (and possibly '98) the allocated buffer is not
// necessarily 8 byte aligned (this does not occur under NT).
// This version of OCI::BufferImpl always ensures this.
//
class BufferImpl
{
    //
    // The "default" buffer, large enough to hold 1024 bytes of data
    // plus headers. This buffer is not allocated with malloc(), which
    // improves performance for small messages that can fit into this
    // buffer.
    //
    CORBA::Octet defBuf_[1200];

    bool rel_; // Whether to delete the buffer
    CORBA::Octet* real_; // Points to the "real" buffer
    CORBA::ULong max_; // The maximum size of the buffer

public:

    CORBA::Octet* data_; // Points to the buffer
    CORBA::ULong len_; // The requested size of the buffer
    CORBA::Octet* cur_; // The current position
    CORBA::Octet* last_; // The last valid position

    void doRealloc(CORBA::ULong);

public:

    BufferImpl()
	: rel_(false), real_(0), max_(0), data_(0), len_(0), cur_(0), last_(0)
    {
    }

    BufferImpl(CORBA::Octet* data, CORBA::ULong len)
        : rel_(false), real_(data), max_(len), data_(data), len_(len),
          cur_(data), last_(data_ + len_)
    {
    }

    BufferImpl(CORBA::ULong);

    virtual ~BufferImpl();

    //
    // Standard IDL to C++ Mapping
    //
    CORBA::Octet* data() { return data_; }
    CORBA::Octet* rest() { return cur_; }

    CORBA::ULong length() { return len_; }
    CORBA::ULong rest_length() { return len_ - (cur_ - data_); }

    CORBA::ULong pos() { return (cur_ - data_); }
    void pos(CORBA::ULong pos) { cur_ = data_ + pos; }
    void advance(CORBA::ULong delta) { cur_ += delta; }

    CORBA::Boolean is_full() { return cur_ >= last_; }

    //
    // Additional ORBacus specific functions
    //
    void alloc(CORBA::ULong);

    void realloc(CORBA::ULong n)
    {
        //
        // Shrinking the buffer is illegal
        //
        assert(n >= len_);

        if(n > max_)
	{
            doRealloc(n);
	}
        else
        {
#ifdef OB_CLEAR_MEM
	    memset(data_ + len_, 0, n - len_);
#endif
            len_ = n;
            last_ = data_ + len_;
        }
    }

    void consume(BufferImpl*);
};

//
// The Buffer class (reference counted)
//
class Buffer : public BufferImpl, public OB::SimpleRefCount
{
public:

    Buffer() { }
    Buffer(CORBA::Octet* data, CORBA::ULong len) : BufferImpl(data, len) { }
    Buffer(CORBA::ULong n) : BufferImpl(n) { }

    //
    // Standard IDL to C++ Mapping
    //
    static inline Buffer_ptr _duplicate(Buffer_ptr p)
    { if(p) p -> _OB_incRef(); return p; }
    static inline Buffer_ptr _nil()
    { return 0; }
};    

typedef OB::ObjSeq< Buffer, int > BufferSeq;
typedef OB::SeqVar< OB::ObjSeq< Buffer, int > > BufferSeq_var;

} // End of namespace OCI

namespace CORBA
{

inline void
release(OCI::Buffer_ptr p)
{
    if(p)
        p -> _OB_decRef();
}

inline Boolean
is_nil(OCI::Buffer_ptr p)
{
    return p == 0;
}

} // End of namespace CORBA

#endif
