1
0
mirror of https://github.com/alliedmodders/hl2sdk.git synced 2025-01-09 10:39:03 +08:00
hl2sdk/public/tier1/keyvalues3.h
Vladimir f3b44f206d
Update KeyValues3Context & its allocation lifecycle (#289)
Co-authored-by: GAMMACASE <31375974+GAMMACASE@users.noreply.github.com>
Co-authored-by: Nyano <dison2233@126.com>
2025-01-01 22:36:06 +03:00

1617 lines
54 KiB
C++

#ifndef KEYVALUES3_H
#define KEYVALUES3_H
#ifdef _WIN32
#pragma once
#endif
#include "tier0/platform.h"
#include "tier0/dbg.h"
#include "tier1/bufferstring.h"
#include "tier1/generichash.h"
#include "tier1/strtools.h"
#include "tier1/utlbuffer.h"
#include "tier1/utlhashtable.h"
#include "tier1/utlmap.h"
#include "tier1/utlstring.h"
#include "tier1/utlstringtoken.h"
#include "tier1/utlsymbollarge.h"
#include "mathlib/vector4d.h"
#include "Color.h"
#include "bitvec.h"
#include "entityhandle.h"
#include <type_traits>
#include "tier0/memdbgon.h"
class KeyValues3;
class CKeyValues3Array;
class CKeyValues3Table;
class CKeyValues3Context;
struct KV1ToKV3Translation_t;
struct KV3ToKV1Translation_t;
/*
KeyValues3 is a data storage format. See https://developer.valvesoftware.com/wiki/KeyValues3
Supports various specific data types targeted at the Source2.
Each specific type corresponds to one of the basic types.
There are 2 ways to create KeyValues3:
1. Via CKeyValues3Context:
- KV's, arrays and tables are stored in fixed memory blocks (clusters) and therefore memory is allocated only when clusters are created.
- Supports metadata and some other things.
2. Directly through the constructor.
*/
struct KV3ID_t
{
const char* m_name;
uint64 m_data1;
uint64 m_data2;
};
// encodings
const KV3ID_t g_KV3Encoding_Text = { "text", 0x41C58A33E21C7F3Cull, 0xDAA323A6DA77799ull };
const KV3ID_t g_KV3Encoding_Binary = { "binary", 0x40C1F7D81B860500ull, 0x14E76782A47582ADull };
const KV3ID_t g_KV3Encoding_BinaryLZ4 = { "binary_lz4", 0x4F5C63A16847348Aull, 0x19B1D96F805397A1ull };
const KV3ID_t g_KV3Encoding_BinaryZSTD = { "binary_zstd", 0x4305FEF06F620A00ull, 0x29DBB14623045FA3ull };
const KV3ID_t g_KV3Encoding_BinaryBC = { "binary_bc", 0x4F6C95BC95791A46ull, 0xD2DFB7A1BC050BA7ull };
const KV3ID_t g_KV3Encoding_BinaryAuto = { "binary_auto", 0x45836B856EB109E6ull, 0x8C06046E3A7012A3ull };
// formats
const KV3ID_t g_KV3Format_Generic = { "generic", 0x469806E97412167Cull, 0xE73790B53EE6F2AFull };
enum KV1TextEscapeBehavior_t
{
KV1TEXT_ESC_BEHAVIOR_UNK1 = 0,
KV1TEXT_ESC_BEHAVIOR_UNK2 = 1,
};
enum KV3SaveTextFlags_t
{
KV3_SAVE_TEXT_NONE = 0,
KV3_SAVE_TEXT_TAGGED = (1 << 0), // adds subtype name before value
};
PLATFORM_OVERLOAD void DebugPrintKV3( const KeyValues3* kv );
// When using some LoadKV3/SaveKV3 functions, KV3ID_t structures must be filled in, which specify the format or encoding of the data.
PLATFORM_OVERLOAD bool LoadKV3( CKeyValues3Context* context, CUtlString* error, CUtlBuffer* input, const KV3ID_t& format, const char* kv_name );
PLATFORM_OVERLOAD bool LoadKV3( KeyValues3* kv, CUtlString* error, CUtlBuffer* input, const KV3ID_t& format, const char* kv_name );
PLATFORM_OVERLOAD bool LoadKV3( KeyValues3* kv, CUtlString* error, const char* input, const KV3ID_t& format, const char* kv_name );
PLATFORM_OVERLOAD bool LoadKV3FromFile( CKeyValues3Context* context, CUtlString* error, const char* filename, const char* path, const KV3ID_t& format );
PLATFORM_OVERLOAD bool LoadKV3FromFile( KeyValues3* kv, CUtlString* error, const char* filename, const char* path, const KV3ID_t& format );
PLATFORM_OVERLOAD bool LoadKV3FromJSON( KeyValues3* kv, CUtlString* error, const char* input, const char* kv_name );
PLATFORM_OVERLOAD bool LoadKV3FromJSONFile( KeyValues3* kv, CUtlString* error, const char* path, const char* filename );
PLATFORM_OVERLOAD bool LoadKV3FromKV1File( KeyValues3* kv, CUtlString* error, const char* path, const char* filename, KV1TextEscapeBehavior_t esc_behavior );
PLATFORM_OVERLOAD bool LoadKV3FromKV1Text( KeyValues3* kv, CUtlString* error, const char* input, KV1TextEscapeBehavior_t esc_behavior, const char* kv_name, bool unk );
PLATFORM_OVERLOAD bool LoadKV3FromKV1Text_Translated( KeyValues3* kv, CUtlString* error, const char* input, KV1TextEscapeBehavior_t esc_behavior, const KV1ToKV3Translation_t* translation, int unk1, const char* kv_name, bool unk2 );
PLATFORM_OVERLOAD bool LoadKV3FromKV3OrKV1( KeyValues3* kv, CUtlString* error, CUtlBuffer* input, const KV3ID_t& format, const char* kv_name );
PLATFORM_OVERLOAD bool LoadKV3FromOldSchemaText( KeyValues3* kv, CUtlString* error, CUtlBuffer* input, const KV3ID_t& format, const char* kv_name );
PLATFORM_OVERLOAD bool LoadKV3Text_NoHeader( KeyValues3* kv, CUtlString* error, const char* input, const KV3ID_t& format, const char* kv_name );
PLATFORM_OVERLOAD bool SaveKV3( const KV3ID_t& encoding, const KV3ID_t& format, const KeyValues3* kv, CUtlString* error, CUtlBuffer* output, uint flags = KV3_SAVE_TEXT_NONE );
PLATFORM_OVERLOAD bool SaveKV3AsJSON( const KeyValues3* kv, CUtlString* error, CUtlBuffer* output );
PLATFORM_OVERLOAD bool SaveKV3AsJSON( const KeyValues3* kv, CUtlString* error, CUtlString* output );
PLATFORM_OVERLOAD bool SaveKV3AsKV1Text( const KeyValues3* kv, CUtlString* error, CUtlBuffer* output, KV1TextEscapeBehavior_t esc_behavior );
PLATFORM_OVERLOAD bool SaveKV3AsKV1Text_Translated( const KeyValues3* kv, CUtlString* error, CUtlBuffer* output, KV1TextEscapeBehavior_t esc_behavior, const KV3ToKV1Translation_t* translation, int unk );
PLATFORM_OVERLOAD bool SaveKV3Text_NoHeader( const KeyValues3* kv, CUtlString* error, CBufferString* output, uint flags = KV3_SAVE_TEXT_NONE );
PLATFORM_OVERLOAD bool SaveKV3Text_NoHeader( const KeyValues3* kv, CUtlString* error, CUtlBuffer* output, uint flags = KV3_SAVE_TEXT_NONE );
PLATFORM_OVERLOAD bool SaveKV3Text_NoHeader( const KeyValues3* kv, CUtlString* error, CUtlString* output, uint flags = KV3_SAVE_TEXT_NONE );
PLATFORM_OVERLOAD bool SaveKV3Text_ToString( const KV3ID_t& format, const KeyValues3* kv, CUtlString* error, CBufferString* output, uint flags = KV3_SAVE_TEXT_NONE );
PLATFORM_OVERLOAD bool SaveKV3Text_ToString( const KV3ID_t& format, const KeyValues3* kv, CUtlString* error, CUtlString* output, uint flags = KV3_SAVE_TEXT_NONE );
PLATFORM_OVERLOAD bool SaveKV3ToFile( const KV3ID_t& encoding, const KV3ID_t& format, const KeyValues3* kv, CUtlString* error, const char* filename, const char* path, uint flags = KV3_SAVE_TEXT_NONE );
typedef int32 KV3MemberId_t;
#define KV3_INVALID_MEMBER ((KV3MemberId_t)-1)
// AMNOTE: These constants aren't actual constants, but rather calculated at compile time
// but the way they are calculated is unknown, previously it was using CUtlLeanVector min/max calculations
// but in here they seem to not match that behaviour.
enum
{
ALLOC_KV3TABLE_MIN = 4,
ALLOC_KV3TABLE_MAX = 0x6186154,
ALLOC_KV3ARRAY_MIN = 4,
ALLOC_KV3ARRAY_MAX = 0xFFFFF7F,
ALLOC_CONTEXT_NODELIST_MIN = 32,
ALLOC_CONTEXT_NODELIST_MAX = INT_MAX
};
enum
{
KV3_ARRAY_MAX_FIXED_MEMBERS = 6,
KV3_TABLE_MAX_FIXED_MEMBERS = 8,
KV3_CONTEXT_SIZE = 4608,
KV3_ARRAY_INIT_SIZE = 32,
KV3_TABLE_INIT_SIZE = 64,
KV3_CLUSTER_MAX_ELEMENTS = 253
};
enum KV3Type_t : uint8
{
KV3_TYPE_INVALID = 0,
KV3_TYPE_NULL,
KV3_TYPE_BOOL,
KV3_TYPE_INT,
KV3_TYPE_UINT,
KV3_TYPE_DOUBLE,
KV3_TYPE_STRING,
KV3_TYPE_BINARY_BLOB,
KV3_TYPE_ARRAY,
KV3_TYPE_TABLE,
KV3_TYPE_COUNT,
};
enum KV3TypeOpt_t : uint8
{
KV3_TYPEOPT_NONE = 0,
KV3_TYPEOPT_STRING_SHORT,
KV3_TYPEOPT_STRING_EXTERN,
KV3_TYPEOPT_BINARY_BLOB_EXTERN,
KV3_TYPEOPT_ARRAY_FLOAT32,
KV3_TYPEOPT_ARRAY_FLOAT64,
KV3_TYPEOPT_ARRAY_INT16,
KV3_TYPEOPT_ARRAY_INT32,
KV3_TYPEOPT_ARRAY_UINT8_SHORT,
KV3_TYPEOPT_ARRAY_INT16_SHORT,
};
enum KV3TypeEx_t : uint8
{
KV3_TYPEEX_INVALID = 0,
KV3_TYPEEX_NULL,
KV3_TYPEEX_BOOL,
KV3_TYPEEX_INT,
KV3_TYPEEX_UINT,
KV3_TYPEEX_DOUBLE,
KV3_TYPEEX_STRING = KV3_TYPE_STRING,
KV3_TYPEEX_STRING_SHORT = (KV3_TYPEEX_STRING|(KV3_TYPEOPT_STRING_SHORT << 4)),
KV3_TYPEEX_STRING_EXTERN = (KV3_TYPEEX_STRING|(KV3_TYPEOPT_STRING_EXTERN << 4)),
KV3_TYPEEX_BINARY_BLOB = KV3_TYPE_BINARY_BLOB,
KV3_TYPEEX_BINARY_BLOB_EXTERN = (KV3_TYPEEX_BINARY_BLOB|(KV3_TYPEOPT_BINARY_BLOB_EXTERN << 4)),
KV3_TYPEEX_ARRAY = KV3_TYPE_ARRAY,
KV3_TYPEEX_ARRAY_FLOAT32 = (KV3_TYPEEX_ARRAY|(KV3_TYPEOPT_ARRAY_FLOAT32 << 4)),
KV3_TYPEEX_ARRAY_FLOAT64 = (KV3_TYPEEX_ARRAY|(KV3_TYPEOPT_ARRAY_FLOAT64 << 4)),
KV3_TYPEEX_ARRAY_INT16 = (KV3_TYPEEX_ARRAY|(KV3_TYPEOPT_ARRAY_INT16 << 4)),
KV3_TYPEEX_ARRAY_INT32 = (KV3_TYPEEX_ARRAY|(KV3_TYPEOPT_ARRAY_INT32 << 4)),
KV3_TYPEEX_ARRAY_UINT8_SHORT = (KV3_TYPEEX_ARRAY|(KV3_TYPEOPT_ARRAY_UINT8_SHORT << 4)),
KV3_TYPEEX_ARRAY_INT16_SHORT = (KV3_TYPEEX_ARRAY|(KV3_TYPEOPT_ARRAY_INT16_SHORT << 4)),
KV3_TYPEEX_TABLE = KV3_TYPE_TABLE,
};
enum KV3SubType_t : uint8
{
KV3_SUBTYPE_INVALID = 0,
// string types
KV3_SUBTYPE_RESOURCE,
KV3_SUBTYPE_RESOURCE_NAME,
KV3_SUBTYPE_PANORAMA,
KV3_SUBTYPE_SOUNDEVENT,
KV3_SUBTYPE_SUBCLASS, // table type
KV3_SUBTYPE_ENTITY_NAME, // string type
KV3_SUBTYPE_LOCALIZE,
KV3_SUBTYPE_UNSPECIFIED,
KV3_SUBTYPE_NULL,
KV3_SUBTYPE_BINARY_BLOB,
KV3_SUBTYPE_ARRAY,
KV3_SUBTYPE_TABLE,
KV3_SUBTYPE_BOOL8,
KV3_SUBTYPE_CHAR8,
KV3_SUBTYPE_UCHAR32,
KV3_SUBTYPE_INT8,
KV3_SUBTYPE_UINT8,
KV3_SUBTYPE_INT16,
KV3_SUBTYPE_UINT16,
KV3_SUBTYPE_INT32,
KV3_SUBTYPE_UINT32,
KV3_SUBTYPE_INT64,
KV3_SUBTYPE_UINT64,
KV3_SUBTYPE_FLOAT32,
KV3_SUBTYPE_FLOAT64,
KV3_SUBTYPE_STRING,
KV3_SUBTYPE_POINTER,
KV3_SUBTYPE_COLOR32,
// vector types
KV3_SUBTYPE_VECTOR,
KV3_SUBTYPE_VECTOR2D,
KV3_SUBTYPE_VECTOR4D,
KV3_SUBTYPE_ROTATION_VECTOR,
KV3_SUBTYPE_QUATERNION,
KV3_SUBTYPE_QANGLE,
KV3_SUBTYPE_MATRIX3X4,
KV3_SUBTYPE_TRANSFORM,
KV3_SUBTYPE_STRING_TOKEN,
KV3_SUBTYPE_EHANDLE,
KV3_SUBTYPE_COUNT,
};
enum KV3ArrayAllocType_t
{
KV3_ARRAY_ALLOC_EXTERN = 0,
KV3_ARRAY_ALLOC_NORMAL = 1,
KV3_ARRAY_ALLOC_EXTERN_FREE = 2,
};
enum KV3ToStringFlags_t
{
KV3_TO_STRING_NONE = 0,
KV3_TO_STRING_DONT_CLEAR_BUFF = (1 << 0),
KV3_TO_STRING_DONT_APPEND_STRINGS = (1 << 1),
KV3_TO_STRING_APPEND_ONLY_NUMERICS = (1 << 2),
KV3_TO_STRING_RETURN_NON_NUMERICS = (1 << 3),
};
enum KV3MetaDataFlags_t
{
KV3_METADATA_MULTILINE_STRING = (1 << 0),
KV3_METADATA_SINGLE_QUOTED_STRING = (1 << 1),
};
namespace KV3Helpers
{
template <typename T, typename... Ts>
constexpr size_t PackAlignOf()
{
if constexpr (sizeof...(Ts) == 0)
return alignof(T);
else
return (alignof(T) > PackAlignOf<Ts...>()) ? alignof(T) : PackAlignOf<Ts...>();
}
template <size_t ALIGN, typename... Ts>
constexpr size_t PackSizeOf( int size )
{
return ((ALIGN_VALUE( size * sizeof( Ts ), ALIGN )) + ... + 0);
}
inline int CalcNewBufferSize( int old_size, int requested_size, int min_size, int max_size )
{
int new_size = MAX( old_size, min_size );
while(new_size < requested_size)
{
if(new_size < max_size / 2)
new_size *= 2;
else
{
new_size = max_size;
break;
}
}
return new_size;
}
}
struct KV3MetaData_t
{
KV3MetaData_t() : m_nLine( 0 ), m_nColumn( 0 ), m_nFlags( 0 ) {}
void Clear()
{
m_nLine = 0;
m_nColumn = 0;
m_nFlags = 0;
m_sName = CUtlSymbolLarge();
m_Comments.RemoveAll();
}
void Purge()
{
m_nLine = 0;
m_nColumn = 0;
m_nFlags = 0;
m_sName = CUtlSymbolLarge();
m_Comments.Purge();
}
typedef CUtlMap<int, CBufferStringGrowable<8>, int, CDefLess<int>> CommentsMap_t;
int m_nLine;
int m_nColumn;
uint m_nFlags;
CUtlSymbolLarge m_sName;
CommentsMap_t m_Comments;
};
struct KV3BinaryBlob_t
{
size_t m_nSize;
union
{
const byte* m_pubData;
byte m_ubData[1];
};
bool m_bFreeMemory;
};
class CKV3MemberName
{
public:
inline CKV3MemberName(const char* pszString): m_nHashCode(), m_pszString("")
{
if (!pszString || !pszString[0])
return;
m_nHashCode = MakeStringToken( pszString );
m_pszString = pszString;
}
inline CKV3MemberName(): m_nHashCode(), m_pszString("") {}
inline CKV3MemberName(unsigned int nHashCode, const char* pszString = ""): m_nHashCode(nHashCode), m_pszString(pszString) {}
inline unsigned int GetHashCode() const { return m_nHashCode.GetHashCode(); }
inline const char* GetString() const { return m_pszString; }
private:
CUtlStringToken m_nHashCode;
const char* m_pszString;
};
template<size_t SIZE, typename T>
class CKeyValues3ClusterImpl;
using CKeyValues3Cluster = CKeyValues3ClusterImpl<KV3_CLUSTER_MAX_ELEMENTS, KeyValues3>;
using CKeyValues3TableCluster = CKeyValues3ClusterImpl<KV3_TABLE_INIT_SIZE, CKeyValues3Table>;
using CKeyValues3ArrayCluster = CKeyValues3ClusterImpl<KV3_ARRAY_INIT_SIZE, CKeyValues3Array>;
class KeyValues3
{
public:
KeyValues3( KV3TypeEx_t type = KV3_TYPEEX_NULL, KV3SubType_t subtype = KV3_SUBTYPE_UNSPECIFIED );
KeyValues3( int cluster_elem, KV3TypeEx_t type, KV3SubType_t subtype );
~KeyValues3();
CKeyValues3Context* GetContext() const;
KV3MetaData_t* GetMetaData( CKeyValues3Context** ppCtx = nullptr ) const;
KV3Type_t GetType() const { return ( KV3Type_t )( m_TypeEx & 0xF ); }
KV3TypeEx_t GetTypeEx() const { return ( KV3TypeEx_t )m_TypeEx; }
KV3SubType_t GetSubType() const { return ( KV3SubType_t )m_SubType; }
const char* GetTypeAsString() const;
const char* GetSubTypeAsString() const;
const char* ToString( CBufferString& buff, uint flags = KV3_TO_STRING_NONE ) const;
void SetToNull() { PrepareForType( KV3_TYPEEX_NULL, KV3_SUBTYPE_NULL ); }
bool GetBool( bool defaultValue = false ) const { return GetValue<bool>( defaultValue ); }
char8 GetChar( char8 defaultValue = 0 ) const { return GetValue<char8>( defaultValue ); }
uchar32 GetUChar32( uchar32 defaultValue = 0 ) const { return GetValue<uint32>( defaultValue ); }
int8 GetInt8( int8 defaultValue = 0 ) const { return GetValue<int8>( defaultValue ); }
uint8 GetUInt8( uint8 defaultValue = 0 ) const { return GetValue<uint8>( defaultValue ); }
int16 GetShort( int16 defaultValue = 0 ) const { return GetValue<int16>( defaultValue ); }
uint16 GetUShort( uint16 defaultValue = 0 ) const { return GetValue<uint16>( defaultValue ); }
int32 GetInt( int32 defaultValue = 0 ) const { return GetValue<int32>( defaultValue ); }
uint32 GetUInt( uint32 defaultValue = 0 ) const { return GetValue<uint32>( defaultValue ); }
int64 GetInt64( int64 defaultValue = 0 ) const { return GetValue<int64>( defaultValue ); }
uint64 GetUInt64( uint64 defaultValue = 0 ) const { return GetValue<uint64>( defaultValue ); }
float32 GetFloat( float32 defaultValue = 0.0f ) const { return GetValue<float32>( defaultValue ); }
float64 GetDouble( float64 defaultValue = 0.0 ) const { return GetValue<float64>( defaultValue ); }
void SetBool( bool value ) { SetValue<bool>( value, KV3_TYPEEX_BOOL, KV3_SUBTYPE_BOOL8 ); }
void SetChar( char8 value ) { SetValue<char8>( value, KV3_TYPEEX_INT, KV3_SUBTYPE_CHAR8 ); }
void SetUChar32( uchar32 value ){ SetValue<uint32>( value, KV3_TYPEEX_UINT, KV3_SUBTYPE_UCHAR32 ); }
void SetInt8( int8 value ) { SetValue<int8>( value, KV3_TYPEEX_INT, KV3_SUBTYPE_INT8 ); }
void SetUInt8( uint8 value ) { SetValue<uint8>( value, KV3_TYPEEX_UINT, KV3_SUBTYPE_UINT8 ); }
void SetShort( int16 value ) { SetValue<int16>( value, KV3_TYPEEX_INT, KV3_SUBTYPE_INT16 ); }
void SetUShort( uint16 value ) { SetValue<uint16>( value, KV3_TYPEEX_UINT, KV3_SUBTYPE_UINT16 ); }
void SetInt( int32 value ) { SetValue<int32>( value, KV3_TYPEEX_INT, KV3_SUBTYPE_INT32 ); }
void SetUInt( uint32 value ) { SetValue<uint32>( value, KV3_TYPEEX_UINT, KV3_SUBTYPE_UINT32 ); }
void SetInt64( int64 value ) { SetValue<int64>( value, KV3_TYPEEX_INT, KV3_SUBTYPE_INT64 ); }
void SetUInt64( uint64 value ) { SetValue<uint64>( value, KV3_TYPEEX_UINT, KV3_SUBTYPE_UINT64 ); }
void SetFloat( float32 value ) { SetValue<float32>( value, KV3_TYPEEX_DOUBLE, KV3_SUBTYPE_FLOAT32 ); }
void SetDouble( float64 value ) { SetValue<float64>( value, KV3_TYPEEX_DOUBLE, KV3_SUBTYPE_FLOAT64 ); }
void* GetPointer( void *defaultValue = ( void* )0 ) const { return ( GetSubType() == KV3_SUBTYPE_POINTER ) ? ( void* )m_Data.m_UInt : defaultValue; }
void SetPointer( void* ptr ) { SetValue<uint64>( ( uint64 )ptr, KV3_TYPEEX_UINT, KV3_SUBTYPE_POINTER ); }
CUtlStringToken GetStringToken( CUtlStringToken defaultValue = CUtlStringToken() ) const { return ( GetSubType() == KV3_SUBTYPE_STRING_TOKEN ) ? CUtlStringToken( ( uint32 )m_Data.m_UInt ) : defaultValue; }
void SetStringToken( CUtlStringToken token ) { SetValue<uint32>( token.GetHashCode(), KV3_TYPEEX_UINT, KV3_SUBTYPE_STRING_TOKEN ); }
CEntityHandle GetEHandle( CEntityHandle defaultValue = CEntityHandle() ) const { return ( GetSubType() == KV3_SUBTYPE_EHANDLE ) ? CEntityHandle( ( uint32 )m_Data.m_UInt ) : defaultValue; }
void SetEHandle( CEntityHandle ehandle ) { SetValue<uint32>( ehandle.ToInt(), KV3_TYPEEX_UINT, KV3_SUBTYPE_EHANDLE ); }
const char* GetString( const char *defaultValue = "" ) const;
void SetString( const char* pString, KV3SubType_t subtype = KV3_SUBTYPE_STRING );
void SetStringExternal( const char* pString, KV3SubType_t subtype = KV3_SUBTYPE_STRING );
const byte* GetBinaryBlob() const;
int GetBinaryBlobSize() const;
void SetToBinaryBlob( const byte* blob, int size );
void SetToBinaryBlobExternal( const byte* blob, int size, bool free_mem );
Color GetColor( const Color &defaultValue = Color( 0, 0, 0, 255 ) ) const;
void SetColor( const Color &color );
Vector GetVector( const Vector &defaultValue = Vector( 0.0f, 0.0f, 0.0f ) ) const { return GetVecBasedObj<Vector>( 3, defaultValue ); }
Vector2D GetVector2D( const Vector2D &defaultValue = Vector2D( 0.0f, 0.0f ) ) const { return GetVecBasedObj<Vector2D>( 2, defaultValue ); }
Vector4D GetVector4D( const Vector4D &defaultValue = Vector4D( 0.0f, 0.0f, 0.0f, 0.0f ) ) const { return GetVecBasedObj<Vector4D>( 4, defaultValue ); }
Quaternion GetQuaternion( const Quaternion &defaultValue = Quaternion( 0.0f, 0.0f, 0.0f, 0.0f ) ) const { return GetVecBasedObj<Quaternion>( 4, defaultValue ); }
QAngle GetQAngle( const QAngle &defaultValue = QAngle( 0.0f, 0.0f, 0.0f ) ) const { return GetVecBasedObj<QAngle>( 3, defaultValue ); }
matrix3x4_t GetMatrix3x4( const matrix3x4_t &defaultValue = matrix3x4_t( Vector( 0.0f, 0.0f, 0.0f ), Vector( 0.0f, 0.0f, 0.0f ), Vector( 0.0f, 0.0f, 0.0f ), Vector( 0.0f, 0.0f, 0.0f ) ) ) const { return GetVecBasedObj<matrix3x4_t>( 3*4, defaultValue ); }
void SetVector( const Vector &vec ) { SetVecBasedObj<Vector>( vec, 3, KV3_SUBTYPE_VECTOR ); }
void SetVector2D( const Vector2D &vec2d ) { SetVecBasedObj<Vector2D>( vec2d, 2, KV3_SUBTYPE_VECTOR2D ); }
void SetVector4D( const Vector4D &vec4d ) { SetVecBasedObj<Vector4D>( vec4d, 4, KV3_SUBTYPE_VECTOR4D ); }
void SetQuaternion( const Quaternion &quat ) { SetVecBasedObj<Quaternion>( quat, 4, KV3_SUBTYPE_QUATERNION ); }
void SetQAngle( const QAngle &ang ) { SetVecBasedObj<QAngle>( ang, 3, KV3_SUBTYPE_QANGLE ); }
void SetMatrix3x4( const matrix3x4_t &matrix ) { SetVecBasedObj<matrix3x4_t>( matrix, 3*4, KV3_SUBTYPE_MATRIX3X4 ); }
int GetArrayElementCount() const;
KeyValues3** GetArrayBase();
KeyValues3* GetArrayElement( int elem );
KeyValues3* InsertArrayElementBefore( int elem );
KeyValues3* InsertArrayElementAfter( int elem ) { return InsertArrayElementBefore( elem + 1 ); }
KeyValues3* AddArrayElementToTail();
void SetArrayElementCount( int count, KV3TypeEx_t type = KV3_TYPEEX_NULL, KV3SubType_t subtype = KV3_SUBTYPE_UNSPECIFIED );
void SetToEmptyArray() { SetArrayElementCount( 0 ); }
void RemoveArrayElements( int elem, int num );
void RemoveArrayElement( int elem ) { RemoveArrayElements( elem, 1 ); }
int GetMemberCount() const;
KeyValues3* GetMember( KV3MemberId_t id );
const KeyValues3* GetMember( KV3MemberId_t id ) const { return const_cast<KeyValues3*>(this)->GetMember( id ); }
const char* GetMemberName( KV3MemberId_t id ) const;
CKV3MemberName GetMemberNameEx( KV3MemberId_t id ) const;
unsigned int GetMemberHash( KV3MemberId_t id ) const;
KeyValues3* FindMember( const CKV3MemberName &name, KeyValues3* defaultValue = NULL );
KeyValues3* FindOrCreateMember( const CKV3MemberName &name, bool *pCreated = NULL );
void SetToEmptyTable();
bool RemoveMember( KV3MemberId_t id );
bool RemoveMember( const KeyValues3* kv );
bool RemoveMember( const CKV3MemberName &name );
KeyValues3& operator=( const KeyValues3& src );
private:
KeyValues3( const KeyValues3& other );
union Data_t
{
Data_t() : m_nMemory(0)
{
}
bool m_Bool;
int64 m_Int;
uint64 m_UInt;
float64 m_Double;
const char* m_pString;
char m_szStringShort[8];
KV3BinaryBlob_t* m_pBinaryBlob;
CKeyValues3Array* m_pArray;
CKeyValues3Table* m_pTable;
union Array_t
{
float32* m_f32;
Vector *m_vec;
Vector2D *m_vec2;
Vector4D *m_vec4;
Quaternion *m_quat;
QAngle *m_ang;
matrix3x4_t *m_mat;
float64* m_f64;
int16* m_i16;
int32* m_i32;
uint8 m_u8Short[8];
int16 m_i16Short[4];
} m_Array;
uint64 m_nMemory;
void* m_pMemory;
char m_Memory[1];
};
void Alloc( int initial_size = 0, Data_t data = {}, int bytes_available = 0, bool should_free = false );
CKeyValues3Array *AllocArray( int initial_size = 0 );
CKeyValues3Table *AllocTable( int initial_size = 0 );
void AllocArrayInPlace( int initial_size, Data_t data, int preallocated_size, bool should_free );
void AllocTableInPlace( int initial_size, Data_t data, int preallocated_size, bool should_free );
template <typename T>
T *AllocateOnHeap( int initial_size = 0 );
template <typename T>
void FreeOnHeap( T *element );
void FreeArray( CKeyValues3Array *element, bool clearing_context = false );
void FreeTable( CKeyValues3Table *element, bool clearing_context = false );
KeyValues3 *AllocMember( KV3TypeEx_t type = KV3_TYPEEX_NULL, KV3SubType_t subtype = KV3_SUBTYPE_UNSPECIFIED );
void FreeMember( KeyValues3 *member );
void Free( bool bClearingContext = false );
void ResolveUnspecified();
void PrepareForType( KV3TypeEx_t type, KV3SubType_t subtype );
void OnClearContext();
void CopyFrom( const KeyValues3* pSrc );
int GetClusterElement() const { return m_nClusterElement; }
void SetClusterElement( int element ) { m_bExternalStorage = (element == -1); m_nClusterElement = element; }
CKeyValues3Cluster* GetCluster() const;
template < typename T > T FromString( T defaultValue ) const;
template < typename T > void SetDirect( T value );
template < typename T > T GetValue( T defaultValue ) const;
template < typename T > void SetValue( T value, KV3TypeEx_t type, KV3SubType_t subtype );
template < typename T > T GetVecBasedObj( int size, const T &defaultValue ) const;
template < typename T > void SetVecBasedObj( const T &obj, int size, KV3SubType_t subtype );
template < typename T >
void NormalizeArray( KV3TypeEx_t type, KV3SubType_t subtype, int size, const T* data, bool bFree );
void NormalizeArray();
template < typename T >
void AllocArray( int size, const T* data, KV3ArrayAllocType_t alloc_type, KV3TypeEx_t type_short, KV3TypeEx_t type_ptr, KV3SubType_t subtype, KV3TypeEx_t type_elem, KV3SubType_t subtype_elem );
bool ReadArrayInt32( int size, int32* data ) const;
bool ReadArrayFloat32( int size, float32* data ) const;
static constexpr size_t TotalSizeOf( int initial_size ) { return sizeof(KeyValues3); }
static constexpr size_t TotalSizeOfData( int size ) { return sizeof(Data_t); }
static constexpr size_t TotalSizeWithoutStaticData() { return sizeof(KeyValues3) - TotalSizeOfData( 0 ); }
private:
uint64 m_bExternalStorage : 1;
uint64 m_bFreeArrayMemory : 1;
uint64 m_TypeEx : 8;
uint64 m_SubType : 8;
uint64 m_nFlags : 8;
uint64 m_nClusterElement : 16;
uint64 m_nNumArrayElements : 5;
uint64 m_nReserved : 17;
Data_t m_Data;
friend CKeyValues3Cluster;
friend CKeyValues3ArrayCluster;
friend CKeyValues3TableCluster;
friend class CKeyValues3Context;
friend class CKeyValues3Table;
friend class CKeyValues3Array;
};
COMPILE_TIME_ASSERT(sizeof(KeyValues3) == 16);
class CKeyValues3Array
{
public:
typedef KeyValues3 *Element_t;
static const size_t DATA_SIZE = KV3_ARRAY_MAX_FIXED_MEMBERS;
static const size_t DATA_ALIGNMENT = KV3Helpers::PackAlignOf<Element_t>();
CKeyValues3Array( int cluster_elem = -1, int alloc_size = DATA_SIZE );
~CKeyValues3Array() { PurgeBuffers(); }
int GetClusterElement() const { return m_nClusterElement; }
void SetClusterElement( int element ) { m_nClusterElement = element; }
CKeyValues3ArrayCluster* GetCluster() const;
CKeyValues3Context* GetContext() const;
Element_t *Base() { return IsBaseStatic() ? &m_StaticElements[0] : m_pDynamicElements; };
Element_t const *Base() const { return const_cast<CKeyValues3Array *>(this)->Base(); }
Element_t Element( int i );
const Element_t Element( int i ) const { return const_cast<CKeyValues3Array*>(this)->Element( i ); }
int Count() const { return m_nCount; }
void EnsureElementCapacity( int count, bool force = false, bool dont_move = false );
void SetCount( KeyValues3 *parent, int count, KV3TypeEx_t type = KV3_TYPEEX_NULL, KV3SubType_t subtype = KV3_SUBTYPE_UNSPECIFIED );
Element_t* InsertMultipleBefore( KeyValues3 *parent, int from, int num );
void CopyFrom( KeyValues3 *parent, const CKeyValues3Array* pSrc );
void RemoveMultiple( KeyValues3 *parent, int from, int num );
void OnClearContext() { PurgeContent( nullptr, true ); }
void PurgeContent( KeyValues3 *parent = nullptr, bool clearing_context = false );
void PurgeBuffers();
static constexpr size_t TotalSizeOf( int initial_size ) { return ALIGN_VALUE( TotalSizeWithoutStaticData() + TotalSizeOfData( MAX( initial_size, 0 ) ), 8 ); }
static constexpr size_t TotalSizeOfData( int size ) { return MAX( (KV3Helpers::PackSizeOf<DATA_ALIGNMENT, Element_t>( size )), sizeof( m_pDynamicElements ) ); }
static constexpr size_t TotalSizeWithoutStaticData() { return sizeof( CKeyValues3Array ) - sizeof( m_StaticElements ); }
private:
int GetAllocatedChunks() const { return m_nAllocatedChunks; }
bool IsBaseStatic() { return !m_bIsDynamicallySized; }
size_t GetAllocatedBytesSize() const { return TotalSizeOfData( GetAllocatedChunks() ); }
private:
int m_nClusterElement;
int m_nAllocatedChunks;
int m_nCount;
uint8 m_nInitialSize;
bool m_bIsDynamicallySized;
bool m_unk001;
bool m_unk002;
union
{
Element_t m_StaticElements[DATA_SIZE];
Element_t *m_pDynamicElements;
};
};
COMPILE_TIME_ASSERT(sizeof(CKeyValues3Array) == 64);
class CKeyValues3Table
{
public:
enum : uint8
{
TABLEFL_NONE = 0,
TABLEFL_NAME_EXTERNAL = (1 << 0)
};
typedef uint32 Hash_t;
typedef KeyValues3* Member_t;
typedef const char* Name_t;
typedef uint8 Flags_t;
static const size_t DATA_SIZE = KV3_TABLE_MAX_FIXED_MEMBERS;
static const size_t DATA_ALIGNMENT = KV3Helpers::PackAlignOf<Hash_t, Member_t, Name_t, Flags_t>();
CKeyValues3Table( int cluster_elem = -1, int alloc_size = DATA_SIZE );
~CKeyValues3Table() { PurgeBuffers(); }
int GetClusterElement() const { return m_nClusterElement; }
void SetClusterElement( int element ) { m_nClusterElement = element; }
CKeyValues3TableCluster* GetCluster() const;
CKeyValues3Context* GetContext() const;
int GetMemberCount() const { return m_nCount; }
Member_t GetMember( KV3MemberId_t id );
const Member_t GetMember( KV3MemberId_t id ) const { return const_cast<CKeyValues3Table*>(this)->GetMember( id ); }
const Name_t GetMemberName( KV3MemberId_t id ) const;
const Hash_t GetMemberHash( KV3MemberId_t id ) const;
void PurgeFastSearch();
void EnableFastSearch();
void EnsureMemberCapacity( int num, bool force = false, bool dont_move = false );
KV3MemberId_t FindMember( const KeyValues3* kv ) const;
KV3MemberId_t FindMember( const CKV3MemberName &name );
KV3MemberId_t CreateMember( KeyValues3 *parent, const CKV3MemberName &name, bool name_external = false );
void CopyFrom( KeyValues3 *parent, const CKeyValues3Table* src );
void RemoveMember( KeyValues3 *parent, KV3MemberId_t id );
void RemoveAll( KeyValues3 *parent, int new_size = 0 );
void OnClearContext() { PurgeContent( nullptr, true ); }
void PurgeContent( KeyValues3 *parent = nullptr, bool bClearingContext = false );
void PurgeBuffers();
static constexpr size_t TotalSizeOf( int initial_size ) { return ALIGN_VALUE( TotalSizeWithoutStaticData() + TotalSizeOfData( MAX( initial_size, 0 ) ), 8 ); }
static constexpr size_t TotalSizeOfData( int size ) { return MAX( (KV3Helpers::PackSizeOf<DATA_ALIGNMENT, Hash_t, Member_t, Name_t, Flags_t>( size )), sizeof(m_pDynamicBuffer) ); }
static constexpr size_t TotalSizeWithoutStaticData() { return sizeof(CKeyValues3Table) - sizeof(m_StaticBuffer); }
private:
int GetAllocatedChunks() const { return m_nAllocatedChunks; }
bool IsBaseStatic() { return !m_bIsDynamicallySized; }
size_t GetAllocatedBytesSize() const { return TotalSizeOfData( GetAllocatedChunks() ); }
constexpr size_t OffsetToHashesBase( int size ) const { return 0; }
constexpr size_t OffsetToMembersBase( int size ) const { return KV3Helpers::PackSizeOf<DATA_ALIGNMENT, Hash_t>( size ); }
constexpr size_t OffsetToNamesBase( int size ) const { return KV3Helpers::PackSizeOf<DATA_ALIGNMENT, Hash_t, Member_t>( size ); }
constexpr size_t OffsetToFlagsBase( int size ) const { return KV3Helpers::PackSizeOf<DATA_ALIGNMENT, Hash_t, Member_t, Name_t>( size ); }
// Gets the base address (can change when adding elements!)
void *Base() { return IsBaseStatic() ? &m_StaticBuffer : m_pDynamicBuffer; };
Hash_t *HashesBase() { return reinterpret_cast<Hash_t *>((uint8 *)Base() + OffsetToHashesBase( GetAllocatedChunks() )); }
Member_t *MembersBase() { return reinterpret_cast<Member_t *>((uint8 *)Base() + OffsetToMembersBase( GetAllocatedChunks() )); }
Name_t *NamesBase() { return reinterpret_cast<Name_t *>((uint8 *)Base() + OffsetToNamesBase( GetAllocatedChunks() )); }
Flags_t *FlagsBase() { return reinterpret_cast<Flags_t *>((uint8 *)Base() + OffsetToFlagsBase( GetAllocatedChunks() )); }
const void *Base() const { return const_cast<CKeyValues3Table *>(this)->Base(); }
const Hash_t *HashesBase() const { return const_cast<CKeyValues3Table *>(this)->HashesBase(); }
const Member_t *MembersBase() const { return const_cast<CKeyValues3Table *>(this)->MembersBase(); }
const Name_t *NamesBase() const { return const_cast<CKeyValues3Table *>(this)->NamesBase(); }
const Flags_t *FlagsBase() const { return const_cast<CKeyValues3Table *>(this)->FlagsBase(); }
private:
int m_nClusterElement;
int m_nAllocatedChunks;
struct kv3tablefastsearch_t {
kv3tablefastsearch_t() : m_ignore( false ), m_ignores_counter( 0 ) {}
~kv3tablefastsearch_t() { Clear(); }
void Clear()
{
m_ignore = false;
m_ignores_counter = 0;
m_member_ids.RemoveAll();
}
struct EmptyHashFunctor { unsigned int operator()( uint32 n ) const { return n; } };
typedef CUtlHashtable<unsigned int, KV3MemberId_t, EmptyHashFunctor> Hashtable_t;
bool m_ignore;
int8 m_ignores_counter;
Hashtable_t m_member_ids;
} *m_pFastSearch;
int m_nCount;
uint8 m_nInitialSize;
bool m_bIsDynamicallySized;
bool m_unk001;
bool m_unk002;
union
{
struct
{
Hash_t m_Hashes[DATA_SIZE];
Member_t m_Members[DATA_SIZE];
Name_t m_Names[DATA_SIZE];
Flags_t m_Flags[DATA_SIZE];
} m_StaticBuffer;
void* m_pDynamicBuffer;
};
};
COMPILE_TIME_ASSERT(sizeof(CKeyValues3Table) == 192);
template <size_t SIZE, typename T>
class CKeyValues3ClusterImpl
{
public:
typedef T NodeType;
static const size_t CLUSTER_SIZE = SIZE;
union Node
{
Node() : m_pNextFree( nullptr ) {}
~Node() {}
NodeType m_Value;
Node *m_pNextFree;
};
enum
{
HEAP_MARKER = (1 << 31),
FLAGS_MASK = ~(HEAP_MARKER)
};
CKeyValues3ClusterImpl( CKeyValues3Context *context, bool allocated_on_heap = false, int initial_size = SIZE );
~CKeyValues3ClusterImpl() { Purge(); }
CKeyValues3Context *GetContext() const { return m_pContext; }
bool IsFull() const { return NumCount() >= NumAllocated(); }
bool IsAllocatedOnHeap() const { return (m_nAllocatedElements & HEAP_MARKER) != 0; }
int NumAllocated() const { return m_nAllocatedElements & FLAGS_MASK; }
int NumCount() const { return m_nElementCount; }
template <typename... Args, typename = std::enable_if_t<std::is_constructible_v<NodeType, Args...>, int>>
NodeType *Alloc( Args&&... args );
void Free( int element, bool clearing_context = false );
void Free( NodeType *node, bool clearing_context = false );
void Purge();
void Clear();
Node *GetNextFree() const { return m_pFirstFreeNode; }
void SetNextFree( Node *free ) { m_pFirstFreeNode = free; }
CKeyValues3ClusterImpl *GetNext() const { return m_pNext; }
void SetNext( CKeyValues3ClusterImpl *cluster ) { m_pNext = cluster; }
CKeyValues3ClusterImpl *GetPrev() const { return m_pPrev; }
void SetPrev( CKeyValues3ClusterImpl *cluster ) { m_pPrev = cluster; }
Node *Head() { return &m_Values[0]; }
Node *Tail() { return &m_Values[NumAllocated()]; }
const Node *Head() const { return const_cast<CKeyValues3ClusterImpl *>(this)->Head(); }
const Node *Tail() const { return const_cast<CKeyValues3ClusterImpl *>(this)->Tail(); }
void EnableMetaData( bool bEnable );
void ClearMetaData();
void PurgeMetaData();
void PurgeMetaData( int element );
KV3MetaData_t *GetMetaData( int element ) const;
int GetNodeIndex( NodeType *node ) const;
static constexpr size_t TotalSizeOf( int initial_size ) { return ALIGN_VALUE( TotalSizeWithoutStaticData() + TotalSizeOfData( MAX( initial_size, 0 ) ), 8 ); }
static constexpr size_t TotalSizeOfData( int size ) { return sizeof( Node ) * size; }
static constexpr size_t TotalSizeWithoutStaticData() { return sizeof( CKeyValues3ClusterImpl ) - TotalSizeOfData( SIZE ); }
friend CKeyValues3Cluster *KeyValues3::GetCluster() const;
friend CKeyValues3ArrayCluster *CKeyValues3Array::GetCluster() const;
friend CKeyValues3TableCluster *CKeyValues3Table::GetCluster() const;
private:
void InitNodes();
void PurgeNodes( bool clearing_context = false );
private:
struct kv3metadata_t
{
int m_AllocatedElements;
KV3MetaData_t m_elements[SIZE];
};
CKeyValues3Context *m_pContext;
Node *m_pFirstFreeNode;
int m_nAllocatedElements;
int m_nElementCount;
CKeyValues3ClusterImpl *m_pPrev;
CKeyValues3ClusterImpl *m_pNext;
kv3metadata_t *m_pMetaData;
Node m_Values[SIZE];
};
class CKeyValues3ContextBase
{
public:
CKeyValues3ContextBase( CKeyValues3Context* context );
~CKeyValues3ContextBase() { Purge(); }
void Clear();
void Purge();
protected:
template <typename CLUSTER>
struct ClusterNodeChain
{
ClusterNodeChain() : m_pTail( nullptr ), m_pHead( nullptr )
{}
void Reset()
{
m_pTail = nullptr;
m_pHead = nullptr;
}
void AddToChain( CLUSTER *cluster );
void RemoveFromChain( CLUSTER *cluster );
CLUSTER *m_pTail;
CLUSTER *m_pHead;
};
template <typename NODE>
class NodeList
{
public:
struct ListEntry
{
ListEntry *m_pNext;
NODE m_Value;
};
NodeList() : m_nUsedBytes( 0 ), m_nAllocatedBytes( 0 ), m_pData( nullptr )
{}
~NodeList() { Free(); }
NODE *Alloc( int initial_size );
void Free() { Purge(); }
void Purge();
void Clear();
int UsedBytes() const { return m_nUsedBytes; }
int AllocatedBytes() const { return m_nAllocatedBytes; }
int FreeBytes() const { return m_nAllocatedBytes - m_nUsedBytes; }
bool IsFull() const { return m_nUsedBytes >= m_nAllocatedBytes; }
ListEntry *Head() { return &m_pData[0]; }
ListEntry *Tail() { return reinterpret_cast<ListEntry *>((uint8 *)Head() + m_nUsedBytes); }
bool IsWithinRange( NODE *element ) { return AllocatedBytes() > 0 && element >= (void *)Head() && element < (void *)Tail(); }
private:
void EnsureByteSize( int bytes_needed );
private:
int m_nUsedBytes;
int m_nAllocatedBytes;
ListEntry *m_pData;
};
CKeyValues3Context* m_pContext;
CUtlBuffer m_BinaryData;
CKeyValues3Cluster m_KV3BaseCluster;
ClusterNodeChain<CKeyValues3Cluster> m_KV3PartialClusters;
ClusterNodeChain<CKeyValues3Cluster> m_KV3FullClusters;
ClusterNodeChain<CKeyValues3ArrayCluster> m_PartialArrayClusters;
ClusterNodeChain<CKeyValues3ArrayCluster> m_FullArrayClusters;
NodeList<CKeyValues3Array> m_RawArrayEntries;
ClusterNodeChain<CKeyValues3TableCluster> m_PartialTableClusters;
ClusterNodeChain<CKeyValues3TableCluster> m_FullTableClusters;
NodeList<CKeyValues3Table> m_RawTableEntries;
CUtlSymbolTableLarge m_Symbols;
bool m_bMetaDataEnabled: 1;
bool m_bFormatConverted: 1;
bool m_bRootAvailabe: 1;
IParsingErrorListener* m_pParsingErrorListener;
friend class KeyValues3;
};
class CKeyValues3Context : public CKeyValues3ContextBase
{
typedef CKeyValues3ContextBase BaseClass;
public:
CKeyValues3Context( bool bNoRoot = false );
~CKeyValues3Context() { Purge(); }
KeyValues3* AllocKV( KV3TypeEx_t type = KV3_TYPEEX_NULL, KV3SubType_t subtype = KV3_SUBTYPE_UNSPECIFIED );
// WARNING: kv must belong to this context!!!
void FreeKV( KeyValues3* kv );
// gets the pre-allocated kv if we indicated its existence when creating the context
KeyValues3* Root();
const KeyValues3* Root() const { return const_cast<CKeyValues3Context*>(this)->Root(); }
bool IsMetaDataEnabled() const { return m_bMetaDataEnabled; }
// returns true if the desired format was converted to another after loading via LoadKV3*
bool IsFormatConverted() const { return m_bFormatConverted; }
bool IsRootAvailabe() const { return m_bRootAvailabe; }
// filled in after loading via LoadKV3* in binary encoding
CUtlBuffer& GetBinaryData() { return m_BinaryData; }
IParsingErrorListener* GetParsingErrorListener() const { return m_pParsingErrorListener; }
void SetParsingErrorListener( IParsingErrorListener* listener ) { m_pParsingErrorListener = listener; }
const char* AllocString( const char* pString );
void EnableMetaData( bool bEnable );
void CopyMetaData( KV3MetaData_t* pDest, const KV3MetaData_t* pSrc );
void Clear();
void Purge();
template <typename CLUSTER>
void ClearClusterNodeChain( ClusterNodeChain<CLUSTER> &cluster_node );
template <typename CLUSTER>
void PurgeClusterNodeChain( ClusterNodeChain<CLUSTER> &cluster_node );
bool IsArrayRawAllocated( CKeyValues3Array *element ) { return m_RawArrayEntries.IsWithinRange( element ); }
bool IsTableRawAllocated( CKeyValues3Table *element ) { return m_RawTableEntries.IsWithinRange( element ); }
private:
template <typename CLUSTER>
void MoveToPartial( ClusterNodeChain<CLUSTER> &full_cluster, ClusterNodeChain<CLUSTER> &partial_cluster );
template <typename CLUSTER, typename... Args, typename = typename std::enable_if_t<std::is_constructible_v<typename CLUSTER::NodeType, Args...>, int>>
auto Alloc( ClusterNodeChain<CLUSTER> &partial_clusters, ClusterNodeChain<CLUSTER> &full_clusters, int initial_size, Args&&... args );
template <typename CLUSTER, typename NODE, typename... Args, typename = typename std::enable_if_t<std::is_constructible_v<typename CLUSTER::NodeType, Args...>, int>>
NODE *RawAlloc( NodeList<NODE> &raw_array, ClusterNodeChain<CLUSTER> &partial_clusters, ClusterNodeChain<CLUSTER> &full_clusters, int initial_size, Args&&... args );
CKeyValues3Array *AllocArray( int initial_size = 0 ) { return RawAlloc( m_RawArrayEntries, m_PartialArrayClusters, m_FullArrayClusters, initial_size ); }
CKeyValues3Table *AllocTable( int initial_size = 0 ) { return RawAlloc( m_RawTableEntries, m_PartialTableClusters, m_FullTableClusters, initial_size ); }
template<typename CLUSTER, typename NODE>
void Free( NODE *element, ClusterNodeChain<CLUSTER> &partial_clusters, ClusterNodeChain<CLUSTER> &full_clusters );
inline void FreeArray( CKeyValues3Array *element ) { Free( element, m_PartialArrayClusters, m_FullArrayClusters ); }
inline void FreeTable( CKeyValues3Table *element ) { Free( element, m_PartialTableClusters, m_FullTableClusters ); }
private:
uint8 pad[ KV3_CONTEXT_SIZE - ( sizeof( BaseClass ) % KV3_CONTEXT_SIZE ) ];
friend class KeyValues3;
};
COMPILE_TIME_ASSERT(sizeof(CKeyValues3Context) == KV3_CONTEXT_SIZE);
template < typename T > inline T KeyValues3::FromString( T defaultValue ) const { Assert( 0 ); return defaultValue; }
template <> inline bool KeyValues3::FromString( bool defaultValue ) const { return V_StringToBool( GetString(), defaultValue ); }
template <> inline char8 KeyValues3::FromString( char8 defaultValue ) const { return V_StringToInt8( GetString(), defaultValue ); }
template <> inline int8 KeyValues3::FromString( int8 defaultValue ) const { return V_StringToInt8( GetString(), defaultValue ); }
template <> inline uint8 KeyValues3::FromString( uint8 defaultValue ) const { return V_StringToUint8( GetString(), defaultValue ); }
template <> inline int16 KeyValues3::FromString( int16 defaultValue ) const { return V_StringToInt16( GetString(), defaultValue ); }
template <> inline uint16 KeyValues3::FromString( uint16 defaultValue ) const { return V_StringToUint16( GetString(), defaultValue ); }
template <> inline int32 KeyValues3::FromString( int32 defaultValue ) const { return V_StringToInt32( GetString(), defaultValue ); }
template <> inline uint32 KeyValues3::FromString( uint32 defaultValue ) const { return V_StringToUint32( GetString(), defaultValue ); }
template <> inline int64 KeyValues3::FromString( int64 defaultValue ) const { return V_StringToInt64( GetString(), defaultValue ); }
template <> inline uint64 KeyValues3::FromString( uint64 defaultValue ) const { return V_StringToUint64( GetString(), defaultValue ); }
template <> inline float32 KeyValues3::FromString( float32 defaultValue ) const { return V_StringToFloat32( GetString(), defaultValue ); }
template <> inline float64 KeyValues3::FromString( float64 defaultValue ) const { return V_StringToFloat64( GetString(), defaultValue ); }
template < typename T > inline void KeyValues3::SetDirect( T value ) { Assert( 0 ); }
template <> inline void KeyValues3::SetDirect( bool value ) { m_Data.m_Bool = value; }
template <> inline void KeyValues3::SetDirect( char8 value ) { m_Data.m_Int = ( int64 )value; }
template <> inline void KeyValues3::SetDirect( int8 value ) { m_Data.m_Int = ( int64 )value; }
template <> inline void KeyValues3::SetDirect( uint8 value ) { m_Data.m_UInt = ( uint64 )value; }
template <> inline void KeyValues3::SetDirect( int16 value ) { m_Data.m_Int = ( int64 )value; }
template <> inline void KeyValues3::SetDirect( uint16 value ) { m_Data.m_UInt = ( uint64 )value; }
template <> inline void KeyValues3::SetDirect( int32 value ) { m_Data.m_Int = ( int64 )value; }
template <> inline void KeyValues3::SetDirect( uint32 value ) { m_Data.m_UInt = ( uint64 )value; }
template <> inline void KeyValues3::SetDirect( int64 value ) { m_Data.m_Int = value; }
template <> inline void KeyValues3::SetDirect( uint64 value ) { m_Data.m_UInt = value; }
template <> inline void KeyValues3::SetDirect( float32 value ) { m_Data.m_Double = ( float64 )value; }
template <> inline void KeyValues3::SetDirect( float64 value ) { m_Data.m_Double = value; }
template < typename T >
T KeyValues3::GetVecBasedObj( int size, const T &defaultValue ) const
{
T obj;
if ( !ReadArrayFloat32( size, obj.Base() ) )
obj = defaultValue;
return obj;
}
template < typename T >
void KeyValues3::SetVecBasedObj( const T &obj, int size, KV3SubType_t subtype )
{
AllocArray<float32>( size, obj.Base(), KV3_ARRAY_ALLOC_NORMAL, KV3_TYPEEX_INVALID, KV3_TYPEEX_ARRAY_FLOAT32, subtype, KV3_TYPEEX_DOUBLE, KV3_SUBTYPE_FLOAT32 );
}
template < typename T >
T KeyValues3::GetValue( T defaultValue ) const
{
switch ( GetType() )
{
case KV3_TYPE_BOOL:
return ( T )m_Data.m_Bool;
case KV3_TYPE_INT:
return ( T )m_Data.m_Int;
case KV3_TYPE_UINT:
return ( GetSubType() != KV3_SUBTYPE_POINTER ) ? ( T )m_Data.m_UInt : defaultValue;
case KV3_TYPE_DOUBLE:
return ( T )m_Data.m_Double;
case KV3_TYPE_STRING:
return FromString<T>( defaultValue );
default:
return defaultValue;
}
}
template < typename T >
void KeyValues3::SetValue( T value, KV3TypeEx_t type, KV3SubType_t subtype )
{
PrepareForType( type, subtype );
SetDirect<T>( value );
}
template < typename T >
void KeyValues3::NormalizeArray( KV3TypeEx_t type, KV3SubType_t subtype, int size, const T* data, bool bFree )
{
m_TypeEx = KV3_TYPEEX_ARRAY;
Alloc( size );
m_Data.m_pArray->SetCount( this, size, type, subtype );
CKeyValues3Array::Element_t* arr = m_Data.m_pArray->Base();
for ( int i = 0; i < m_Data.m_pArray->Count(); ++i )
arr[ i ]->SetDirect( data[ i ] );
if ( bFree )
free( (void*)data );
}
template<typename T>
inline T *KeyValues3::AllocateOnHeap( int initial_size )
{
if(initial_size <= 0)
initial_size = T::DATA_SIZE;
auto element = (T *)g_pMemAlloc->RegionAlloc( MEMALLOC_REGION_ALLOC_4, T::TotalSizeOf( initial_size ) );
Construct( element, -1, initial_size );
return element;
}
template<typename T>
inline void KeyValues3::FreeOnHeap( T *element )
{
Destruct( element );
g_pMemAlloc->RegionFree( MEMALLOC_REGION_FREE_4, element );
}
template < typename T >
void KeyValues3::AllocArray( int size, const T* data, KV3ArrayAllocType_t alloc_type, KV3TypeEx_t type_short, KV3TypeEx_t type_ptr, KV3SubType_t subtype, KV3TypeEx_t type_elem, KV3SubType_t subtype_elem )
{
int nMaxSizeShort = sizeof( uint64 ) / sizeof( T );
if ( type_short != KV3_TYPEEX_INVALID && size <= nMaxSizeShort )
{
if ( alloc_type == KV3_ARRAY_ALLOC_EXTERN && type_ptr != KV3_TYPEEX_INVALID )
{
PrepareForType( type_ptr, subtype );
m_bFreeArrayMemory = false;
m_nNumArrayElements = size;
m_Data.m_pMemory = (void*)data;
}
else
{
PrepareForType( type_short, subtype );
m_bFreeArrayMemory = false;
m_nNumArrayElements = size;
m_Data.m_pMemory = NULL;
memcpy( &m_Data.m_pMemory, data, size * sizeof( T ) );
if ( alloc_type == KV3_ARRAY_ALLOC_EXTERN_FREE )
free( (void*)data );
}
}
else if ( type_ptr != KV3_TYPEEX_INVALID && size <= 31 )
{
PrepareForType( type_ptr, subtype );
m_nNumArrayElements = size;
if ( alloc_type == KV3_ARRAY_ALLOC_EXTERN )
{
m_bFreeArrayMemory = false;
m_Data.m_pMemory = (void*)data;
}
else if ( alloc_type == KV3_ARRAY_ALLOC_EXTERN_FREE )
{
m_bFreeArrayMemory = true;
m_Data.m_pMemory = (void*)data;
}
else
{
m_bFreeArrayMemory = true;
m_Data.m_pMemory = malloc( size * sizeof( T ) );
memcpy( m_Data.m_pMemory, data, size * sizeof( T ) );
}
}
else
{
PrepareForType( KV3_TYPEEX_ARRAY, subtype );
m_Data.m_pArray->SetCount( this, size, type_elem, subtype_elem );
CKeyValues3Array::Element_t* arr = m_Data.m_pArray->Base();
for ( int i = 0; i < m_Data.m_pArray->Count(); ++i )
arr[ i ]->SetValue<T>( data[ i ], type_elem, subtype_elem );
if ( alloc_type == KV3_ARRAY_ALLOC_EXTERN_FREE )
free( (void*)data );
}
}
template<size_t SIZE, typename T>
inline CKeyValues3ClusterImpl<SIZE, T>::CKeyValues3ClusterImpl( CKeyValues3Context *context, bool allocated_on_heap, int initial_size ) :
m_pContext( context ),
m_pFirstFreeNode( nullptr ),
m_nAllocatedElements( initial_size | (allocated_on_heap ? HEAP_MARKER : 0) ),
m_nElementCount( 0 ),
m_pPrev( nullptr ),
m_pNext( nullptr ),
m_pMetaData( nullptr )
{
InitNodes();
}
template<size_t SIZE, typename T>
template<typename... Args, typename>
inline T *CKeyValues3ClusterImpl<SIZE, T>::Alloc( Args&&... args )
{
Assert( !IsFull() );
Node *node = GetNextFree();
Assert( node != nullptr );
SetNextFree( node->m_pNextFree );
Construct( &node->m_Value, std::forward<Args>( args )... );
node->m_Value.SetClusterElement( GetNodeIndex( &node->m_Value ) );
m_nElementCount++;
return &node->m_Value;
}
template<size_t SIZE, typename T>
inline void CKeyValues3ClusterImpl<SIZE, T>::Free( NodeType* node, bool clearing_context )
{
Assert( node >= (void *)Head() && node < (void *)Tail() );
Free( GetNodeIndex( node ), clearing_context );
}
template<size_t SIZE, typename T>
inline void CKeyValues3ClusterImpl<SIZE, T>::Free( int element, bool clearing_context )
{
Assert( element >= 0 && element < NumAllocated() );
Node *node = &m_Values[element];
if(clearing_context)
node->m_Value.OnClearContext();
else
Destruct( &node->m_Value );
m_nElementCount--;
node->m_pNextFree = GetNextFree();
SetNextFree( node );
}
template<size_t SIZE, typename T>
inline void CKeyValues3ClusterImpl<SIZE, T>::InitNodes()
{
Node *iter = Tail() - 1;
Node *prev = nullptr;
for(int i = 0; i < NumAllocated(); i++, iter--)
{
iter->m_pNextFree = prev;
prev = iter;
}
m_nElementCount = 0;
SetNextFree( prev );
}
template<size_t SIZE, typename T>
inline void CKeyValues3ClusterImpl<SIZE, T>::PurgeNodes( bool clearing_context )
{
CVarBitVec free_nodes( NumAllocated() * BITS_PER_INT );
for(auto iter = GetNextFree(); iter; iter = iter->m_pNextFree)
{
free_nodes.Set( GetNodeIndex( &iter->m_Value ) );
}
if(!free_nodes.IsAllSet())
{
for(int i = 0; i < NumAllocated(); i++)
{
if(!free_nodes.IsBitSet( i ))
{
Free( i, clearing_context );
}
}
InitNodes();
}
}
template<size_t SIZE, typename T>
inline int CKeyValues3ClusterImpl<SIZE, T>::GetNodeIndex( NodeType *element ) const
{
Node *node = reinterpret_cast<Node *>(element);
auto head = Head();
if(node < head || node >= Tail())
return -1;
return node - head;
}
template<size_t SIZE, typename T>
inline void CKeyValues3ClusterImpl<SIZE, T>::Purge()
{
PurgeNodes( false );
PurgeMetaData();
}
template<size_t SIZE, typename T>
inline void CKeyValues3ClusterImpl<SIZE, T>::Clear()
{
PurgeNodes( true );
ClearMetaData();
}
template<size_t SIZE, typename T>
void CKeyValues3ClusterImpl<SIZE, T>::EnableMetaData( bool bEnable )
{
if(bEnable)
{
if(!m_pMetaData)
{
m_pMetaData = (kv3metadata_t *)g_pMemAlloc->RegionAlloc( MEMALLOC_REGION_ALLOC_4, (NumAllocated() * sizeof(KV3MetaData_t)) + 8 );
m_pMetaData->m_AllocatedElements = NumAllocated();
}
}
else
{
PurgeMetaData();
}
}
template<size_t SIZE, typename T>
void CKeyValues3ClusterImpl<SIZE, T>::ClearMetaData()
{
if(m_pMetaData)
{
for(int i = 0; i < m_pMetaData->m_AllocatedElements; i++)
{
m_pMetaData->m_elements[i].Clear();
}
}
}
template<size_t SIZE, typename T>
void CKeyValues3ClusterImpl<SIZE, T>::PurgeMetaData()
{
if(m_pMetaData)
{
for(int i = 0; i < m_pMetaData->m_AllocatedElements; i++)
{
m_pMetaData->m_elements[i].Purge();
}
g_pMemAlloc->RegionFree( MEMALLOC_REGION_FREE_4, m_pMetaData );
}
m_pMetaData = nullptr;
}
template<size_t SIZE, typename T>
void CKeyValues3ClusterImpl<SIZE, T>::PurgeMetaData( int element )
{
if(!m_pMetaData)
return;
Assert( element >= 0 && element < m_pMetaData->m_AllocatedElements );
GetMetaData( element )->Clear();
}
template<size_t SIZE, typename T>
KV3MetaData_t *CKeyValues3ClusterImpl<SIZE, T>::GetMetaData( int element ) const
{
if(!m_pMetaData)
return nullptr;
Assert( element >= 0 && element < m_pMetaData->m_AllocatedElements );
return &m_pMetaData->m_elements[element];
}
template<typename CLUSTER>
inline void CKeyValues3ContextBase::ClusterNodeChain<CLUSTER>::AddToChain( CLUSTER *cluster )
{
if(m_pTail)
m_pTail->SetNext( cluster );
else
m_pHead = cluster;
cluster->SetNext( nullptr );
cluster->SetPrev( m_pTail );
m_pTail = cluster;
}
template<typename CLUSTER>
inline void CKeyValues3ContextBase::ClusterNodeChain<CLUSTER>::RemoveFromChain( CLUSTER *cluster )
{
auto prev = cluster->GetPrev();
auto next = cluster->GetNext();
if(prev)
prev->SetNext( next );
else
m_pHead = next;
if(next)
next->SetPrev( prev );
else
m_pTail = prev;
cluster->SetPrev( nullptr );
cluster->SetNext( nullptr );
}
template<typename NODE>
inline void CKeyValues3ContextBase::NodeList<NODE>::EnsureByteSize( int bytes_needed )
{
if(bytes_needed < m_nAllocatedBytes)
return;
int new_alloc_size = KV3Helpers::CalcNewBufferSize( m_nAllocatedBytes, bytes_needed, ALLOC_CONTEXT_NODELIST_MIN, ALLOC_CONTEXT_NODELIST_MAX );
m_pData = (ListEntry *)realloc( m_pData, new_alloc_size );
m_nAllocatedBytes = new_alloc_size;
}
template<typename NODE>
inline NODE *CKeyValues3ContextBase::NodeList<NODE>::Alloc( int initial_size )
{
int byte_size_needed = m_nUsedBytes + NODE::TotalSizeOf( initial_size ) + 8;
EnsureByteSize( byte_size_needed );
auto entry = Tail();
m_nUsedBytes = byte_size_needed;
Construct( &entry->m_Value, -1, initial_size );
entry->m_pNext = Tail();
return &entry->m_Value;
}
template<typename NODE>
inline void CKeyValues3ContextBase::NodeList<NODE>::Clear()
{
if(m_nAllocatedBytes > 0)
{
for(auto iter = Head(); iter; iter = iter->m_pNext)
{
Destruct( iter );
}
}
m_nUsedBytes = 0;
}
template<typename NODE>
inline void CKeyValues3ContextBase::NodeList<NODE>::Purge()
{
Clear();
free( m_pData );
m_pData = nullptr;
}
template<typename CLUSTER>
inline void CKeyValues3Context::PurgeClusterNodeChain( ClusterNodeChain<CLUSTER> &cluster_node )
{
for(auto node = cluster_node.m_pTail; node; node = node->GetPrev())
{
if(node->IsAllocatedOnHeap())
{
node->Purge();
g_pMemAlloc->RegionFree( MEMALLOC_REGION_FREE_4, node );
}
else
{
node->Clear();
}
}
cluster_node.Reset();
}
template<typename CLUSTER>
inline void CKeyValues3Context::ClearClusterNodeChain( ClusterNodeChain<CLUSTER> &cluster_node )
{
for(auto node = cluster_node.m_pTail; node; node = node->GetPrev())
{
node->Clear();
}
}
template<typename CLUSTER>
inline void CKeyValues3Context::MoveToPartial( ClusterNodeChain<CLUSTER> &full_cluster, ClusterNodeChain<CLUSTER> &partial_cluster )
{
for(auto node = full_cluster.m_pTail; node; node = node->GetPrev())
{
partial_cluster.AddToChain( node );
}
full_cluster.Reset();
}
template <typename CLUSTER, typename... Args, typename>
auto CKeyValues3Context::Alloc( ClusterNodeChain<CLUSTER> &partial_clusters,
ClusterNodeChain<CLUSTER> &full_clusters,
int initial_size, Args&&... args )
{
auto cluster = partial_clusters.m_pTail;
typename CLUSTER::NodeType *elem = nullptr;
if(cluster)
{
elem = cluster->Alloc( std::forward<Args>( args )... );
if(cluster->IsFull())
{
partial_clusters.RemoveFromChain( cluster );
full_clusters.AddToChain( cluster );
}
}
else
{
cluster = (CLUSTER *)g_pMemAlloc->RegionAlloc( MEMALLOC_REGION_ALLOC_4, CLUSTER::TotalSizeOf( initial_size ) );
Construct( cluster, this, true, initial_size );
partial_clusters.AddToChain( cluster );
elem = cluster->Alloc( std::forward<Args>( args )... );
}
return elem;
}
template<typename CLUSTER, typename NODE, typename ...Args, typename>
inline NODE *CKeyValues3Context::RawAlloc( NodeList<NODE> &raw_array, ClusterNodeChain<CLUSTER> &partial_clusters, ClusterNodeChain<CLUSTER> &full_clusters, int initial_size, Args && ...args )
{
int needed_byte_size = MAX( NODE::TotalSizeOf( initial_size ), 32 );
if(raw_array.IsFull() || needed_byte_size > raw_array.FreeBytes())
{
if(initial_size <= NODE::DATA_SIZE)
return Alloc( partial_clusters, full_clusters, CLUSTER::CLUSTER_SIZE );
else
return nullptr;
}
return raw_array.Alloc( initial_size );
}
template<typename CLUSTER, typename NODE>
void CKeyValues3Context::Free( NODE *element, ClusterNodeChain<CLUSTER> &partial_clusters, ClusterNodeChain<CLUSTER> &full_clusters )
{
auto cluster = element->GetCluster();
Assert( cluster != nullptr && cluster->GetContext() == m_pContext );
cluster->Free( element );
int num_allocated = cluster->NumAllocated();
if(cluster->NumCount() > 0)
{
if(cluster->NumCount() == cluster->NumAllocated() - 1)
{
full_clusters.RemoveFromChain( cluster );
partial_clusters.AddToChain( cluster );
}
}
else if(cluster->IsAllocatedOnHeap())
{
partial_clusters.RemoveFromChain( cluster );
Destruct( cluster );
g_pMemAlloc->RegionFree( MEMALLOC_REGION_FREE_4, cluster );
}
}
#include "tier0/memdbgoff.h"
#endif // KEYVALUES3_H