1
0
mirror of https://github.com/alliedmodders/hl2sdk.git synced 2025-09-19 20:16:10 +08:00

Update CBufferString

This commit is contained in:
GAMMACASE
2025-01-17 01:15:47 +03:00
parent 4362433e95
commit 369b2fcb0f

View File

@ -12,42 +12,30 @@
class CFormatStringElement; class CFormatStringElement;
class IFormatOutputStream; class IFormatOutputStream;
template<size_t MAX_SIZE, bool AllowHeapAllocation>
class CBufferStringGrowable;
/* /*
Main idea of CBufferString is to provide the base class for the CBufferStringGrowable wich implements stack allocation Main idea of CBufferString is to provide stack allocated string
with the ability to convert to the heap allocation if allowed. with the ability to convert to the heap allocation if allowed.
Example usage of CBufferStringGrowable class: By default CBufferString provides 8 bytes of stack allocation and could be increased by
using CBufferStringN<SIZE> where custom stack SIZE could be used.
Example usage of CBufferStringN class:
* Basic buffer allocation: * Basic buffer allocation:
``` ```
CBufferStringGrowable<256> buff; CBufferStringN<256> buff;
buff.Insert(0, "Hello World!"); buff.Insert(0, "Hello World!");
printf("Result: %s\n", buff.Get()); printf("Result: %s\n", buff.Get());
``` ```
additionaly the heap allocation of the buffer could be disabled, by providing ``AllowHeapAllocation`` template argument, additionaly the heap allocation of the buffer could be disabled. If the heap allocation is disabled and
by disabling heap allocation, if the buffer capacity is not enough to perform the operation, the app would exit with an Assert; if the buffer capacity is not enough to perform the growing operation, the app would exit with an Assert;
* Additional usage: * Most, if not all the functions would ensure the buffer capacity and enlarge it when needed.
CBufferString::IsStackAllocated() - could be used to check if the buffer is stack allocated; In case of stack allocated buffers, if the requested size exceeds stack size, it would switch to heap allocation instead.
CBufferString::IsHeapAllocated() - could be used to check if the buffer is heap allocated;
CBufferString::Get() - would return a pointer to the data, or an empty string if it's not allocated.
* Additionaly current length of the buffer could be read via CBufferString::GetTotalNumber()
and currently allocated amount of bytes could be read via CBufferString::GetAllocatedNumber()
* Most, if not all the functions would ensure the buffer capacity and enlarge it when needed,
in case of stack allocated buffers, it would switch to heap allocation instead.
*/ */
class CBufferString class CBufferString
{ {
protected:
// You shouldn't be initializing this class, use CBufferStringGrowable instead.
CBufferString() {}
public: public:
enum EAllocationOption_t enum EAllocationOption_t
{ {
@ -55,8 +43,7 @@ public:
UNK2 = 0, UNK2 = 0,
UNK3 = (1 << 1), UNK3 = (1 << 1),
UNK4 = (1 << 8), UNK4 = (1 << 8),
UNK5 = (1 << 9), UNK5 = (1 << 9)
ALLOW_HEAP_ALLOCATION = (1 << 31)
}; };
enum EAllocationFlags_t enum EAllocationFlags_t
@ -64,10 +51,91 @@ public:
LENGTH_MASK = (1 << 30) - 1, LENGTH_MASK = (1 << 30) - 1,
FLAGS_MASK = ~LENGTH_MASK, FLAGS_MASK = ~LENGTH_MASK,
STACK_ALLOCATION_MARKER = (1 << 30), // Flags in m_nLength
HEAP_ALLOCATION_MARKER = (1 << 31) // Means it tried to grow larger than static size and heap allocation was disabled
OVERFLOWED_MARKER = (1 << 30),
// Means it owns the heap buffer and it needs to be cleaned up
FREE_HEAP_MARKER = (1 << 31),
// Flags in m_nAllocatedSize
// Means it uses stack allocated buffer
STACK_ALLOCATED_MARKER = (1 << 30),
// Allows the buffer to grow beyond the static size on the heap
ALLOW_HEAP_ALLOCATION = (1 << 31)
}; };
public:
CBufferString( bool bAllowHeapAllocation = true ) :
m_nLength( 0 ),
m_nAllocatedSize( (bAllowHeapAllocation * ALLOW_HEAP_ALLOCATION) | STACK_ALLOCATED_MARKER | sizeof( m_szString ) ),
m_pString( nullptr )
{ }
CBufferString( const char *pString, bool bAllowHeapAllocation = true ) :
CBufferString( bAllowHeapAllocation )
{
Insert( 0, pString );
}
protected:
CBufferString( size_t nAllocatedSize, bool bAllowHeapAllocation = true ) :
m_nLength( 0 ),
m_nAllocatedSize( (bAllowHeapAllocation * ALLOW_HEAP_ALLOCATION) | STACK_ALLOCATED_MARKER | (nAllocatedSize + sizeof( m_szString )) ),
m_pString( nullptr )
{
Assert( nAllocatedSize > 8 );
}
public:
CBufferString( const CBufferString &other ) : CBufferString() { *this = other; }
CBufferString &operator=( const CBufferString &src )
{
Clear();
Insert( 0, src.Get() );
return *this;
}
~CBufferString() { Purge(); }
void SetHeapAllocationState( bool state )
{
if(state)
m_nAllocatedSize |= ALLOW_HEAP_ALLOCATION;
else
m_nAllocatedSize &= ~ALLOW_HEAP_ALLOCATION;
}
int AllocatedNum() const { return m_nAllocatedSize & LENGTH_MASK; }
int Length() const { return m_nLength & LENGTH_MASK; }
bool CanHeapAllocate() const { return (m_nAllocatedSize & ALLOW_HEAP_ALLOCATION) != 0; }
bool IsStackAllocated() const { return (m_nAllocatedSize & STACK_ALLOCATED_MARKER) != 0; }
bool ShouldFreeMemory() const { return (m_nLength & FREE_HEAP_MARKER) != 0; }
bool IsOverflowed() const { return (m_nLength & OVERFLOWED_MARKER) != 0; }
bool IsInputStringUnsafe( const char *pData ) const
{
return ((void *)pData >= this && (void *)pData < &this[1]) ||
(!IsAllocationEmpty() && pData >= Base() && pData < (Base() + AllocatedNum()));
}
bool IsAllocationEmpty() const { return AllocatedNum() == 0; }
protected:
char *Base() { return IsStackAllocated() ? m_szString : (!IsAllocationEmpty() ? m_pString : nullptr); }
const char *Base() const { return const_cast<CBufferString *>( this )->Base(); }
public:
const char *Get() const { auto base = Base(); return base ? base : StringFuncs<char>::EmptyString(); }
void Clear()
{
if(!IsAllocationEmpty())
Base()[0] = '\0';
m_nLength &= ~LENGTH_MASK;
}
public: public:
DLL_CLASS_IMPORT const char *AppendConcat(int, const char * const *, const int *, bool bIgnoreAlignment = false); DLL_CLASS_IMPORT const char *AppendConcat(int, const char * const *, const int *, bool bIgnoreAlignment = false);
DLL_CLASS_IMPORT const char *AppendConcat(const char *, const char *, ...) FMTFUNCTION(3, 4); DLL_CLASS_IMPORT const char *AppendConcat(const char *, const char *, ...) FMTFUNCTION(3, 4);
@ -79,22 +147,25 @@ public:
DLL_CLASS_IMPORT const char *AppendRepeat(char cChar, int nChars, bool bIgnoreAlignment = false); DLL_CLASS_IMPORT const char *AppendRepeat(char cChar, int nChars, bool bIgnoreAlignment = false);
// Given a path and a filename, composes "path\filename", inserting the (OS correct) separator if necessary
DLL_CLASS_IMPORT const char *ComposeFileName(const char *pPath, const char *pFile, char cSeparator); DLL_CLASS_IMPORT const char *ComposeFileName(const char *pPath, const char *pFile, char cSeparator);
DLL_CLASS_IMPORT const char *ConvertIn(unsigned int const *pData, int nSize, bool bIgnoreAlignment = false); DLL_CLASS_IMPORT const char *ConvertIn(unsigned int const *pData, int nSize, bool bIgnoreAlignment = false);
DLL_CLASS_IMPORT const char *ConvertIn(wchar_t const *pData, int nSize, bool bIgnoreAlignment = false); DLL_CLASS_IMPORT const char *ConvertIn(wchar_t const *pData, int nSize, bool bIgnoreAlignment = false);
DLL_CLASS_IMPORT const char *DefaultExtension(const char *pExt); // Make path end with extension if it doesn't already have an extension
DLL_CLASS_IMPORT const char *DefaultExtension(const char *extension);
DLL_CLASS_IMPORT bool EndsWith(const char *pMatch) const; // Does string end with 'pSuffix'? (case sensitive/insensitive variants)
DLL_CLASS_IMPORT bool EndsWith_FastCaseInsensitive(const char *pMatch) const; DLL_CLASS_IMPORT bool EndsWith(const char *pSuffix) const;
DLL_CLASS_IMPORT bool EndsWith_FastCaseInsensitive(const char *pSuffix) const;
// Ensures the nCapacity condition is met and grows the local buffer if needed. // Ensures the nCapacity condition is met and grows the local buffer if needed.
// Returns pResultingBuffer pointer to the newly allocated data, as well as resulting capacity that was allocated in bytes. // Returns pResultingBuffer pointer to the newly allocated data, as well as resulting capacity that was allocated in bytes.
DLL_CLASS_IMPORT int EnsureCapacity(int nCapacity, char **pResultingBuffer, bool bIgnoreAlignment = false, bool bForceGrow = true); DLL_CLASS_IMPORT int EnsureCapacity(int nCapacity, char **pResultingBuffer, bool bIgnoreAlignment = false, bool bForceGrow = false);
DLL_CLASS_IMPORT int EnsureAddedCapacity(int nCapacity, char **pResultingBuffer, bool bIgnoreAlignment = false, bool bForceGrow = true); DLL_CLASS_IMPORT int EnsureAddedCapacity(int nCapacity, char **pResultingBuffer, bool bIgnoreAlignment = false, bool bForceGrow = false);
DLL_CLASS_IMPORT char *EnsureLength(int nCapacity, bool bIgnoreAlignment = false, int *pNewCapacity = NULL); DLL_CLASS_IMPORT char *EnsureLength(int nCapacity, bool bIgnoreAlignment = false, int *pNewCapacity = nullptr);
DLL_CLASS_IMPORT char *EnsureOwnedAllocation(CBufferString::EAllocationOption_t eAlloc); DLL_CLASS_IMPORT char *EnsureOwnedAllocation(CBufferString::EAllocationOption_t eAlloc);
DLL_CLASS_IMPORT const char *EnsureTrailingSlash(char cSeparator, bool bDontAppendIfEmpty = true); DLL_CLASS_IMPORT const char *EnsureTrailingSlash(char cSeparator, bool bDontAppendIfEmpty = true);
@ -102,11 +173,20 @@ public:
DLL_CLASS_IMPORT const char *ExtendPath(const char *pPath, char cSeparator); DLL_CLASS_IMPORT const char *ExtendPath(const char *pPath, char cSeparator);
DLL_CLASS_IMPORT const char *ExtractFileBase(const char *pPath); DLL_CLASS_IMPORT const char *ExtractFileBase(const char *pPath);
// Copy out the file extension into dest
DLL_CLASS_IMPORT const char *ExtractFileExtension(const char *pPath); DLL_CLASS_IMPORT const char *ExtractFileExtension(const char *pPath);
// Copy out the path except for the stuff after the final pathseparator
DLL_CLASS_IMPORT const char *ExtractFilePath(const char *pPath, bool); DLL_CLASS_IMPORT const char *ExtractFilePath(const char *pPath, bool);
DLL_CLASS_IMPORT const char *ExtractFirstDir(const char *pPath); DLL_CLASS_IMPORT const char *ExtractFirstDir(const char *pPath);
// Force slashes of either type to be = separator character
DLL_CLASS_IMPORT const char *FixSlashes(char cSeparator = CORRECT_PATH_SEPARATOR); DLL_CLASS_IMPORT const char *FixSlashes(char cSeparator = CORRECT_PATH_SEPARATOR);
// Fixes up a file name, removing dot slashes, fixing slashes, converting to lowercase, etc.
DLL_CLASS_IMPORT const char *FixupPathName(char cSeparator); DLL_CLASS_IMPORT const char *FixupPathName(char cSeparator);
DLL_CLASS_IMPORT int Format(const char *pFormat, ...) FMTFUNCTION(2, 3); DLL_CLASS_IMPORT int Format(const char *pFormat, ...) FMTFUNCTION(2, 3);
@ -122,21 +202,29 @@ public:
// Returns the resulting char buffer (Same as to what CBufferString->Get() returns). // Returns the resulting char buffer (Same as to what CBufferString->Get() returns).
DLL_CLASS_IMPORT const char *Insert(int nIndex, const char *pBuf, int nCount = -1, bool bIgnoreAlignment = false); DLL_CLASS_IMPORT const char *Insert(int nIndex, const char *pBuf, int nCount = -1, bool bIgnoreAlignment = false);
DLL_CLASS_IMPORT char *GetInsertPtr(int nIndex, int nChars, bool bIgnoreAlignment = false, int *pNewCapacity = NULL); DLL_CLASS_IMPORT char *GetInsertPtr(int nIndex, int nChars, bool bIgnoreAlignment = false, int *pNewCapacity = nullptr);
DLL_CLASS_IMPORT char *GetReplacePtr(int nIndex, int nOldChars, int nNewChars, bool bIgnoreAlignment = false, int *pNewCapacity = NULL); DLL_CLASS_IMPORT char *GetReplacePtr(int nIndex, int nOldChars, int nNewChars, bool bIgnoreAlignment = false, int *pNewCapacity = nullptr);
DLL_CLASS_IMPORT int GrowByChunks(int, int); DLL_CLASS_IMPORT int GrowByChunks(int, int);
// Wrapper around V_MakeAbsolutePath() // If pPath is a relative path, this function makes it into an absolute path
// using the current working directory as the base, or pStartingDir if it's non-NULL.
// Returns NULL if it runs out of room in the string, or if pPath tries to ".." past the root directory.
DLL_CLASS_IMPORT const char *MakeAbsolutePath(const char *pPath, const char *pStartingDir); DLL_CLASS_IMPORT const char *MakeAbsolutePath(const char *pPath, const char *pStartingDir);
// Wrapper around V_MakeAbsolutePath() but also does separator fixup
// Same as MakeAbsolutePath, but also does separator fixup
DLL_CLASS_IMPORT const char *MakeFixedAbsolutePath(const char *pPath, const char *pStartingDir, char cSeparator = CORRECT_PATH_SEPARATOR); DLL_CLASS_IMPORT const char *MakeFixedAbsolutePath(const char *pPath, const char *pStartingDir, char cSeparator = CORRECT_PATH_SEPARATOR);
// Wrapper around V_MakeRelativePath()
// Creates a relative path given two full paths
// The first is the full path of the file to make a relative path for.
// The second is the full path of the directory to make the first file relative to
// Returns NULL if they can't be made relative (on separate drives, for example)
DLL_CLASS_IMPORT const char *MakeRelativePath(const char *pFullPath, const char *pDirectory); DLL_CLASS_IMPORT const char *MakeRelativePath(const char *pFullPath, const char *pDirectory);
// Copies data from pOther and then purges it
DLL_CLASS_IMPORT void MoveFrom(CBufferString &pOther); DLL_CLASS_IMPORT void MoveFrom(CBufferString &pOther);
DLL_CLASS_IMPORT void Purge(int nLength); DLL_CLASS_IMPORT void Purge(int nAllocatedBytesToPreserve = 0);
DLL_CLASS_IMPORT char *Relinquish(CBufferString::EAllocationOption_t eAlloc); DLL_CLASS_IMPORT char *Relinquish(CBufferString::EAllocationOption_t eAlloc);
@ -161,10 +249,10 @@ public:
DLL_CLASS_IMPORT const char *ReverseChars(int nIndex, int nChars); DLL_CLASS_IMPORT const char *ReverseChars(int nIndex, int nChars);
// Appends the pExt to the local buffer, also appends '.' in between even if it wasn't provided in pExt. // Strips any current extension from path and ensures that extension is the new extension
DLL_CLASS_IMPORT const char *SetExtension(const char *pExt); DLL_CLASS_IMPORT const char *SetExtension(const char *extension);
DLL_CLASS_IMPORT char *SetLength(int nLen, bool bIgnoreAlignment = false, int *pNewCapacity = NULL); DLL_CLASS_IMPORT char *SetLength(int nLen, bool bIgnoreAlignment = false, int *pNewCapacity = nullptr);
DLL_CLASS_IMPORT void SetPtr(char *pBuf, int nBufferChars, int, bool, bool); DLL_CLASS_IMPORT void SetPtr(char *pBuf, int nBufferChars, int, bool, bool);
// Frees the buffer (if it was heap allocated) and writes "~DSTRCT" to the local buffer. // Frees the buffer (if it was heap allocated) and writes "~DSTRCT" to the local buffer.
@ -193,90 +281,40 @@ public:
DLL_CLASS_IMPORT int UnicodeCaseConvert(int, EStringConvertErrorPolicy eErrorPolicy); DLL_CLASS_IMPORT int UnicodeCaseConvert(int, EStringConvertErrorPolicy eErrorPolicy);
// Casts to CBufferStringGrowable. Very dirty solution until someone figures out the sane one.
template<size_t MAX_SIZE = 8, bool AllowHeapAllocation = true, typename T = CBufferStringGrowable<MAX_SIZE, AllowHeapAllocation>>
T *ToGrowable()
{
return (T *)this;
}
};
template<size_t MAX_SIZE, bool AllowHeapAllocation = true>
class CBufferStringGrowable : public CBufferString
{
friend class CBufferString;
public:
CBufferStringGrowable() : m_nTotalCount(0), m_nAllocated(STACK_ALLOCATION_MARKER | (MAX_SIZE & LENGTH_MASK))
{
memset(m_Memory.m_szString, 0, sizeof(m_Memory.m_szString));
if (AllowHeapAllocation)
{
m_nAllocated |= ALLOW_HEAP_ALLOCATION;
}
}
~CBufferStringGrowable()
{
if (IsHeapAllocated() && m_Memory.m_pString)
{
#if PLATFORM_WINDOWS
g_pMemAlloc->Free((void*)m_Memory.m_pString);
#else
delete[] m_Memory.m_pString;
#endif
}
}
inline int GetAllocatedNumber() const
{
return m_nAllocated & LENGTH_MASK;
}
inline int GetTotalNumber() const
{
return m_nTotalCount & LENGTH_MASK;
}
inline bool IsStackAllocated() const
{
return (m_nAllocated & STACK_ALLOCATION_MARKER) != 0;
}
inline bool IsHeapAllocated() const
{
return (m_nTotalCount & HEAP_ALLOCATION_MARKER) != 0;
}
inline bool IsInputStringUnsafe(const char *pData) const
{
return ((void *)pData >= this && (void *)pData < &this[1]) ||
(GetAllocatedNumber() != 0 && pData >= Get() && pData < (Get() + GetAllocatedNumber()));
}
inline const char *Get() const
{
if (IsStackAllocated())
{
return m_Memory.m_szString;
}
else if (GetAllocatedNumber() != 0)
{
return m_Memory.m_pString;
}
return StringFuncs<char>::EmptyString();
}
private: private:
int m_nTotalCount; int m_nLength;
int m_nAllocated; int m_nAllocatedSize;
union union
{ {
char *m_pString; char *m_pString;
char m_szString[MAX_SIZE]; char m_szString[8];
} m_Memory; };
}; };
template<size_t SIZE>
class CBufferStringN : public CBufferString
{
public:
static const size_t DATA_SIZE = ALIGN_VALUE( SIZE - sizeof( char[8] ), 8 );
CBufferStringN( bool bAllowHeapAllocation = true ) : CBufferString( DATA_SIZE, bAllowHeapAllocation ), m_FixedData{} {}
CBufferStringN( const char *pString, bool bAllowHeapAllocation = true ) : CBufferStringN( bAllowHeapAllocation )
{
Insert( 0, pString );
}
~CBufferStringN() { PurgeN(); }
// Should be preferred over CBufferString::Purge as it preserves stack space correctly
void PurgeN() { Purge( DATA_SIZE ); }
private:
char m_FixedData[DATA_SIZE];
};
// AMNOTE: CBufferStringN name is preferred to be used, altho CBufferStringGrowable is left as a small bcompat
template <size_t SIZE>
using CBufferStringGrowable = CBufferStringN<SIZE>;
#endif /* BUFFERSTRING_H */ #endif /* BUFFERSTRING_H */