csgo-2018-source/public/datalinker_interface.h
2021-07-24 21:11:47 -07:00

426 lines
11 KiB
C++

//========= 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