FieldPath.h
// FieldPath.h
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2006
@import "cxObject.h"
#include "Ceda/cxUtils/StreamInterfaces.h"
#include "Ceda/cxUtils/VariableLengthSerialise.h"
// yuk- needed for FieldPathArchive
#include "Ceda/cxUtils/BasicTypes.h"
#include "Ceda/cxUtils/Archive.h"
#include "Ceda/cxUtils/PagedBuffer.h"
#include "Ceda/cxUtils/PagedBufferAsStream.h"
namespace ceda
{
class Archive;
/*
TODO:
* The FieldPath class as written is suitable when deserialising a path. However when *generating*
a path it should be possible to provide a special implementation for the given size. In the
common case where the path fits into a local buffer we can avoid the need for a destructor to run.
This should increase performance of operation generation.
We of course need to ensure binary compatabilty!
*/
@def int PATH_LOCALBUF_SIZE = 4*4
// Binary compatible with FieldPath! It is safe to create a FieldPath4 on the frame, and reinterpret cast it
// to a const FieldPath&
class FieldPath4
{
public:
FieldPath4(ssize_t v) : m_size(4), m_capacity(4), m_localBuffer(v) {}
private:
ssize_t AvoidClangWarning() { return m_size+m_capacity+m_localBuffer; }
private:
ssize_t m_size;
ssize_t m_capacity;
ssize_t m_localBuffer;
};
class FieldPathArchive;
class @api FieldPath
{
public:
FieldPath() :
m_size(0),
m_capacity(PATH_LOCALBUF_SIZE)
{
}
FieldPath(const void* buffer, ssize_t size);
@if (false)
{
// Constructors that take ints on the command line
@scope
{
@def mWriteBuffer(buffer,int i) =
{
@if (i > 0)
{
mWriteBuffer(buffer,i-1)
*reinterpret_cast<int32*>(&buffer[@((i-1)*4)]) = v@@i;
}
}
@def mArgs(int i) =
{
@if (i == 1) {int32 v1} @else {mArgs(i-1),int32 v@@i}
}
@let ssize_t i = 1
@while(i*4 <= PATH_LOCALBUF_SIZE)
{
@if (i == 1) {explicit}
FieldPath(mArgs(i)) : m_size(@(i*4)), m_capacity(PATH_LOCALBUF_SIZE)
{
mWriteBuffer(m_localBuffer,i)
}
@let i = i+1
}
@while(i*4 <= PATH_HEAPBUF_SIZE)
{
FieldPath(mArgs(i)) : m_size(@(i*4)), m_capacity(@(i*4))
{
m_heapBuffer = new octet_t[m_capacity];
mWriteBuffer(m_heapBuffer,i)
}
@let i = i+1
}
}
}
~FieldPath();
FieldPath(const FieldPath& rhs);
FieldPath& operator=(const FieldPath& rhs);
/////////////// InitBuffer ///////////////
/*
Used for the efficient initialisation of paths which can be stored in
the local buffer (i.e. size <= PATH_LOCALBUF_SIZE) and for which the size is known
up front.
Must be called just after default construction of the path.
Returns the address of the buffer to subsequently be initialised by the client
The following example initialises a path to [0x01 0x01 0x02 0x01 0x04]:
FieldPath path;
octet_t* p = path.InitLocalBuffer(5);
* (int32*) p = 0x01020101; // initialise first 4 bytes
p[4] = 0x04; // initialise fifth byte
Unfortunately it is not possible to avoid having the client know about the distinction
between local and heap allocation buffers, because MSVC has been found inadequate
at compiler optimisation. It seems that once it sees a function perform heap
allocation, it refuses to optimise it (even though it could in theory find that heap
allocation is impossible in that instance).
*/
octet_t* InitLocalBuffer(ssize_t size)
{
cxAssert(m_capacity == PATH_LOCALBUF_SIZE);
cxAssert(0 <= size && size <= PATH_LOCALBUF_SIZE);
m_size = size;
return m_localBuffer;
}
// As for InitLocalBuffer, except when it is known that size > PATH_LOCALBUF_SIZE
// Must only be called after default construction of the path.
octet_t* InitHeapBuffer(ssize_t size);
// Like InitLocalBuffer() and InitHeapBuffer() without the constraint on the size,
// or the assumption that it is called just after the default ctor.
octet_t* InitBuffer(ssize_t size);
/////////////// BeginReserve - EndReserve ///////////////
/*
Used for the efficient initialisation of paths for which there is an upper bound on
the size, such that it is known it will fit into the local buffer
(i.e. size <= PATH_LOCALBUF_SIZE), but the actual size is not known until after
writing the content.
Must be called just after default construction of the path.
Returns the address of the buffer to be initialised by the calling function
Example:
FieldPath path;
octet_t* p = path.BeginReserveLocal();
* (int32*) p = 0x01020101; P += 4; // initialise first 4 bytes
p = SerialiseVariableLengthInteger(p,x); // Write variable length x (1-5 bytes)
*p++ = 0x04;
path.EndReserveLocal(p);
*/
octet_t* BeginReserveLocal()
{
cxAssert(m_capacity == PATH_LOCALBUF_SIZE);
return m_localBuffer;
}
void EndReserveLocal(octet_t* pos)
{
cxAssert(m_capacity == PATH_LOCALBUF_SIZE);
m_size = pos - m_localBuffer;
cxAssert(0 <= m_size && m_size <= m_capacity);
}
octet_t* BeginReserveHeap(ssize_t capacity);
void EndReserveHeap(octet_t* pos)
{
cxAssert(m_size == 0);
cxAssert(m_capacity > PATH_LOCALBUF_SIZE);
m_size = pos - m_heapBuffer;
cxAssert(0 <= m_size && m_size <= m_capacity);
}
octet_t* BeginReserve(ssize_t capacity);
void EndReserve(octet_t* pos);
///////////////
void reserve(ssize_t capacity);
void clear();
bool empty() const { return m_size == 0; }
/// Append the given buffer to the end of the path
void Append(const void* buffer, ssize_t numBytes);
// Append x by writing as a variable length uint32. Returns the number of bytes
// added to the path
ssize_t AppendUint32(uint32 x)
{
octet_t buffer[5];
ssize_t n = SerialiseVariableLengthInteger(buffer,x) - buffer;
Append(buffer,n);
return n;
}
ssize_t Append_ssize_t(ssize_t x)
{
octet_t buffer[5];
ssize_t n = SerialiseVariableLengthInteger(buffer,x) - buffer;
Append(buffer,n);
return n;
}
// The inverse of Append
void EraseTail(ssize_t numBytes)
{
cxAssert(m_size >= numBytes);
m_size -= numBytes;
}
bool operator==(const FieldPath& rhs) const;
bool operator!=(const FieldPath& rhs) const { return !operator==(rhs); }
bool operator<(const FieldPath& rhs) const;
const octet_t* begin() const { return (m_capacity > PATH_LOCALBUF_SIZE) ? m_heapBuffer : m_localBuffer; }
const octet_t* end() const { return begin() + m_size; }
octet_t* begin() { return (m_capacity > PATH_LOCALBUF_SIZE) ? m_heapBuffer : m_localBuffer; }
octet_t* end() { return begin() + m_size; }
ssize_t size() const { return m_size; }
void Write(xostream& os) const;
void Serialise(Archive& ar) const;
void Deserialise(InputArchive& ar);
private:
// Current size in bytes. Must have 0 <= m_size <= m_capacity
ssize_t m_size;
// If m_capacity > PATH_LOCALBUF_SIZE then the heap allocated buffer is used,
// otherwise the local buffer
ssize_t m_capacity;
union
{
octet_t* m_heapBuffer;
octet_t m_localBuffer[PATH_LOCALBUF_SIZE];
};
};
///////////////////////////////////////////////////////////////////////////////////////////////////
// Adapter FieldPath to IOutputStream
struct PathAsOutputStream : public IOutputStream
{
PathAsOutputStream(FieldPath& path) : m_path(path) {}
// Implementation of IOutputStream
virtual void WriteStream(const void* buffer, ssize_t numBytes)
{
m_path.Append(buffer,numBytes);
}
virtual void FlushStream() {}
FieldPath& m_path;
};
///////////////////////////////////////////////////////////////////////////////////////////////////
// FieldPathArchive
class @api FieldPathArchive : public Archive
{
public:
FieldPathArchive() :
Archive(&m_os),
m_os(m_pb)
{
}
void InitPath(FieldPath& path);
private:
PagedBufferToOutputStreamAdapter m_os;
PagedBuffer m_pb;
friend class FieldPath;
};
///////////////////////////////////////////////////////////////////////////////////////////////////
class AppendPath
{
public:
AppendPath(FieldPath& path, ssize_t index) :
path_(path)
{
n_ = path_.Append_ssize_t(index);
}
~AppendPath()
{
path_.EraseTail(n_);
}
private:
FieldPath& path_;
ssize_t n_;
};
} // namespace ceda