//========= 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 struct DataLinkerClassProperties { enum {kDataAlignment = nAlignment}; enum {kDataCookie = nCookie};// 0 means no cookie }; template struct DataLinkerClassPropertiesSelector: public DataLinkerClassProperties // 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 {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 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 struct OffsetAndSize_t: public Offset_t { int32 size; const T& operator []( int index ) const { Assert(index < size); return this->GetPtr()[index]; } }; template struct OffsetSizeAndStride_t : public OffsetAndSize_t { 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 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 T* Write(uint count = 1); template T* WriteWithCookie(uint count = 1); template T* WriteWithCookieStrided(uint count, uint stride); template void WriteSimple(const T x) { *Write() = 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 T* WriteAndLink(Offset_t*pOffset, uint count = 1); template T* WriteAndLink(OffsetAndSize_t*pOffset, uint count = 1); // intentionally not implemented - use explicit WriteAndLinkArray() template T* WriteAndLinkArray(OffsetAndSize_t*pOffsetAndSize, uint count = 1); template T* WriteAndLinkStrided(OffsetSizeAndStride_t*pOffset, uint nStride, uint count = 1); template void Link(Offset_t*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 inline T* IBasicStream::Write(uint count) { // TODO: insert reflection code here uint nAlignment = DataLinkerClassPropertiesSelector::kDataAlignment; Align(nAlignment); return (T*)WriteBytes(count * sizeof(T)); } template inline T* IBasicStream::WriteWithCookie(uint count) { // TODO: insert reflection code here uint nAlignment = DataLinkerClassPropertiesSelector::kDataAlignment; if(nCookie) { if(nAlignment > sizeof(int32)) { Align(nAlignment, -4); } else { Align(sizeof(int32));// we want the cookie itself aligned properly } WriteSimple(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 inline T* IBasicStream::WriteWithCookieStrided(uint count, uint stride) { // TODO: insert reflection code here uint nAlignment = DataLinkerClassPropertiesSelector::kDataAlignment; if(nCookie) { if(nAlignment > sizeof(int32)) { Align(nAlignment, -4); } else { Align(sizeof(int32));// we want the cookie itself aligned properly } WriteSimple(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 inline void IStream::Link(Offset_t*pOffset, const T* pTarget) { Link(&pOffset->offset, pTarget); } template inline T* IStream::WriteAndLink(Offset_t*pOffset, uint count) { T* p = WriteWithCookie(count); Link(&pOffset->offset, p); return p; } template inline T* IStream::WriteAndLinkArray(OffsetAndSize_t*pOffsetAndSize, uint count) { pOffsetAndSize->size = count; if(count) { T* p = WriteWithCookie(count); Link(&pOffsetAndSize->offset, p); return p; } else { pOffsetAndSize->offset = 0; return NULL; } } template inline T* IStream::WriteAndLinkStrided(OffsetSizeAndStride_t*pOffset, uint stride, uint count) { pOffset->size = count; pOffset->stride = stride; if(count) { T* p = WriteWithCookieStrided(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