|
|
//========= Copyright c 1996-2008, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef DATA_LINKER_INTERFACE_H
#define DATA_LINKER_INTERFACE_H
#include "tier0/platform.h"
#include "tier0/basetypes.h"
#include "tier0/dbg.h"
template <uint nAlignment = 1, uint nCookie = 0> struct DataLinkerClassProperties { enum {kDataAlignment = nAlignment}; enum {kDataCookie = nCookie};// 0 means no cookie
};
template <typename T> struct DataLinkerClassPropertiesSelector: public DataLinkerClassProperties<sizeof(T)&1?1:sizeof(T)&3?2:4> // heuristic: 1-, 2- and 4-byte aligned type size calls for 1-, 2- and 4-byte alignment by default
{ };
//#define DATALINKER_ALIGNMENT(ALIGNMENT) enum {kDataAlignment = ALIGNMENT}
#define DATALINKER_CLASS_ALIGNMENT(CLASS, ALIGNMENT) template <> struct DataLinkerClassPropertiesSelector<CLASS> {enum {kDataAlignment = ALIGNMENT};}
DATALINKER_CLASS_ALIGNMENT(float,4); DATALINKER_CLASS_ALIGNMENT(int32,4); DATALINKER_CLASS_ALIGNMENT(int16,2); DATALINKER_CLASS_ALIGNMENT(int8,1); DATALINKER_CLASS_ALIGNMENT(uint32,4); DATALINKER_CLASS_ALIGNMENT(uint16,2); DATALINKER_CLASS_ALIGNMENT(uint8,1);
// undefine DATALINKER_CHECK_COOKIES if you don't want runtime cookie checks
#define DATALINKER_CHECK_COOKIES
namespace DataLinker {
inline byte *ResolveOffset(int32 *pOffset) { int offset = *pOffset; return offset ? ((byte*)pOffset) + offset : NULL; }
inline byte *ResolveOffsetFast(const int32 *pOffset) { int offset = *pOffset; Assert(offset != 0); return ((byte*)pOffset) + offset; }
// AT RUN-TIME ONLY the offset converts automatically into pointers to the appropritate type
// at tool-time, you should use LinkSource_t and LinkTarget_t
template <typename T, int32 nCookie = 0> struct Offset_t { enum {kDataAlignment = 4};
int32 offset; bool operator == (int zero)const {Assert(zero == 0); return offset == zero;} bool IsNull()const {return offset == 0;} int32 NotNull()const {return offset;} const T* GetPtr()const { // validate
byte *ptr = ResolveOffsetFast(&offset); #ifdef DATALINKER_CHECK_COOKIES
if(nCookie) { if(nCookie != ((int*)ptr)[-1]) { Error("Invalid data cookie %d != %d\n", ((int*)ptr)[-1], nCookie); } } #endif
return (const T*)ptr; } //const T* GetPtrFast()const {return (const T*)ResolveOffsetFast(&offset);}
operator const T* ()const{return GetPtr();} const T* operator ->() const{return GetPtr();} };
template <typename T,int32 nCookie = 0> struct OffsetAndSize_t: public Offset_t<T,nCookie> { int32 size;
const T& operator []( int index ) const { Assert(index < size); return this->GetPtr()[index]; } };
template <typename T,int32 nCookie = 0> struct OffsetSizeAndStride_t : public OffsetAndSize_t<T,nCookie> { int32 stride; const T& operator []( int index ) const { Assert(index < this->size); return *(const T*)(ResolveOffsetFast(&this->offset) + stride * index); } };
enum {kSpecialTargetUndefined = -1}; enum {kSpecialTargetNull = 0}; enum {kSpecialTargetDefault = 1};// resolved pointer/offset right in the LinkSource
// this is resolved or unresolved reference within the data block
// it's unique for the life cycle of the Stream and must either be resolved or
// never referenced by the end
struct LinkTarget_t { int m_id; friend class Stream; LinkTarget_t(){m_id = kSpecialTargetUndefined;} // -1 is anonymous/undefined reference
};
struct LinkTargetNull_t: public LinkTarget_t { LinkTargetNull_t(){m_id = kSpecialTargetNull;} // 0 is special default case meaning null pointer/offset
};
/*
template <typename T> struct Target_t { T* m_ptr; operator T* () {return m_ptr;} T* operator -> () {return m_ptr;} }; */
struct LinkSource_t { protected: int m_offset; friend class Stream; LinkSource_t(){} };
enum OffsetTypeEnum { kOtRelative32bit, kOtRelative16bit, kOtAbsolute32bit };
//////////////////////////////////////////////////////////////////////////
// this class may be useful at runtime to use fast serialize interface (without data linking)
//
abstract_class IBasicStream { public: virtual void* WriteBytes(uint numBytes) = 0;
virtual void Align(uint nAlignment, int nOffset = 0) = 0; inline void AlignPointer(){Align(4);} virtual long Tell() = 0;
// the Begin/End semantics differ in different implementations
// in some, it can be ignored, some others can use it for statistics and some others may set fixed page boundaries based on the names and/or flags
virtual void Begin(const char *nName, uint flags = 0) = 0; virtual void End() = 0; virtual void PrintStats() = 0; virtual void ClearStats() = 0;
template <typename T> T* Write(uint count = 1);
template <typename T, int32 nCookie> T* WriteWithCookie(uint count = 1);
template <typename T, int32 nCookie> T* WriteWithCookieStrided(uint count, uint stride);
template <typename T> void WriteSimple(const T x) { *Write<T>() = x; }
virtual void EnsureAvailable(uint addCapacity) = 0;
inline void WriteFloat(float x) { WriteSimple(x); } inline void WriteU32(uint32 x) { WriteSimple(x); } inline void WriteU16(uint16 x) { WriteSimple(x); } inline void WriteByte(byte x) { WriteSimple(x); }
};
//////////////////////////////////////////////////////////////////////////
// this is light-weight version of IStream with Links
// It can link on-the-fly, has minimal overhead but cannot late-bind symbols
// you basically have to manage all your late-bindings by managing
// pointers to offsets, which is fine in 99% of use cases.
// WARNING: there are no error-checking facilities here. If you forget to set
// a link, you'll have a NULL offset and no warnings or errors
//
abstract_class IStream: public IBasicStream { public: template <typename T,int32 nCookie> T* WriteAndLink(Offset_t<T,nCookie>*pOffset, uint count = 1); template <typename T,int32 nCookie> T* WriteAndLink(OffsetAndSize_t<T,nCookie>*pOffset, uint count = 1); // intentionally not implemented - use explicit WriteAndLinkArray()
template <typename T,int32 nCookie> T* WriteAndLinkArray(OffsetAndSize_t<T,nCookie>*pOffsetAndSize, uint count = 1); template <typename T,int32 nCookie> T* WriteAndLinkStrided(OffsetSizeAndStride_t<T,nCookie>*pOffset, uint nStride, uint count = 1); template <typename T,int32 nCookie> void Link(Offset_t<T,nCookie>*pOffset, const T* pTarget);
virtual void Link(int32 *pOffset, const void *pTarget) = 0; virtual void Link(int16 *pOffset, const void *pTarget) = 0;
virtual bool Compile(void *pBuffer) = 0; virtual uint GetTotalSize()const = 0; };
//////////////////////////////////////////////////////////////////////////
// This is IStream with late-binding capabilities. You can link multiple sources to
// the same target, then change your mind and reset the target somewhere and it'll
// automatically track all sources and reset them to the latest target at compilation.
// You can create unresolved targets to resolve them later.
// You can extend this to multiple object files linked at later stage (szDescription
// must be unique identifiers)
//
abstract_class ILinkStream: public IStream { public: virtual LinkSource_t WriteOffset(const char *szDescription) = 0; virtual LinkSource_t WriteOffset(LinkTarget_t linkTarget, const char *szDescription) = 0; virtual LinkSource_t WriteNullOffset(const char *szDescription) = 0;
virtual void Link(LinkSource_t, LinkTarget_t, const char *szDescription) = 0; virtual LinkSource_t LinkToHere(int32 *pOffset, const char *szDescription) = 0; virtual LinkSource_t Link(int32 *pOffset, LinkTarget_t linkTarget, const char *szDescription) = 0;
virtual LinkTarget_t NewTarget() = 0; // create new, unresolved target
virtual LinkTarget_t NewTarget(void *pWhere) = 0; virtual LinkTarget_t NewTargetHere() = 0; // creates a target right here
virtual void SetTargetHere(LinkTarget_t) = 0; // sets the given target to point to right here
virtual void SetTargetNull(LinkTarget_t) = 0; // set this target to point to NULL
virtual LinkSource_t NewOffset(int *pOffset, const char *szDescription) = 0;
virtual bool IsDeclared(LinkTarget_t linkTarget)const = 0; virtual bool IsSet(LinkTarget_t linkTarget)const = 0; virtual bool IsDefined(LinkSource_t linkSource)const = 0; virtual bool IsLinked(LinkSource_t linkSource)const = 0; };
template <typename T> inline T* IBasicStream::Write(uint count) { // TODO: insert reflection code here
uint nAlignment = DataLinkerClassPropertiesSelector<T>::kDataAlignment; Align(nAlignment); return (T*)WriteBytes(count * sizeof(T)); }
template <typename T, int32 nCookie> inline T* IBasicStream::WriteWithCookie(uint count) { // TODO: insert reflection code here
uint nAlignment = DataLinkerClassPropertiesSelector<T>::kDataAlignment;
if(nCookie) { if(nAlignment > sizeof(int32)) { Align(nAlignment, -4); } else { Align(sizeof(int32));// we want the cookie itself aligned properly
} WriteSimple<int32>(nCookie); Assert((Tell()&(nAlignment-1)) == 0); // must be correctly aligned by now
} else { if(nAlignment > 1) { Align(nAlignment); } } return (T*)WriteBytes(count * sizeof(T)); }
template <typename T, int32 nCookie> inline T* IBasicStream::WriteWithCookieStrided(uint count, uint stride) { // TODO: insert reflection code here
uint nAlignment = DataLinkerClassPropertiesSelector<T>::kDataAlignment;
if(nCookie) { if(nAlignment > sizeof(int32)) { Align(nAlignment, -4); } else { Align(sizeof(int32));// we want the cookie itself aligned properly
} WriteSimple<int32>(nCookie); Assert((Tell()&(nAlignment-1)) == 0); // must be correctly aligned by now
} else { if(nAlignment > 1) { Align(nAlignment); } } if(count) return (T*)WriteBytes((count-1) * stride + sizeof(T)); else return (T*)WriteBytes(0); }
template <typename T,int32 nCookie> inline void IStream::Link(Offset_t<T,nCookie>*pOffset, const T* pTarget) { Link(&pOffset->offset, pTarget); }
template <typename T, int32 nCookie> inline T* IStream::WriteAndLink(Offset_t<T,nCookie>*pOffset, uint count) { T* p = WriteWithCookie<T,nCookie>(count); Link(&pOffset->offset, p); return p; }
template <typename T, int32 nCookie> inline T* IStream::WriteAndLinkArray(OffsetAndSize_t<T,nCookie>*pOffsetAndSize, uint count) { pOffsetAndSize->size = count; if(count) { T* p = WriteWithCookie<T,nCookie>(count); Link(&pOffsetAndSize->offset, p); return p; } else { pOffsetAndSize->offset = 0; return NULL; } }
template <typename T, int32 nCookie> inline T* IStream::WriteAndLinkStrided(OffsetSizeAndStride_t<T,nCookie>*pOffset, uint stride, uint count) { pOffset->size = count; pOffset->stride = stride; if(count) { T* p = WriteWithCookieStrided<T,nCookie>(count, stride); Link(&pOffset->offset, p); return p; } else { pOffset->offset = 0; return NULL; } }
}
struct DataLinkerBasicStreamRange_t { DataLinker::IBasicStream *m_pStream; DataLinkerBasicStreamRange_t(DataLinker::IBasicStream *pStream, const char *name): m_pStream(pStream){pStream->Begin(name);} ~DataLinkerBasicStreamRange_t(){m_pStream->End();} };
#define DATALINKER_RANGE(STREAM,NAME) DataLinkerBasicStreamRange_t dataLinker_basicStreamRange##__LINE((STREAM),(NAME))
#endif
|