mirror of
https://github.com/beefytech/Beef.git
synced 2025-07-06 16:25:59 +02:00
Initial checkin
This commit is contained in:
parent
c74712dad9
commit
078564ac9e
3242 changed files with 1616395 additions and 0 deletions
487
BeefySysLib/util/AllocDebug.cpp
Normal file
487
BeefySysLib/util/AllocDebug.cpp
Normal file
|
@ -0,0 +1,487 @@
|
|||
#include "../Common.h"
|
||||
#include "CritSect.h"
|
||||
#include "util/Dictionary.h"
|
||||
|
||||
#pragma warning(disable:4996)
|
||||
#include <cstdio>
|
||||
#include "AllocDebug.h"
|
||||
|
||||
USING_NS_BF;
|
||||
|
||||
#ifdef DEF_BF_ALLOCDEBUG
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define USE_STOMP
|
||||
|
||||
#define STOMP_MAGIC 0xBF12BF34
|
||||
|
||||
class BfBitSet
|
||||
{
|
||||
public:
|
||||
uint32* mBits;
|
||||
|
||||
public:
|
||||
BfBitSet(int numBits);
|
||||
~BfBitSet();
|
||||
|
||||
bool IsSet(int idx);
|
||||
void Set(int idx);
|
||||
void Clear(int idx);
|
||||
};
|
||||
|
||||
|
||||
BfBitSet::BfBitSet(int numBits)
|
||||
{
|
||||
int numInts = (numBits + 31) / 32;
|
||||
mBits = new uint32[numInts];
|
||||
memset(mBits, 0, numInts * 4);
|
||||
}
|
||||
|
||||
BfBitSet::~BfBitSet()
|
||||
{
|
||||
delete mBits;
|
||||
}
|
||||
|
||||
bool BfBitSet::IsSet(int idx)
|
||||
{
|
||||
return (mBits[idx / 32] & (1 << (idx % 32))) != 0;
|
||||
}
|
||||
|
||||
void BfBitSet::Set(int idx)
|
||||
{
|
||||
mBits[idx / 32] |= (1 << (idx % 32));
|
||||
}
|
||||
|
||||
void BfBitSet::Clear(int idx)
|
||||
{
|
||||
mBits[idx / 32] &= ~(1 << (idx % 32));
|
||||
}
|
||||
|
||||
struct SA_AllocHeader
|
||||
{
|
||||
int mNumPages;
|
||||
int mMagic;
|
||||
};
|
||||
|
||||
struct SA_AllocRange
|
||||
{
|
||||
public:
|
||||
static const int PAGE_SIZE = 4096;
|
||||
static const int NUM_PAGES = 0x8000;
|
||||
uint8* mMemory;
|
||||
int mSize;
|
||||
int mLastUsedIdx;
|
||||
BfBitSet mUsedBits;
|
||||
|
||||
public:
|
||||
SA_AllocRange() : mUsedBits(NUM_PAGES)
|
||||
{
|
||||
mMemory = (uint8*)::VirtualAlloc(0, PAGE_SIZE * NUM_PAGES, MEM_RESERVE, PAGE_READWRITE);
|
||||
mSize = 0;
|
||||
mLastUsedIdx = -1;
|
||||
}
|
||||
|
||||
~SA_AllocRange()
|
||||
{
|
||||
if (mMemory != NULL)
|
||||
::VirtualFree(mMemory, 0, MEM_RELEASE);
|
||||
}
|
||||
|
||||
int FindFreeRange(int numPages, int from, int to)
|
||||
{
|
||||
int lastUsedIdx = from - 1;
|
||||
for (int pageIdx = from; pageIdx < to; pageIdx++)
|
||||
{
|
||||
if (mUsedBits.IsSet(pageIdx))
|
||||
{
|
||||
lastUsedIdx = pageIdx;
|
||||
}
|
||||
else if (pageIdx - lastUsedIdx >= numPages)
|
||||
{
|
||||
return lastUsedIdx + 1;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void* Alloc(int size)
|
||||
{
|
||||
int numPages = (size + sizeof(SA_AllocHeader) + PAGE_SIZE - 1) / PAGE_SIZE;
|
||||
|
||||
int startIdx = FindFreeRange(numPages, mLastUsedIdx + 1, NUM_PAGES);
|
||||
if (startIdx == -1)
|
||||
startIdx = FindFreeRange(numPages, 0, mLastUsedIdx);
|
||||
if (startIdx == -1)
|
||||
return NULL;
|
||||
|
||||
mLastUsedIdx = startIdx + numPages - 1;
|
||||
for (int markIdx = startIdx; markIdx < startIdx + numPages; markIdx++)
|
||||
{
|
||||
mUsedBits.Set(markIdx);
|
||||
}
|
||||
|
||||
uint8* ptr = mMemory + startIdx*PAGE_SIZE;
|
||||
auto allocHeader = (SA_AllocHeader*)ptr;
|
||||
::VirtualAlloc(ptr, numPages * PAGE_SIZE, MEM_COMMIT, PAGE_READWRITE);
|
||||
|
||||
allocHeader->mNumPages = numPages;
|
||||
allocHeader->mMagic = STOMP_MAGIC;
|
||||
|
||||
int alignedOffset = sizeof(SA_AllocHeader);
|
||||
bool alignAtEnd = true;
|
||||
if (alignAtEnd)
|
||||
{
|
||||
alignedOffset = (PAGE_SIZE - (size % PAGE_SIZE));
|
||||
if (alignedOffset < sizeof(SA_AllocHeader))
|
||||
{
|
||||
// For cases where the alloc size (mod PAGE_SIZE) is almost equal to the page size, we need to bump the offset into the next page
|
||||
// so we don't clobber the SA_AllocHeader
|
||||
alignedOffset += PAGE_SIZE;
|
||||
}
|
||||
}
|
||||
return ptr + alignedOffset;
|
||||
}
|
||||
|
||||
bool Free(void* ptr, bool leaveAllocated = false)
|
||||
{
|
||||
uint8* memPtr = (uint8*)ptr;
|
||||
if ((memPtr < mMemory) || (memPtr >= mMemory + PAGE_SIZE * NUM_PAGES))
|
||||
return false;
|
||||
|
||||
int pageStart = (int)(memPtr - mMemory) / PAGE_SIZE;
|
||||
int pageOfs = (int)(memPtr - mMemory) % PAGE_SIZE;
|
||||
if (pageOfs < sizeof(SA_AllocHeader))
|
||||
pageStart--; // We actually allocated on the previous page
|
||||
|
||||
SA_AllocHeader* allocHeader = (SA_AllocHeader*)(mMemory + pageStart*PAGE_SIZE);
|
||||
assert(allocHeader->mMagic == STOMP_MAGIC);
|
||||
if (leaveAllocated)
|
||||
{
|
||||
DWORD oldProtect;
|
||||
::VirtualProtect(allocHeader, allocHeader->mNumPages * PAGE_SIZE, /*PAGE_NOACCESS*/PAGE_READONLY, &oldProtect);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int pageIdx = pageStart; pageIdx < pageStart + allocHeader->mNumPages; pageIdx++)
|
||||
{
|
||||
assert(mUsedBits.IsSet(pageIdx));
|
||||
mUsedBits.Clear(pageIdx);
|
||||
}
|
||||
::VirtualFree(allocHeader, allocHeader->mNumPages * PAGE_SIZE, MEM_DECOMMIT);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
static std::list<SA_AllocRange>* gAllocRanges = NULL;
|
||||
static CritSect* gSA_CritSect = NULL;
|
||||
static bool gStompInside = false;
|
||||
|
||||
void StompInit()
|
||||
{
|
||||
gStompInside = true;
|
||||
gAllocRanges = new std::list<SA_AllocRange>();
|
||||
gSA_CritSect = new CritSect();
|
||||
gStompInside = false;
|
||||
}
|
||||
|
||||
static void* StompAlloc(int size)
|
||||
{
|
||||
//return malloc(size);
|
||||
|
||||
if (gStompInside)
|
||||
return malloc(size);
|
||||
if (gSA_CritSect == NULL)
|
||||
StompInit();
|
||||
|
||||
AutoCrit autoCrit(*gSA_CritSect);
|
||||
if (gStompInside)
|
||||
return malloc(size);
|
||||
gStompInside = true;
|
||||
|
||||
while (true)
|
||||
{
|
||||
for (auto itr = gAllocRanges->rbegin(); itr != gAllocRanges->rend(); itr++)
|
||||
{
|
||||
auto& alloc = *itr;
|
||||
void* result = alloc.Alloc(size);
|
||||
if (result != NULL)
|
||||
{
|
||||
gStompInside = false;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
gAllocRanges->resize(gAllocRanges->size() + 1);
|
||||
}
|
||||
gStompInside = false;
|
||||
}
|
||||
|
||||
static void StompFree(void* addr)
|
||||
{
|
||||
if (gSA_CritSect == NULL)
|
||||
StompInit();
|
||||
|
||||
AutoCrit autoCrit(*gSA_CritSect);
|
||||
|
||||
bool leaveAllocated = true;
|
||||
for (auto& alloc : *gAllocRanges)
|
||||
{
|
||||
if (alloc.Free(addr, leaveAllocated))
|
||||
return;
|
||||
}
|
||||
|
||||
free(addr);
|
||||
//assert("Invalid address" == 0);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct AllocRecord
|
||||
{
|
||||
uint8_t* mPtr;
|
||||
int mSize;
|
||||
|
||||
const char* mAllocFileName;
|
||||
int mAllocLineNum;
|
||||
int mAllocTransactionIdx;
|
||||
|
||||
const char* mFreeFileName;
|
||||
int mFreeLineNum;
|
||||
int mFreeTransactionIdx;
|
||||
|
||||
int mHashAtFree;
|
||||
};
|
||||
|
||||
int gDbgHeapTransactionIdx = 1;
|
||||
int64 gDbgHeapAllocBytes = 0;
|
||||
int64 gDbgHeapFreedBytes = 0;
|
||||
std::vector<AllocRecord> gDbgHeapRecords;
|
||||
|
||||
#define ALLOC_PADDING 8
|
||||
#define ALLOC_MAGIC 0xBF
|
||||
|
||||
static int DbgHeapHashMemory(void* ptr, int size)
|
||||
{
|
||||
int curHash = 0;
|
||||
uint8_t* curHashPtr = (uint8_t*) ptr;
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
curHash = ((curHash ^ *curHashPtr) << 5) - curHash;
|
||||
curHashPtr++;
|
||||
}
|
||||
return curHash;
|
||||
}
|
||||
|
||||
static void* DbgHeapAlloc(std::size_t size, const char* fileName, int lineNum)
|
||||
{
|
||||
gDbgHeapAllocBytes += size;
|
||||
|
||||
if (gDbgHeapRecords.size() == 0)
|
||||
gDbgHeapRecords.reserve(8192);
|
||||
|
||||
AllocRecord allocRecord;
|
||||
allocRecord.mAllocFileName = fileName;
|
||||
allocRecord.mAllocLineNum = lineNum;
|
||||
allocRecord.mAllocTransactionIdx = gDbgHeapTransactionIdx++;
|
||||
allocRecord.mFreeFileName = NULL;
|
||||
allocRecord.mFreeLineNum = 0;
|
||||
allocRecord.mHashAtFree = 0;
|
||||
allocRecord.mFreeTransactionIdx = 0;
|
||||
allocRecord.mSize = (int)size;
|
||||
allocRecord.mPtr = new uint8_t[ALLOC_PADDING + size + ALLOC_PADDING];
|
||||
memset(allocRecord.mPtr, ALLOC_MAGIC, ALLOC_PADDING + size + ALLOC_PADDING);
|
||||
|
||||
gDbgHeapRecords.push_back(allocRecord);
|
||||
|
||||
return allocRecord.mPtr + ALLOC_PADDING;
|
||||
}
|
||||
|
||||
void DbgHeapFree(const void* ptr, const char* fileName, int lineNum)
|
||||
{
|
||||
if (ptr == NULL)
|
||||
return;
|
||||
|
||||
bool found = false;
|
||||
|
||||
for (int i = 0; i < (int) gDbgHeapRecords.size(); i++)
|
||||
{
|
||||
auto& allocRecord = gDbgHeapRecords[i];
|
||||
if (allocRecord.mPtr + ALLOC_PADDING == ptr)
|
||||
{
|
||||
assert(allocRecord.mFreeTransactionIdx == 0);
|
||||
|
||||
gDbgHeapFreedBytes += allocRecord.mSize;
|
||||
allocRecord.mFreeFileName = fileName;
|
||||
allocRecord.mFreeLineNum = lineNum;
|
||||
allocRecord.mFreeTransactionIdx = gDbgHeapTransactionIdx++;;
|
||||
allocRecord.mHashAtFree = DbgHeapHashMemory(allocRecord.mPtr + ALLOC_PADDING, allocRecord.mSize);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
assert("Not found" == 0);
|
||||
}
|
||||
|
||||
void DbgHeapCheck()
|
||||
{
|
||||
for (int i = 0; i < (int) gDbgHeapRecords.size(); i++)
|
||||
{
|
||||
auto& allocRecord = gDbgHeapRecords[i];
|
||||
|
||||
for (int i = 0; i < ALLOC_PADDING; i++)
|
||||
{
|
||||
assert(allocRecord.mPtr[i] == ALLOC_MAGIC);
|
||||
assert(allocRecord.mPtr[ALLOC_PADDING + allocRecord.mSize + ALLOC_PADDING - i - 1] == ALLOC_MAGIC);
|
||||
}
|
||||
|
||||
if (allocRecord.mFreeTransactionIdx != 0)
|
||||
{
|
||||
int curHash = DbgHeapHashMemory(allocRecord.mPtr + ALLOC_PADDING, allocRecord.mSize);
|
||||
assert(allocRecord.mHashAtFree == curHash);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef BF_MINGW
|
||||
_CrtCheckMemory();
|
||||
#endif
|
||||
}
|
||||
|
||||
void DbgHeapCheckLeaks()
|
||||
{
|
||||
OutputDebugStringA("DbgHeapCheckLeaks____________________\n");
|
||||
|
||||
for (int i = 0; i < (int) gDbgHeapRecords.size(); i++)
|
||||
{
|
||||
auto& allocRecord = gDbgHeapRecords[i];
|
||||
if (allocRecord.mFreeTransactionIdx == 0)
|
||||
{
|
||||
char str[1024];
|
||||
sprintf(str, "Alloc #%d in %s on line %d\n", allocRecord.mAllocTransactionIdx, allocRecord.mAllocFileName, allocRecord.mAllocLineNum);
|
||||
OutputDebugStringA(str);
|
||||
|
||||
sprintf(str, " %08X", allocRecord.mAllocTransactionIdx /*, allocRecord.mAllocFileName, allocRecord.mAllocLineNum*/);
|
||||
|
||||
for (int i = 0; i < std::min(allocRecord.mSize, 16); i++)
|
||||
sprintf(str + strlen(str), " %02X", allocRecord.mPtr[i + ALLOC_PADDING]);
|
||||
|
||||
strcat(str, "\n");
|
||||
OutputDebugStringA(str);
|
||||
|
||||
delete allocRecord.mPtr;
|
||||
}
|
||||
}
|
||||
gDbgHeapRecords.clear();
|
||||
|
||||
#ifndef BF_MINGW
|
||||
_CrtDumpMemoryLeaks();
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma push_macro("new")
|
||||
#undef new
|
||||
|
||||
void* __cdecl operator new(std::size_t size)
|
||||
{
|
||||
#ifdef USE_STOMP
|
||||
return StompAlloc((int)size);
|
||||
#else
|
||||
return DbgHeapAlloc(size, fileName, lineNum);
|
||||
#endif
|
||||
}
|
||||
|
||||
void* __cdecl operator new(std::size_t size, const char* fileName, int lineNum)
|
||||
{
|
||||
#ifdef USE_STOMP
|
||||
return StompAlloc((int)size);
|
||||
#else
|
||||
return DbgHeapAlloc(size, fileName, lineNum);
|
||||
#endif
|
||||
}
|
||||
|
||||
void operator delete(void* ptr, const char* fileName, int lineNum)
|
||||
{
|
||||
#ifdef USE_STOMP
|
||||
StompFree(ptr);
|
||||
#else
|
||||
DbgHeapFree(ptr, fileName, lineNum);
|
||||
#endif
|
||||
}
|
||||
|
||||
void operator delete[](void* ptr, const char* fileName, int lineNum)
|
||||
{
|
||||
#ifdef USE_STOMP
|
||||
StompFree(ptr);
|
||||
#else
|
||||
DbgHeapFree(ptr, fileName, lineNum);
|
||||
#endif
|
||||
}
|
||||
|
||||
void operator delete(void* ptr)
|
||||
{
|
||||
#ifdef USE_STOMP
|
||||
StompFree(ptr);
|
||||
#else
|
||||
DbgHeapFree(ptr, fileName, lineNum);
|
||||
#endif
|
||||
}
|
||||
|
||||
void operator delete[](void* ptr)
|
||||
{
|
||||
#ifdef USE_STOMP
|
||||
StompFree(ptr);
|
||||
#else
|
||||
DbgHeapFree(ptr, fileName, lineNum);
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma pop_macro("new")
|
||||
|
||||
#endif //USE_BF_ALLOCDEBUG
|
||||
|
||||
static CritSect gCritSect;
|
||||
static Dictionary<String, int> gAllocDicts;
|
||||
|
||||
void BpAllocName(const char* str, int size)
|
||||
{
|
||||
AutoCrit autoCrit(gCritSect);
|
||||
|
||||
int* sizePtr;
|
||||
gAllocDicts.TryAdd(String(str), NULL, &sizePtr);
|
||||
*sizePtr += size;
|
||||
}
|
||||
|
||||
void BpDump()
|
||||
{
|
||||
AutoCrit autoCrit(gCritSect);
|
||||
|
||||
int totalSize = 0;
|
||||
|
||||
OutputDebugStr("BeDump:\n");
|
||||
|
||||
String str;
|
||||
for (auto& kv : gAllocDicts)
|
||||
{
|
||||
str.Clear();
|
||||
str.Append(' ');
|
||||
str.Append(kv.mKey);
|
||||
str.Append(' ');
|
||||
while (str.mLength < 32)
|
||||
str.Append(' ');
|
||||
|
||||
str += StrFormat("%8dk\n", (kv.mValue + 1023) / 1024);
|
||||
totalSize += kv.mValue;
|
||||
|
||||
OutputDebugStr(str);
|
||||
}
|
||||
|
||||
str.Clear();
|
||||
str.Clear();
|
||||
str.Append(" TOTAL ");
|
||||
while (str.mLength < 32)
|
||||
str.Append(' ');
|
||||
|
||||
str += StrFormat("%8dk\n", (totalSize + 1023) / 1024);
|
||||
OutputDebugStr(str);
|
||||
}
|
87
BeefySysLib/util/AllocDebug.h
Normal file
87
BeefySysLib/util/AllocDebug.h
Normal file
|
@ -0,0 +1,87 @@
|
|||
#pragma once
|
||||
|
||||
//#define BP_ALLOC_TRACK
|
||||
|
||||
#ifdef BF_PLATFORM_WINDOWS
|
||||
|
||||
#define _CRTDBG_MAP_ALLOC
|
||||
#include <stdlib.h>
|
||||
#include <crtdbg.h>
|
||||
|
||||
//#define USE_BF_ALLOCDEBUG
|
||||
|
||||
#ifdef USE_BF_ALLOCDEBUG
|
||||
|
||||
#define DBG_NEW new ( __FILE__ , __LINE__ )
|
||||
#define new DBG_NEW
|
||||
#undef delete
|
||||
|
||||
#pragma push_macro("new")
|
||||
#undef new
|
||||
#undef delete
|
||||
void* operator new(std::size_t size);
|
||||
void* operator new(std::size_t size, const char* fileName, int lineNum);
|
||||
void operator delete(void* ptr, const char* fileName, int lineNum);
|
||||
void operator delete[](void* ptr, const char* fileName, int lineNum);
|
||||
void operator delete(void* ptr);
|
||||
void operator delete[](void* ptr);
|
||||
|
||||
#pragma pop_macro("new")
|
||||
|
||||
/*#undef delete
|
||||
#define delete DbgHeapDeleter(__FILE__, __LINE__) <<
|
||||
|
||||
void DbgHeapFree(const void* ptr, const char* fileName, int lineNum);
|
||||
|
||||
struct DbgHeapDeleter
|
||||
{
|
||||
const char* mFileName;
|
||||
int mLineNum;
|
||||
|
||||
DbgHeapDeleter(const char* fileName, int lineNum)
|
||||
{
|
||||
mFileName = fileName;
|
||||
mLineNum = lineNum;
|
||||
}
|
||||
|
||||
void operator<<(const void* ptr)
|
||||
{
|
||||
DbgHeapFree(ptr, mFileName, mLineNum);
|
||||
}
|
||||
};*/
|
||||
|
||||
extern int gDbgHeapTransactionIdx;
|
||||
void DbgHeapCheck();
|
||||
void DbgHeapCheckLeaks();
|
||||
|
||||
#elif (defined _WIN32) && (!defined BF_MINGW)//USE_BF_ALLOCDEBUG
|
||||
|
||||
#ifndef DBG_NEW
|
||||
#define DBG_NEW new ( _NORMAL_BLOCK , __FILE__ , __LINE__ )
|
||||
#define new DBG_NEW
|
||||
#endif
|
||||
|
||||
#define DbgHeapCheck _CrtCheckMemory
|
||||
#define DbgHeapCheckLeaks _CrtDumpMemoryLeaks
|
||||
|
||||
#endif // USE_BF_ALLOCDEBUG
|
||||
|
||||
#endif // BF_PLATFORM_WINDOWS
|
||||
|
||||
void BpAllocName(const char* str, int size);
|
||||
void BpDump();
|
||||
|
||||
#ifdef BP_ALLOC_TRACK
|
||||
|
||||
#define BP_ALLOC(str, size) BpAllocName(str, size);
|
||||
#define BP_ALLOC_RAW_T(T) BpAllocName(#T, sizeof(T))
|
||||
//#define BP_ALLOC_T(T) BpAllocName(#T, sizeof(T))
|
||||
#define BP_ALLOC_T(T)
|
||||
|
||||
#else
|
||||
|
||||
#define BP_ALLOC(str, size)
|
||||
#define BP_ALLOC_T(T)
|
||||
#define BP_ALLOC_RAW_T(T)
|
||||
|
||||
#endif
|
1091
BeefySysLib/util/Array.h
Normal file
1091
BeefySysLib/util/Array.h
Normal file
File diff suppressed because it is too large
Load diff
134
BeefySysLib/util/BSpline.cpp
Normal file
134
BeefySysLib/util/BSpline.cpp
Normal file
|
@ -0,0 +1,134 @@
|
|||
#include "BSpline.h"
|
||||
|
||||
USING_NS_BF;
|
||||
|
||||
static void compute_intervals(int *u, int n, int t) // figure out the knots
|
||||
{
|
||||
int j;
|
||||
|
||||
for (j=0; j<=n+t; j++)
|
||||
{
|
||||
if (j<t)
|
||||
u[j]=0;
|
||||
else
|
||||
if ((t<=j) && (j<=n))
|
||||
u[j]=j-t+1;
|
||||
else
|
||||
if (j>n)
|
||||
u[j]=n-t+2; // if n-t=-2 then we're screwed, everything goes to 0
|
||||
}
|
||||
}
|
||||
|
||||
BSpline2D::BSpline2D()
|
||||
{
|
||||
mUVals = NULL;
|
||||
}
|
||||
|
||||
BSpline2D::~BSpline2D()
|
||||
{
|
||||
delete mUVals;
|
||||
}
|
||||
|
||||
void BSpline2D::AddPt(float x, float y)
|
||||
{
|
||||
if (mUVals != NULL)
|
||||
{
|
||||
delete mUVals;
|
||||
mUVals = NULL;
|
||||
}
|
||||
|
||||
Point2D pt;
|
||||
pt.mX = x;
|
||||
pt.mY = y;
|
||||
mInputPoints.push_back(pt);
|
||||
}
|
||||
|
||||
void BSpline2D::Calculate()
|
||||
{
|
||||
int n = (int) mInputPoints.size();
|
||||
int t = 1;
|
||||
Point2D* control = &mInputPoints[0];
|
||||
|
||||
|
||||
mUVals=new int[n+t+1];
|
||||
compute_intervals(mUVals, n, t);
|
||||
|
||||
//increment=(float) (n-t+2)/(num_output-1); // how much parameter goes up each time
|
||||
//interval=0;
|
||||
|
||||
/*for (output_index=0; output_index<num_output-1; output_index++)
|
||||
{
|
||||
compute_point(u, n, t, interval, control, &calcxyz);
|
||||
output[output_index].x = calcxyz.x;
|
||||
output[output_index].y = calcxyz.y;
|
||||
output[output_index].z = calcxyz.z;
|
||||
interval=interval+increment; // increment our parameter
|
||||
}
|
||||
output[num_output-1].x=control[n].x; // put in the last point
|
||||
output[num_output-1].y=control[n].y;
|
||||
output[num_output-1].z=control[n].z;
|
||||
|
||||
delete u;*/
|
||||
}
|
||||
|
||||
static float blend(int k, int t, int *u, float v) // calculate the blending value
|
||||
{
|
||||
float value;
|
||||
|
||||
if (t==1) // base case for the recursion
|
||||
{
|
||||
if ((u[k]<=v) && (v<u[k+1]))
|
||||
value=1;
|
||||
else
|
||||
value=0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((u[k+t-1]==u[k]) && (u[k+t]==u[k+1])) // check for divide by zero
|
||||
value = 0;
|
||||
else
|
||||
if (u[k+t-1]==u[k]) // if a term's denominator is zero,use just the other
|
||||
value = (u[k+t] - v) / (u[k+t] - u[k+1]) * blend(k+1, t-1, u, v);
|
||||
else
|
||||
if (u[k+t]==u[k+1])
|
||||
value = (v - u[k]) / (u[k+t-1] - u[k]) * blend(k, t-1, u, v);
|
||||
else
|
||||
value = (v - u[k]) / (u[k+t-1] - u[k]) * blend(k, t-1, u, v) +
|
||||
(u[k+t] - v) / (u[k+t] - u[k+1]) * blend(k+1, t-1, u, v);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
/*void compute_point(int *u, int n, int t, float v, point *control,
|
||||
point *output)*/
|
||||
|
||||
void BSpline2D::Evaluate(float pct, float* x, float* y)
|
||||
{
|
||||
if (mUVals == NULL)
|
||||
Calculate();
|
||||
|
||||
int k;
|
||||
float temp;
|
||||
|
||||
int n = (int) mInputPoints.size();
|
||||
int t = (int)mInputPoints.size() - 3; // ????
|
||||
t = 1;
|
||||
|
||||
Point2D* control = &mInputPoints[0];
|
||||
|
||||
// initialize the variables that will hold our outputted point
|
||||
|
||||
float oX = 0;
|
||||
float oY = 0;
|
||||
|
||||
for (k=0; k<=n; k++)
|
||||
{
|
||||
temp = blend(t,t,mUVals,pct); // same blend is used for each dimension coordinate
|
||||
oX = oX + (control[k]).mX * temp;
|
||||
oY = oY + (control[k]).mY * temp;
|
||||
}
|
||||
|
||||
*x = oX;
|
||||
*y = oY;
|
||||
}
|
24
BeefySysLib/util/BSpline.h
Normal file
24
BeefySysLib/util/BSpline.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
#include "Common.h"
|
||||
#include "Point.h"
|
||||
|
||||
NS_BF_BEGIN;
|
||||
|
||||
class BSpline2D
|
||||
{
|
||||
public:
|
||||
Array<Point2D> mInputPoints;
|
||||
int* mUVals;
|
||||
|
||||
public:
|
||||
BSpline2D();
|
||||
~BSpline2D();
|
||||
|
||||
void AddPt(float x, float y);
|
||||
|
||||
void Calculate();
|
||||
void Evaluate(float pct, float* x, float* y);
|
||||
};
|
||||
|
||||
NS_BF_END;
|
1762
BeefySysLib/util/BeefPerf.cpp
Normal file
1762
BeefySysLib/util/BeefPerf.cpp
Normal file
File diff suppressed because it is too large
Load diff
331
BeefySysLib/util/BeefPerf.h
Normal file
331
BeefySysLib/util/BeefPerf.h
Normal file
|
@ -0,0 +1,331 @@
|
|||
#pragma once
|
||||
|
||||
#ifndef BP_NOINC
|
||||
#include "../Common.h"
|
||||
#include "CritSect.h"
|
||||
#endif
|
||||
|
||||
#include "Dictionary.h"
|
||||
#include "TLSingleton.h"
|
||||
|
||||
#ifdef BF_PLATFORM_WINDOWS
|
||||
#include <winsock.h>
|
||||
#else
|
||||
#include <sys/types.h> /* See NOTES */
|
||||
#include <sys/socket.h>
|
||||
typedef int SOCKET;
|
||||
#endif
|
||||
|
||||
enum BpConnectState
|
||||
{
|
||||
BpConnectState_NotConnected,
|
||||
BpConnectState_Connecting,
|
||||
BpConnectState_Connected,
|
||||
BpConnectState_Failed
|
||||
};
|
||||
|
||||
#ifndef BP_DISABLED
|
||||
|
||||
NS_BF_BEGIN
|
||||
|
||||
class Buffer
|
||||
{
|
||||
public:
|
||||
uint8* mPtr;
|
||||
int mBufSize;
|
||||
int mDataSize;
|
||||
|
||||
public:
|
||||
Buffer();
|
||||
~Buffer();
|
||||
uint8* Alloc(int size);
|
||||
void Free();
|
||||
void Clear();
|
||||
};
|
||||
|
||||
class CircularBuffer
|
||||
{
|
||||
public:
|
||||
// Either points to an internal segment of the CircularBuffer, or if the range is discontinuous then
|
||||
// it presents a temporary pointer that is later committed
|
||||
class View
|
||||
{
|
||||
public:
|
||||
uint8* mPtr;
|
||||
int mSrcIdx;
|
||||
int mSrcSize;
|
||||
uint8* mTempBuf;
|
||||
int mTempBufSize;
|
||||
|
||||
CircularBuffer* mCircularBuffer;
|
||||
|
||||
public:
|
||||
View();
|
||||
~View();
|
||||
|
||||
void Commit(int size = -1);
|
||||
};
|
||||
|
||||
public:
|
||||
uint8* mBuffer;
|
||||
int mTail;
|
||||
int mDataSize;
|
||||
int mBufSize;
|
||||
|
||||
public:
|
||||
CircularBuffer();
|
||||
~CircularBuffer();
|
||||
|
||||
void Clear();
|
||||
void Resize(int newSize);
|
||||
void GrowReserve(int addSize);
|
||||
void Grow(int addSize);
|
||||
void GrowFront(int addSize);
|
||||
int GetSize();
|
||||
void MapView(int idx, int len, View& view);
|
||||
void Read(void* ptr, int idx, int len);
|
||||
void Write(void* ptr, int idx, int len);
|
||||
|
||||
void RemoveFront(int len);
|
||||
};
|
||||
|
||||
class BpContext
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
enum BpResult
|
||||
{
|
||||
BpResult_Ok = 0,
|
||||
BpResult_InternalError,
|
||||
BpResult_AlreadyInitialized
|
||||
};
|
||||
|
||||
#define BP_CHUNKSIZE 4096
|
||||
#define BP_NUMCHUNKS 8
|
||||
|
||||
#define BP_CLIENT_VERSION 2
|
||||
|
||||
enum BpCmd
|
||||
{
|
||||
BpCmd_Init,
|
||||
BpCmd_Enter,
|
||||
BpCmd_EnterDyn,
|
||||
BpCmd_Leave,
|
||||
BpCmd_LODSmallEntry,
|
||||
BpCmd_StrEntry,
|
||||
BpCmd_ThreadAdd,
|
||||
BpCmd_ThreadRemove,
|
||||
BpCmd_SetThread,
|
||||
BpCmd_ThreadName,
|
||||
BpCmd_Tick,
|
||||
BpCmd_PrevTick,
|
||||
BpCmd_KeepAlive,
|
||||
BpCmd_ClockInfo,
|
||||
BpCmd_StreamSplitInfo,
|
||||
BpCmd_Event,
|
||||
BpCmd_LODEvent,
|
||||
BpCmd_Cmd
|
||||
};
|
||||
|
||||
class BpCmdTarget
|
||||
{
|
||||
public:
|
||||
CritSect mCritSect;
|
||||
Buffer mOutBuffer;
|
||||
char* mThreadName;
|
||||
|
||||
int mCurDynStrIdx;
|
||||
const char* mDynStrs[64];
|
||||
|
||||
int mCurDepth;
|
||||
|
||||
public:
|
||||
BpCmdTarget();
|
||||
void Disable();
|
||||
// Note: returned "const char*" is actually an index into a temporary string table
|
||||
const char* DynamicString(const char* str);
|
||||
const char* ToStrPtr(const char* str);
|
||||
|
||||
void Enter(const char* name);
|
||||
void Enter(const char* name, va_list vargs);
|
||||
void EnterF(const char* name, ...);
|
||||
void Leave();
|
||||
|
||||
void Event(const char* name, const char* details);
|
||||
};
|
||||
|
||||
class BpRootCmdTarget : public BpCmdTarget
|
||||
{
|
||||
public:
|
||||
void Init();
|
||||
void Tick();
|
||||
void KeepAlive();
|
||||
void AddThread(int threadId, BfpThreadId nativeThreadId);
|
||||
};
|
||||
|
||||
class BpThreadInfo : public BpCmdTarget
|
||||
{
|
||||
public:
|
||||
BfpThreadId mNativeThreadId;
|
||||
int mThreadId;
|
||||
bool mReadyToSend;
|
||||
bool mHasTerminated;
|
||||
|
||||
public:
|
||||
BpThreadInfo();
|
||||
~BpThreadInfo();
|
||||
void SetThreadName(const char* name);
|
||||
void RemoveThread();
|
||||
};
|
||||
|
||||
struct BpZoneName
|
||||
{
|
||||
public:
|
||||
int mIdx;
|
||||
int mSize;
|
||||
};
|
||||
|
||||
class BpManager
|
||||
{
|
||||
public:
|
||||
#ifdef BF_PLATFORM_WINDOWS
|
||||
HANDLE mMutex;
|
||||
HANDLE mSharedMemoryFile;
|
||||
#endif
|
||||
SOCKET mSocket;
|
||||
BfpThread* mThread;
|
||||
String mServerName;
|
||||
String mSessionName;
|
||||
String mSessionID;
|
||||
BpRootCmdTarget mRootCmdTarget;
|
||||
BpConnectState mConnectState;
|
||||
SyncEvent mShutdownEvent;
|
||||
CritSect mCritSect;
|
||||
Array<BpThreadInfo*> mThreadInfos;
|
||||
String mClientName;
|
||||
TLSDtor mTLSDtor;
|
||||
bool mCollectData;
|
||||
bool mThreadRunning;
|
||||
int64 mInitTimeStamp;
|
||||
uint mInitTickCount;
|
||||
int mPauseCount;
|
||||
int mInitCount;
|
||||
|
||||
CircularBuffer mOutBuffer;
|
||||
int mOutBlockSizeLeft;
|
||||
int mCurThreadId;
|
||||
|
||||
Dictionary<const char*, BpZoneName> mZoneNameMap;
|
||||
|
||||
BF_TLS_DECLSPEC static BpThreadInfo* sBpThreadInfo;
|
||||
static BpManager* sBpManager;
|
||||
|
||||
int64 mCurTick;
|
||||
|
||||
protected:
|
||||
bool Connect();
|
||||
void FinishWorkThread();
|
||||
void ThreadProc();
|
||||
static void BFP_CALLTYPE ThreadProcThunk(void* ptr);
|
||||
uint8* StartCmd(uint8 cmd, CircularBuffer::View& view, int maxLen);
|
||||
void EndCmd(CircularBuffer::View& view, uint8* ptr);
|
||||
void TrySendData();
|
||||
void LostConnection();
|
||||
virtual BpThreadInfo* SlowGetCurThreadInfo();
|
||||
|
||||
public:
|
||||
virtual void* AllocBytes(int size);
|
||||
virtual void FreeBytes(void*);
|
||||
|
||||
public:
|
||||
BpManager();
|
||||
~BpManager();
|
||||
|
||||
void Clear();
|
||||
|
||||
void SetClientName(const StringImpl& clientName);
|
||||
BpResult Init(const char* serverName, const char* sessionName);
|
||||
void RetryConnect();
|
||||
void Pause();
|
||||
void Unpause();
|
||||
void Shutdown();
|
||||
bool IsDisconnected();
|
||||
|
||||
BpContext* CreateContext(const char* name);
|
||||
void CloseContext();
|
||||
void Tick();
|
||||
|
||||
static BpThreadInfo* GetCurThreadInfo();
|
||||
static BpManager* Get();
|
||||
};
|
||||
|
||||
NS_BF_END
|
||||
|
||||
class BpAutoZone
|
||||
{
|
||||
public:
|
||||
BpAutoZone(const char* name)
|
||||
{
|
||||
Beefy::BpManager::GetCurThreadInfo()->Enter(name);
|
||||
}
|
||||
|
||||
~BpAutoZone()
|
||||
{
|
||||
Beefy::BpManager::sBpThreadInfo->Leave();
|
||||
}
|
||||
};
|
||||
|
||||
class BpAutoZoneF
|
||||
{
|
||||
public:
|
||||
BpAutoZoneF(const char* name, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, name);
|
||||
Beefy::BpManager::GetCurThreadInfo()->Enter(name, args);
|
||||
}
|
||||
|
||||
~BpAutoZoneF()
|
||||
{
|
||||
Beefy::BpManager::sBpThreadInfo->Leave();
|
||||
}
|
||||
};
|
||||
|
||||
#define BP_ENTER(zoneName) BpEnter(name)
|
||||
|
||||
#define BP_DYN_STR(str) BpDynStr(str)
|
||||
|
||||
// We purposely 'incorrectly' define BP_ZONE as a variadic macro so compilation will fail rather than warn if we attempt to add params
|
||||
#define BP_TOKENPASTE(x, y) x ## y
|
||||
#define BP_TOKENPASTE2(x, y) BP_TOKENPASTE(x, y)
|
||||
|
||||
#define BP_ZONE(zoneName, ...) BpAutoZone BP_TOKENPASTE2(bpZone_, __COUNTER__)(zoneName, ##__VA_ARGS__)
|
||||
#define BP_ZONE_F(zoneName, ...) BpAutoZoneF BP_TOKENPASTE2(bpZone_, __COUNTER__)(zoneName, ##__VA_ARGS__)
|
||||
#define BP_SHUTDOWN() BpShutdown()
|
||||
|
||||
#else
|
||||
|
||||
#define BP_ENTER(zoneName) do { } while (0)
|
||||
#define BP_DYN_STR(str) str
|
||||
|
||||
#define BP_ZONE(zoneName, ...) do { } while (0)
|
||||
#define BP_ZONE_F(zoneName, ...) do { } while (0)
|
||||
#define BP_SHUTDOWN() do { } while (0)
|
||||
|
||||
#endif
|
||||
|
||||
BF_EXPORT void BF_CALLTYPE BpInit(const char* serverName, const char* sessionName);
|
||||
BF_EXPORT void BF_CALLTYPE BpShutdown();
|
||||
BF_EXPORT BpConnectState BF_CALLTYPE BpGetConnectState();
|
||||
BF_EXPORT void BF_CALLTYPE BpRetryConnect();
|
||||
BF_EXPORT void BF_CALLTYPE BpPause();
|
||||
BF_EXPORT void BF_CALLTYPE BpUnpause();
|
||||
BF_EXPORT void BF_CALLTYPE BpSetClientName(const char* clientName);
|
||||
BF_EXPORT void BF_CALLTYPE BpSetThreadName(const char* threadName);
|
||||
BF_EXPORT void BF_CALLTYPE BpEnter(const char* zoneName);
|
||||
BF_EXPORT void BF_CALLTYPE BpEnterF(const char* zoneName, ...);
|
||||
BF_EXPORT void BF_CALLTYPE BpLeave();
|
||||
BF_EXPORT void BF_CALLTYPE BpFrameTick();
|
||||
BF_EXPORT void BF_CALLTYPE BpEvent(const char* name, const char* details);
|
||||
BF_EXPORT const char* BF_CALLTYPE BpDynStr(const char* str);
|
252
BeefySysLib/util/BumpAllocator.h
Normal file
252
BeefySysLib/util/BumpAllocator.h
Normal file
|
@ -0,0 +1,252 @@
|
|||
#pragma once
|
||||
|
||||
//#define BUMPALLOC_ETW_TRACING
|
||||
|
||||
#include "../Common.h"
|
||||
|
||||
//#define BUMPALLOC_TRACKALLOCS
|
||||
|
||||
#ifdef BUMPALLOC_ETW_TRACING
|
||||
#include "VSCustomNativeHeapEtwProvider.h"
|
||||
#endif
|
||||
|
||||
#ifdef BUMPALLOC_TRACKALLOCS
|
||||
#include "Dictionary.h"
|
||||
#endif
|
||||
|
||||
NS_BF_BEGIN
|
||||
|
||||
#ifdef BUMPALLOC_TRACKALLOCS
|
||||
struct BumpAllocTrackedEntry
|
||||
{
|
||||
int mCount;
|
||||
int mSize;
|
||||
|
||||
BumpAllocTrackedEntry()
|
||||
{
|
||||
mCount = 0;
|
||||
mSize = 0;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
template <int ALLOC_SIZE = 0x2000>
|
||||
class BumpAllocatorT
|
||||
{
|
||||
public:
|
||||
Array<uint8*> mPools;
|
||||
Array<int> mSizes;
|
||||
uint8* mCurAlloc;
|
||||
uint8* mCurPtr;
|
||||
int mCurChunkNum;
|
||||
int mPrevSizes;
|
||||
bool mBumpAlloc;
|
||||
bool mDisableDebugTracing;
|
||||
#ifdef BUMPALLOC_ETW_TRACING
|
||||
static VSHeapTracker::CHeapTracker* mVSHeapTracker;
|
||||
#endif
|
||||
#ifdef BUMPALLOC_TRACKALLOCS
|
||||
Dictionary<String, BumpAllocTrackedEntry> mTrackedAllocs;
|
||||
#endif
|
||||
|
||||
public:
|
||||
BumpAllocatorT()
|
||||
{
|
||||
#ifdef BUMPALLOC_ETW_TRACING
|
||||
if (mVSHeapTracker == NULL)
|
||||
mVSHeapTracker = new VSHeapTracker::CHeapTracker("BumpAllocator");
|
||||
#endif
|
||||
mCurAlloc = NULL;
|
||||
mCurPtr = (uint8*)(intptr)ALLOC_SIZE;
|
||||
mBumpAlloc = true;
|
||||
mDisableDebugTracing = false;
|
||||
mCurChunkNum = -1;
|
||||
mPrevSizes = 0;
|
||||
}
|
||||
|
||||
~BumpAllocatorT()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
mCurAlloc = NULL;
|
||||
mCurPtr = (uint8*)(intptr)ALLOC_SIZE;
|
||||
for (auto ptr : mPools)
|
||||
free(ptr);
|
||||
mPools.Clear();
|
||||
mSizes.Clear();
|
||||
mCurChunkNum = -1;
|
||||
mPrevSizes = 0;
|
||||
|
||||
#ifdef BUMPALLOC_TRACKALLOCS
|
||||
mTrackedAllocs.Clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
int GetAllocSize() const
|
||||
{
|
||||
return (int)(((mPools.size() - 1) * ALLOC_SIZE) + (mCurPtr - mCurAlloc));
|
||||
}
|
||||
|
||||
int GetTotalAllocSize() const
|
||||
{
|
||||
return (int)mPools.size() * ALLOC_SIZE;
|
||||
}
|
||||
|
||||
int CalcUsedSize() const
|
||||
{
|
||||
int totalSize = 0;
|
||||
for (auto& size : mSizes)
|
||||
totalSize += size;
|
||||
totalSize += mCurPtr - mCurAlloc;
|
||||
return totalSize;
|
||||
}
|
||||
|
||||
#ifdef BUMPALLOC_ETW_TRACING
|
||||
__declspec(allocator) void* RecordAlloc(void* addr, int size)
|
||||
{
|
||||
if (!mDisableDebugTracing)
|
||||
mVSHeapTracker->AllocateEvent(addr, size);
|
||||
return addr;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
bool ContainsPtr(void* ptr) const
|
||||
{
|
||||
for (auto poolPtr : mPools)
|
||||
{
|
||||
if ((ptr >= poolPtr) && (ptr < poolPtr + ALLOC_SIZE))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void GrowPool()
|
||||
{
|
||||
mCurChunkNum = (int)mPools.size();
|
||||
int curSize = (int)(mCurPtr - mCurAlloc);
|
||||
mPrevSizes += curSize;
|
||||
mSizes.push_back(curSize);
|
||||
mCurAlloc = (uint8*)malloc(ALLOC_SIZE);
|
||||
memset(mCurAlloc, 0, ALLOC_SIZE);
|
||||
mPools.push_back(mCurAlloc);
|
||||
mCurPtr = mCurAlloc;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* Alloc(int extraBytes = 0)
|
||||
{
|
||||
int alignSize = alignof(T);
|
||||
mCurPtr = (uint8*)(((intptr)mCurPtr + alignSize - 1) & ~(alignSize - 1));
|
||||
|
||||
//AutoPerf perf("Alloc");
|
||||
int wantSize = sizeof(T) + extraBytes;
|
||||
|
||||
#ifdef BUMPALLOC_TRACKALLOCS
|
||||
const char* name = typeid(T).name();
|
||||
BumpAllocTrackedEntry* allocSizePtr;
|
||||
mTrackedAllocs.TryAdd(name, NULL, &allocSizePtr);
|
||||
allocSizePtr->mCount++;
|
||||
allocSizePtr->mSize += wantSize;
|
||||
#endif
|
||||
|
||||
if (mCurPtr + wantSize >= mCurAlloc + ALLOC_SIZE)
|
||||
GrowPool();
|
||||
|
||||
#ifdef BUMPALLOC_ETW_TRACING
|
||||
//mVSHeapTracker->AllocateEvent(retVal, wantSize);
|
||||
T* addr = (T*)RecordAlloc(mCurPtr, wantSize);
|
||||
T* retVal = new (addr) T();
|
||||
#else
|
||||
T* retVal = new (mCurPtr) T();
|
||||
#endif
|
||||
//mWriteCurPtr = (uint8*)(((intptr)mWriteCurPtr + wantSize + 7) & ~7);
|
||||
mCurPtr += wantSize;
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
int GetChunkedId(void* ptr) const
|
||||
{
|
||||
return (mCurChunkNum * ALLOC_SIZE) + (int)((uint8*)ptr - mCurAlloc);
|
||||
}
|
||||
|
||||
uint8* GetChunkedPtr(int id) const
|
||||
{
|
||||
int chunkNum = id / ALLOC_SIZE;
|
||||
int chunkOfs = id % ALLOC_SIZE;
|
||||
return mPools[chunkNum] + chunkOfs;
|
||||
}
|
||||
|
||||
int GetStreamPos(void* ptr) const
|
||||
{
|
||||
return mPrevSizes + (int)((uint8*)ptr - mCurAlloc);
|
||||
}
|
||||
|
||||
uint8* AllocBytes(int wantSize, int alignSize, const char* dbgName = "AllocBytes")
|
||||
{
|
||||
mCurPtr = (uint8*)(((intptr)mCurPtr + alignSize - 1) & ~(alignSize - 1));
|
||||
|
||||
uint8* retVal = AllocBytes(wantSize, dbgName);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
uint8* AllocBytes(int wantSize, const char* dbgName = "AllocBytes")
|
||||
{
|
||||
#ifdef BUMPALLOC_TRACKALLOCS
|
||||
BumpAllocTrackedEntry* allocSizePtr;
|
||||
mTrackedAllocs.TryAdd(dbgName, NULL, &allocSizePtr);
|
||||
allocSizePtr->mCount++;
|
||||
allocSizePtr->mSize += wantSize;
|
||||
#endif
|
||||
|
||||
if (wantSize > ALLOC_SIZE / 2)
|
||||
{
|
||||
uint8* bigData = (uint8*)malloc(wantSize);
|
||||
memset(bigData, 0, wantSize);
|
||||
mPools.push_back(bigData);
|
||||
return bigData;
|
||||
}
|
||||
|
||||
//AutoPerf perf("Alloc");
|
||||
if (mCurPtr + wantSize >= mCurAlloc + ALLOC_SIZE)
|
||||
GrowPool();
|
||||
|
||||
#ifdef BUMPALLOC_ETW_TRACING
|
||||
uint8* retVal = (uint8*)RecordAlloc(mCurPtr, wantSize);
|
||||
#else
|
||||
uint8* retVal = mCurPtr;
|
||||
#endif
|
||||
mCurPtr += wantSize;
|
||||
|
||||
return retVal;
|
||||
}
|
||||
};
|
||||
|
||||
class BumpAllocator : public BumpAllocatorT<0x2000>
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class AllocatorBump
|
||||
{
|
||||
public:
|
||||
BumpAllocator* mAlloc;
|
||||
|
||||
T* allocate(intptr count)
|
||||
{
|
||||
return (T*)mAlloc->AllocBytes((int)(sizeof(T) * count), alignof(T));
|
||||
}
|
||||
|
||||
void deallocate(T* ptr)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
NS_BF_END
|
||||
|
157
BeefySysLib/util/CabUtil.cpp
Normal file
157
BeefySysLib/util/CabUtil.cpp
Normal file
|
@ -0,0 +1,157 @@
|
|||
#include "CabUtil.h"
|
||||
|
||||
#ifdef BF_PLATFORM_WINDOWS
|
||||
|
||||
#pragma warning(disable:4996)
|
||||
|
||||
#pragma comment(lib, "cabinet.lib")
|
||||
|
||||
#include <FDI.h>
|
||||
#include <io.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
USING_NS_BF;
|
||||
|
||||
static char* FDIErrorToString(FDIERROR err)
|
||||
{
|
||||
switch (err)
|
||||
{
|
||||
case FDIERROR_NONE:
|
||||
return "No error";
|
||||
case FDIERROR_CABINET_NOT_FOUND:
|
||||
return "Cabinet not found";
|
||||
case FDIERROR_NOT_A_CABINET:
|
||||
return "Not a cabinet";
|
||||
case FDIERROR_UNKNOWN_CABINET_VERSION:
|
||||
return "Unknown cabinet version";
|
||||
case FDIERROR_CORRUPT_CABINET:
|
||||
return "Corrupt cabinet";
|
||||
case FDIERROR_ALLOC_FAIL:
|
||||
return "Memory allocation failed";
|
||||
case FDIERROR_BAD_COMPR_TYPE:
|
||||
return "Unknown compression type";
|
||||
case FDIERROR_MDI_FAIL:
|
||||
return "Failure decompressing data";
|
||||
case FDIERROR_TARGET_FILE:
|
||||
return "Failure writing to target file";
|
||||
case FDIERROR_RESERVE_MISMATCH:
|
||||
return "Cabinets in set have different RESERVE sizes";
|
||||
case FDIERROR_WRONG_CABINET:
|
||||
return "Cabinet returned on fdintNEXT_CABINET is incorrect";
|
||||
case FDIERROR_USER_ABORT:
|
||||
return "Application aborted";
|
||||
default:
|
||||
return "Unknown error";
|
||||
}
|
||||
}
|
||||
|
||||
CabFile::CabFile()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static FNALLOC(CabMemAlloc)
|
||||
{
|
||||
return malloc(cb);
|
||||
}
|
||||
|
||||
static FNFREE(CabMemFree)
|
||||
{
|
||||
free(pv);
|
||||
}
|
||||
|
||||
static FNOPEN(CabFileOpen)
|
||||
{
|
||||
return _open(pszFile, oflag, pmode);
|
||||
}
|
||||
|
||||
static FNREAD(CabFileRead)
|
||||
{
|
||||
return _read((int)hf, pv, cb);
|
||||
}
|
||||
|
||||
static FNWRITE(CabFileWrite)
|
||||
{
|
||||
return _write((int)hf, pv, cb);
|
||||
}
|
||||
|
||||
static FNCLOSE(CabFileClose)
|
||||
{
|
||||
return _close((int)hf);
|
||||
}
|
||||
|
||||
static FNSEEK(CabFileSeek)
|
||||
{
|
||||
return _lseek((int)hf, dist, seektype);
|
||||
}
|
||||
|
||||
static FNFDINOTIFY(CabFDINotify)
|
||||
{
|
||||
CabFile* cabFile = (CabFile*)pfdin->pv;
|
||||
|
||||
switch (fdint)
|
||||
{
|
||||
case fdintCOPY_FILE:
|
||||
{
|
||||
String destName = cabFile->mDestDir;
|
||||
destName += "/";
|
||||
destName += pfdin->psz1;
|
||||
int fh = (int)CabFileOpen((LPSTR)destName.c_str(), _O_BINARY | _O_CREAT | _O_WRONLY | _O_SEQUENTIAL,
|
||||
_S_IREAD | _S_IWRITE);
|
||||
if (fh == -1)
|
||||
cabFile->Fail(StrFormat("Failed to create '%s'", destName.c_str()));
|
||||
return fh;
|
||||
}
|
||||
case fdintCLOSE_FILE_INFO:
|
||||
CabFileClose(pfdin->hf);
|
||||
return TRUE;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CabFile::Fail(const StringImpl& err)
|
||||
{
|
||||
if (mError.IsEmpty())
|
||||
mError = err;
|
||||
}
|
||||
|
||||
bool CabFile::Load(const StringImpl& path)
|
||||
{
|
||||
if (!FileExists(path))
|
||||
{
|
||||
Fail(StrFormat("File '%s' not found", path.c_str()));
|
||||
return false;
|
||||
}
|
||||
mSrcFileName = path;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CabFile::DecompressAll(const StringImpl& destDir)
|
||||
{
|
||||
if (mSrcFileName.IsEmpty())
|
||||
{
|
||||
Fail("No file specified");
|
||||
return false;
|
||||
}
|
||||
|
||||
ERF erf = { 0 };
|
||||
HFDI fdi = FDICreate(CabMemAlloc, CabMemFree, CabFileOpen, CabFileRead, CabFileWrite, CabFileClose, CabFileSeek, cpu80386, &erf);
|
||||
if (!fdi)
|
||||
return false;
|
||||
|
||||
RecursiveCreateDirectory(destDir);
|
||||
mDestDir = destDir;
|
||||
bool result = FDICopy(fdi, (LPSTR)GetFileName(mSrcFileName).c_str(), (LPSTR)(GetFileDir(mSrcFileName) + "/").c_str(), 0, CabFDINotify, NULL, this) != 0;
|
||||
|
||||
if (!result)
|
||||
Fail(FDIErrorToString((FDIERROR)erf.erfOper));
|
||||
|
||||
FDIDestroy(fdi);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
27
BeefySysLib/util/CabUtil.h
Normal file
27
BeefySysLib/util/CabUtil.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
#pragma once
|
||||
|
||||
#include "../Common.h"
|
||||
#include "../FileStream.h"
|
||||
|
||||
NS_BF_BEGIN
|
||||
|
||||
|
||||
class CabFile
|
||||
{
|
||||
public:
|
||||
String mSrcFileName;
|
||||
String mDestDir;
|
||||
String mError;
|
||||
|
||||
public:
|
||||
void Fail(const StringImpl& err);
|
||||
|
||||
public:
|
||||
CabFile();
|
||||
|
||||
bool Load(const StringImpl& path);
|
||||
bool DecompressAll(const StringImpl& destDir);
|
||||
};
|
||||
|
||||
NS_BF_END
|
||||
|
24
BeefySysLib/util/CatmullRom.cpp
Normal file
24
BeefySysLib/util/CatmullRom.cpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
#include "CatmullRom.h"
|
||||
|
||||
USING_NS_BF;
|
||||
|
||||
Point2D Beefy::CatmullRomEvaluate(Point2D &p0, Point2D &p1, Point2D &p2, Point2D &p3, float tension, float t)
|
||||
{
|
||||
float t2 = t * t;
|
||||
float t3 = t2 * t;
|
||||
|
||||
/*
|
||||
* Formula: s(-ttt + 2tt - t)P1 + s(-ttt + tt)P2 + (2ttt - 3tt + 1)P2 + s(ttt - 2tt + t)P3 + (-2ttt + 3tt)P3 + s(ttt - tt)P4
|
||||
*/
|
||||
float s = (1 - tension) / 2;
|
||||
|
||||
float b1 = s * ((-t3 + (2 * t2)) - t); // s(-t3 + 2 t2 - t)P1
|
||||
float b2 = s * (-t3 + t2) + (2 * t3 - 3 * t2 + 1); // s(-t3 + t2)P2 + (2 t3 - 3 t2 + 1)P2
|
||||
float b3 = s * (t3 - 2 * t2 + t) + (-2 * t3 + 3 * t2); // s(t3 - 2 t2 + t)P3 + (-2 t3 + 3 t2)P3
|
||||
float b4 = s * (t3 - t2); // s(t3 - t2)P4
|
||||
|
||||
float x = (p0.mX*b1 + p1.mX*b2 + p2.mX*b3 + p3.mX*b4);
|
||||
float y = (p0.mY*b1 + p1.mY*b2 + p2.mY*b3 + p3.mY*b4);
|
||||
|
||||
return Point2D(x,y);
|
||||
}
|
10
BeefySysLib/util/CatmullRom.h
Normal file
10
BeefySysLib/util/CatmullRom.h
Normal file
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include "Common.h"
|
||||
#include "Point.h"
|
||||
|
||||
NS_BF_BEGIN;
|
||||
|
||||
Point2D CatmullRomEvaluate(Point2D &p0, Point2D &p1, Point2D &p2, Point2D &p3, float tension, float t);
|
||||
|
||||
NS_BF_END;
|
195
BeefySysLib/util/ChunkedDataBuffer.cpp
Normal file
195
BeefySysLib/util/ChunkedDataBuffer.cpp
Normal file
|
@ -0,0 +1,195 @@
|
|||
#include "ChunkedDataBuffer.h"
|
||||
#include "DataStream.h"
|
||||
|
||||
USING_NS_BF;
|
||||
|
||||
int Beefy::ChunkedDataBuffer::sBlocksAllocated = 0;
|
||||
|
||||
/*static void Log(const char* fmt ...)
|
||||
{
|
||||
static FILE* fp = fopen("chunked_dbg.txt", "wb");
|
||||
|
||||
va_list argList;
|
||||
va_start(argList, fmt);
|
||||
String aResult = vformat(fmt, argList);
|
||||
va_end(argList);
|
||||
|
||||
fwrite(aResult.c_str(), 1, aResult.length(), fp);
|
||||
fflush(fp);
|
||||
}*/
|
||||
|
||||
ChunkedDataBuffer::ChunkedDataBuffer()
|
||||
{
|
||||
mWriteCurAlloc = NULL;
|
||||
mWriteCurPtr = (uint8*)(intptr)ALLOC_SIZE;
|
||||
mReadCurAlloc = NULL;
|
||||
mReadCurPtr = (uint8*)(intptr)ALLOC_SIZE;
|
||||
mReadNextAlloc = mReadCurPtr;
|
||||
mReadPoolIdx = -1;
|
||||
mSize = 0;
|
||||
}
|
||||
|
||||
ChunkedDataBuffer::~ChunkedDataBuffer()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
void ChunkedDataBuffer::InitFlatRef(void* ptr, int size)
|
||||
{
|
||||
mReadPoolIdx = 0;
|
||||
mReadCurPtr = (uint8*)ptr;
|
||||
mReadCurAlloc = mReadCurPtr;
|
||||
mReadNextAlloc = mReadCurPtr + size;
|
||||
}
|
||||
|
||||
void ChunkedDataBuffer::Clear()
|
||||
{
|
||||
mWriteCurAlloc = NULL;
|
||||
mWriteCurPtr = (uint8*)(intptr)ALLOC_SIZE;
|
||||
mReadCurAlloc = NULL;
|
||||
mReadCurPtr = (uint8*)(intptr)ALLOC_SIZE;
|
||||
mReadNextAlloc = mReadCurPtr;
|
||||
mReadPoolIdx = -1;
|
||||
mSize = 0;
|
||||
for (auto ptr : mPools)
|
||||
{
|
||||
//Log("Free %p\n", ptr);
|
||||
free(ptr);
|
||||
sBlocksAllocated--;
|
||||
}
|
||||
mPools.Clear();
|
||||
}
|
||||
|
||||
int ChunkedDataBuffer::GetSize()
|
||||
{
|
||||
//int calcSize = (int)(((mPools.mSize - 1) * ALLOC_SIZE) + (mWriteCurPtr - mWriteCurAlloc));
|
||||
//BF_ASSERT(calcSize == mSize);
|
||||
return mSize;
|
||||
}
|
||||
|
||||
void ChunkedDataBuffer::GrowPool()
|
||||
{
|
||||
int curSize = (int)(mWriteCurPtr - mWriteCurAlloc);
|
||||
mWriteCurAlloc = (uint8*)malloc(ALLOC_SIZE);
|
||||
//Log("Alloc %p\n", mWriteCurAlloc);
|
||||
sBlocksAllocated++;
|
||||
memset(mWriteCurAlloc, 0, ALLOC_SIZE);
|
||||
mPools.push_back(mWriteCurAlloc);
|
||||
mWriteCurPtr = mWriteCurAlloc;
|
||||
}
|
||||
|
||||
void ChunkedDataBuffer::Write(const void* data, int size)
|
||||
{
|
||||
while (mWriteCurPtr + size >= mWriteCurAlloc + ALLOC_SIZE)
|
||||
{
|
||||
int curSize = (int)((mWriteCurAlloc + ALLOC_SIZE) - mWriteCurPtr);
|
||||
if (curSize > 0)
|
||||
memcpy(mWriteCurPtr, data, curSize);
|
||||
|
||||
GrowPool();
|
||||
data = (uint8*)data + curSize;
|
||||
size -= curSize;
|
||||
mSize += curSize;
|
||||
if (size == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy(mWriteCurPtr, data, size);
|
||||
mWriteCurPtr += size;
|
||||
mSize += size;
|
||||
}
|
||||
|
||||
void ChunkedDataBuffer::Write(uint8 byte)
|
||||
{
|
||||
while (mWriteCurPtr == mWriteCurAlloc + ALLOC_SIZE)
|
||||
GrowPool();
|
||||
*(mWriteCurPtr++) = byte;
|
||||
mSize++;
|
||||
}
|
||||
|
||||
int ChunkedDataBuffer::GetReadPos()
|
||||
{
|
||||
return mReadPoolIdx * ALLOC_SIZE + (int)(mReadCurPtr - mReadCurAlloc);
|
||||
}
|
||||
|
||||
void ChunkedDataBuffer::SetReadPos(int pos)
|
||||
{
|
||||
mReadPoolIdx = pos / ALLOC_SIZE;
|
||||
mReadCurAlloc = mPools[mReadPoolIdx];
|
||||
mReadCurPtr = mReadCurAlloc + pos % ALLOC_SIZE;
|
||||
mReadNextAlloc = mReadCurPtr + ALLOC_SIZE;
|
||||
}
|
||||
|
||||
void ChunkedDataBuffer::NextReadPool()
|
||||
{
|
||||
mReadCurAlloc = mPools[++mReadPoolIdx];
|
||||
mReadCurPtr = mReadCurAlloc;
|
||||
mReadNextAlloc = mReadCurPtr + ALLOC_SIZE;
|
||||
}
|
||||
|
||||
void ChunkedDataBuffer::Read(void* data, int size)
|
||||
{
|
||||
while (mReadCurPtr + size > mReadNextAlloc)
|
||||
{
|
||||
int curSize = (int)(mReadNextAlloc - mReadCurPtr);
|
||||
if (curSize > 0)
|
||||
memcpy(data, mReadCurPtr, curSize);
|
||||
|
||||
NextReadPool();
|
||||
data = (uint8*)data + curSize;
|
||||
size -= curSize;
|
||||
}
|
||||
|
||||
memcpy(data, mReadCurPtr, size);
|
||||
mReadCurPtr += size;
|
||||
}
|
||||
|
||||
void* ChunkedDataBuffer::FastRead(void * data, int size)
|
||||
{
|
||||
// Fast case- no crossing chunk boundary
|
||||
if (mReadCurPtr + size <= mReadNextAlloc)
|
||||
{
|
||||
void* retVal = mReadCurPtr;
|
||||
mReadCurPtr += size;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
void* dataHead = data;
|
||||
while (mReadCurPtr + size > mReadNextAlloc)
|
||||
{
|
||||
int curSize = (int)(mReadNextAlloc - mReadCurPtr);
|
||||
if (curSize > 0)
|
||||
memcpy(data, mReadCurPtr, curSize);
|
||||
|
||||
NextReadPool();
|
||||
data = (uint8*)data + curSize;
|
||||
size -= curSize;
|
||||
}
|
||||
|
||||
memcpy(data, mReadCurPtr, size);
|
||||
mReadCurPtr += size;
|
||||
return dataHead;
|
||||
}
|
||||
|
||||
uint8 ChunkedDataBuffer::Read()
|
||||
{
|
||||
if (mReadCurPtr == mReadNextAlloc)
|
||||
NextReadPool();
|
||||
return *(mReadCurPtr++);
|
||||
}
|
||||
|
||||
void ChunkedDataBuffer::Read(DataStream& stream, int size)
|
||||
{
|
||||
while (mReadCurPtr + size > mReadNextAlloc)
|
||||
{
|
||||
int curSize = (int)(mReadNextAlloc - mReadCurPtr);
|
||||
if (curSize > 0)
|
||||
stream.Write(mReadCurPtr, curSize);
|
||||
|
||||
NextReadPool();
|
||||
size -= curSize;
|
||||
}
|
||||
|
||||
stream.Write(mReadCurPtr, size);
|
||||
mReadCurPtr += size;
|
||||
}
|
45
BeefySysLib/util/ChunkedDataBuffer.h
Normal file
45
BeefySysLib/util/ChunkedDataBuffer.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
#pragma once
|
||||
|
||||
#include "../Common.h"
|
||||
|
||||
NS_BF_BEGIN;
|
||||
|
||||
class DataStream;
|
||||
|
||||
class ChunkedDataBuffer
|
||||
{
|
||||
public:
|
||||
static const int ALLOC_SIZE = 0x1000;
|
||||
|
||||
public:
|
||||
Array<uint8*> mPools;
|
||||
uint8* mWriteCurAlloc;
|
||||
uint8* mWriteCurPtr;
|
||||
uint8* mReadCurAlloc;
|
||||
uint8* mReadCurPtr;
|
||||
uint8* mReadNextAlloc;
|
||||
int mReadPoolIdx;
|
||||
int mSize;
|
||||
static int sBlocksAllocated;
|
||||
|
||||
public:
|
||||
ChunkedDataBuffer();
|
||||
~ChunkedDataBuffer();
|
||||
|
||||
void InitFlatRef(void* ptr, int size);
|
||||
void Clear();
|
||||
int GetSize();
|
||||
void GrowPool();
|
||||
void Write(const void* data, int size);
|
||||
void Write(uint8 byte);
|
||||
int GetReadPos();
|
||||
void SetReadPos(int pos);
|
||||
void NextReadPool();
|
||||
void Read(void* data, int size);
|
||||
void* FastRead(void* data, int size); // Can either return pointer into the stream or read data, depending on if the read crosses a chunk
|
||||
uint8 Read();
|
||||
|
||||
void Read(DataStream& stream, int size);
|
||||
};
|
||||
|
||||
NS_BF_END;
|
149
BeefySysLib/util/ChunkedVector.h
Normal file
149
BeefySysLib/util/ChunkedVector.h
Normal file
|
@ -0,0 +1,149 @@
|
|||
#pragma once
|
||||
|
||||
#include "../Common.h"
|
||||
|
||||
NS_BF_BEGIN
|
||||
|
||||
template <typename T, size_t NumChunkElements = 0x2000 / sizeof(T)>
|
||||
class ChunkedVector
|
||||
{
|
||||
public:
|
||||
Array<T*> mChunks;
|
||||
T* mCurChunkPtr;
|
||||
T* mCurChunkEnd;
|
||||
|
||||
public:
|
||||
class Iterator
|
||||
{
|
||||
public:
|
||||
ChunkedVector* mVec;
|
||||
int mChunkIdx;
|
||||
T* mCurChunkPtr;
|
||||
T* mCurChunkEnd;
|
||||
|
||||
public:
|
||||
Iterator(ChunkedVector& vec) : mVec(&vec)
|
||||
{
|
||||
}
|
||||
|
||||
Iterator& operator++()
|
||||
{
|
||||
mCurChunkPtr++;
|
||||
if (mCurChunkPtr == mCurChunkEnd)
|
||||
{
|
||||
if (mChunkIdx < (int)mVec->mChunks.size() - 1)
|
||||
{
|
||||
mChunkIdx++;
|
||||
mCurChunkPtr = mVec->mChunks[mChunkIdx];
|
||||
mCurChunkEnd = mCurChunkPtr + NumChunkElements;
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator!=(const Iterator& itr) const
|
||||
{
|
||||
return (mCurChunkPtr != itr.mCurChunkPtr);
|
||||
}
|
||||
|
||||
T& operator*()
|
||||
{
|
||||
return *mCurChunkPtr;
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
ChunkedVector()
|
||||
{
|
||||
mCurChunkPtr = NULL;
|
||||
mCurChunkEnd = NULL;
|
||||
}
|
||||
|
||||
~ChunkedVector()
|
||||
{
|
||||
for (auto chunk : mChunks)
|
||||
delete [] chunk;
|
||||
}
|
||||
|
||||
T& operator[](int idx)
|
||||
{
|
||||
return mChunks[idx / NumChunkElements][idx % NumChunkElements];
|
||||
}
|
||||
|
||||
T& AllocBack()
|
||||
{
|
||||
if (mCurChunkPtr == mCurChunkEnd)
|
||||
{
|
||||
mCurChunkPtr = new T[NumChunkElements];
|
||||
mCurChunkEnd = mCurChunkPtr + NumChunkElements;
|
||||
mChunks.push_back(mCurChunkPtr);
|
||||
}
|
||||
return *(mCurChunkPtr++);
|
||||
}
|
||||
|
||||
void push_back(T val)
|
||||
{
|
||||
AllocBack() = val;
|
||||
}
|
||||
|
||||
size_t size()
|
||||
{
|
||||
if (mChunks.size() == 0)
|
||||
return 0;
|
||||
return (mChunks.size() - 1)*NumChunkElements + (NumChunkElements - (mCurChunkEnd - mCurChunkPtr));
|
||||
}
|
||||
|
||||
T& front()
|
||||
{
|
||||
return mChunks[0][0];
|
||||
}
|
||||
|
||||
T& back()
|
||||
{
|
||||
return mCurChunkPtr[-1];
|
||||
}
|
||||
|
||||
Iterator begin()
|
||||
{
|
||||
Iterator itr(*this);
|
||||
if (mChunks.size() == 0)
|
||||
{
|
||||
itr.mChunkIdx = -1;
|
||||
itr.mCurChunkPtr = NULL;
|
||||
itr.mCurChunkEnd = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
itr.mChunkIdx = 0;
|
||||
itr.mCurChunkPtr = mChunks[0];
|
||||
itr.mCurChunkEnd = itr.mCurChunkPtr + NumChunkElements;
|
||||
}
|
||||
return itr;
|
||||
}
|
||||
|
||||
Iterator end()
|
||||
{
|
||||
Iterator itr(*this);
|
||||
itr.mChunkIdx = -1;
|
||||
itr.mCurChunkPtr = mCurChunkPtr;
|
||||
itr.mCurChunkEnd = NULL;
|
||||
return itr;
|
||||
}
|
||||
|
||||
Iterator GetIterator(size_t idx)
|
||||
{
|
||||
Iterator itr(*this);
|
||||
itr.mChunkIdx = (int)(idx / NumChunkElements);
|
||||
if (itr.mChunkIdx == mChunks.size())
|
||||
{
|
||||
BF_ASSERT(idx == size());
|
||||
return end();
|
||||
}
|
||||
itr.mCurChunkPtr = mChunks[itr.mChunkIdx] + (idx % NumChunkElements);
|
||||
itr.mCurChunkEnd = mChunks[itr.mChunkIdx] + NumChunkElements;
|
||||
return itr;
|
||||
}
|
||||
};
|
||||
|
||||
NS_BF_END
|
108
BeefySysLib/util/CritSect.h
Normal file
108
BeefySysLib/util/CritSect.h
Normal file
|
@ -0,0 +1,108 @@
|
|||
#pragma once
|
||||
|
||||
#include "../Common.h"
|
||||
|
||||
NS_BF_BEGIN;
|
||||
|
||||
class CritSect
|
||||
{
|
||||
public:
|
||||
BfpCritSect* mCritSect;
|
||||
int32 mLockCount;
|
||||
|
||||
public:
|
||||
CritSect()
|
||||
{
|
||||
mCritSect = BfpCritSect_Create();
|
||||
mLockCount = 0;
|
||||
}
|
||||
|
||||
~CritSect()
|
||||
{
|
||||
BfpCritSect_Release(mCritSect);
|
||||
BF_ASSERT(mLockCount == 0);
|
||||
}
|
||||
|
||||
bool TryLock()
|
||||
{
|
||||
bool locked = BfpCritSect_TryEnter(mCritSect, 0);
|
||||
if (locked)
|
||||
mLockCount++;
|
||||
return locked;
|
||||
}
|
||||
|
||||
bool TryLock(int waitMS)
|
||||
{
|
||||
bool locked = BfpCritSect_TryEnter(mCritSect, waitMS);
|
||||
if (locked)
|
||||
mLockCount++;
|
||||
return locked;
|
||||
}
|
||||
|
||||
void Lock()
|
||||
{
|
||||
BfpCritSect_Enter(mCritSect);
|
||||
mLockCount++;
|
||||
}
|
||||
|
||||
void Unlock()
|
||||
{
|
||||
mLockCount--;
|
||||
BfpCritSect_Leave(mCritSect);
|
||||
}
|
||||
};
|
||||
|
||||
class SyncEvent
|
||||
{
|
||||
public:
|
||||
BfpEvent* mEvent;
|
||||
|
||||
public:
|
||||
SyncEvent(bool manualReset = false, bool initialState = false)
|
||||
{
|
||||
BfpEventFlags flags = (manualReset) ? BfpEventFlag_AllowManualReset : (BfpEventFlags)(BfpEventFlag_AllowAutoReset | BfpEventFlag_AllowManualReset);
|
||||
if (initialState)
|
||||
flags = (BfpEventFlags)(flags | (manualReset ? BfpEventFlag_InitiallySet_Manual : BfpEventFlag_InitiallySet_Auto));
|
||||
mEvent = BfpEvent_Create(flags);
|
||||
}
|
||||
|
||||
~SyncEvent()
|
||||
{
|
||||
BfpEvent_Release(mEvent);
|
||||
}
|
||||
|
||||
void Set(bool requireManualReset = false)
|
||||
{
|
||||
BfpEvent_Set(mEvent, requireManualReset);
|
||||
}
|
||||
|
||||
void Reset()
|
||||
{
|
||||
BfpEvent_Reset(mEvent, NULL);
|
||||
}
|
||||
|
||||
bool WaitFor(int timeoutMS = -1)
|
||||
{
|
||||
return BfpEvent_WaitFor(mEvent, timeoutMS);
|
||||
}
|
||||
};
|
||||
|
||||
class AutoCrit
|
||||
{
|
||||
public:
|
||||
CritSect* mCritSect;
|
||||
|
||||
public:
|
||||
AutoCrit(CritSect& critSect)
|
||||
{
|
||||
mCritSect = &critSect;
|
||||
mCritSect->Lock();
|
||||
}
|
||||
|
||||
~AutoCrit()
|
||||
{
|
||||
mCritSect->Unlock();
|
||||
}
|
||||
};
|
||||
|
||||
NS_BF_END;
|
285
BeefySysLib/util/CubicFuncSpline.cpp
Normal file
285
BeefySysLib/util/CubicFuncSpline.cpp
Normal file
|
@ -0,0 +1,285 @@
|
|||
#include "CubicFuncSpline.h"
|
||||
|
||||
USING_NS_BF;
|
||||
|
||||
CubicFuncSpline::CubicFuncSpline()
|
||||
{
|
||||
lagpoly = NULL;
|
||||
intpoly = NULL;
|
||||
slopes = NULL;
|
||||
}
|
||||
|
||||
CubicFuncSpline::~CubicFuncSpline()
|
||||
{
|
||||
delete lagpoly;
|
||||
delete intpoly;
|
||||
delete slopes;
|
||||
}
|
||||
|
||||
void CubicFuncSpline::AddPt(float x, float y)
|
||||
{
|
||||
delete lagpoly;
|
||||
delete intpoly;
|
||||
delete slopes;
|
||||
lagpoly = NULL;
|
||||
intpoly = NULL;
|
||||
slopes = NULL;
|
||||
|
||||
mInputPoints.push_back(Point2D(x, y));
|
||||
}
|
||||
|
||||
int CubicFuncSpline::GetLength()
|
||||
{
|
||||
return (int) mInputPoints.size();
|
||||
}
|
||||
|
||||
void CubicFuncSpline::Lagrange()
|
||||
{
|
||||
int nPts = (int) mInputPoints.size();
|
||||
int i, j, jj, k;
|
||||
for( j = 0; j < nPts; j++ )
|
||||
intpoly[j] = 0.0;
|
||||
for( i = 0; i < nPts; i++ )
|
||||
{
|
||||
lagpoly[i+0*nPts] = 1.0;
|
||||
float fac = mInputPoints[i].mY;
|
||||
j = 0;
|
||||
for( k = 0; k < nPts; k++ )
|
||||
{
|
||||
if( k == i )
|
||||
continue;
|
||||
lagpoly[i+(j+1)*nPts] = lagpoly[i+j*nPts];
|
||||
for( jj = j; jj > 0; jj-- )
|
||||
lagpoly[i+jj*nPts] = lagpoly[i+(jj-1)*nPts] - lagpoly[i+jj*nPts]*mInputPoints[k].mX;
|
||||
lagpoly[i+0*nPts] *= -mInputPoints[k].mX;
|
||||
j++;
|
||||
fac /= ( mInputPoints[i].mX - mInputPoints[k].mX );
|
||||
}
|
||||
for( j = 0; j < nPts; j++ )
|
||||
lagpoly[i+j*nPts] *= fac;
|
||||
for( j = 0; j < nPts; j++ )
|
||||
intpoly[j] += lagpoly[i+j*nPts];
|
||||
}
|
||||
}
|
||||
|
||||
void CubicFuncSpline::ComputeSplineSlopes()
|
||||
{
|
||||
int n = (int) mInputPoints.size() - 1;
|
||||
int i;
|
||||
float *h = new float[n];
|
||||
float *hinv = new float[n];
|
||||
float *g = new float[n];
|
||||
float *a = new float[n+1];
|
||||
float *b = new float[n+1];
|
||||
float fac;
|
||||
|
||||
for( i = 0; i < n; i++ )
|
||||
{
|
||||
h[i] = mInputPoints[i+1].mX - mInputPoints[i].mX;
|
||||
hinv[i] = 1.0f / h[i];
|
||||
g[i] = 3 * ( mInputPoints[i+1].mY-mInputPoints[i].mY ) * hinv[i] * hinv[i];
|
||||
}
|
||||
a[0] = 2 * hinv[0];
|
||||
b[0] = g[0];
|
||||
for( i = 1; i <= n; i++ )
|
||||
{
|
||||
fac = hinv[i-1]/a[i-1];
|
||||
a[i] = (2-fac) * hinv[i-1];
|
||||
b[i] = g[i-1] - fac * b[i-1];
|
||||
if( i < n )
|
||||
{
|
||||
a[i] += 2 * hinv[i];
|
||||
b[i] += g[i];
|
||||
}
|
||||
}
|
||||
slopes[n] = b[n] / a[n];
|
||||
for( i = n-1; i >= 0; i-- )
|
||||
slopes[i] = ( b[i] - hinv[i] * slopes[i+1] ) / a[i];
|
||||
delete [] h;
|
||||
delete [] hinv;
|
||||
delete [] g;
|
||||
delete [] a;
|
||||
delete [] b;
|
||||
}
|
||||
|
||||
void CubicFuncSpline::Calculate()
|
||||
{
|
||||
int n = (int) mInputPoints.size() - 1;
|
||||
slopes = new float[n+1];
|
||||
intpoly = new float[n+1];
|
||||
lagpoly = new float[(n+1)*(n+1)];
|
||||
|
||||
Lagrange();
|
||||
ComputeSplineSlopes();
|
||||
}
|
||||
|
||||
float CubicFuncSpline::Evaluate(float x)
|
||||
{
|
||||
if (lagpoly == NULL)
|
||||
Calculate();
|
||||
|
||||
int idx = (int) 0;
|
||||
while ((idx < (int) mInputPoints.size()) && (x > mInputPoints[idx].mX))
|
||||
idx++;
|
||||
|
||||
if ((idx == mInputPoints.size()) || (idx == 0))
|
||||
{
|
||||
// Past end - extrapolate
|
||||
if (idx == mInputPoints.size())
|
||||
idx--;
|
||||
float s1 = slopes[idx];
|
||||
return mInputPoints[idx].mY + (x - mInputPoints[idx].mX) * s1;
|
||||
}
|
||||
|
||||
float x0 = mInputPoints[idx-1].mX;
|
||||
float x1 = mInputPoints[idx].mX;
|
||||
float y0 = mInputPoints[idx-1].mY;
|
||||
float y1 = mInputPoints[idx].mY;
|
||||
float s0 = slopes[idx-1];
|
||||
float s1 = slopes[idx];
|
||||
|
||||
float h = x1 - x0;
|
||||
float t = (x-x0)/h;
|
||||
float u = 1 - t;
|
||||
|
||||
return u * u * ( y0 * ( 2 * t + 1 ) + s0 * h * t )
|
||||
+ t * t * ( y1 * ( 3 - 2 * t ) - s1 * h * u );
|
||||
}
|
||||
|
||||
///
|
||||
|
||||
CubicUnitFuncSpline::CubicUnitFuncSpline()
|
||||
{
|
||||
lagpoly = NULL;
|
||||
intpoly = NULL;
|
||||
slopes = NULL;
|
||||
}
|
||||
|
||||
CubicUnitFuncSpline::~CubicUnitFuncSpline()
|
||||
{
|
||||
delete lagpoly;
|
||||
delete intpoly;
|
||||
delete slopes;
|
||||
}
|
||||
|
||||
void CubicUnitFuncSpline::AddPt(float y)
|
||||
{
|
||||
delete lagpoly;
|
||||
delete intpoly;
|
||||
delete slopes;
|
||||
lagpoly = NULL;
|
||||
intpoly = NULL;
|
||||
slopes = NULL;
|
||||
|
||||
mInputPoints.push_back(y);
|
||||
}
|
||||
|
||||
int CubicUnitFuncSpline::GetLength()
|
||||
{
|
||||
return (int) mInputPoints.size();
|
||||
}
|
||||
|
||||
void CubicUnitFuncSpline::Lagrange()
|
||||
{
|
||||
int nPts = (int) mInputPoints.size();
|
||||
int i, j, jj, k;
|
||||
for( j = 0; j < nPts; j++ )
|
||||
intpoly[j] = 0.0;
|
||||
for( i = 0; i < nPts; i++ )
|
||||
{
|
||||
lagpoly[i+0*nPts] = 1.0;
|
||||
float fac = mInputPoints[i];
|
||||
j = 0;
|
||||
for( k = 0; k < nPts; k++ )
|
||||
{
|
||||
if( k == i )
|
||||
continue;
|
||||
lagpoly[i+(j+1)*nPts] = lagpoly[i+j*nPts];
|
||||
for( jj = j; jj > 0; jj-- )
|
||||
lagpoly[i+jj*nPts] = lagpoly[i+(jj-1)*nPts] - lagpoly[i+jj*nPts]*k;
|
||||
lagpoly[i+0*nPts] *= -k;
|
||||
j++;
|
||||
fac /= ( i - k);
|
||||
}
|
||||
for( j = 0; j < nPts; j++ )
|
||||
lagpoly[i+j*nPts] *= fac;
|
||||
for( j = 0; j < nPts; j++ )
|
||||
intpoly[j] += lagpoly[i+j*nPts];
|
||||
}
|
||||
}
|
||||
|
||||
void CubicUnitFuncSpline::ComputeSplineSlopes()
|
||||
{
|
||||
int n = (int) mInputPoints.size() - 1;
|
||||
int i;
|
||||
float *h = new float[n];
|
||||
float *hinv = new float[n];
|
||||
float *g = new float[n];
|
||||
float *a = new float[n+1];
|
||||
float *b = new float[n+1];
|
||||
float fac;
|
||||
|
||||
for( i = 0; i < n; i++ )
|
||||
{
|
||||
h[i] = 1;
|
||||
hinv[i] = 1.0f / h[i];
|
||||
g[i] = 3 * ( mInputPoints[i+1] - mInputPoints[i] ) * hinv[i] * hinv[i];
|
||||
}
|
||||
a[0] = 2 * hinv[0];
|
||||
b[0] = g[0];
|
||||
|
||||
for( i = 1; i <= n; i++ )
|
||||
{
|
||||
fac = hinv[i-1]/a[i-1];
|
||||
a[i] = (2-fac) * hinv[i-1];
|
||||
b[i] = g[i-1] - fac * b[i-1];
|
||||
if( i < n )
|
||||
{
|
||||
a[i] += 2 * hinv[i];
|
||||
b[i] += g[i];
|
||||
}
|
||||
}
|
||||
slopes[n] = b[n] / a[n];
|
||||
|
||||
for( i = n-1; i >= 0; i-- )
|
||||
slopes[i] = ( b[i] - hinv[i] * slopes[i+1] ) / a[i];
|
||||
delete [] h;
|
||||
delete [] hinv;
|
||||
delete [] g;
|
||||
delete [] a;
|
||||
delete [] b;
|
||||
}
|
||||
|
||||
void CubicUnitFuncSpline::Calculate()
|
||||
{
|
||||
int n = (int) mInputPoints.size() - 1;
|
||||
slopes = new float[n+1];
|
||||
intpoly = new float[n+1];
|
||||
lagpoly = new float[(n+1)*(n+1)];
|
||||
|
||||
Lagrange();
|
||||
ComputeSplineSlopes();
|
||||
}
|
||||
|
||||
float CubicUnitFuncSpline::Evaluate(float x)
|
||||
{
|
||||
if (lagpoly == NULL)
|
||||
Calculate();
|
||||
|
||||
int idx = (int) x;
|
||||
|
||||
float x0 = (float)idx;
|
||||
float x1 = (float)idx + 1;
|
||||
float y0 = mInputPoints[idx];
|
||||
float y1 = mInputPoints[idx+1];
|
||||
float s0 = slopes[idx];
|
||||
float s1 = slopes[idx+1];
|
||||
|
||||
float h = x1 - x0;
|
||||
float t = (x-x0)/h;
|
||||
float u = 1 - t;
|
||||
|
||||
return u * u * ( y0 * ( 2 * t + 1 ) + s0 * h * t )
|
||||
+ t * t * ( y1 * ( 3 - 2 * t ) - s1 * h * u );
|
||||
}
|
||||
|
57
BeefySysLib/util/CubicFuncSpline.h
Normal file
57
BeefySysLib/util/CubicFuncSpline.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
#pragma once
|
||||
|
||||
#include "Common.h"
|
||||
#include "Point.h"
|
||||
#include <vector>
|
||||
|
||||
NS_BF_BEGIN;
|
||||
|
||||
class CubicFuncSpline
|
||||
{
|
||||
public:
|
||||
std::vector<Point2D> mInputPoints;
|
||||
float* lagpoly;
|
||||
float* intpoly;
|
||||
float* slopes;
|
||||
|
||||
protected:
|
||||
void Lagrange();
|
||||
void ComputeSplineSlopes();
|
||||
|
||||
public:
|
||||
CubicFuncSpline();
|
||||
~CubicFuncSpline();
|
||||
|
||||
void AddPt(float x, float y);
|
||||
int GetLength();
|
||||
|
||||
void Calculate();
|
||||
float Evaluate(float x);
|
||||
|
||||
};
|
||||
|
||||
class CubicUnitFuncSpline
|
||||
{
|
||||
public:
|
||||
std::vector<float> mInputPoints;
|
||||
float* lagpoly;
|
||||
float* intpoly;
|
||||
float* slopes;
|
||||
|
||||
protected:
|
||||
void Lagrange();
|
||||
void ComputeSplineSlopes();
|
||||
|
||||
public:
|
||||
CubicUnitFuncSpline();
|
||||
~CubicUnitFuncSpline();
|
||||
|
||||
void AddPt(float y);
|
||||
int GetLength();
|
||||
|
||||
void Calculate();
|
||||
float Evaluate(float x);
|
||||
|
||||
};
|
||||
|
||||
NS_BF_END;
|
109
BeefySysLib/util/CubicSpline.cpp
Normal file
109
BeefySysLib/util/CubicSpline.cpp
Normal file
|
@ -0,0 +1,109 @@
|
|||
#include "CubicSpline.h"
|
||||
|
||||
USING_NS_BF;
|
||||
|
||||
/* calculates the natural cubic spline that interpolates
|
||||
y[0], y[1], ... y[n]
|
||||
The first segment is returned as
|
||||
C[0].a + C[0].b*u + C[0].c*u^2 + C[0].d*u^3 0<=u <1
|
||||
the other segments are in C[1], C[2], ... C[n-1] */
|
||||
|
||||
CubicVal* CubicSpline2D::SolveCubic(std::vector<float> vals)
|
||||
{
|
||||
int n = (int) vals.size() - 1;
|
||||
const float* x = &vals[0];
|
||||
|
||||
float* gamma = new float[n+1];
|
||||
float* delta = new float[n+1];
|
||||
float* D = new float[n+1];
|
||||
int i;
|
||||
/* We solve the equation
|
||||
[2 1 ] [D[0]] [3(x[1] - x[0]) ]
|
||||
|1 4 1 | |D[1]| |3(x[2] - x[0]) |
|
||||
| 1 4 1 | | . | = | . |
|
||||
| ..... | | . | | . |
|
||||
| 1 4 1| | . | |3(x[n] - x[n-2])|
|
||||
[ 1 2] [D[n]] [3(x[n] - x[n-1])]
|
||||
|
||||
by using row operations to convert the matrix to upper triangular
|
||||
and then back sustitution. The D[i] are the derivatives at the knots.
|
||||
*/
|
||||
|
||||
gamma[0] = 1.0f/2.0f;
|
||||
for ( i = 1; i < n; i++)
|
||||
gamma[i] = 1/(4-gamma[i-1]);
|
||||
gamma[n] = 1/(2-gamma[n-1]);
|
||||
|
||||
delta[0] = 3*(x[1]-x[0])*gamma[0];
|
||||
for ( i = 1; i < n; i++)
|
||||
delta[i] = (3*(x[i+1]-x[i-1])-delta[i-1])*gamma[i];
|
||||
delta[n] = (3*(x[n]-x[n-1])-delta[n-1])*gamma[n];
|
||||
|
||||
D[n] = delta[n];
|
||||
for ( i = n-1; i >= 0; i--)
|
||||
D[i] = delta[i] - gamma[i]*D[i+1];
|
||||
|
||||
/* now compute the coefficients of the cubics */
|
||||
CubicVal* C = new CubicVal[n];
|
||||
for ( i = 0; i < n; i++)
|
||||
{
|
||||
C[i].Set((float)x[i], D[i], 3*(x[i+1] - x[i]) - 2*D[i] - D[i+1],
|
||||
2*(x[i] - x[i+1]) + D[i] + D[i+1]);
|
||||
}
|
||||
return C;
|
||||
}
|
||||
|
||||
CubicSpline2D::CubicSpline2D()
|
||||
{
|
||||
mXCubicArray = NULL;
|
||||
mYCubicArray = NULL;
|
||||
}
|
||||
|
||||
CubicSpline2D::~CubicSpline2D()
|
||||
{
|
||||
delete mXCubicArray;
|
||||
delete mYCubicArray;
|
||||
}
|
||||
|
||||
void CubicSpline2D::AddPt(float x, float y)
|
||||
{
|
||||
delete mXCubicArray;
|
||||
mXCubicArray = NULL;
|
||||
delete mYCubicArray;
|
||||
mYCubicArray = NULL;
|
||||
|
||||
mInputPoints.push_back(Point2D(x, y));
|
||||
}
|
||||
|
||||
int CubicSpline2D::GetLength()
|
||||
{
|
||||
return (int) mInputPoints.size();
|
||||
}
|
||||
|
||||
void CubicSpline2D::Calculate()
|
||||
{
|
||||
std::vector<float> xVals;
|
||||
std::vector<float> yVals;
|
||||
for (int i = 0; i < (int) mInputPoints.size(); i++)
|
||||
{
|
||||
xVals.push_back(mInputPoints[i].mX);
|
||||
yVals.push_back(mInputPoints[i].mY);
|
||||
}
|
||||
|
||||
mXCubicArray = SolveCubic(xVals);
|
||||
mYCubicArray = SolveCubic(yVals);
|
||||
}
|
||||
|
||||
Point2D CubicSpline2D::Evaluate(float t)
|
||||
{
|
||||
if (mXCubicArray == NULL)
|
||||
Calculate();
|
||||
|
||||
int idx = (int) t;
|
||||
float frac = t - idx;
|
||||
|
||||
if (idx >= (int) mInputPoints.size() - 1)
|
||||
return mInputPoints[mInputPoints.size() - 1];
|
||||
|
||||
return Point2D(mXCubicArray[idx].Evaluate(frac), mYCubicArray[idx].Evaluate(frac));
|
||||
}
|
59
BeefySysLib/util/CubicSpline.h
Normal file
59
BeefySysLib/util/CubicSpline.h
Normal file
|
@ -0,0 +1,59 @@
|
|||
#pragma once
|
||||
|
||||
#include "Common.h"
|
||||
#include "Point.h"
|
||||
#include <vector>
|
||||
|
||||
NS_BF_BEGIN;
|
||||
|
||||
class CubicVal
|
||||
{
|
||||
public:
|
||||
float a,b,c,d; /* a + b*u + c*u^2 +d*u^3 */
|
||||
|
||||
public:
|
||||
CubicVal(float a = 0, float b = 0, float c = 0, float d = 0)
|
||||
{
|
||||
this->a = a;
|
||||
this->b = b;
|
||||
this->c = c;
|
||||
this->d = d;
|
||||
}
|
||||
|
||||
void Set(float a, float b, float c, float d)
|
||||
{
|
||||
this->a = a;
|
||||
this->b = b;
|
||||
this->c = c;
|
||||
this->d = d;
|
||||
}
|
||||
|
||||
/** evaluate cubic */
|
||||
float Evaluate(float u)
|
||||
{
|
||||
return (((d*u) + c)*u + b)*u + a;
|
||||
}
|
||||
};
|
||||
|
||||
class CubicSpline2D
|
||||
{
|
||||
public:
|
||||
std::vector<Point2D> mInputPoints;
|
||||
CubicVal* mXCubicArray;
|
||||
CubicVal* mYCubicArray;
|
||||
|
||||
public:
|
||||
CubicVal* SolveCubic(std::vector<float> vals);
|
||||
|
||||
public:
|
||||
CubicSpline2D();
|
||||
~CubicSpline2D();
|
||||
|
||||
void AddPt(float x, float y);
|
||||
int GetLength();
|
||||
|
||||
void Calculate();
|
||||
Point2D Evaluate(float t);
|
||||
};
|
||||
|
||||
NS_BF_END;
|
195
BeefySysLib/util/DLIList.h
Normal file
195
BeefySysLib/util/DLIList.h
Normal file
|
@ -0,0 +1,195 @@
|
|||
#pragma once
|
||||
|
||||
#include "../Common.h"
|
||||
|
||||
// Doubly Linked Intrusive List (no 'node' wrapper, requires mNext member in T)
|
||||
|
||||
NS_BF_BEGIN;
|
||||
|
||||
template <typename T>
|
||||
class DLIList
|
||||
{
|
||||
public:
|
||||
T mHead;
|
||||
T mTail;
|
||||
|
||||
struct RemovableIterator
|
||||
{
|
||||
public:
|
||||
T* mPrevNextPtr;
|
||||
|
||||
public:
|
||||
RemovableIterator(T* prevNextPtr)
|
||||
{
|
||||
mPrevNextPtr = prevNextPtr;
|
||||
}
|
||||
|
||||
RemovableIterator& operator++()
|
||||
{
|
||||
T newNode = *mPrevNextPtr;
|
||||
if (newNode != NULL)
|
||||
mPrevNextPtr = &newNode->mNext;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator!=(const RemovableIterator& itr) const
|
||||
{
|
||||
if (itr.mPrevNextPtr == NULL)
|
||||
return *mPrevNextPtr != NULL;
|
||||
return *itr.mPrevNextPtr != *mPrevNextPtr;
|
||||
}
|
||||
|
||||
T operator*()
|
||||
{
|
||||
return *mPrevNextPtr;
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
DLIList()
|
||||
{
|
||||
mHead = NULL;
|
||||
mTail = NULL;
|
||||
}
|
||||
|
||||
void Size()
|
||||
{
|
||||
int size = 0;
|
||||
T checkNode = mHead;
|
||||
while (checkNode != NULL)
|
||||
{
|
||||
size++;
|
||||
checkNode = checkNode->mNext;
|
||||
}
|
||||
}
|
||||
|
||||
void PushBack(T node)
|
||||
{
|
||||
BF_ASSERT(node->mNext == NULL);
|
||||
|
||||
if (mHead == NULL)
|
||||
mHead = node;
|
||||
else
|
||||
{
|
||||
mTail->mNext = node;
|
||||
node->mPrev = mTail;
|
||||
}
|
||||
mTail = node;
|
||||
}
|
||||
|
||||
void AddAfter(T refNode, T newNode)
|
||||
{
|
||||
auto prevNext = refNode->mNext;
|
||||
refNode->mNext = newNode;
|
||||
newNode->mPrev = refNode;
|
||||
newNode->mNext = prevNext;
|
||||
if (prevNext != NULL)
|
||||
prevNext->mPrev = newNode;
|
||||
if (refNode == mTail)
|
||||
mTail = newNode;
|
||||
}
|
||||
|
||||
void PushFront(T node)
|
||||
{
|
||||
if (mHead == NULL)
|
||||
mTail = node;
|
||||
else
|
||||
{
|
||||
mHead->mPrev = node;
|
||||
node->mNext = mHead;
|
||||
}
|
||||
mHead = node;
|
||||
}
|
||||
|
||||
T PopFront()
|
||||
{
|
||||
T node = mHead;
|
||||
mHead = node->mNext;
|
||||
mHead->mPrev = NULL;
|
||||
node->mNext = NULL;
|
||||
return node;
|
||||
}
|
||||
|
||||
void Remove(T node)
|
||||
{
|
||||
if (node->mPrev == NULL)
|
||||
{
|
||||
mHead = node->mNext;
|
||||
if (mHead != NULL)
|
||||
mHead->mPrev = NULL;
|
||||
}
|
||||
else
|
||||
node->mPrev->mNext = node->mNext;
|
||||
|
||||
if (node->mNext == NULL)
|
||||
{
|
||||
mTail = node->mPrev;
|
||||
if (mTail != NULL)
|
||||
mTail->mNext = NULL;
|
||||
}
|
||||
else
|
||||
node->mNext->mPrev = node->mPrev;
|
||||
|
||||
node->mPrev = NULL;
|
||||
node->mNext = NULL;
|
||||
}
|
||||
|
||||
void Replace(T oldNode, T newNode)
|
||||
{
|
||||
if (oldNode->mPrev != NULL)
|
||||
{
|
||||
oldNode->mPrev->mNext = newNode;
|
||||
newNode->mPrev = oldNode->mPrev;
|
||||
oldNode->mPrev = NULL;
|
||||
}
|
||||
else
|
||||
mHead = newNode;
|
||||
|
||||
if (oldNode->mNext != NULL)
|
||||
{
|
||||
oldNode->mNext->mPrev = newNode;
|
||||
newNode->mNext = oldNode->mNext;
|
||||
oldNode->mNext = NULL;
|
||||
}
|
||||
else
|
||||
mTail = newNode;
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
T checkNode = mHead;
|
||||
while (checkNode != NULL)
|
||||
{
|
||||
T next = checkNode->mNext;
|
||||
checkNode->mNext = NULL;
|
||||
checkNode->mPrev = NULL;
|
||||
checkNode = next;
|
||||
}
|
||||
|
||||
mHead = NULL;
|
||||
mTail = NULL;
|
||||
}
|
||||
|
||||
void ClearFast()
|
||||
{
|
||||
mHead = NULL;
|
||||
mTail = NULL;
|
||||
}
|
||||
|
||||
bool IsEmpty()
|
||||
{
|
||||
return mHead == NULL;
|
||||
}
|
||||
|
||||
RemovableIterator begin()
|
||||
{
|
||||
return RemovableIterator(&mHead);
|
||||
}
|
||||
|
||||
RemovableIterator end()
|
||||
{
|
||||
return RemovableIterator(NULL);
|
||||
}
|
||||
};
|
||||
|
||||
NS_BF_END;
|
1100
BeefySysLib/util/Deque.h
Normal file
1100
BeefySysLib/util/Deque.h
Normal file
File diff suppressed because it is too large
Load diff
679
BeefySysLib/util/Dictionary.h
Normal file
679
BeefySysLib/util/Dictionary.h
Normal file
|
@ -0,0 +1,679 @@
|
|||
#pragma once
|
||||
|
||||
#include "../Common.h"
|
||||
#include <unordered_map>
|
||||
|
||||
#define NEW_DICTIONAY
|
||||
|
||||
NS_BF_BEGIN;
|
||||
|
||||
#ifdef NEW_DICTIONAY
|
||||
|
||||
template <typename TKey, typename TValue>
|
||||
class Dictionary
|
||||
{
|
||||
public:
|
||||
typedef int int_cosize;
|
||||
typedef TKey key_type;
|
||||
typedef TValue value_type;
|
||||
|
||||
struct EntryPair
|
||||
{
|
||||
public:
|
||||
TKey mKey; // Key of entry
|
||||
TValue mValue; // Value of entry
|
||||
};
|
||||
|
||||
struct RawEntryPair
|
||||
{
|
||||
public:
|
||||
typename std::aligned_storage<sizeof(TKey), alignof(TKey)>::type mKey;
|
||||
typename std::aligned_storage<sizeof(TValue), alignof(TValue)>::type mValue;
|
||||
};
|
||||
|
||||
public:
|
||||
struct Entry : public RawEntryPair
|
||||
{
|
||||
int_cosize mNext; // Index of next entry, -1 if last
|
||||
int32 mHashCode; // Lower 31 bits of hash code, -1 if unused
|
||||
};
|
||||
|
||||
public:
|
||||
int_cosize* mBuckets;
|
||||
Entry* mEntries;
|
||||
int_cosize mAllocSize;
|
||||
int_cosize mCount;
|
||||
int_cosize mFreeList;
|
||||
int_cosize mFreeCount;
|
||||
|
||||
public:
|
||||
struct iterator
|
||||
{
|
||||
public:
|
||||
typedef std::bidirectional_iterator_tag iterator_category;
|
||||
//typedef T value_type;
|
||||
//typedef intptr difference_type;
|
||||
|
||||
//typedef T* pointer;
|
||||
//typedef T& reference;
|
||||
|
||||
public:
|
||||
Dictionary* mDictionary;
|
||||
int_cosize mIdx;
|
||||
int_cosize mCurrentIdx;
|
||||
|
||||
protected:
|
||||
void MoveNext()
|
||||
{
|
||||
while ((uintptr)mIdx < (uintptr)mDictionary->mCount)
|
||||
{
|
||||
if (mDictionary->mEntries[mIdx].mHashCode >= 0)
|
||||
{
|
||||
mCurrentIdx = mIdx;
|
||||
mIdx++;
|
||||
return;
|
||||
}
|
||||
mIdx++;
|
||||
}
|
||||
|
||||
mIdx = mDictionary->mCount + 1;
|
||||
mCurrentIdx = -1;
|
||||
}
|
||||
|
||||
public:
|
||||
iterator()
|
||||
{
|
||||
mDictionary = NULL;
|
||||
mIdx = 0;
|
||||
mCurrentIdx = -1;
|
||||
}
|
||||
|
||||
iterator(Dictionary* dict, int idx)
|
||||
{
|
||||
mDictionary = dict;
|
||||
mIdx = idx;
|
||||
mCurrentIdx = idx;
|
||||
}
|
||||
|
||||
iterator(Dictionary* dict, int idx, int currentIdx)
|
||||
{
|
||||
mDictionary = dict;
|
||||
mIdx = idx;
|
||||
mCurrentIdx = currentIdx;
|
||||
}
|
||||
|
||||
iterator& operator++()
|
||||
{
|
||||
MoveNext();
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator operator++(int)
|
||||
{
|
||||
auto prevVal = *this;
|
||||
MoveNext();
|
||||
return prevVal;
|
||||
}
|
||||
|
||||
/*iterator& operator--()
|
||||
{
|
||||
mPtr--;
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator operator--(int)
|
||||
{
|
||||
auto prevVal = *this;
|
||||
mPtr--;
|
||||
return prevVal;
|
||||
}*/
|
||||
|
||||
bool operator!=(const iterator& itr) const
|
||||
{
|
||||
return (itr.mDictionary != mDictionary) || (itr.mIdx != mIdx);
|
||||
}
|
||||
|
||||
bool operator==(const iterator& itr) const
|
||||
{
|
||||
return (itr.mDictionary == mDictionary) && (itr.mIdx == mIdx);
|
||||
}
|
||||
|
||||
EntryPair& operator*()
|
||||
{
|
||||
return *(EntryPair*)&mDictionary->mEntries[mCurrentIdx];
|
||||
}
|
||||
|
||||
EntryPair* operator->()
|
||||
{
|
||||
return (EntryPair*)&mDictionary->mEntries[mCurrentIdx];
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
int_cosize GetPrimeish(int_cosize min)
|
||||
{
|
||||
// This is a minimal effort to help address-aligned dataa
|
||||
return (min | 1);
|
||||
}
|
||||
|
||||
int_cosize ExpandSize(int_cosize oldSize)
|
||||
{
|
||||
int_cosize newSize = 2 * oldSize;
|
||||
|
||||
// Allow the hashtables to grow to maximum possible size (~2G elements) before encoutering capacity overflow.
|
||||
// Note that this check works even when mAllocSize overflowed thanks to the (uint) cast
|
||||
/*if ((uint)newSize > MaxPrimeArrayLength && MaxPrimeArrayLength > oldSize)
|
||||
{
|
||||
Contract.Assert( MaxPrimeArrayLength == GetPrime(MaxPrimeArrayLength), "Invalid MaxPrimeArrayLength");
|
||||
return MaxPrimeArrayLength;
|
||||
}*/
|
||||
|
||||
return GetPrimeish(newSize);
|
||||
}
|
||||
|
||||
void Resize()
|
||||
{
|
||||
Resize(ExpandSize(mCount), false);
|
||||
}
|
||||
|
||||
void Resize(intptr newSize, bool forceNewHashCodes)
|
||||
{
|
||||
BF_ASSERT(newSize >= mAllocSize);
|
||||
int_cosize* newBuckets = new int_cosize[newSize];
|
||||
for (int_cosize i = 0; i < newSize; i++)
|
||||
newBuckets[i] = -1;
|
||||
Entry* newEntries = new Entry[newSize];
|
||||
//mEntries.CopyTo(newEntries, 0, 0, mCount);
|
||||
for (int i = 0; i < mCount; i++)
|
||||
{
|
||||
auto& newEntry = newEntries[i];
|
||||
auto& oldEntry = mEntries[i];
|
||||
newEntry.mHashCode = oldEntry.mHashCode;
|
||||
newEntry.mNext = oldEntry.mNext;
|
||||
new (&newEntry.mKey) TKey(std::move(*(TKey*)&oldEntry.mKey));
|
||||
new (&newEntry.mValue) TValue(std::move(*(TValue*)&oldEntry.mValue));
|
||||
}
|
||||
for (int i = mCount; i < newSize; i++)
|
||||
{
|
||||
newEntries[i].mHashCode = -1;
|
||||
}
|
||||
if (forceNewHashCodes)
|
||||
{
|
||||
for (int_cosize i = 0; i < mCount; i++)
|
||||
{
|
||||
if (newEntries[i].mHashCode != -1)
|
||||
{
|
||||
newEntries[i].mHashCode = (int_cosize)BeefHash<TKey>()(*(TKey*)&newEntries[i].mKey) & 0x7FFFFFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int_cosize i = 0; i < mCount; i++)
|
||||
{
|
||||
if (newEntries[i].mHashCode >= 0)
|
||||
{
|
||||
int_cosize bucket = (int_cosize)(newEntries[i].mHashCode % newSize);
|
||||
newEntries[i].mNext = newBuckets[bucket];
|
||||
newBuckets[bucket] = i;
|
||||
}
|
||||
}
|
||||
|
||||
delete [] mBuckets;
|
||||
delete [] mEntries;
|
||||
|
||||
mBuckets = newBuckets;
|
||||
mEntries = newEntries;
|
||||
mAllocSize = (int_cosize)newSize;
|
||||
}
|
||||
|
||||
int FindEntry(const TKey& key)
|
||||
{
|
||||
if (mBuckets != NULL)
|
||||
{
|
||||
int_cosize hashCode = (int_cosize)BeefHash<TKey>()(key) & 0x7FFFFFFF;
|
||||
for (int_cosize i = mBuckets[hashCode % mAllocSize]; i >= 0; i = mEntries[i].mNext)
|
||||
{
|
||||
if (mEntries[i].mHashCode == hashCode && (*(TKey*)&mEntries[i].mKey == key)) return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
template <typename TAltKey>
|
||||
int FindEntryWith(const TAltKey& key)
|
||||
{
|
||||
if (mBuckets != NULL)
|
||||
{
|
||||
int_cosize hashCode = (int_cosize)BeefHash<TAltKey>()(key) & 0x7FFFFFFF;
|
||||
for (int_cosize i = mBuckets[hashCode % mAllocSize]; i >= 0; i = mEntries[i].mNext)
|
||||
{
|
||||
if (mEntries[i].mHashCode == hashCode && (*(TKey*)&mEntries[i].mKey == key)) return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void Initialize(intptr capacity)
|
||||
{
|
||||
int_cosize size = GetPrimeish((int_cosize)capacity);
|
||||
mBuckets = new int_cosize[size];
|
||||
mAllocSize = size;
|
||||
for (int_cosize i = 0; i < (int_cosize)mAllocSize; i++) mBuckets[i] = -1;
|
||||
mEntries = new Entry[size];
|
||||
mFreeList = -1;
|
||||
}
|
||||
|
||||
bool Insert(const TKey& key, bool add, TKey** keyPtr, TValue** valuePtr)
|
||||
{
|
||||
if (mBuckets == NULL) Initialize(0);
|
||||
int_cosize hashCode = (int_cosize)BeefHash<TKey>()(key) & 0x7FFFFFFF;
|
||||
int_cosize targetBucket = hashCode % (int_cosize)mAllocSize;
|
||||
|
||||
for (int_cosize i = mBuckets[targetBucket]; i >= 0; i = mEntries[i].mNext)
|
||||
{
|
||||
if ((mEntries[i].mHashCode == hashCode) && (*(TKey*)&mEntries[i].mKey == key))
|
||||
{
|
||||
if (add)
|
||||
{
|
||||
BF_FATAL("Duplicate key");
|
||||
//ThrowUnimplemented();
|
||||
//ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_AddingDuplicate);
|
||||
}
|
||||
//entries[i].value = value;
|
||||
//mVersion++;
|
||||
if (keyPtr != NULL)
|
||||
*keyPtr = (TKey*)&mEntries[i].mKey;
|
||||
if (valuePtr != NULL)
|
||||
*valuePtr = (TValue*)&mEntries[i].mValue;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
int_cosize index;
|
||||
if (mFreeCount > 0)
|
||||
{
|
||||
index = mFreeList;
|
||||
mFreeList = mEntries[index].mNext;
|
||||
mFreeCount--;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mCount == mAllocSize)
|
||||
{
|
||||
Resize();
|
||||
targetBucket = hashCode % (int_cosize)mAllocSize;
|
||||
}
|
||||
index = mCount;
|
||||
mCount++;
|
||||
}
|
||||
|
||||
mEntries[index].mHashCode = hashCode;
|
||||
mEntries[index].mNext = mBuckets[targetBucket];
|
||||
new (&mEntries[index].mKey) TKey(key);
|
||||
mBuckets[targetBucket] = index;
|
||||
//mVersion++;
|
||||
|
||||
if (keyPtr != NULL)
|
||||
*keyPtr = (TKey*)&mEntries[index].mKey;
|
||||
if (valuePtr != NULL)
|
||||
*valuePtr = (TValue*)&mEntries[index].mValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
void RemoveIdx(int_cosize bucket, int_cosize i, int_cosize last)
|
||||
{
|
||||
if (last < 0)
|
||||
{
|
||||
mBuckets[bucket] = mEntries[i].mNext;
|
||||
}
|
||||
else
|
||||
{
|
||||
mEntries[last].mNext = mEntries[i].mNext;
|
||||
}
|
||||
mEntries[i].mHashCode = -1;
|
||||
mEntries[i].mNext = mFreeList;
|
||||
((TKey*)&mEntries[i].mKey)->~TKey();
|
||||
((TValue*)&mEntries[i].mValue)->~TValue();
|
||||
mFreeList = i;
|
||||
mFreeCount++;
|
||||
}
|
||||
|
||||
public:
|
||||
Dictionary()
|
||||
{
|
||||
mBuckets = NULL;
|
||||
mEntries = NULL;
|
||||
mAllocSize = 0;
|
||||
mCount = 0;
|
||||
mFreeList = 0;
|
||||
mFreeCount = 0;
|
||||
}
|
||||
|
||||
Dictionary(const Dictionary& val)
|
||||
{
|
||||
mAllocSize = val.mAllocSize;
|
||||
mCount = val.mCount;
|
||||
|
||||
if (mAllocSize == 0)
|
||||
{
|
||||
mBuckets = NULL;
|
||||
mEntries = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
mBuckets = new int_cosize[mAllocSize];
|
||||
mEntries = new Entry[mAllocSize];
|
||||
|
||||
for (int_cosize i = 0; i < mAllocSize; i++)
|
||||
mBuckets[i] = val.mBuckets[i];
|
||||
|
||||
for (int i = 0; i < mCount; i++)
|
||||
{
|
||||
auto& newEntry = mEntries[i];
|
||||
auto& oldEntry = val.mEntries[i];
|
||||
newEntry.mHashCode = oldEntry.mHashCode;
|
||||
newEntry.mNext = oldEntry.mNext;
|
||||
new (&newEntry.mKey) TKey(*(TKey*)&oldEntry.mKey);
|
||||
new (&newEntry.mValue) TValue(*(TValue*)&oldEntry.mValue);
|
||||
}
|
||||
for (int i = mCount; i < mAllocSize; i++)
|
||||
{
|
||||
mEntries[i].mHashCode = -1;
|
||||
}
|
||||
}
|
||||
|
||||
mFreeCount = val.mFreeCount;
|
||||
mFreeList = val.mFreeList;
|
||||
}
|
||||
|
||||
Dictionary(Dictionary&& val)
|
||||
{
|
||||
mAllocSize = val.mAllocSize;
|
||||
mCount = val.mCount;
|
||||
mBuckets = val.mBuckets;
|
||||
mEntries = val.mEntries;
|
||||
mFreeCount = val.mFreeCount;
|
||||
mFreeList = val.mFreeList;
|
||||
}
|
||||
|
||||
~Dictionary()
|
||||
{
|
||||
if (!std::is_pod<TKey>::value)
|
||||
{
|
||||
for (int_cosize i = 0; i < mCount; i++)
|
||||
{
|
||||
if (mEntries[i].mHashCode != -1)
|
||||
((TKey*)&mEntries[i].mKey)->~TKey();
|
||||
}
|
||||
}
|
||||
|
||||
if (!std::is_pod<TValue>::value)
|
||||
{
|
||||
for (int_cosize i = 0; i < mCount; i++)
|
||||
{
|
||||
if (mEntries[i].mHashCode != -1)
|
||||
((TValue*)&mEntries[i].mValue)->~TValue();
|
||||
}
|
||||
}
|
||||
|
||||
delete [] mBuckets;
|
||||
delete [] mEntries;
|
||||
}
|
||||
|
||||
Dictionary& operator=(const Dictionary& rhs)
|
||||
{
|
||||
Clear();
|
||||
for (auto& kv : rhs)
|
||||
TryAdd(kv.mKey, kv.mValue);
|
||||
return *this;
|
||||
}
|
||||
|
||||
intptr GetCount() const
|
||||
{
|
||||
return mCount - mFreeCount;
|
||||
}
|
||||
|
||||
intptr size() const
|
||||
{
|
||||
return mCount - mFreeCount;
|
||||
}
|
||||
|
||||
bool IsEmpty() const
|
||||
{
|
||||
return mCount - mFreeCount == 0;
|
||||
}
|
||||
|
||||
void Reserve(intptr size)
|
||||
{
|
||||
if (size > mAllocSize)
|
||||
Resize(size, false);
|
||||
}
|
||||
|
||||
TValue& operator[](const TKey& key)
|
||||
{
|
||||
TValue* valuePtr;
|
||||
if (Insert(key, false, NULL, &valuePtr))
|
||||
{
|
||||
new (valuePtr) TValue();
|
||||
}
|
||||
return *valuePtr;
|
||||
}
|
||||
|
||||
const TValue& operator[](const TKey& key) const
|
||||
{
|
||||
int_cosize i = (int_cosize)FindEntry(key);
|
||||
if (i >= 0)
|
||||
return mEntries[i].mValue;
|
||||
BF_FATAL("Key not found");
|
||||
return TValue();
|
||||
}
|
||||
|
||||
bool TryAdd(const TKey& key, const TValue& value)
|
||||
{
|
||||
TValue* valuePtr;
|
||||
if (!Insert(key, false, NULL, &valuePtr))
|
||||
return false;
|
||||
new (valuePtr) TValue(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TryAdd(const TKey& key, TKey** keyPtr, TValue** valuePtr)
|
||||
{
|
||||
if (!Insert(key, false, keyPtr, valuePtr))
|
||||
return false;
|
||||
new (*valuePtr) TValue();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns uninitialized valuePtr - must use placement new
|
||||
bool TryAddRaw(const TKey& key, TKey** keyPtr, TValue** valuePtr)
|
||||
{
|
||||
return Insert(key, false, keyPtr, valuePtr);
|
||||
}
|
||||
|
||||
bool TryGetValue(const TKey& key, TValue** valuePtr)
|
||||
{
|
||||
int_cosize i = (int_cosize)FindEntry(key);
|
||||
if (i >= 0)
|
||||
{
|
||||
*valuePtr = (TValue*)&mEntries[i].mValue;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename TAltKey>
|
||||
bool TryGetValueWith(const TAltKey& key, TValue** valuePtr)
|
||||
{
|
||||
int_cosize i = (int_cosize)FindEntryWith(key);
|
||||
if (i >= 0)
|
||||
{
|
||||
*valuePtr = (TValue*)&mEntries[i].mValue;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TryGetValue(const TKey& key, TValue* valuePtr)
|
||||
{
|
||||
int_cosize i = (int_cosize)FindEntry(key);
|
||||
if (i >= 0)
|
||||
{
|
||||
*valuePtr = *(TValue*)&mEntries[i].mValue;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
iterator Find(const TKey& key)
|
||||
{
|
||||
int_cosize i = (int_cosize)FindEntry(key);
|
||||
iterator itr;
|
||||
if (i >= 0)
|
||||
{
|
||||
return iterator((Dictionary*)this, i);
|
||||
}
|
||||
else
|
||||
return iterator((Dictionary*)this, mCount + 1, -1);
|
||||
}
|
||||
|
||||
bool Remove(const TKey& key)
|
||||
{
|
||||
if (mBuckets != NULL)
|
||||
{
|
||||
int_cosize hashCode = (int_cosize)BeefHash<TKey>()(key) & 0x7FFFFFFF;
|
||||
int_cosize bucket = hashCode % (int_cosize)mAllocSize;
|
||||
int_cosize last = -1;
|
||||
for (int_cosize i = mBuckets[bucket]; i >= 0; last = i, i = mEntries[i].mNext)
|
||||
{
|
||||
if ((mEntries[i].mHashCode == hashCode) && (*(TKey*)&mEntries[i].mKey == key))
|
||||
{
|
||||
RemoveIdx(bucket, i, last);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Remove(const TKey& key, TValue* valuePtr)
|
||||
{
|
||||
if (mBuckets != NULL)
|
||||
{
|
||||
int_cosize hashCode = (int_cosize)BeefHash<TKey>()(key) & 0x7FFFFFFF;
|
||||
int_cosize bucket = hashCode % (int_cosize)mAllocSize;
|
||||
int_cosize last = -1;
|
||||
for (int_cosize i = mBuckets[bucket]; i >= 0; last = i, i = mEntries[i].mNext)
|
||||
{
|
||||
if ((mEntries[i].mHashCode == hashCode) && (*(TKey*)&mEntries[i].mKey == key))
|
||||
{
|
||||
*valuePtr = *(TValue*)&mEntries[i].mValue;
|
||||
RemoveIdx(bucket, i, last);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
iterator Remove(const iterator& itr)
|
||||
{
|
||||
iterator nextItr = itr;
|
||||
++nextItr;
|
||||
|
||||
auto& entry = mEntries[itr.mCurrentIdx];
|
||||
BF_ASSERT(entry.mHashCode >= 0);
|
||||
|
||||
// We have to iterate through to mCurrentIdx so we can get 'last'
|
||||
int_cosize hashCode = entry.mHashCode;
|
||||
int_cosize bucket = hashCode % (int_cosize)mAllocSize;
|
||||
int_cosize last = -1;
|
||||
for (int_cosize i = mBuckets[bucket]; i >= 0; last = i, i = mEntries[i].mNext)
|
||||
{
|
||||
if ((mEntries[i].mHashCode == hashCode) && (i == itr.mCurrentIdx))
|
||||
{
|
||||
RemoveIdx(bucket, i, last);
|
||||
return nextItr;
|
||||
}
|
||||
}
|
||||
|
||||
BF_FATAL("not found");
|
||||
return nextItr;
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
if (mCount > 0)
|
||||
{
|
||||
for (int_cosize i = 0; i < mAllocSize; i++) mBuckets[i] = -1;
|
||||
|
||||
if (!std::is_pod<TKey>::value)
|
||||
{
|
||||
for (int_cosize i = 0; i < mCount; i++)
|
||||
{
|
||||
if (mEntries[i].mHashCode != -1)
|
||||
((TKey*)&mEntries[i].mKey)->~TKey();
|
||||
}
|
||||
}
|
||||
|
||||
if (!std::is_pod<TValue>::value)
|
||||
{
|
||||
for (int_cosize i = 0; i < mCount; i++)
|
||||
{
|
||||
if (mEntries[i].mHashCode != -1)
|
||||
((TValue*)&mEntries[i].mValue)->~TValue();
|
||||
}
|
||||
}
|
||||
|
||||
for (int_cosize i = 0; i < mCount; i++)
|
||||
{
|
||||
mEntries[i].mHashCode = -1;
|
||||
}
|
||||
|
||||
mFreeList = -1;
|
||||
mCount = 0;
|
||||
mFreeCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool ContainsKey(const TKey& key)
|
||||
{
|
||||
return FindEntry(key) >= 0;
|
||||
}
|
||||
|
||||
iterator begin() const
|
||||
{
|
||||
iterator itr((Dictionary*)this, 0);
|
||||
++itr;
|
||||
return itr;
|
||||
}
|
||||
|
||||
iterator end() const
|
||||
{
|
||||
return iterator((Dictionary*)this, mCount + 1, -1);
|
||||
}
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
template <typename TKey, typename TValue>
|
||||
class Dictionary : public std::unordered_map<TKey, TValue>
|
||||
{
|
||||
public:
|
||||
|
||||
public:
|
||||
TValue* GetValuePtr(const TKey& key)
|
||||
{
|
||||
auto itr = find(key);
|
||||
if (itr == end())
|
||||
return NULL;
|
||||
return &itr->second;
|
||||
}
|
||||
|
||||
bool ContainsKey(const TKey& key)
|
||||
{
|
||||
return find(key) != end();
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
NS_BF_END;
|
88
BeefySysLib/util/FileEnumerator.cpp
Normal file
88
BeefySysLib/util/FileEnumerator.cpp
Normal file
|
@ -0,0 +1,88 @@
|
|||
#include "FileEnumerator.h"
|
||||
|
||||
USING_NS_BF;
|
||||
|
||||
String FileEnumeratorEntry::GetFilePath() const
|
||||
{
|
||||
char outName[4096];
|
||||
int outSize = 4096;
|
||||
BfpFindFileData_GetFileName(mFindData, outName, &outSize, NULL);
|
||||
return mDirPath + "/" + outName;
|
||||
}
|
||||
|
||||
String FileEnumeratorEntry::GetFileName() const
|
||||
{
|
||||
char outName[4096];
|
||||
int outSize = 4096;
|
||||
BfpFindFileData_GetFileName(mFindData, outName, &outSize, NULL);
|
||||
return outName;
|
||||
}
|
||||
|
||||
const FileEnumerator::Iterator& FileEnumerator::Iterator::operator++()
|
||||
{
|
||||
BF_ASSERT(mIdx == mFileEnumerator->mIdx);
|
||||
mFileEnumerator->Next();
|
||||
mIdx = mFileEnumerator->mIdx;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool FileEnumerator::Iterator::operator==(const FileEnumerator::Iterator& rhs)
|
||||
{
|
||||
return
|
||||
(rhs.mFileEnumerator == mFileEnumerator) &&
|
||||
(rhs.mIdx == mIdx);
|
||||
}
|
||||
|
||||
bool FileEnumerator::Iterator::operator!=(const FileEnumerator::Iterator& rhs)
|
||||
{
|
||||
return
|
||||
(rhs.mFileEnumerator != mFileEnumerator) ||
|
||||
(rhs.mIdx != mIdx);
|
||||
}
|
||||
|
||||
const FileEnumerator::FileEnumeratorEntry& FileEnumerator::Iterator::operator*()
|
||||
{
|
||||
return *mFileEnumerator;
|
||||
}
|
||||
|
||||
FileEnumerator::FileEnumerator(const String& dirPath, Flags flags)
|
||||
{
|
||||
mDirPath = dirPath;
|
||||
mFindData = BfpFindFileData_FindFirstFile((dirPath + "/*.*").c_str(), (BfpFindFileFlags)flags, NULL);
|
||||
mFlags = flags;
|
||||
if (mFindData != NULL)
|
||||
mIdx = 0;
|
||||
else
|
||||
mIdx = -1;
|
||||
}
|
||||
|
||||
FileEnumerator::~FileEnumerator()
|
||||
{
|
||||
if (mFindData != NULL)
|
||||
BfpFindFileData_Release(mFindData);
|
||||
}
|
||||
|
||||
bool FileEnumerator::Next()
|
||||
{
|
||||
if (BfpFindFileData_FindNextFile(mFindData))
|
||||
mIdx++;
|
||||
else
|
||||
mIdx = -1;
|
||||
return mIdx != -1;
|
||||
}
|
||||
|
||||
FileEnumerator::Iterator FileEnumerator::begin()
|
||||
{
|
||||
Iterator itr;
|
||||
itr.mFileEnumerator = this;
|
||||
itr.mIdx = mIdx;
|
||||
return itr;
|
||||
}
|
||||
|
||||
FileEnumerator::Iterator FileEnumerator::end()
|
||||
{
|
||||
Iterator itr;
|
||||
itr.mFileEnumerator = this;
|
||||
itr.mIdx = -1;
|
||||
return itr;
|
||||
}
|
52
BeefySysLib/util/FileEnumerator.h
Normal file
52
BeefySysLib/util/FileEnumerator.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
#pragma once
|
||||
|
||||
#include "../Common.h"
|
||||
|
||||
NS_BF_BEGIN;
|
||||
|
||||
class FileEnumeratorEntry
|
||||
{
|
||||
public:
|
||||
String mDirPath;
|
||||
BfpFindFileData* mFindData;
|
||||
|
||||
String GetFilePath() const;
|
||||
String GetFileName() const;
|
||||
};
|
||||
|
||||
class FileEnumerator : public FileEnumeratorEntry
|
||||
{
|
||||
public:
|
||||
enum Flags
|
||||
{
|
||||
Flags_Files = 1,
|
||||
Flags_Directories = 2,
|
||||
};
|
||||
|
||||
class Iterator
|
||||
{
|
||||
public:
|
||||
FileEnumerator* mFileEnumerator;
|
||||
int mIdx;
|
||||
|
||||
const Iterator& operator++();
|
||||
bool operator==(const Iterator& rhs);
|
||||
bool operator!=(const Iterator& rhs);
|
||||
const FileEnumeratorEntry& operator*();
|
||||
};
|
||||
|
||||
public:
|
||||
Flags mFlags;
|
||||
int mIdx;
|
||||
|
||||
public:
|
||||
FileEnumerator(const String& fileName, Flags flags = Flags_Files);
|
||||
~FileEnumerator();
|
||||
|
||||
bool Next();
|
||||
|
||||
Iterator begin();
|
||||
Iterator end();
|
||||
};
|
||||
|
||||
NS_BF_END;
|
1485
BeefySysLib/util/Hash.cpp
Normal file
1485
BeefySysLib/util/Hash.cpp
Normal file
File diff suppressed because it is too large
Load diff
140
BeefySysLib/util/Hash.h
Normal file
140
BeefySysLib/util/Hash.h
Normal file
|
@ -0,0 +1,140 @@
|
|||
#pragma once
|
||||
|
||||
#include "../Common.h"
|
||||
#include "../FileStream.h"
|
||||
|
||||
NS_BF_BEGIN
|
||||
|
||||
class Val128
|
||||
{
|
||||
public:
|
||||
struct Hash
|
||||
{
|
||||
size_t operator()(const Val128& entry) const
|
||||
{
|
||||
return (size_t)(entry.mLow ^ entry.mHigh);
|
||||
}
|
||||
};
|
||||
|
||||
struct Equals
|
||||
{
|
||||
bool operator()(const Val128& lhs, const Val128& rhs) const
|
||||
{
|
||||
return (lhs.mLow == rhs.mLow) && (lhs.mHigh == rhs.mHigh);
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
uint64 mLow;
|
||||
uint64 mHigh;
|
||||
|
||||
public:
|
||||
Val128()
|
||||
{
|
||||
mLow = 0;
|
||||
mHigh = 0;
|
||||
}
|
||||
|
||||
Val128(int val)
|
||||
{
|
||||
mLow = (uint64)val;
|
||||
mHigh = 0;
|
||||
}
|
||||
|
||||
Val128& operator=(int val)
|
||||
{
|
||||
mLow = (uint64)val;
|
||||
mHigh = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool IsZero()
|
||||
{
|
||||
return (mLow == 0) && (mHigh == 0);
|
||||
}
|
||||
|
||||
explicit operator int()
|
||||
{
|
||||
return (int)mLow;
|
||||
}
|
||||
|
||||
String ToString()
|
||||
{
|
||||
return StrFormat("%lX%lX", mHigh, mLow);
|
||||
}
|
||||
};
|
||||
|
||||
static bool operator!=(const Val128& l, int rLow)
|
||||
{
|
||||
return (l.mHigh != 0) || (l.mLow != rLow);
|
||||
}
|
||||
|
||||
static bool operator==(const Val128& l, const Val128& r)
|
||||
{
|
||||
return (l.mLow == r.mLow) && (l.mHigh == r.mHigh);
|
||||
}
|
||||
|
||||
static bool operator!=(const Val128& l, const Val128& r)
|
||||
{
|
||||
return (l.mLow != r.mLow) || (l.mHigh != r.mHigh);
|
||||
}
|
||||
|
||||
static bool operator<(const Val128& l, const Val128& r)
|
||||
{
|
||||
int* lPtr = (int*)&l.mLow;
|
||||
int* rPtr = (int*)&r.mLow;
|
||||
if (lPtr[3] != rPtr[3]) //-V557
|
||||
return lPtr[3] < rPtr[3]; //-V557
|
||||
if (lPtr[2] != rPtr[2]) //-V557
|
||||
return lPtr[2] < rPtr[2]; //-V557
|
||||
if (lPtr[1] != rPtr[1])
|
||||
return lPtr[1] < rPtr[1];
|
||||
return lPtr[0] < rPtr[0];
|
||||
}
|
||||
|
||||
uint64 Hash64(uint64 hash, uint64 seed);
|
||||
uint64 Hash64(const void* data, int length, uint64 seed = 0);
|
||||
Val128 Hash128(const void* data, int length);
|
||||
Val128 Hash128(const void* data, int length, const Val128& seed);
|
||||
String HashEncode64(uint64 val); // Note: this only encodes the low 60 bits. Returns up to 10 characters.
|
||||
StringT<21> HashEncode128(Val128 val); // Returns up to 20 characters.
|
||||
|
||||
#define HASH128_MIXIN(hashVal, data) hashVal = Hash128(&data, sizeof(data), hashVal)
|
||||
#define HASH128_MIXIN_PTR(hashVal, data, size) hashVal = Hash128(data, size, hashVal)
|
||||
#define HASH128_MIXIN_STR(hashVal, str) hashVal = Hash128(str.c_str(), (int)str.length(), hashVal)
|
||||
|
||||
class HashContext
|
||||
{
|
||||
public:
|
||||
uint8 mBuf[1024];
|
||||
int mBufSize;
|
||||
int mBufOffset;
|
||||
bool mDbgViz;
|
||||
FileStream* mDbgVizStream;
|
||||
|
||||
public:
|
||||
HashContext()
|
||||
{
|
||||
mBufOffset = 0;
|
||||
mBufSize = 0;
|
||||
mDbgViz = false;
|
||||
mDbgVizStream = NULL;
|
||||
}
|
||||
|
||||
~HashContext();
|
||||
|
||||
void Reset();
|
||||
void Mixin(const void* data, int size);
|
||||
template <typename T>
|
||||
void Mixin(const T& val)
|
||||
{
|
||||
Mixin((void*)&val, (int)sizeof(val));
|
||||
}
|
||||
void MixinHashContext(HashContext& ctx);
|
||||
void MixinStr(const char* str);
|
||||
void MixinStr(const StringImpl& str);
|
||||
Val128 Finish128();
|
||||
uint64 Finish64();
|
||||
};
|
||||
|
||||
NS_BF_END
|
645
BeefySysLib/util/HashSet.h
Normal file
645
BeefySysLib/util/HashSet.h
Normal file
|
@ -0,0 +1,645 @@
|
|||
#pragma once
|
||||
|
||||
#include "../Common.h"
|
||||
#include <unordered_map>
|
||||
|
||||
NS_BF_BEGIN;
|
||||
|
||||
template <typename TKey>
|
||||
class HashSet
|
||||
{
|
||||
public:
|
||||
typedef int int_cosize;
|
||||
typedef TKey key_type;
|
||||
|
||||
public:
|
||||
struct Entry
|
||||
{
|
||||
typename std::aligned_storage<sizeof(TKey), alignof(TKey)>::type mKey;
|
||||
int_cosize mNext; // Index of next entry, -1 if last
|
||||
int32 mHashCode; // Lower 31 bits of hash code, -1 if unused
|
||||
};
|
||||
|
||||
public:
|
||||
int_cosize* mBuckets;
|
||||
Entry* mEntries;
|
||||
int_cosize mAllocSize;
|
||||
int_cosize mCount;
|
||||
int_cosize mFreeList;
|
||||
int_cosize mFreeCount;
|
||||
|
||||
public:
|
||||
struct iterator
|
||||
{
|
||||
public:
|
||||
typedef std::bidirectional_iterator_tag iterator_category;
|
||||
|
||||
public:
|
||||
HashSet* mDictionary;
|
||||
int_cosize mIdx;
|
||||
int_cosize mCurrentIdx;
|
||||
|
||||
protected:
|
||||
void MoveNext()
|
||||
{
|
||||
while ((uintptr)mIdx < (uintptr)mDictionary->mCount)
|
||||
{
|
||||
if (mDictionary->mEntries[mIdx].mHashCode >= 0)
|
||||
{
|
||||
mCurrentIdx = mIdx;
|
||||
mIdx++;
|
||||
return;
|
||||
}
|
||||
mIdx++;
|
||||
}
|
||||
|
||||
mIdx = mDictionary->mCount + 1;
|
||||
mCurrentIdx = -1;
|
||||
}
|
||||
|
||||
public:
|
||||
iterator()
|
||||
{
|
||||
mDictionary = NULL;
|
||||
mIdx = 0;
|
||||
mCurrentIdx = -1;
|
||||
}
|
||||
|
||||
iterator(HashSet* dict, int idx)
|
||||
{
|
||||
mDictionary = dict;
|
||||
mIdx = idx;
|
||||
mCurrentIdx = -1;
|
||||
}
|
||||
|
||||
iterator& operator++()
|
||||
{
|
||||
MoveNext();
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator operator++(int)
|
||||
{
|
||||
auto prevVal = *this;
|
||||
MoveNext();
|
||||
return prevVal;
|
||||
}
|
||||
|
||||
bool operator!=(const iterator& itr) const
|
||||
{
|
||||
return (itr.mDictionary != mDictionary) || (itr.mIdx != mIdx);
|
||||
}
|
||||
|
||||
bool operator==(const iterator& itr) const
|
||||
{
|
||||
return (itr.mDictionary == mDictionary) && (itr.mIdx == mIdx);
|
||||
}
|
||||
|
||||
TKey& operator*()
|
||||
{
|
||||
return *(TKey*)&mDictionary->mEntries[mCurrentIdx].mKey;
|
||||
}
|
||||
|
||||
TKey* operator->()
|
||||
{
|
||||
return (TKey*)&mDictionary->mEntries[mCurrentIdx].mKey;
|
||||
}
|
||||
};
|
||||
|
||||
struct HashSelector
|
||||
{
|
||||
HashSet* mDictionary;
|
||||
int32 mHashCode;
|
||||
|
||||
HashSelector(HashSet* dictionary, int32 hashCode)
|
||||
{
|
||||
mDictionary = dictionary;
|
||||
mHashCode = hashCode;
|
||||
}
|
||||
|
||||
struct iterator
|
||||
{
|
||||
public:
|
||||
typedef std::bidirectional_iterator_tag iterator_category;
|
||||
|
||||
public:
|
||||
HashSelector* mHashSelector;
|
||||
int_cosize mIdx;
|
||||
int_cosize mCurrentIdx;
|
||||
|
||||
protected:
|
||||
void MoveNext()
|
||||
{
|
||||
auto dictionary = mHashSelector->mDictionary;
|
||||
|
||||
while (mIdx >= 0)
|
||||
{
|
||||
if (dictionary->mEntries[mIdx].mHashCode == mHashSelector->mHashCode)
|
||||
{
|
||||
mCurrentIdx = mIdx;
|
||||
mIdx = dictionary->mEntries[mIdx].mNext;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
mIdx = dictionary->mEntries[mIdx].mNext;
|
||||
}
|
||||
}
|
||||
|
||||
mIdx = -1;
|
||||
mCurrentIdx = -1;
|
||||
}
|
||||
|
||||
public:
|
||||
iterator()
|
||||
{
|
||||
mHashSelector = NULL;
|
||||
mIdx = 0;
|
||||
mCurrentIdx = -1;
|
||||
}
|
||||
|
||||
iterator(HashSelector* hashSelector, int idx)
|
||||
{
|
||||
mHashSelector = hashSelector;
|
||||
mIdx = idx;
|
||||
mCurrentIdx = -1;
|
||||
}
|
||||
|
||||
iterator& operator++()
|
||||
{
|
||||
MoveNext();
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator operator++(int)
|
||||
{
|
||||
auto prevVal = *this;
|
||||
MoveNext();
|
||||
return prevVal;
|
||||
}
|
||||
|
||||
/*iterator& operator--()
|
||||
{
|
||||
mPtr--;
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator operator--(int)
|
||||
{
|
||||
auto prevVal = *this;
|
||||
mPtr--;
|
||||
return prevVal;
|
||||
}*/
|
||||
|
||||
bool operator!=(const iterator& itr) const
|
||||
{
|
||||
return (itr.mHashSelector != mHashSelector) || (itr.mCurrentIdx != mCurrentIdx );
|
||||
}
|
||||
|
||||
bool operator==(const iterator& itr) const
|
||||
{
|
||||
return (itr.mHashSelector == mHashSelector) && (itr.mCurrentIdx == mCurrentIdx );
|
||||
}
|
||||
|
||||
TKey& operator*()
|
||||
{
|
||||
return *(TKey*)&mHashSelector->mDictionary->mEntries[mCurrentIdx].mKey;
|
||||
}
|
||||
|
||||
TKey* operator->()
|
||||
{
|
||||
return (TKey*)&mHashSelector->mDictionary->mEntries[mCurrentIdx].mKey;
|
||||
}
|
||||
};
|
||||
|
||||
iterator begin()
|
||||
{
|
||||
int_cosize hashCode = (int_cosize)(mHashCode & 0x7FFFFFFF);
|
||||
|
||||
iterator itr(this, mDictionary->mBuckets[hashCode % mDictionary->mAllocSize]);
|
||||
++itr;
|
||||
return itr;
|
||||
}
|
||||
|
||||
iterator end()
|
||||
{
|
||||
return iterator(this, -1);
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
int_cosize GetPrimeish(int_cosize min)
|
||||
{
|
||||
// This is a minimal effort to help address-aligned dataa
|
||||
return (min | 1);
|
||||
}
|
||||
|
||||
int_cosize ExpandSize(int_cosize oldSize)
|
||||
{
|
||||
int_cosize newSize = 2 * oldSize;
|
||||
|
||||
// Allow the hashtables to grow to maximum possible size (~2G elements) before encoutering capacity overflow.
|
||||
// Note that this check works even when mAllocSize overflowed thanks to the (uint) cast
|
||||
/*if ((uint)newSize > MaxPrimeArrayLength && MaxPrimeArrayLength > oldSize)
|
||||
{
|
||||
Contract.Assert( MaxPrimeArrayLength == GetPrime(MaxPrimeArrayLength), "Invalid MaxPrimeArrayLength");
|
||||
return MaxPrimeArrayLength;
|
||||
}*/
|
||||
|
||||
return GetPrimeish(newSize);
|
||||
}
|
||||
|
||||
void Resize()
|
||||
{
|
||||
Resize(ExpandSize(mCount), false);
|
||||
}
|
||||
|
||||
void Resize(int newSize, bool forceNewHashCodes)
|
||||
{
|
||||
BF_ASSERT(newSize >= mAllocSize);
|
||||
int_cosize* newBuckets = new int_cosize[newSize];
|
||||
for (int_cosize i = 0; i < newSize; i++)
|
||||
newBuckets[i] = -1;
|
||||
Entry* newEntries = new Entry[newSize];
|
||||
//mEntries.CopyTo(newEntries, 0, 0, mCount);
|
||||
for (int i = 0; i < mCount; i++)
|
||||
{
|
||||
auto& newEntry = newEntries[i];
|
||||
auto& oldEntry = mEntries[i];
|
||||
newEntry.mHashCode = oldEntry.mHashCode;
|
||||
newEntry.mNext = oldEntry.mNext;
|
||||
new (&newEntry.mKey) TKey(std::move(*(TKey*)&oldEntry.mKey));
|
||||
}
|
||||
for (int i = mCount; i < newSize; i++)
|
||||
{
|
||||
newEntries[i].mHashCode = -1;
|
||||
}
|
||||
if (forceNewHashCodes)
|
||||
{
|
||||
for (int_cosize i = 0; i < mCount; i++)
|
||||
{
|
||||
if (newEntries[i].mHashCode != -1)
|
||||
{
|
||||
newEntries[i].mHashCode = (int_cosize)BeefHash<TKey>()(*(TKey*)&newEntries[i].mKey) & 0x7FFFFFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int_cosize i = 0; i < mCount; i++)
|
||||
{
|
||||
if (newEntries[i].mHashCode >= 0)
|
||||
{
|
||||
int_cosize bucket = (int_cosize)(newEntries[i].mHashCode % newSize);
|
||||
newEntries[i].mNext = newBuckets[bucket];
|
||||
newBuckets[bucket] = i;
|
||||
}
|
||||
}
|
||||
|
||||
delete[] mBuckets;
|
||||
delete[] mEntries;
|
||||
|
||||
mBuckets = newBuckets;
|
||||
mEntries = newEntries;
|
||||
mAllocSize = newSize;
|
||||
}
|
||||
|
||||
int FindEntry(const TKey& key)
|
||||
{
|
||||
if (mBuckets != NULL)
|
||||
{
|
||||
int_cosize hashCode = (int_cosize)BeefHash<TKey>()(key) & 0x7FFFFFFF;
|
||||
for (int_cosize i = mBuckets[hashCode % mAllocSize]; i >= 0; i = mEntries[i].mNext)
|
||||
{
|
||||
if (mEntries[i].mHashCode == hashCode && (*(TKey*)&mEntries[i].mKey == key)) return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
template <typename TOther>
|
||||
int FindEntryWith(const TOther& key)
|
||||
{
|
||||
if (mBuckets != NULL)
|
||||
{
|
||||
int_cosize hashCode = (int_cosize)BeefHash<TOther>()(key) & 0x7FFFFFFF;
|
||||
for (int_cosize i = mBuckets[hashCode % mAllocSize]; i >= 0; i = mEntries[i].mNext)
|
||||
{
|
||||
if (mEntries[i].mHashCode == hashCode && (*(TKey*)&mEntries[i].mKey == key)) return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void Initialize(intptr capacity)
|
||||
{
|
||||
int_cosize size = GetPrimeish((int_cosize)capacity);
|
||||
mBuckets = new int_cosize[size];
|
||||
mAllocSize = size;
|
||||
for (int_cosize i = 0; i < (int_cosize)mAllocSize; i++) mBuckets[i] = -1;
|
||||
mEntries = new Entry[size];
|
||||
mFreeList = -1;
|
||||
}
|
||||
|
||||
bool Insert(const TKey& key, bool add, TKey** keyPtr)
|
||||
{
|
||||
if (mBuckets == NULL) Initialize(0);
|
||||
int_cosize hashCode = (int_cosize)BeefHash<TKey>()(key) & 0x7FFFFFFF;
|
||||
int_cosize targetBucket = hashCode % (int_cosize)mAllocSize;
|
||||
|
||||
for (int_cosize i = mBuckets[targetBucket]; i >= 0; i = mEntries[i].mNext)
|
||||
{
|
||||
if ((mEntries[i].mHashCode == hashCode) && (*(TKey*)&mEntries[i].mKey == key))
|
||||
{
|
||||
if (add)
|
||||
{
|
||||
BF_FATAL("Duplicate key");
|
||||
//ThrowUnimplemented();
|
||||
//ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_AddingDuplicate);
|
||||
}
|
||||
//entries[i].value = value;
|
||||
//mVersion++;
|
||||
if (keyPtr != NULL)
|
||||
*keyPtr = (TKey*)&mEntries[i].mKey;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
int_cosize index;
|
||||
if (mFreeCount > 0)
|
||||
{
|
||||
index = mFreeList;
|
||||
mFreeList = mEntries[index].mNext;
|
||||
mFreeCount--;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mCount == mAllocSize)
|
||||
{
|
||||
Resize();
|
||||
targetBucket = hashCode % (int_cosize)mAllocSize;
|
||||
}
|
||||
index = mCount;
|
||||
mCount++;
|
||||
}
|
||||
|
||||
mEntries[index].mHashCode = hashCode;
|
||||
mEntries[index].mNext = mBuckets[targetBucket];
|
||||
new (&mEntries[index].mKey) TKey(key);
|
||||
mBuckets[targetBucket] = index;
|
||||
//mVersion++;
|
||||
|
||||
if (keyPtr != NULL)
|
||||
*keyPtr = (TKey*)&mEntries[index].mKey;
|
||||
return true;
|
||||
}
|
||||
|
||||
void RemoveIdx(int_cosize bucket, int_cosize i, int_cosize last)
|
||||
{
|
||||
if (last < 0)
|
||||
{
|
||||
mBuckets[bucket] = mEntries[i].mNext;
|
||||
}
|
||||
else
|
||||
{
|
||||
mEntries[last].mNext = mEntries[i].mNext;
|
||||
}
|
||||
mEntries[i].mHashCode = -1;
|
||||
mEntries[i].mNext = mFreeList;
|
||||
((TKey*)&mEntries[i].mKey)->~TKey();
|
||||
mFreeList = i;
|
||||
mFreeCount++;
|
||||
}
|
||||
|
||||
public:
|
||||
HashSet()
|
||||
{
|
||||
mBuckets = NULL;
|
||||
mEntries = NULL;
|
||||
mAllocSize = 0;
|
||||
mCount = 0;
|
||||
mFreeList = 0;
|
||||
mFreeCount = 0;
|
||||
}
|
||||
|
||||
HashSet(const HashSet& val)
|
||||
{
|
||||
mAllocSize = val.mAllocSize;
|
||||
mCount = val.mCount;
|
||||
|
||||
if (mAllocSize == 0)
|
||||
{
|
||||
mBuckets = NULL;
|
||||
mEntries = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
mBuckets = new int_cosize[mAllocSize];
|
||||
mEntries = new Entry[mAllocSize];
|
||||
|
||||
for (int_cosize i = 0; i < mAllocSize; i++)
|
||||
mBuckets[i] = val.mBuckets[i];
|
||||
|
||||
for (int i = 0; i < mCount; i++)
|
||||
{
|
||||
auto& newEntry = mEntries[i];
|
||||
auto& oldEntry = val.mEntries[i];
|
||||
newEntry.mHashCode = oldEntry.mHashCode;
|
||||
newEntry.mNext = oldEntry.mNext;
|
||||
new (&newEntry.mKey) TKey(*(TKey*)&oldEntry.mKey);
|
||||
}
|
||||
for (int i = mCount; i < mAllocSize; i++)
|
||||
{
|
||||
mEntries[i].mHashCode = -1;
|
||||
}
|
||||
}
|
||||
|
||||
mFreeCount = val.mFreeCount;
|
||||
mFreeList = val.mFreeList;
|
||||
}
|
||||
|
||||
HashSet(HashSet&& val)
|
||||
{
|
||||
mAllocSize = val.mAllocSize;
|
||||
mCount = val.mCount;
|
||||
mBuckets = val.mBuckets;
|
||||
mEntries = val.mEntries;
|
||||
mFreeCount = val.mFreeCount;
|
||||
mFreeList = val.mFreeList;
|
||||
}
|
||||
|
||||
~HashSet()
|
||||
{
|
||||
if (!std::is_pod<TKey>::value)
|
||||
{
|
||||
for (int_cosize i = 0; i < mCount; i++)
|
||||
{
|
||||
if (mEntries[i].mHashCode != -1)
|
||||
((TKey*)&mEntries[i].mKey)->~TKey();
|
||||
}
|
||||
}
|
||||
|
||||
delete[] mBuckets;
|
||||
delete[] mEntries;
|
||||
}
|
||||
|
||||
HashSet& operator=(const HashSet& rhs)
|
||||
{
|
||||
Clear();
|
||||
for (auto& val : rhs)
|
||||
Add(val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
intptr GetCount() const
|
||||
{
|
||||
return mCount - mFreeCount;
|
||||
}
|
||||
|
||||
intptr size() const
|
||||
{
|
||||
return mCount - mFreeCount;
|
||||
}
|
||||
|
||||
bool IsEmpty() const
|
||||
{
|
||||
return mCount - mFreeCount == 0;
|
||||
}
|
||||
|
||||
void Reserve(intptr size)
|
||||
{
|
||||
if (size > mAllocSize)
|
||||
Resize(size, false);
|
||||
}
|
||||
|
||||
bool Add(const TKey& key)
|
||||
{
|
||||
if (!Insert(key, false, NULL))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TryAdd(const TKey& key, TKey** keyPtr)
|
||||
{
|
||||
if (!Insert(key, false, keyPtr))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TryGet(const TKey& key, TKey** keyPtr)
|
||||
{
|
||||
int idx = FindEntry(key);
|
||||
if (idx == -1)
|
||||
return false;
|
||||
*keyPtr = (TKey*)&mEntries[idx].mKey;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename TOther>
|
||||
bool TryGetWith(const TOther& key, TKey** keyPtr)
|
||||
{
|
||||
int idx = FindEntryWith(key);
|
||||
if (idx == -1)
|
||||
return false;
|
||||
*keyPtr = (TKey*)&mEntries[idx].mKey;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Remove(const TKey& key)
|
||||
{
|
||||
if (mBuckets != NULL)
|
||||
{
|
||||
int_cosize hashCode = (int_cosize)BeefHash<TKey>()(key) & 0x7FFFFFFF;
|
||||
int_cosize bucket = hashCode % (int_cosize)mAllocSize;
|
||||
int_cosize last = -1;
|
||||
for (int_cosize i = mBuckets[bucket]; i >= 0; last = i, i = mEntries[i].mNext)
|
||||
{
|
||||
if ((mEntries[i].mHashCode == hashCode) && (*(TKey*)&mEntries[i].mKey == key))
|
||||
{
|
||||
RemoveIdx(bucket, i, last);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
iterator Remove(const iterator& itr)
|
||||
{
|
||||
iterator nextItr = itr;
|
||||
++nextItr;
|
||||
|
||||
auto& entry = mEntries[itr.mCurrentIdx];
|
||||
BF_ASSERT(entry.mHashCode >= 0);
|
||||
|
||||
// We have to iterate through to mCurrentIdx so we can get 'last'
|
||||
int_cosize hashCode = entry.mHashCode;
|
||||
int_cosize bucket = hashCode % (int_cosize)mAllocSize;
|
||||
int_cosize last = -1;
|
||||
for (int_cosize i = mBuckets[bucket]; i >= 0; last = i, i = mEntries[i].mNext)
|
||||
{
|
||||
if ((mEntries[i].mHashCode == hashCode) && (i == itr.mCurrentIdx))
|
||||
{
|
||||
RemoveIdx(bucket, i, last);
|
||||
return nextItr;
|
||||
}
|
||||
}
|
||||
|
||||
BF_FATAL("not found");
|
||||
return nextItr;
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
if (mCount > 0)
|
||||
{
|
||||
for (int_cosize i = 0; i < mAllocSize; i++) mBuckets[i] = -1;
|
||||
|
||||
if (!std::is_pod<TKey>::value)
|
||||
{
|
||||
for (int_cosize i = 0; i < mCount; i++)
|
||||
{
|
||||
if (mEntries[i].mHashCode != -1)
|
||||
((TKey*)&mEntries[i].mKey)->~TKey();
|
||||
}
|
||||
}
|
||||
|
||||
for (int_cosize i = 0; i < mCount; i++)
|
||||
{
|
||||
mEntries[i].mHashCode = -1;
|
||||
}
|
||||
|
||||
mFreeList = -1;
|
||||
mCount = 0;
|
||||
mFreeCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool Contains(const TKey& key)
|
||||
{
|
||||
return FindEntry(key) >= 0;
|
||||
}
|
||||
|
||||
template <typename TOther>
|
||||
bool ContainsWith(const TOther& key)
|
||||
{
|
||||
return FindEntryWith(key) != -1;
|
||||
}
|
||||
|
||||
// Note: there's no const_iterator
|
||||
iterator begin() const
|
||||
{
|
||||
iterator itr((HashSet*)this, 0);
|
||||
++itr;
|
||||
return itr;
|
||||
}
|
||||
|
||||
iterator end() const
|
||||
{
|
||||
return iterator((HashSet*)this, mCount + 1);
|
||||
}
|
||||
|
||||
HashSelector SelectHashes(size_t hashCode)
|
||||
{
|
||||
return HashSelector(this, (int32)hashCode);
|
||||
}
|
||||
};
|
||||
|
||||
NS_BF_END;
|
617
BeefySysLib/util/Json.cpp
Normal file
617
BeefySysLib/util/Json.cpp
Normal file
|
@ -0,0 +1,617 @@
|
|||
#include "Json.h"
|
||||
#include <limits.h>
|
||||
#include <float.h>
|
||||
#include <string.h>
|
||||
|
||||
#pragma warning(disable:4996)
|
||||
|
||||
/*
|
||||
Copyright (c) 2009 Dave Gamble
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/* Json */
|
||||
/* JSON parser in C. */
|
||||
|
||||
USING_NS_BF;
|
||||
|
||||
static const char *ep;
|
||||
|
||||
const char *Json::GetErrorPtr(void) {return ep;}
|
||||
|
||||
static int JSON_strcasecmp(const char *s1,const char *s2)
|
||||
{
|
||||
if (!s1) return (s1==s2)?0:1;if (!s2) return 1;
|
||||
for(; tolower(*s1) == tolower(*s2); ++s1, ++s2) if(*s1 == 0) return 0;
|
||||
return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2);
|
||||
}
|
||||
|
||||
static char* JSON_strdup(const char* str)
|
||||
{
|
||||
size_t len;
|
||||
char* copy;
|
||||
|
||||
len = strlen(str) + 1;
|
||||
if (!(copy = (char*)Json::malloc(len))) return 0;
|
||||
memcpy(copy,str,len);
|
||||
return copy;
|
||||
}
|
||||
|
||||
void *(*Json::malloc)(size_t sz) = ::malloc;
|
||||
void (*Json::free)(void *ptr) = ::free;
|
||||
|
||||
Json::~Json()
|
||||
{
|
||||
if (!(mType&Type_IsReference) && mChild) delete mChild;
|
||||
if (!(mType&Type_IsReference) && mValueString) Json::free(mValueString);
|
||||
if (mName) Json::free(mName);
|
||||
delete mNext;
|
||||
}
|
||||
|
||||
void Json::InitHooks(Json::Hooks* hooks)
|
||||
{
|
||||
if (!hooks)
|
||||
{
|
||||
/* Reset hooks */
|
||||
Json::malloc = ::malloc;
|
||||
Json::free = ::free;
|
||||
return;
|
||||
}
|
||||
|
||||
Json::malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc;
|
||||
Json::free = (hooks->free_fn)?hooks->free_fn:free;
|
||||
}
|
||||
|
||||
/* Internal constructor. */
|
||||
static Json* New_Item(void)
|
||||
{
|
||||
/*Json* node = (Json*)Json::malloc(sizeof(Json));
|
||||
if (node) memset(node,0,sizeof(Json)); */
|
||||
|
||||
Json* node = new Json();
|
||||
if (node) memset(node, 0, sizeof(Json));
|
||||
return node;
|
||||
}
|
||||
|
||||
/* Parse the input text to generate a number, and populate the result into item. */
|
||||
static const char *parse_number(Json* item,const char *num)
|
||||
{
|
||||
double n=0,sign=1,scale=0;int subscale=0,signsubscale=1;
|
||||
|
||||
if (*num=='-') sign=-1,num++; /* Has sign? */
|
||||
if (*num=='0') num++; /* is zero */
|
||||
if (*num>='1' && *num<='9')
|
||||
{
|
||||
do
|
||||
n=(n*10.0)+(*num++ -'0');
|
||||
while (*num>='0' && *num<='9'); /* Number? */
|
||||
}
|
||||
if (*num=='.' && num[1]>='0' && num[1]<='9')
|
||||
{
|
||||
num++;
|
||||
do
|
||||
n=(n*10.0)+(*num++ -'0'),scale--;
|
||||
while (*num>='0' && *num<='9');
|
||||
} /* Fractional part? */
|
||||
if (*num=='e' || *num=='E') /* Exponent? */
|
||||
{ num++;if (*num=='+') num++; else if (*num=='-') signsubscale=-1,num++; /* With sign? */
|
||||
while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0'); /* Number? */
|
||||
}
|
||||
|
||||
n=sign*n*pow(10.0,(scale+subscale*signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */
|
||||
|
||||
item->mValueDouble=n;
|
||||
item->mValueInt=(int)n;
|
||||
item->mType=Json::Type_Number;
|
||||
return num;
|
||||
}
|
||||
|
||||
/* Render the number nicely from the given item into a string. */
|
||||
static char *print_number(Json* item)
|
||||
{
|
||||
char *str;
|
||||
double d=item->mValueDouble;
|
||||
if (fabs(((double)item->mValueInt)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN)
|
||||
{
|
||||
str=(char*)Json::malloc(21); /* 2^64+1 can be represented in 21 chars. */
|
||||
if (str) sprintf(str,"%d",item->mValueInt);
|
||||
}
|
||||
else
|
||||
{
|
||||
str=(char*)Json::malloc(64); /* This is a nice tradeoff. */
|
||||
if (str)
|
||||
{
|
||||
if (fabs(floor(d)-d)<=DBL_EPSILON && fabs(d)<1.0e60)sprintf(str,"%.0f",d);
|
||||
else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9) sprintf(str,"%e",d);
|
||||
else sprintf(str,"%f",d);
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
static unsigned parse_hex4(const char *str)
|
||||
{
|
||||
unsigned h=0;
|
||||
if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
|
||||
h=h<<4;str++;
|
||||
if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
|
||||
h=h<<4;str++;
|
||||
if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
|
||||
h=h<<4;str++;
|
||||
if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
|
||||
return h;
|
||||
}
|
||||
|
||||
/* Parse the input text into an unescaped cstring, and populate item. */
|
||||
static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
|
||||
static const char *parse_string(Json* item,const char *str)
|
||||
{
|
||||
const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc,uc2;
|
||||
if (*str!='\"') {ep=str;return 0;} /* not a string! */
|
||||
|
||||
while (*ptr!='\"' && *ptr && ++len) if (*ptr++ == '\\') ptr++; /* Skip escaped quotes. */
|
||||
|
||||
out=(char*)Json::malloc(len+1); /* This is how long we need for the string, roughly. */
|
||||
if (!out) return 0;
|
||||
|
||||
ptr=str+1;ptr2=out;
|
||||
while (*ptr!='\"' && *ptr)
|
||||
{
|
||||
if (*ptr!='\\') *ptr2++=*ptr++;
|
||||
else
|
||||
{
|
||||
ptr++;
|
||||
switch (*ptr)
|
||||
{
|
||||
case 'b': *ptr2++='\b'; break;
|
||||
case 'f': *ptr2++='\f'; break;
|
||||
case 'n': *ptr2++='\n'; break;
|
||||
case 'r': *ptr2++='\r'; break;
|
||||
case 't': *ptr2++='\t'; break;
|
||||
case 'u': /* transcode utf16 to utf8. */
|
||||
uc=parse_hex4(ptr+1);ptr+=4; /* get the unicode char. */
|
||||
|
||||
if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0) break; /* check for invalid. */
|
||||
|
||||
if (uc>=0xD800 && uc<=0xDBFF) /* UTF16 surrogate pairs. */
|
||||
{
|
||||
if (ptr[1]!='\\' || ptr[2]!='u') break; /* missing second-half of surrogate. */
|
||||
uc2=parse_hex4(ptr+3);ptr+=6;
|
||||
if (uc2<0xDC00 || uc2>0xDFFF) break; /* invalid second-half of surrogate. */
|
||||
uc=0x10000 + (((uc&0x3FF)<<10) | (uc2&0x3FF));
|
||||
}
|
||||
|
||||
len=4;if (uc<0x80) len=1;else if (uc<0x800) len=2;else if (uc<0x10000) len=3; ptr2+=len;
|
||||
|
||||
switch (len) {
|
||||
case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
|
||||
case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
|
||||
case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
|
||||
case 1: *--ptr2 =(uc | firstByteMark[len]);
|
||||
}
|
||||
ptr2+=len;
|
||||
break;
|
||||
default: *ptr2++=*ptr; break;
|
||||
}
|
||||
ptr++;
|
||||
}
|
||||
}
|
||||
*ptr2=0;
|
||||
if (*ptr=='\"') ptr++;
|
||||
item->mValueString=out;
|
||||
item->mType=Json::Type_String;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/* Render the cstring provided to an escaped version that can be printed. */
|
||||
static char *print_string_ptr(const char *str)
|
||||
{
|
||||
const char *ptr;char *ptr2,*out;int len=0;unsigned char token;
|
||||
|
||||
if (!str) return JSON_strdup("");
|
||||
ptr=str;while ((token=*ptr) && ++len) {if (strchr("\"\\\b\f\n\r\t",token)) len++; else if (token<32) len+=5;ptr++;}
|
||||
|
||||
out=(char*)Json::malloc(len+3);
|
||||
if (!out) return 0;
|
||||
|
||||
ptr2=out;ptr=str;
|
||||
*ptr2++='\"';
|
||||
while (*ptr)
|
||||
{
|
||||
if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++;
|
||||
else
|
||||
{
|
||||
*ptr2++='\\';
|
||||
switch (token=*ptr++)
|
||||
{
|
||||
case '\\': *ptr2++='\\'; break;
|
||||
case '\"': *ptr2++='\"'; break;
|
||||
case '\b': *ptr2++='b'; break;
|
||||
case '\f': *ptr2++='f'; break;
|
||||
case '\n': *ptr2++='n'; break;
|
||||
case '\r': *ptr2++='r'; break;
|
||||
case '\t': *ptr2++='t'; break;
|
||||
default: sprintf(ptr2,"u%04x",token);ptr2+=5; break; /* escape and print */
|
||||
}
|
||||
}
|
||||
}
|
||||
*ptr2++='\"';*ptr2++=0;
|
||||
return out;
|
||||
}
|
||||
/* Invote print_string_ptr (which is useful) on an item. */
|
||||
static char *print_string(Json* item) {return print_string_ptr(item->mValueString);}
|
||||
|
||||
/* Predeclare these prototypes. */
|
||||
static const char *parse_value(Json* item,const char *value);
|
||||
static char *print_value(Json* item,int depth,int fmt);
|
||||
static const char *parse_array(Json* item,const char *value);
|
||||
static char *print_array(Json* item,int depth,int fmt);
|
||||
static const char *parse_object(Json* item,const char *value);
|
||||
static char *print_object(Json* item,int depth,int fmt);
|
||||
|
||||
/* Utility to jump whitespace and cr/lf */
|
||||
static const char *skip(const char *in) {while (in && *in && (unsigned char)*in<=32) in++; return in;}
|
||||
|
||||
/* Parse an object - create a new root, and populate. */
|
||||
Json* Json::ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated)
|
||||
{
|
||||
const char *end=0;
|
||||
Json* c=New_Item();
|
||||
ep=0;
|
||||
if (!c) return 0; /* memory fail */
|
||||
|
||||
end=parse_value(c,skip(value));
|
||||
if (!end) {delete(c);return 0;} /* parse failure. ep is set. */
|
||||
|
||||
/* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
|
||||
if (require_null_terminated) {end=skip(end);if (*end) {delete(c);ep=end;return 0;}}
|
||||
if (return_parse_end) *return_parse_end=end;
|
||||
return c;
|
||||
}
|
||||
/* Default options for Json::Parse */
|
||||
Json* Json::Parse(const char *value) {return Json::ParseWithOpts(value,0,0);}
|
||||
|
||||
/* Render a cJSON item/entity/structure to text. */
|
||||
char *Json::Print() {return print_value(this,0,1);}
|
||||
char *Json::PrintUnformatted() {return print_value(this,0,0);}
|
||||
|
||||
/* Parser core - when encountering text, process appropriately. */
|
||||
static const char *parse_value(Json* item,const char *value)
|
||||
{
|
||||
//item->mSrcStart = value;
|
||||
|
||||
if (!value) return 0; /* Fail on null. */
|
||||
if (!strncmp(value,"null",4)) { item->mType=Json::Type_NULL; return value+4; }
|
||||
if (!strncmp(value,"false",5)) { item->mType=Json::Type_False; return value+5; }
|
||||
if (!strncmp(value,"true",4)) { item->mType=Json::Type_True; item->mValueInt=1; return value+4; }
|
||||
if (*value=='\"') { return parse_string(item,value); }
|
||||
if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); }
|
||||
if (*value=='[') { return parse_array(item,value); }
|
||||
if (*value=='{') { return parse_object(item,value); }
|
||||
|
||||
ep=value;return 0; /* failure. */
|
||||
}
|
||||
|
||||
/* Render a value to text. */
|
||||
static char *print_value(Json* item,int depth,int fmt)
|
||||
{
|
||||
char *out=0;
|
||||
if (!item) return 0;
|
||||
switch ((item->mType)&255)
|
||||
{
|
||||
case Json::Type_NULL: out=JSON_strdup("null"); break;
|
||||
case Json::Type_False: out=JSON_strdup("false");break;
|
||||
case Json::Type_True: out=JSON_strdup("true"); break;
|
||||
case Json::Type_Number: out=print_number(item);break;
|
||||
case Json::Type_String: out=print_string(item);break;
|
||||
case Json::Type_Array: out=print_array(item,depth,fmt);break;
|
||||
case Json::Type_Object: out=print_object(item,depth,fmt);break;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/* Build an array from input text. */
|
||||
static const char *parse_array(Json* item,const char *value)
|
||||
{
|
||||
Json* child;
|
||||
if (*value!='[') {ep=value;return 0;} /* not an array! */
|
||||
|
||||
item->mType=Json::Type_Array;
|
||||
value=skip(value+1);
|
||||
if (*value==']') return value+1; /* empty array. */
|
||||
|
||||
item->mChild=child=New_Item();
|
||||
if (!item->mChild) return 0; /* memory fail */
|
||||
value = skip(value);
|
||||
child->mSrcStart = value;
|
||||
value=skip(parse_value(child,value)); /* skip any spacing, get the value. */
|
||||
if (!value) return 0;
|
||||
|
||||
while (*value==',')
|
||||
{
|
||||
Json* new_item;
|
||||
if (!(new_item=New_Item())) return 0; /* memory fail */
|
||||
child->mNext=new_item;new_item->mPrev=child;child=new_item;
|
||||
value = skip(value+1);
|
||||
child->mSrcStart = value;
|
||||
value=skip(parse_value(child,value));
|
||||
if (!value) return 0; /* memory fail */
|
||||
}
|
||||
|
||||
if (*value==']') return value+1; /* end of array */
|
||||
ep=value;return 0; /* malformed. */
|
||||
}
|
||||
|
||||
/* Render an array to text */
|
||||
static char *print_array(Json* item,int depth,int fmt)
|
||||
{
|
||||
char **entries;
|
||||
char *out=0,*ptr,*ret;int len=5;
|
||||
Json* child=item->mChild;
|
||||
int numentries=0,i=0,fail=0;
|
||||
|
||||
/* How many entries in the array? */
|
||||
while (child) numentries++,child=child->mNext;
|
||||
/* Explicitly handle numentries==0 */
|
||||
if (!numentries)
|
||||
{
|
||||
out=(char*)Json::malloc(3);
|
||||
if (out) strcpy(out,"[]");
|
||||
return out;
|
||||
}
|
||||
/* Allocate an array to hold the values for each */
|
||||
entries=(char**)Json::malloc(numentries*sizeof(char*));
|
||||
if (!entries) return 0;
|
||||
memset(entries,0,numentries*sizeof(char*));
|
||||
/* Retrieve all the results: */
|
||||
child=item->mChild;
|
||||
while (child && !fail)
|
||||
{
|
||||
ret=print_value(child,depth+1,fmt);
|
||||
entries[i++]=ret;
|
||||
if (ret) len+=(int)strlen(ret)+2+(fmt?1:0); else fail=1;
|
||||
child=child->mNext;
|
||||
}
|
||||
|
||||
/* If we didn't fail, try to malloc the output string */
|
||||
if (!fail) out=(char*)Json::malloc(len);
|
||||
/* If that fails, we fail. */
|
||||
if (!out) fail=1;
|
||||
|
||||
/* Handle failure. */
|
||||
if (fail)
|
||||
{
|
||||
for (i=0;i<numentries;i++) if (entries[i]) Json::free(entries[i]);
|
||||
Json::free(entries);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Compose the output array. */
|
||||
*out='[';
|
||||
ptr=out+1;*ptr=0;
|
||||
for (i=0;i<numentries;i++)
|
||||
{
|
||||
strcpy(ptr,entries[i]);ptr+=strlen(entries[i]);
|
||||
if (i!=numentries-1) {*ptr++=',';if(fmt)*ptr++=' ';*ptr=0;}
|
||||
Json::free(entries[i]);
|
||||
}
|
||||
Json::free(entries);
|
||||
*ptr++=']';*ptr++=0;
|
||||
return out;
|
||||
}
|
||||
|
||||
/* Build an object from the text. */
|
||||
static const char *parse_object(Json* item,const char *value)
|
||||
{
|
||||
Json* child;
|
||||
if (*value!='{') {ep=value;return 0;} /* not an object! */
|
||||
|
||||
item->mType=Json::Type_Object;
|
||||
value=skip(value+1);
|
||||
if (*value=='}') return value+1; /* empty array. */
|
||||
|
||||
item->mChild=child=New_Item();
|
||||
value = skip(value);
|
||||
child->mSrcStart = value;
|
||||
if (!item->mChild) return 0;
|
||||
value=skip(parse_string(child,value));
|
||||
if (!value) return 0;
|
||||
child->mName=child->mValueString;child->mValueString=0;
|
||||
if (*value!=':') {ep=value;return 0;} /* fail! */
|
||||
value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */
|
||||
if (!value) return 0;
|
||||
|
||||
while (*value==',')
|
||||
{
|
||||
Json* new_item;
|
||||
if (!(new_item=New_Item())) return 0; /* memory fail */
|
||||
child->mNext=new_item;new_item->mPrev=child;child=new_item;
|
||||
value = skip(value+1);
|
||||
child->mSrcStart = value;
|
||||
value=skip(parse_string(child,value));
|
||||
if (!value) return 0;
|
||||
child->mName=child->mValueString;child->mValueString=0;
|
||||
if (*value!=':') {ep=value;return 0;} /* fail! */
|
||||
value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */
|
||||
if (!value) return 0;
|
||||
}
|
||||
|
||||
child->mSrcEnd = value;
|
||||
|
||||
if (*value=='}') return value+1; /* end of array */
|
||||
ep=value;return 0; /* malformed. */
|
||||
}
|
||||
|
||||
/* Render an object to text. */
|
||||
static char *print_object(Json* item,int depth,int fmt)
|
||||
{
|
||||
char **entries=0,**names=0;
|
||||
char *out=0,*ptr,*ret,*str;int len=7,i=0,j;
|
||||
Json* child=item->mChild;
|
||||
int numentries=0,fail=0;
|
||||
/* Count the number of entries. */
|
||||
while (child) numentries++,child=child->mNext;
|
||||
/* Explicitly handle empty object case */
|
||||
if (!numentries)
|
||||
{
|
||||
out=(char*)Json::malloc(fmt?depth+4:3);
|
||||
if (!out) return 0;
|
||||
ptr=out;*ptr++='{';
|
||||
if (fmt) {*ptr++='\n';for (i=0;i<depth-1;i++) *ptr++='\t';}
|
||||
*ptr++='}';*ptr++=0;
|
||||
return out;
|
||||
}
|
||||
/* Allocate space for the names and the objects */
|
||||
entries=(char**)Json::malloc(numentries*sizeof(char*));
|
||||
if (!entries) return 0;
|
||||
names=(char**)Json::malloc(numentries*sizeof(char*));
|
||||
if (!names) {Json::free(entries);return 0;}
|
||||
memset(entries,0,sizeof(char*)*numentries);
|
||||
memset(names,0,sizeof(char*)*numentries);
|
||||
|
||||
/* Collect all the results into our arrays: */
|
||||
child=item->mChild;depth++;if (fmt) len+=depth;
|
||||
while (child)
|
||||
{
|
||||
names[i]=str=print_string_ptr(child->mName);
|
||||
entries[i++]=ret=print_value(child,depth,fmt);
|
||||
if (str && ret) len+= (int)strlen(ret)+ (int)strlen(str)+2+(fmt?2+depth:0); else fail=1;
|
||||
child=child->mNext;
|
||||
}
|
||||
|
||||
/* Try to allocate the output string */
|
||||
if (!fail) out=(char*)Json::malloc(len);
|
||||
if (!out) fail=1;
|
||||
|
||||
/* Handle failure */
|
||||
if (fail)
|
||||
{
|
||||
for (i=0;i<numentries;i++) {if (names[i]) Json::free(names[i]);if (entries[i]) Json::free(entries[i]);}
|
||||
Json::free(names);Json::free(entries);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Compose the output: */
|
||||
*out='{';ptr=out+1;if (fmt)*ptr++='\n';*ptr=0;
|
||||
for (i=0;i<numentries;i++)
|
||||
{
|
||||
if (fmt) for (j=0;j<depth;j++) *ptr++='\t';
|
||||
strcpy(ptr,names[i]);ptr+=(int)strlen(names[i]);
|
||||
*ptr++=':';if (fmt) *ptr++='\t';
|
||||
strcpy(ptr,entries[i]);ptr+=strlen(entries[i]);
|
||||
if (i!=numentries-1) *ptr++=',';
|
||||
if (fmt) *ptr++='\n';*ptr=0;
|
||||
Json::free(names[i]);Json::free(entries[i]);
|
||||
}
|
||||
|
||||
Json::free(names);Json::free(entries);
|
||||
if (fmt) for (i=0;i<depth-1;i++) *ptr++='\t';
|
||||
*ptr++='}';*ptr++=0;
|
||||
return out;
|
||||
}
|
||||
|
||||
/* Get Array size/item / object item. */
|
||||
int Json::GetArraySize() {Json* c=this->mChild;int i=0;while(c)i++,c=c->mNext;return i;}
|
||||
Json* Json::GetArrayItem(int item) {Json* c=this->mChild; while (c && item>0) item--,c=c->mNext; return c;}
|
||||
Json* Json::GetObjectItem(const char *string) {Json* c=this->mChild; while (c && JSON_strcasecmp(c->mName,string)) c=c->mNext; return c;}
|
||||
|
||||
/* Utility for array list handling. */
|
||||
static void suffix_object(Json* prev,Json* item) {prev->mNext=item;item->mPrev=prev;}
|
||||
/* Utility for handling references. */
|
||||
static Json* create_reference(Json* item) {Json* ref=New_Item();if (!ref) return 0;memcpy(ref,item,sizeof(Json));ref->mName=0;ref->mType=(Json::Type)(ref->mType|Json::Type_IsReference);ref->mNext=ref->mPrev=0;return ref;}
|
||||
|
||||
/* Add item to array/object. */
|
||||
void Json::AddItemToArray(Json* item) {Json* c=this->mChild;if (!item) return; if (!c) {this->mChild=item;} else {while (c && c->mNext) c=c->mNext; suffix_object(c,item);}}
|
||||
void Json::AddItemToObject(const char *string,Json* item) {if (!item) return; if (item->mName) Json::free(item->mName);item->mName=JSON_strdup(string);this->AddItemToArray(item);}
|
||||
void Json::AddItemReferenceToArray(Json* item) {this->AddItemToArray(create_reference(item));}
|
||||
void Json::AddItemReferenceToObject(const char *string,Json* item) {this->AddItemToObject(string,create_reference(item));}
|
||||
|
||||
Json* Json::DetachItemFromArray(Json* array,int which) {Json* c=array->mChild;while (c && which>0) c=c->mNext,which--;if (!c) return 0;
|
||||
if (c->mPrev) c->mPrev->mNext=c->mNext;if (c->mNext) c->mNext->mPrev=c->mPrev;if (c==array->mChild) array->mChild=c->mNext;c->mPrev=c->mNext=0;return c;}
|
||||
void deleteItemFromArray(Json* array,int which) {delete(Json::DetachItemFromArray(array,which));}
|
||||
Json* Json::DetachItemFromObject(Json* object,const char *string) {int i=0;Json* c=object->mChild;while (c && JSON_strcasecmp(c->mName,string)) i++,c=c->mNext;if (c) return Json::DetachItemFromArray(object,i);return 0;}
|
||||
void deleteItemFromObject(Json* object,const char *string) {delete(Json::DetachItemFromObject(object,string));}
|
||||
|
||||
/* Replace array/object items with new ones. */
|
||||
void Json::ReplaceItemInArray(Json* array,int which,Json* newitem) {Json* c=array->mChild;while (c && which>0) c=c->mNext,which--;if (!c) return;
|
||||
newitem->mNext=c->mNext;newitem->mPrev=c->mPrev;if (newitem->mNext) newitem->mNext->mPrev=newitem;
|
||||
if (c==array->mChild) array->mChild=newitem; else newitem->mPrev->mNext=newitem;c->mNext=c->mPrev=0;delete(c);}
|
||||
void Json::ReplaceItemInObject(Json* object,const char *string,Json* newitem){int i=0;Json* c=object->mChild;while(c && JSON_strcasecmp(c->mName,string))i++,c=c->mNext;if(c){newitem->mName=JSON_strdup(string);Json::ReplaceItemInArray(object,i,newitem);}}
|
||||
|
||||
/* Create basic types: */
|
||||
Json* Json::CreateNull(void) {Json* item=New_Item();if(item)item->mType=Json::Type_NULL;return item;}
|
||||
Json* Json::CreateTrue(void) {Json* item=New_Item();if(item)item->mType=Json::Type_True;return item;}
|
||||
Json* Json::CreateFalse(void) {Json* item=New_Item();if(item)item->mType=Json::Type_False;return item;}
|
||||
Json* Json::CreateBool(int b) {Json* item=New_Item();if(item)item->mType=b?Json::Type_True:Json::Type_False;return item;}
|
||||
Json* Json::CreateNumber(double num) {Json* item=New_Item();if(item){item->mType=Json::Type_Number;item->mValueDouble=num;item->mValueInt=(int)num;}return item;}
|
||||
Json* Json::CreateString(const char *string) {Json* item=New_Item();if(item){item->mType=Json::Type_String;item->mValueString=JSON_strdup(string);}return item;}
|
||||
Json* Json::CreateArray(void) {Json* item=New_Item();if(item)item->mType=Json::Type_Array;return item;}
|
||||
Json* Json::CreateObject(void) {Json* item=New_Item();if(item)item->mType=Json::Type_Object;return item;}
|
||||
|
||||
/* Create Arrays: */
|
||||
Json* Json::CreateIntArray(const int *numbers,int count) {int i;Json* n=0,*p=0,*a=Json::CreateArray();for(i=0;a && i<count;i++){n=Json::CreateNumber(numbers[i]);if(!i)a->mChild=n;else suffix_object(p,n);p=n;}return a;}
|
||||
Json* Json::CreateFloatArray(const float *numbers,int count) {int i;Json* n=0,*p=0,*a=Json::CreateArray();for(i=0;a && i<count;i++){n=Json::CreateNumber(numbers[i]);if(!i)a->mChild=n;else suffix_object(p,n);p=n;}return a;}
|
||||
Json* Json::CreateDoubleArray(const double *numbers,int count) {int i;Json* n=0,*p=0,*a=Json::CreateArray();for(i=0;a && i<count;i++){n=Json::CreateNumber(numbers[i]);if(!i)a->mChild=n;else suffix_object(p,n);p=n;}return a;}
|
||||
Json* Json::CreateStringArray(const char **strings,int count) {int i;Json* n=0,*p=0,*a=Json::CreateArray();for(i=0;a && i<count;i++){n=Json::CreateString(strings[i]);if(!i)a->mChild=n;else suffix_object(p,n);p=n;}return a;}
|
||||
|
||||
/* Duplication */
|
||||
Json* Json::Duplicate(Json* item,int recurse)
|
||||
{
|
||||
Json* newitem,*cptr,*nptr=0,*newchild;
|
||||
/* Bail on bad ptr */
|
||||
if (!item) return 0;
|
||||
/* Create new item */
|
||||
newitem=New_Item();
|
||||
if (!newitem) return 0;
|
||||
/* Copy over all vars */
|
||||
newitem->mType=(Json::Type)(item->mType&(~Type_IsReference)),newitem->mValueInt=item->mValueInt,newitem->mValueDouble=item->mValueDouble;
|
||||
if (item->mValueString) {newitem->mValueString=JSON_strdup(item->mValueString); if (!newitem->mValueString) {delete(newitem);return 0;}}
|
||||
if (item->mName) {newitem->mName=JSON_strdup(item->mName); if (!newitem->mName) {delete(newitem);return 0;}}
|
||||
/* If non-recursive, then we're done! */
|
||||
if (!recurse) return newitem;
|
||||
/* Walk the ->mNext chain for the child. */
|
||||
cptr=item->mChild;
|
||||
while (cptr)
|
||||
{
|
||||
newchild=Json::Duplicate(cptr,1); /* Duplicate (with recurse) each item in the ->mNext chain */
|
||||
if (!newchild) {delete(newitem);return 0;}
|
||||
if (nptr) {nptr->mNext=newchild,newchild->mPrev=nptr;nptr=newchild;} /* If newitem->mChild already set, then crosswire ->mPrev and ->mNext and move on */
|
||||
else {newitem->mChild=newchild;nptr=newchild;} /* Set newitem->mChild and move to it */
|
||||
cptr=cptr->mNext;
|
||||
}
|
||||
return newitem;
|
||||
}
|
||||
|
||||
void Json::Minify(char *json)
|
||||
{
|
||||
char *into=json;
|
||||
while (*json)
|
||||
{
|
||||
if (*json==' ') json++;
|
||||
else if (*json=='\t') json++; // Whitespace characters.
|
||||
else if (*json=='\r') json++;
|
||||
else if (*json=='\n') json++;
|
||||
else if (*json=='/' && json[1]=='/') while (*json && *json!='\n') json++; // double-slash comments, to end of line.
|
||||
else if (*json=='/' && json[1]=='*') {while (*json && !(*json=='*' && json[1]=='/')) json++;json+=2;} // multiline comments.
|
||||
else if (*json=='\"'){*into++=*json++;while (*json && *json!='\"'){if (*json=='\\') *into++=*json++;*into++=*json++;}*into++=*json++;} // string literals, which are \" sensitive.
|
||||
else *into++=*json++; // All other characters.
|
||||
}
|
||||
*into=0; // and null-terminate.
|
||||
}
|
155
BeefySysLib/util/Json.h
Normal file
155
BeefySysLib/util/Json.h
Normal file
|
@ -0,0 +1,155 @@
|
|||
#pragma once
|
||||
|
||||
/*
|
||||
Copyright (c) 2009 Dave Gamble
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "../Common.h"
|
||||
|
||||
NS_BF_BEGIN
|
||||
|
||||
/* The cJSON structure: */
|
||||
|
||||
|
||||
class Json
|
||||
{
|
||||
public:
|
||||
/* cJSON Types: */
|
||||
enum Type
|
||||
{
|
||||
Type_False,
|
||||
Type_True,
|
||||
Type_NULL,
|
||||
Type_Number,
|
||||
Type_String,
|
||||
Type_Array,
|
||||
Type_Object,
|
||||
|
||||
Type_IsReference = 256
|
||||
};
|
||||
|
||||
Json* mNext;
|
||||
Json* mPrev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
|
||||
Json* mChild; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
|
||||
|
||||
Type mType; /* The type of the item, as above. */
|
||||
|
||||
char* mValueString; /* The item's string, if type==cJSON_String */
|
||||
int mValueInt; /* The item's number, if type==cJSON_Number */
|
||||
double mValueDouble; /* The item's number, if type==cJSON_Number */
|
||||
|
||||
char* mName; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
|
||||
|
||||
const char* mSrcStart;
|
||||
const char* mSrcEnd;
|
||||
|
||||
struct Hooks
|
||||
{
|
||||
void *(*malloc_fn)(size_t sz);
|
||||
void(*free_fn)(void *ptr);
|
||||
};
|
||||
static void *(*malloc)(size_t sz);
|
||||
static void (*free)(void *ptr);
|
||||
|
||||
public:
|
||||
~Json();
|
||||
|
||||
/* Supply malloc, realloc and free functions to Json */
|
||||
void InitHooks(Hooks* hooks);
|
||||
|
||||
/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */
|
||||
char* Print();
|
||||
|
||||
/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call Delete when finished. */
|
||||
static Json* Parse(const char *value);
|
||||
|
||||
/* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */
|
||||
char *PrintUnformatted();
|
||||
|
||||
/* Returns the number of items in an array (or object). */
|
||||
int GetArraySize();
|
||||
/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */
|
||||
Json* GetArrayItem(int item);
|
||||
/* Get item "string" from object. Case insensitive. */
|
||||
Json* GetObjectItem(const char *string);
|
||||
|
||||
/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when Parse() returns 0. 0 when Parse() succeeds. */
|
||||
static const char *GetErrorPtr(void);
|
||||
|
||||
/* These calls create a cJSON item of the appropriate type. */
|
||||
static Json* CreateNull(void);
|
||||
static Json* CreateTrue(void);
|
||||
static Json* CreateFalse(void);
|
||||
static Json* CreateBool(int b);
|
||||
static Json* CreateNumber(double num);
|
||||
static Json* CreateString(const char *string);
|
||||
static Json* CreateArray(void);
|
||||
static Json* CreateObject(void);
|
||||
|
||||
/* These utilities create an Array of count items. */
|
||||
static Json* CreateIntArray(const int *numbers, int count);
|
||||
static Json* CreateFloatArray(const float *numbers, int count);
|
||||
static Json* CreateDoubleArray(const double *numbers, int count);
|
||||
static Json* CreateStringArray(const char **strings, int count);
|
||||
|
||||
/* Append item to the specified array/object. */
|
||||
void AddItemToArray(Json* item);
|
||||
void AddItemToObject(const char *string, Json* item);
|
||||
/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
|
||||
void AddItemReferenceToArray(Json* item);
|
||||
void AddItemReferenceToObject(const char *string, Json* item);
|
||||
|
||||
/* Remove/Detatch items from Arrays/Objects. */
|
||||
static Json* DetachItemFromArray(Json* array, int which);
|
||||
static void DeleteItemFromArray(Json* array, int which);
|
||||
static Json* DetachItemFromObject(Json* object, const char *string);
|
||||
static void DeleteItemFromObject(Json* object, const char *string);
|
||||
|
||||
/* Update array items. */
|
||||
static void ReplaceItemInArray(Json* array, int which, Json* newitem);
|
||||
static void ReplaceItemInObject(Json* object, const char *string, Json* newitem);
|
||||
|
||||
/* Duplicate a cJSON item */
|
||||
static Json* Duplicate(Json* item, int recurse);
|
||||
/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
|
||||
need to be released. With recurse!=0, it will duplicate any children connected to the item.
|
||||
The item->next and ->prev pointers are always zero on return from Duplicate. */
|
||||
|
||||
/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
|
||||
static Json* ParseWithOpts(const char *value, const char **return_parse_end, int require_null_terminated);
|
||||
|
||||
/* Macros for creating things quickly. */
|
||||
void AddNullToObject(const char* name) { AddItemToObject(name, CreateNull()); }
|
||||
void AddTrueToObject(const char* name) { AddItemToObject(name, CreateTrue()); }
|
||||
void AddFalseToObject(const char* name) { AddItemToObject(name, CreateFalse()); }
|
||||
void AddBoolToObject(const char* name, bool b) { AddItemToObject(name, CreateBool(b)); }
|
||||
void AddNumberToObject(const char* name, int n) { AddItemToObject(name, CreateNumber(n)); }
|
||||
void AddNumberToObject(const char* name, float n) { AddItemToObject(name, CreateNumber(n)); }
|
||||
void AddNumberToObject(const char* name, double n) { AddItemToObject(name, CreateNumber(n)); }
|
||||
void AddStringToObject(const char* name, const char* s) { AddItemToObject(name, CreateString(s)); }
|
||||
|
||||
/* When assigning an integer value, it needs to be propagated to valuedouble too. */
|
||||
void SetIntValue(int val) { mValueInt=val; mValueDouble=val; }
|
||||
|
||||
static void Minify(char *json);
|
||||
};
|
||||
|
||||
NS_BF_END
|
46
BeefySysLib/util/MappedFile.cpp
Normal file
46
BeefySysLib/util/MappedFile.cpp
Normal file
|
@ -0,0 +1,46 @@
|
|||
#include "MappedFile.h"
|
||||
|
||||
USING_NS_BF;
|
||||
|
||||
#ifdef BF_PLATFORM_WINDOWS
|
||||
|
||||
MappedFile::MappedFile()
|
||||
{
|
||||
mMappedFile = INVALID_HANDLE_VALUE;
|
||||
mMappedFileMapping = INVALID_HANDLE_VALUE;
|
||||
mData = NULL;
|
||||
mFileSize = 0;
|
||||
}
|
||||
|
||||
MappedFile::~MappedFile()
|
||||
{
|
||||
if (mData != NULL)
|
||||
::UnmapViewOfFile(mData);
|
||||
if (mMappedFileMapping != INVALID_HANDLE_VALUE)
|
||||
::CloseHandle(mMappedFileMapping);
|
||||
if (mMappedFile != INVALID_HANDLE_VALUE)
|
||||
::CloseHandle(mMappedFile);
|
||||
}
|
||||
|
||||
bool MappedFile::Open(const StringImpl& fileName)
|
||||
{
|
||||
mFileName = fileName;
|
||||
mMappedFile = CreateFileA(fileName.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);
|
||||
if (mMappedFile == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD highFileSize = 0;
|
||||
mFileSize = (int)GetFileSize(mMappedFile, &highFileSize);
|
||||
mMappedFileMapping = CreateFileMapping(mMappedFile, NULL, PAGE_READONLY, 0, mFileSize, NULL);
|
||||
mData = MapViewOfFile(mMappedFileMapping, FILE_MAP_READ, 0, 0, mFileSize);
|
||||
if (mData == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
28
BeefySysLib/util/MappedFile.h
Normal file
28
BeefySysLib/util/MappedFile.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
#pragma once
|
||||
|
||||
#include "../Common.h"
|
||||
|
||||
NS_BF_BEGIN
|
||||
|
||||
#ifdef BF_PLATFORM_WINDOWS
|
||||
|
||||
class MappedFile
|
||||
{
|
||||
BF_DISALLOW_COPY(MappedFile);
|
||||
public:
|
||||
String mFileName;
|
||||
HANDLE mMappedFile;
|
||||
void* mData;
|
||||
HANDLE mMappedFileMapping;
|
||||
int mFileSize;
|
||||
|
||||
public:
|
||||
bool Open(const StringImpl& fileName);
|
||||
|
||||
MappedFile();
|
||||
~MappedFile();
|
||||
};
|
||||
|
||||
#endif //BF_PLATFORM_WINDOWS
|
||||
|
||||
NS_BF_END
|
25
BeefySysLib/util/Matrix4.cpp
Normal file
25
BeefySysLib/util/Matrix4.cpp
Normal file
|
@ -0,0 +1,25 @@
|
|||
#include "Matrix4.h"
|
||||
#include "Quaternion.h"
|
||||
|
||||
USING_NS_BF;
|
||||
|
||||
Matrix4 Matrix4::sIdentity(
|
||||
1.0f, 0.0f, 0.0f, 0.0f,
|
||||
0.0f, 1.0f, 0.0f, 0.0f,
|
||||
0.0f, 0.0f, 1.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f);
|
||||
|
||||
Matrix4 Matrix4::CreateTransform(const Vector3& position, const Vector3& scale, const Quaternion& orientation)
|
||||
{
|
||||
// Ordering:
|
||||
// 1. Scale
|
||||
// 2. Rotate
|
||||
// 3. Translate
|
||||
|
||||
Matrix4 rot = orientation.ToMatrix();
|
||||
return Matrix4(
|
||||
scale.mX * rot.m00, scale.mY * rot.m01, scale.mZ * rot.m02, position.mX,
|
||||
scale.mX * rot.m10, scale.mY * rot.m11, scale.mZ * rot.m12, position.mY,
|
||||
scale.mX * rot.m20, scale.mY * rot.m21, scale.mZ * rot.m22, position.mZ,
|
||||
0, 0, 0, 1);
|
||||
}
|
114
BeefySysLib/util/Matrix4.h
Normal file
114
BeefySysLib/util/Matrix4.h
Normal file
|
@ -0,0 +1,114 @@
|
|||
#pragma once
|
||||
|
||||
#include "Vector.h"
|
||||
|
||||
NS_BF_BEGIN;
|
||||
|
||||
class Matrix4
|
||||
{
|
||||
public:
|
||||
union
|
||||
{
|
||||
float mMat[4][4];
|
||||
float mMatFlat[16];
|
||||
struct
|
||||
{
|
||||
float m00;
|
||||
float m01;
|
||||
float m02;
|
||||
float m03;
|
||||
float m10;
|
||||
float m11;
|
||||
float m12;
|
||||
float m13;
|
||||
float m20;
|
||||
float m21;
|
||||
float m22;
|
||||
float m23;
|
||||
float m30;
|
||||
float m31;
|
||||
float m32;
|
||||
float m33;
|
||||
};
|
||||
};
|
||||
|
||||
public:
|
||||
static Matrix4 sIdentity;
|
||||
|
||||
public:
|
||||
inline Matrix4()
|
||||
{
|
||||
}
|
||||
|
||||
inline Matrix4(
|
||||
float m00, float m01, float m02, float m03,
|
||||
float m10, float m11, float m12, float m13,
|
||||
float m20, float m21, float m22, float m23,
|
||||
float m30, float m31, float m32, float m33)
|
||||
{
|
||||
this->m00 = m00;
|
||||
this->m01 = m01;
|
||||
this->m02 = m02;
|
||||
this->m03 = m03;
|
||||
this->m10 = m10;
|
||||
this->m11 = m11;
|
||||
this->m12 = m12;
|
||||
this->m13 = m13;
|
||||
this->m20 = m20;
|
||||
this->m21 = m21;
|
||||
this->m22 = m22;
|
||||
this->m23 = m23;
|
||||
this->m30 = m30;
|
||||
this->m31 = m31;
|
||||
this->m32 = m32;
|
||||
this->m33 = m33;
|
||||
}
|
||||
|
||||
static Matrix4 Multiply(const Matrix4 &m1, const Matrix4 &m2)
|
||||
{
|
||||
Matrix4 r;
|
||||
r.m00 = m1.m00 * m2.m00 + m1.m01 * m2.m10 + m1.m02 * m2.m20 + m1.m03 * m2.m30;
|
||||
r.m01 = m1.m00 * m2.m01 + m1.m01 * m2.m11 + m1.m02 * m2.m21 + m1.m03 * m2.m31;
|
||||
r.m02 = m1.m00 * m2.m02 + m1.m01 * m2.m12 + m1.m02 * m2.m22 + m1.m03 * m2.m32;
|
||||
r.m03 = m1.m00 * m2.m03 + m1.m01 * m2.m13 + m1.m02 * m2.m23 + m1.m03 * m2.m33;
|
||||
|
||||
r.m10 = m1.m10 * m2.m00 + m1.m11 * m2.m10 + m1.m12 * m2.m20 + m1.m13 * m2.m30;
|
||||
r.m11 = m1.m10 * m2.m01 + m1.m11 * m2.m11 + m1.m12 * m2.m21 + m1.m13 * m2.m31;
|
||||
r.m12 = m1.m10 * m2.m02 + m1.m11 * m2.m12 + m1.m12 * m2.m22 + m1.m13 * m2.m32;
|
||||
r.m13 = m1.m10 * m2.m03 + m1.m11 * m2.m13 + m1.m12 * m2.m23 + m1.m13 * m2.m33;
|
||||
|
||||
r.m20 = m1.m20 * m2.m00 + m1.m21 * m2.m10 + m1.m22 * m2.m20 + m1.m23 * m2.m30;
|
||||
r.m21 = m1.m20 * m2.m01 + m1.m21 * m2.m11 + m1.m22 * m2.m21 + m1.m23 * m2.m31;
|
||||
r.m22 = m1.m20 * m2.m02 + m1.m21 * m2.m12 + m1.m22 * m2.m22 + m1.m23 * m2.m32;
|
||||
r.m23 = m1.m20 * m2.m03 + m1.m21 * m2.m13 + m1.m22 * m2.m23 + m1.m23 * m2.m33;
|
||||
|
||||
r.m30 = m1.m30 * m2.m00 + m1.m31 * m2.m10 + m1.m32 * m2.m20 + m1.m33 * m2.m30;
|
||||
r.m31 = m1.m30 * m2.m01 + m1.m31 * m2.m11 + m1.m32 * m2.m21 + m1.m33 * m2.m31;
|
||||
r.m32 = m1.m30 * m2.m02 + m1.m31 * m2.m12 + m1.m32 * m2.m22 + m1.m33 * m2.m32;
|
||||
r.m33 = m1.m30 * m2.m03 + m1.m31 * m2.m13 + m1.m32 * m2.m23 + m1.m33 * m2.m33;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static Matrix4 Transpose(const Matrix4 &m)
|
||||
{
|
||||
return Matrix4(
|
||||
m.m00, m.m10, m.m20, m.m30,
|
||||
m.m01, m.m11, m.m21, m.m31,
|
||||
m.m02, m.m12, m.m22, m.m32,
|
||||
m.m03, m.m13, m.m23, m.m33);
|
||||
}
|
||||
|
||||
static Matrix4 CreateTranslation(float x, float y, float z)
|
||||
{
|
||||
return Matrix4(
|
||||
1, 0, 0, x,
|
||||
0, 1, 0, y,
|
||||
0, 0, 1, z,
|
||||
0, 0, 0, 1);
|
||||
}
|
||||
|
||||
static Matrix4 CreateTransform(const Vector3& position, const Vector3& scale, const Quaternion& orientation);
|
||||
};
|
||||
|
||||
NS_BF_END;
|
336
BeefySysLib/util/MultiHashSet.h
Normal file
336
BeefySysLib/util/MultiHashSet.h
Normal file
|
@ -0,0 +1,336 @@
|
|||
#pragma once
|
||||
|
||||
#include "../Common.h"
|
||||
#include "Array.h"
|
||||
|
||||
NS_BF_BEGIN;
|
||||
|
||||
struct MultiHashSetFuncs
|
||||
{
|
||||
void* Allocate(intptr size, intptr align)
|
||||
{
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
void* AllocateZero(intptr size, intptr align)
|
||||
{
|
||||
void* mem = malloc(size);
|
||||
memset(mem, 0, size);
|
||||
return mem;
|
||||
}
|
||||
|
||||
void Deallocate(void* ptr)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
bool DeallocateAll()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename TFuncs = AllocatorCLib<T> >
|
||||
class MultiHashSet : public TFuncs
|
||||
{
|
||||
public:
|
||||
struct Entry
|
||||
{
|
||||
T mValue;
|
||||
Entry* mNext;
|
||||
int mHash;
|
||||
};
|
||||
|
||||
struct Iterator
|
||||
{
|
||||
public:
|
||||
MultiHashSet* mSet;
|
||||
Entry* mCurEntry;
|
||||
int mCurBucket;
|
||||
|
||||
public:
|
||||
Iterator(MultiHashSet* set)
|
||||
{
|
||||
this->mSet = set;
|
||||
this->mCurBucket = 0;
|
||||
this->mCurEntry = NULL;
|
||||
}
|
||||
|
||||
Iterator& operator++()
|
||||
{
|
||||
if (this->mCurEntry != NULL)
|
||||
{
|
||||
this->mCurEntry = this->mCurEntry->mNext;
|
||||
if (this->mCurEntry != NULL)
|
||||
return *this;
|
||||
this->mCurBucket++;
|
||||
}
|
||||
|
||||
if (mSet->mHashHeads == NULL)
|
||||
{
|
||||
this->mCurBucket = this->mSet->mHashSize;
|
||||
return *this; // At end
|
||||
}
|
||||
|
||||
while (this->mCurBucket < mSet->mHashSize)
|
||||
{
|
||||
this->mCurEntry = this->mSet->mHashHeads[mCurBucket];
|
||||
if (this->mCurEntry != NULL)
|
||||
return *this;
|
||||
this->mCurBucket++;
|
||||
}
|
||||
|
||||
return *this; // At end
|
||||
}
|
||||
|
||||
bool operator!=(const Iterator& itr) const
|
||||
{
|
||||
return ((itr.mCurEntry != this->mCurEntry) || (itr.mCurBucket != this->mCurBucket));
|
||||
}
|
||||
|
||||
T operator*()
|
||||
{
|
||||
return this->mCurEntry->mValue;
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return this->mCurEntry != NULL;
|
||||
}
|
||||
|
||||
void MoveToNextHashMatch()
|
||||
{
|
||||
int wantHash = this->mCurEntry->mHash;
|
||||
do
|
||||
{
|
||||
this->mCurEntry = this->mCurEntry->mNext;
|
||||
}
|
||||
while ((this->mCurEntry != NULL) && (this->mCurEntry->mHash != wantHash));
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
Entry** mHashHeads;
|
||||
const int cDefaultHashSize = 17;
|
||||
int mHashSize;
|
||||
int mCount;
|
||||
|
||||
MultiHashSet()
|
||||
{
|
||||
this->mHashHeads = NULL;
|
||||
this->mHashSize = cDefaultHashSize;
|
||||
this->mCount = 0;
|
||||
}
|
||||
|
||||
~MultiHashSet()
|
||||
{
|
||||
this->Clear();
|
||||
}
|
||||
|
||||
void Add(T value)
|
||||
{
|
||||
if (this->mHashHeads == NULL)
|
||||
this->mHashHeads = (Entry**)TFuncs::AllocateZero(sizeof(Entry*) * mHashSize, alignof(Entry*));
|
||||
|
||||
int hash = TFuncs::GetHash(value);
|
||||
int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize;
|
||||
Entry* headEntry = this->mHashHeads[hashIdx];
|
||||
|
||||
Entry* newEntry = (Entry*)TFuncs::Allocate(sizeof(Entry), alignof(Entry));
|
||||
newEntry->mValue = value;
|
||||
newEntry->mNext = headEntry;
|
||||
newEntry->mHash = hash;
|
||||
mCount++;
|
||||
|
||||
mHashHeads[hashIdx] = newEntry;
|
||||
}
|
||||
|
||||
void AddAfter(T value, Entry* afterEntry)
|
||||
{
|
||||
int hash = TFuncs::GetHash(value);
|
||||
int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize;
|
||||
BF_ASSERT(hash == afterEntry->mHash);
|
||||
|
||||
Entry* newEntry = (Entry*)TFuncs::Allocate(sizeof(Entry), alignof(Entry));
|
||||
newEntry->mValue = value;
|
||||
newEntry->mNext = afterEntry->mNext;
|
||||
newEntry->mHash = hash;
|
||||
mCount++;
|
||||
|
||||
afterEntry->mNext = newEntry;
|
||||
}
|
||||
|
||||
void Rehash(int newHashSize)
|
||||
{
|
||||
auto newHashHeads = (Entry**)TFuncs::AllocateZero(sizeof(Entry*) * newHashSize, alignof(Entry*));
|
||||
|
||||
for (int hashIdx = 0; hashIdx < mHashSize; hashIdx++)
|
||||
{
|
||||
Entry* checkEntry = mHashHeads[hashIdx];
|
||||
while (checkEntry != NULL)
|
||||
{
|
||||
auto nextEntry = checkEntry->mNext;
|
||||
int newHashIdx = (checkEntry->mHash & 0x7FFFFFFF) % newHashSize;
|
||||
checkEntry->mNext = newHashHeads[newHashIdx];
|
||||
newHashHeads[newHashIdx] = checkEntry;
|
||||
|
||||
checkEntry = nextEntry;
|
||||
}
|
||||
}
|
||||
|
||||
TFuncs::Deallocate(mHashHeads);
|
||||
mHashHeads = newHashHeads;
|
||||
mHashSize = newHashSize;
|
||||
}
|
||||
|
||||
void CheckRehash()
|
||||
{
|
||||
// Make the lookup load reasonable
|
||||
if (mHashSize < mCount)
|
||||
{
|
||||
this->Rehash(BF_MAX(mCount, (int)(mHashSize * 1.5f)) | 1);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename TKey>
|
||||
bool TryGet(const TKey& key, T* val)
|
||||
{
|
||||
if (mHashHeads == NULL)
|
||||
return false;
|
||||
|
||||
this->CheckRehash();
|
||||
|
||||
int hash = TFuncs::GetHash(key);
|
||||
int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize;
|
||||
Entry* checkEntry = this->mHashHeads[hashIdx];
|
||||
while (checkEntry != NULL)
|
||||
{
|
||||
if ((checkEntry->mHash == hash) && (TFuncs::Matches(key, checkEntry->mValue)))
|
||||
{
|
||||
if (val != NULL)
|
||||
*val = checkEntry->mValue;
|
||||
return true;
|
||||
}
|
||||
checkEntry = checkEntry->mNext;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename TKey>
|
||||
Iterator TryGet(const TKey& key)
|
||||
{
|
||||
if (mHashHeads == NULL)
|
||||
return end();
|
||||
|
||||
this->CheckRehash();
|
||||
|
||||
int hash = TFuncs::GetHash(key);
|
||||
int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize;
|
||||
Entry* checkEntry = this->mHashHeads[hashIdx];
|
||||
while (checkEntry != NULL)
|
||||
{
|
||||
if ((checkEntry->mHash == hash) && (TFuncs::Matches(key, checkEntry->mValue)))
|
||||
{
|
||||
Iterator itr(this);
|
||||
itr.mCurEntry = checkEntry;
|
||||
itr.mCurBucket = hashIdx;
|
||||
return itr;
|
||||
}
|
||||
checkEntry = checkEntry->mNext;
|
||||
}
|
||||
|
||||
return end();
|
||||
}
|
||||
|
||||
template <typename TKey>
|
||||
bool Remove(const TKey& key)
|
||||
{
|
||||
if (mHashHeads == NULL)
|
||||
return false;
|
||||
|
||||
this->CheckRehash();
|
||||
|
||||
int hash = TFuncs::GetHash(key);
|
||||
int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize;
|
||||
|
||||
Entry** srcCheckEntryPtr = &this->mHashHeads[hashIdx];
|
||||
Entry* checkEntry = *srcCheckEntryPtr;
|
||||
while (checkEntry != NULL)
|
||||
{
|
||||
if ((checkEntry->mHash == hash) && (TFuncs::Matches(key, checkEntry->mValue)))
|
||||
{
|
||||
this->mCount--;
|
||||
*srcCheckEntryPtr = checkEntry->mNext;
|
||||
TFuncs::Deallocate(checkEntry);
|
||||
return true;
|
||||
}
|
||||
srcCheckEntryPtr = &checkEntry->mNext;
|
||||
checkEntry = checkEntry->mNext;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Iterator Erase(const Iterator& itr)
|
||||
{
|
||||
Iterator next = itr;
|
||||
++next;
|
||||
|
||||
bool found = false;
|
||||
|
||||
auto entry = itr.mCurEntry;
|
||||
int hashIdx = (entry->mHash & 0x7FFFFFFF) % this->mHashSize;
|
||||
|
||||
Entry** srcCheckEntryPtr = &this->mHashHeads[hashIdx];
|
||||
Entry* checkEntry = *srcCheckEntryPtr;
|
||||
while (checkEntry != NULL)
|
||||
{
|
||||
if (checkEntry == itr.mCurEntry)
|
||||
{
|
||||
this->mCount--;
|
||||
*srcCheckEntryPtr = checkEntry->mNext;
|
||||
found = true;
|
||||
}
|
||||
srcCheckEntryPtr = &checkEntry->mNext;
|
||||
checkEntry = checkEntry->mNext;
|
||||
}
|
||||
|
||||
BF_ASSERT(found);
|
||||
TFuncs::Deallocate(entry);
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
if (!TFuncs::DeallocateAll())
|
||||
{
|
||||
auto itr = begin();
|
||||
auto endItr = end();
|
||||
while (itr != endItr)
|
||||
{
|
||||
auto entry = itr.mCurEntry;
|
||||
++itr;
|
||||
TFuncs::Deallocate(entry);
|
||||
}
|
||||
TFuncs::Deallocate(this->mHashHeads);
|
||||
}
|
||||
|
||||
this->mHashSize = cDefaultHashSize;
|
||||
this->mHashHeads = NULL;
|
||||
this->mCount = 0;
|
||||
}
|
||||
|
||||
Iterator begin()
|
||||
{
|
||||
return ++Iterator(this);
|
||||
}
|
||||
|
||||
Iterator end()
|
||||
{
|
||||
Iterator itr(this);
|
||||
itr.mCurBucket = this->mHashSize;
|
||||
return itr;
|
||||
}
|
||||
};
|
||||
|
||||
NS_BF_END;
|
491
BeefySysLib/util/PerfTimer.cpp
Normal file
491
BeefySysLib/util/PerfTimer.cpp
Normal file
|
@ -0,0 +1,491 @@
|
|||
#include "PerfTimer.h"
|
||||
#include "BFApp.h"
|
||||
|
||||
#pragma warning(disable:4996)
|
||||
|
||||
#ifdef _DEBUG
|
||||
#define PERF_WANTS_THREAD_CHECK
|
||||
#endif
|
||||
|
||||
USING_NS_BF;
|
||||
|
||||
PerfManager* Beefy::gPerfManager = NULL;
|
||||
|
||||
PerfEntry::PerfEntry()
|
||||
{
|
||||
mCount = 0;
|
||||
mTimeTotal = 0;
|
||||
mCurTimingStart = 0;
|
||||
mChild = NULL;
|
||||
}
|
||||
|
||||
void PerfEntry::DbgPrint(const StringImpl& name, int level, std::map<String, int>& uniqueTimingMap)
|
||||
{
|
||||
String text;
|
||||
|
||||
uint64 childrenTime = 0;
|
||||
|
||||
/*PerfEntryMap::iterator itr = mChildren.begin();
|
||||
while (itr != mChildren.end())
|
||||
{
|
||||
PerfEntry* child = &itr->second;
|
||||
childrenTime += child->mTimeTotal;
|
||||
++itr;
|
||||
}*/
|
||||
|
||||
for (auto& kv : mChildren)
|
||||
{
|
||||
PerfEntry* child = kv.mValue;
|
||||
childrenTime += child->mTimeTotal;
|
||||
}
|
||||
|
||||
for (int i = 0; i <= level; i++)
|
||||
text += " ";
|
||||
String prefix = text;
|
||||
|
||||
text += name;
|
||||
|
||||
while (text.length() < 80)
|
||||
text += ' ';
|
||||
|
||||
int selfTime = (int)(mTimeTotal - childrenTime);
|
||||
|
||||
if (uniqueTimingMap.find(name) == uniqueTimingMap.end())
|
||||
uniqueTimingMap[name] = selfTime;
|
||||
else
|
||||
uniqueTimingMap[name] += selfTime;
|
||||
|
||||
text += StrFormat("%4d\t%6.1f\t%6.1f\n", mCount, mTimeTotal / 1000.0f, (int) (selfTime) / 1000.0f);
|
||||
|
||||
/*for (int i = 0; i < (int) mLog.size(); i++)
|
||||
{
|
||||
PerfLogEntry* logEntry = &mLog[i];
|
||||
text += prefix + StrFormat("@%d: %s\n", logEntry->mTick, logEntry->mString.c_str());
|
||||
}*/
|
||||
|
||||
OutputDebugStrF(text.c_str());
|
||||
|
||||
for (auto& kv : mChildren)
|
||||
{
|
||||
PerfEntry* child = kv.mValue;
|
||||
child->DbgPrint(kv.mKey, level + 1, uniqueTimingMap);
|
||||
}
|
||||
}
|
||||
|
||||
void PerfManager::Clear()
|
||||
{
|
||||
mLogEntries.ClearFast();
|
||||
mAlloc.Clear();
|
||||
|
||||
mFrames.clear();
|
||||
mCurStackIdx = 0;
|
||||
mOverhead = 0;
|
||||
}
|
||||
|
||||
PerfManager::PerfManager()
|
||||
{
|
||||
mRecording = false;
|
||||
mCurStackIdx = 0;
|
||||
mOwningThreadId = 0;
|
||||
mOverhead = 0;
|
||||
mAlloc.mDisableDebugTracing = true;
|
||||
}
|
||||
|
||||
void PerfManager::NextFrame()
|
||||
{
|
||||
if (!mRecording)
|
||||
return;
|
||||
|
||||
BF_ASSERT(mCurStackIdx <= 1);
|
||||
while (mCurStackIdx > 0)
|
||||
ZoneEnd(true);
|
||||
|
||||
auto logEntryFrameStart = mAlloc.Alloc<PerfLogEntry_FrameStart>();
|
||||
logEntryFrameStart->mType = PerfLogEntryType_FrameStart;
|
||||
logEntryFrameStart->mTimingStart = BFGetTickCountMicroFast();
|
||||
mLogEntries.PushBack(logEntryFrameStart);
|
||||
mCurStackIdx++;
|
||||
}
|
||||
|
||||
void PerfManager::ZoneStart(const char* name)
|
||||
{
|
||||
if (!mRecording)
|
||||
return;
|
||||
|
||||
#ifdef PERF_WANTS_THREAD_CHECK
|
||||
BF_ASSERT(mOwningThreadId == BfpThread_GetCurrentId());
|
||||
#endif
|
||||
|
||||
uint64 callTimerStart = BFGetTickCountMicroFast();
|
||||
|
||||
mCurStackIdx++;
|
||||
int nameLen = (int)strlen(name);
|
||||
auto logEntryStart = mAlloc.Alloc<PerfLogEntry_Start>(nameLen + 1);
|
||||
char* namePtr = (char*)(logEntryStart + 1);
|
||||
strcpy(namePtr, name);
|
||||
logEntryStart->mType = PerfLogEntryType_Start;
|
||||
logEntryStart->mName = namePtr;
|
||||
mLogEntries.PushBack(logEntryStart);
|
||||
|
||||
logEntryStart->mTimingStart = BFGetTickCountMicroFast();;
|
||||
mOverhead += logEntryStart->mTimingStart - callTimerStart;
|
||||
/*
|
||||
|
||||
|
||||
if (mCurStackIdx == 0)
|
||||
GetCurFrame();
|
||||
PerfEntry* curParent = mCurStack[mCurStackIdx - 1];
|
||||
|
||||
PerfEntry* entry = NULL;
|
||||
if (curParent->mChild != NULL)
|
||||
{
|
||||
if (curParent->mChild->mName == name)
|
||||
entry = curParent->mChild;
|
||||
}
|
||||
|
||||
if (entry == NULL)
|
||||
{
|
||||
PerfEntryMap::iterator itr = curParent->mChildren.find(name);
|
||||
|
||||
if (itr != curParent->mChildren.end())
|
||||
{
|
||||
entry = &itr->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
itr = curParent->mChildren.insert(PerfEntryMap::value_type(name, PerfEntry())).first;
|
||||
entry = &itr->second;
|
||||
entry->mName = name;
|
||||
}
|
||||
|
||||
curParent->mChild = entry;
|
||||
}
|
||||
|
||||
entry->mCount++;
|
||||
entry->mCurTimingStart = BFGetTickCountMicroFast();
|
||||
mCurStack[mCurStackIdx++] = entry;
|
||||
|
||||
mOverhead += entry->mCurTimingStart - callTimerStart;*/
|
||||
}
|
||||
|
||||
void PerfManager::ZoneEnd(bool allowFrameEnd)
|
||||
{
|
||||
if (!mRecording)
|
||||
return;
|
||||
|
||||
if ((mCurStackIdx <= 1) && (!allowFrameEnd))
|
||||
return;
|
||||
|
||||
auto logEntryEnd = mAlloc.Alloc<PerfLogEntry_End>();
|
||||
logEntryEnd->mType = PerfLogEntryType_End;
|
||||
logEntryEnd->mTimingEnd = BFGetTickCountMicroFast();
|
||||
mLogEntries.PushBack(logEntryEnd);
|
||||
mCurStackIdx--;
|
||||
|
||||
/*PerfEntry* entry = mCurStack[mCurStackIdx - 1];
|
||||
mCurStackIdx--;
|
||||
entry->mTimeTotal += BFGetTickCountMicroFast() - entry->mCurTimingStart;*/
|
||||
}
|
||||
|
||||
void PerfManager::Message(const StringImpl& theString)
|
||||
{
|
||||
if (!mRecording)
|
||||
return;
|
||||
|
||||
//TODO: Implement
|
||||
/*if (mCurStackIdx == 0)
|
||||
GetCurFrame();
|
||||
|
||||
PerfEntry* entry = mCurStack[mCurStackIdx - 1];
|
||||
|
||||
PerfLogEntry logEntry;
|
||||
logEntry.mTick = BFTickCount();
|
||||
logEntry.mString = theString;
|
||||
entry->mLog.push_back(logEntry);*/
|
||||
}
|
||||
|
||||
void PerfManager::StartRecording()
|
||||
{
|
||||
#ifdef PERF_WANTS_THREAD_CHECK
|
||||
mOwningThreadId = BfpThread_GetCurrentId();
|
||||
#endif
|
||||
|
||||
mRecording = true;
|
||||
Clear();
|
||||
NextFrame();
|
||||
}
|
||||
|
||||
void PerfManager::StopRecording(bool dbgPrint)
|
||||
{
|
||||
if (!mRecording)
|
||||
return;
|
||||
|
||||
#ifdef PERF_WANTS_THREAD_CHECK
|
||||
BF_ASSERT(mOwningThreadId == ::BfpThread_GetCurrentId());
|
||||
#endif
|
||||
|
||||
mRecording = false;
|
||||
|
||||
auto logEntryStopRecording = mAlloc.Alloc<PerfLogEntry_StopRecording>();
|
||||
logEntryStopRecording->mType = PerfLogEntryType_StopRecording;
|
||||
logEntryStopRecording->mTimingEnd = BFGetTickCountMicroFast();
|
||||
mLogEntries.PushBack(logEntryStopRecording);
|
||||
|
||||
if (dbgPrint)
|
||||
DbgPrint();
|
||||
|
||||
/*if (mFrames.size() > 0)
|
||||
{
|
||||
PerfFrame* frame = &mFrames.back();
|
||||
frame->mTimeTotal = BFGetTickCountMicroFast() - frame->mCurTimingStart;
|
||||
}*/
|
||||
}
|
||||
|
||||
bool PerfManager::IsRecording()
|
||||
{
|
||||
return mRecording;
|
||||
}
|
||||
|
||||
void PerfManager::DbgPrint()
|
||||
{
|
||||
#ifdef PERF_WANTS_THREAD_CHECK
|
||||
BF_ASSERT(mOwningThreadId == ::BfpThread_GetCurrentId());
|
||||
#endif
|
||||
|
||||
if (mFrames.empty())
|
||||
{
|
||||
mCurStackIdx = 0;
|
||||
PerfFrame* curFrame = NULL;
|
||||
|
||||
for (auto logEntry : mLogEntries)
|
||||
{
|
||||
if (logEntry->mType == PerfLogEntryType_FrameStart)
|
||||
{
|
||||
PerfLogEntry_FrameStart* frameStartEntry = (PerfLogEntry_FrameStart*)logEntry;
|
||||
|
||||
if (mFrames.size() > 0)
|
||||
{
|
||||
PerfFrame* frame = &mFrames.back();
|
||||
frame->mTimeTotal = frameStartEntry->mTimingStart - frame->mCurTimingStart;
|
||||
}
|
||||
|
||||
mFrames.push_back(PerfFrame());
|
||||
PerfFrame* frame = &mFrames.back();
|
||||
frame->mCurTimingStart = frameStartEntry->mTimingStart;
|
||||
|
||||
#ifndef BF_NO_BFAPP
|
||||
if (gBFApp != NULL)
|
||||
frame->mAppFrameNum = gBFApp->mUpdateCnt;
|
||||
else
|
||||
frame->mAppFrameNum = 0;
|
||||
#else
|
||||
frame->mAppFrameNum = 0;
|
||||
#endif
|
||||
|
||||
frame->mCount = 1;
|
||||
|
||||
mCurStack[mCurStackIdx++] = frame;
|
||||
}
|
||||
else if (logEntry->mType == PerfLogEntryType_Start)
|
||||
{
|
||||
PerfLogEntry_Start* entryStart = (PerfLogEntry_Start*)logEntry;
|
||||
|
||||
PerfEntry* curParent = mCurStack[mCurStackIdx - 1];
|
||||
|
||||
PerfEntry* entry = NULL;
|
||||
if (curParent->mChild != NULL)
|
||||
{
|
||||
if (curParent->mChild->mName == entryStart->mName)
|
||||
entry = curParent->mChild;
|
||||
}
|
||||
|
||||
if (entry == NULL)
|
||||
{
|
||||
/*PerfEntryMap::iterator itr = curParent->mChildren.find(entryStart->mName);
|
||||
|
||||
if (itr != curParent->mChildren.end())
|
||||
{
|
||||
entry = &itr->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
itr = curParent->mChildren.insert(PerfEntryMap::value_type(entryStart->mName, PerfEntry())).first;
|
||||
entry = &itr->second;
|
||||
entry->mName = entryStart->mName;
|
||||
}*/
|
||||
|
||||
PerfEntry** entryPtr = NULL;
|
||||
if (curParent->mChildren.TryAdd(entryStart->mName, NULL, &entryPtr))
|
||||
{
|
||||
entry = mAlloc.Alloc<PerfEntry>();
|
||||
*entryPtr = entry;
|
||||
entry->mName = entryStart->mName;
|
||||
}
|
||||
else
|
||||
{
|
||||
entry = *entryPtr;
|
||||
}
|
||||
|
||||
curParent->mChild = entry;
|
||||
}
|
||||
|
||||
entry->mCount++;
|
||||
entry->mCurTimingStart = entryStart->mTimingStart;
|
||||
mCurStack[mCurStackIdx++] = entry;
|
||||
}
|
||||
else if (logEntry->mType == PerfLogEntryType_End)
|
||||
{
|
||||
PerfLogEntry_End* entryEnd = (PerfLogEntry_End*)logEntry;
|
||||
PerfEntry* entry = mCurStack[mCurStackIdx - 1];
|
||||
mCurStackIdx--;
|
||||
entry->mTimeTotal += entryEnd->mTimingEnd - entry->mCurTimingStart;
|
||||
}
|
||||
else if (logEntry->mType == PerfLogEntryType_StopRecording)
|
||||
{
|
||||
PerfLogEntry_StopRecording* stopRecordingEntry = (PerfLogEntry_StopRecording*)logEntry;
|
||||
if (mFrames.size() > 0)
|
||||
{
|
||||
PerfFrame* frame = &mFrames.back();
|
||||
frame->mTimeTotal = stopRecordingEntry->mTimingEnd - frame->mCurTimingStart;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::map<String, int> uniqueTimingMap;
|
||||
|
||||
OutputDebugStrF("PerfManager_Results____________________________________________________________Calls___MSTotal__MSSelf\n");
|
||||
PerfFrameList::iterator frameItr = mFrames.begin();
|
||||
while (frameItr != mFrames.end())
|
||||
{
|
||||
PerfFrame* frame = &(*frameItr);
|
||||
frame->DbgPrint(StrFormat("Frame %d", frame->mAppFrameNum), 0, uniqueTimingMap);
|
||||
++frameItr;
|
||||
}
|
||||
|
||||
OutputDebugStr("\nPerfManager_Combined_Results____________________________________________________________________MSSelf\n");
|
||||
typedef std::multimap<int, String> SortedMap;
|
||||
SortedMap sortedMap;
|
||||
for (auto& entry : uniqueTimingMap)
|
||||
{
|
||||
sortedMap.insert(SortedMap::value_type(-entry.second, entry.first));
|
||||
}
|
||||
for (auto& entry : sortedMap)
|
||||
{
|
||||
String text = entry.second;
|
||||
for (int i = (int)text.length(); i < 96; i++)
|
||||
text += " ";
|
||||
text += StrFormat("%6.1f\n", -entry.first / 1000.0f);
|
||||
OutputDebugStr(text.c_str());
|
||||
}
|
||||
|
||||
{
|
||||
String text = "PerfTimer Timing Overhead";
|
||||
for (int i = (int) text.length(); i < 96; i++)
|
||||
text += " ";
|
||||
text += StrFormat("%6.1f\n", mOverhead / 1000.0f);
|
||||
OutputDebugStr(text.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
AutoPerf::AutoPerf(const char* name, PerfManager* perfManager)
|
||||
{
|
||||
if ((perfManager == NULL) || (!perfManager->mRecording))
|
||||
{
|
||||
mPerfManager = NULL;
|
||||
mStartStackIdx = 0;
|
||||
return;
|
||||
}
|
||||
mPerfManager = perfManager;
|
||||
mPerfManager->ZoneStart(name);
|
||||
mStartStackIdx = mPerfManager->mCurStackIdx - 1;
|
||||
}
|
||||
|
||||
void AutoPerf::Leave()
|
||||
{
|
||||
if (mStartStackIdx <= 0)
|
||||
return;
|
||||
|
||||
BF_ASSERT(mPerfManager->mCurStackIdx <= mStartStackIdx + 1);
|
||||
if (mPerfManager->mCurStackIdx > mStartStackIdx)
|
||||
mPerfManager->ZoneEnd();
|
||||
|
||||
mStartStackIdx = -1;
|
||||
}
|
||||
|
||||
AutoPerf::~AutoPerf()
|
||||
{
|
||||
Leave();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AutoPerfRecordAndPrint::AutoPerfRecordAndPrint(PerfManager* perfManager)
|
||||
{
|
||||
mPerfManager = perfManager;
|
||||
mDidStart = !perfManager->mRecording;
|
||||
if (mDidStart)
|
||||
perfManager->StartRecording();
|
||||
}
|
||||
|
||||
AutoPerfRecordAndPrint::~AutoPerfRecordAndPrint()
|
||||
{
|
||||
if (mDidStart)
|
||||
mPerfManager->StopRecording(true);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AutoDbgTime::AutoDbgTime(const StringImpl& name)
|
||||
{
|
||||
mName = name;
|
||||
mStartTick = BFTickCount();
|
||||
}
|
||||
|
||||
AutoDbgTime::~AutoDbgTime()
|
||||
{
|
||||
int totalTime = BFTickCount() - mStartTick;
|
||||
OutputDebugStrF("%s: %d\n", mName.c_str(), totalTime);
|
||||
}
|
||||
|
||||
///
|
||||
|
||||
#ifdef BFSYSLIB_DYNAMIC
|
||||
|
||||
BF_EXPORT void BF_CALLTYPE PerfTimer_ZoneStart(const char* name)
|
||||
{
|
||||
gPerfManager->ZoneStart(name);
|
||||
}
|
||||
|
||||
BF_EXPORT void BF_CALLTYPE PerfTimer_ZoneEnd()
|
||||
{
|
||||
gPerfManager->ZoneEnd();
|
||||
}
|
||||
|
||||
BF_EXPORT void BF_CALLTYPE PerfTimer_Message(const char* theString)
|
||||
{
|
||||
gPerfManager->Message(theString);
|
||||
}
|
||||
|
||||
BF_EXPORT int BF_CALLTYPE PerfTimer_IsRecording()
|
||||
{
|
||||
return gPerfManager->IsRecording() ? 1 : 0;
|
||||
}
|
||||
|
||||
BF_EXPORT void BF_CALLTYPE PerfTimer_StartRecording()
|
||||
{
|
||||
gPerfManager->StartRecording();
|
||||
}
|
||||
|
||||
BF_EXPORT void BF_CALLTYPE PerfTimer_StopRecording()
|
||||
{
|
||||
gPerfManager->StopRecording();
|
||||
}
|
||||
|
||||
BF_EXPORT void BF_CALLTYPE PerfTimer_DbgPrint()
|
||||
{
|
||||
gPerfManager->DbgPrint();
|
||||
}
|
||||
|
||||
#endif
|
188
BeefySysLib/util/PerfTimer.h
Normal file
188
BeefySysLib/util/PerfTimer.h
Normal file
|
@ -0,0 +1,188 @@
|
|||
#pragma once
|
||||
|
||||
#include "../Common.h"
|
||||
//#include "boost/unordered/unordered_map.hpp"
|
||||
#include "BumpAllocator.h"
|
||||
#include "SLIList.h"
|
||||
#include <unordered_map>
|
||||
#include "BumpAllocator.h"
|
||||
#include "Dictionary.h"
|
||||
#include <map>
|
||||
#include <list>
|
||||
|
||||
NS_BF_BEGIN;
|
||||
|
||||
class PerfEntry;
|
||||
|
||||
class PerfEntry
|
||||
{
|
||||
public:
|
||||
String mName;
|
||||
Dictionary<String, PerfEntry*> mChildren;
|
||||
PerfEntry* mChild;
|
||||
|
||||
int mCount;
|
||||
uint64 mTimeTotal;
|
||||
uint64 mCurTimingStart;
|
||||
|
||||
public:
|
||||
PerfEntry();
|
||||
|
||||
void DbgPrint(const StringImpl& name, int level, std::map<String, int>& uniqueTimingMap);
|
||||
};
|
||||
|
||||
class PerfFrame : public PerfEntry
|
||||
{
|
||||
public:
|
||||
int mAppFrameNum;
|
||||
};
|
||||
|
||||
typedef std::list<PerfFrame> PerfFrameList;
|
||||
|
||||
const int MAX_PERFMANGER_STACKSIZE = 256;
|
||||
|
||||
enum PerfLogEntryType
|
||||
{
|
||||
PerfLogEntryType_FrameStart,
|
||||
PerfLogEntryType_Start,
|
||||
PerfLogEntryType_End,
|
||||
PerfLogEntryType_Log,
|
||||
PerfLogEntryType_StopRecording
|
||||
};
|
||||
|
||||
struct PerfLogEntry
|
||||
{
|
||||
int mType;
|
||||
PerfLogEntry* mNext;
|
||||
};
|
||||
|
||||
struct PerfLogEntry_FrameStart : PerfLogEntry
|
||||
{
|
||||
uint64 mTimingStart;
|
||||
};
|
||||
|
||||
struct PerfLogEntry_StopRecording : PerfLogEntry
|
||||
{
|
||||
uint64 mTimingEnd;
|
||||
};
|
||||
|
||||
// We could use 32-bit timing start and timing end values and still
|
||||
// record timings up to 71 minutes long
|
||||
struct PerfLogEntry_Start : PerfLogEntry
|
||||
{
|
||||
const char* mName;
|
||||
uint64 mTimingStart;
|
||||
};
|
||||
|
||||
struct PerfLogEntry_End : PerfLogEntry
|
||||
{
|
||||
uint64 mTimingEnd;
|
||||
};
|
||||
|
||||
class PerfManager
|
||||
{
|
||||
public:
|
||||
BumpAllocator mAlloc;
|
||||
SLIList<PerfLogEntry*> mLogEntries;
|
||||
|
||||
PerfFrameList mFrames;
|
||||
PerfEntry* mCurStack[MAX_PERFMANGER_STACKSIZE];
|
||||
int mCurStackIdx;
|
||||
bool mRecording;
|
||||
BfpThreadId mOwningThreadId;
|
||||
uint64 mOverhead;
|
||||
|
||||
protected:
|
||||
void Clear();
|
||||
|
||||
public:
|
||||
PerfManager();
|
||||
|
||||
void NextFrame();
|
||||
|
||||
void ZoneStart(const char* name);
|
||||
void ZoneEnd(bool allowFrameEnd = false);
|
||||
void Message(const StringImpl& theString);
|
||||
|
||||
void StartRecording();
|
||||
void StopRecording(bool dbgPrint = false);
|
||||
bool IsRecording();
|
||||
|
||||
void DbgPrint();
|
||||
};
|
||||
|
||||
extern PerfManager* gPerfManager;
|
||||
|
||||
class AutoPerf
|
||||
{
|
||||
public:
|
||||
PerfManager* mPerfManager;
|
||||
int mStartStackIdx;
|
||||
|
||||
public:
|
||||
AutoPerf(const char* name, PerfManager* perfManager = gPerfManager);
|
||||
~AutoPerf();
|
||||
|
||||
void Leave();
|
||||
};
|
||||
|
||||
class AutoDbgTime
|
||||
{
|
||||
public:
|
||||
uint32 mStartTick;
|
||||
String mName;
|
||||
|
||||
public:
|
||||
AutoDbgTime(const StringImpl& name);
|
||||
~AutoDbgTime();
|
||||
};
|
||||
|
||||
class AutoPerfRecordAndPrint
|
||||
{
|
||||
public:
|
||||
PerfManager* mPerfManager;
|
||||
bool mDidStart;
|
||||
|
||||
public:
|
||||
AutoPerfRecordAndPrint(PerfManager* perfManager = gPerfManager);
|
||||
~AutoPerfRecordAndPrint();
|
||||
};
|
||||
|
||||
|
||||
class DebugTimeGuard
|
||||
{
|
||||
public:
|
||||
uint32 mStartTick;
|
||||
int mMaxTicks;
|
||||
String mName;
|
||||
|
||||
public:
|
||||
DebugTimeGuard(int maxTicks, const StringImpl& name = "DebugTimeGuard")
|
||||
{
|
||||
mName = name;
|
||||
mMaxTicks = maxTicks;
|
||||
Start();
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
mStartTick = BFTickCount();
|
||||
}
|
||||
|
||||
void Stop()
|
||||
{
|
||||
if (mMaxTicks == 0)
|
||||
return;
|
||||
int maxTime = (int)(BFTickCount() - mStartTick);
|
||||
if (maxTime > mMaxTicks)
|
||||
OutputDebugStrF("%s took too long: %dms\n", mName.c_str(), maxTime);
|
||||
mMaxTicks = 0;
|
||||
}
|
||||
|
||||
~DebugTimeGuard()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
};
|
||||
|
||||
NS_BF_END;
|
2
BeefySysLib/util/Point.cpp
Normal file
2
BeefySysLib/util/Point.cpp
Normal file
|
@ -0,0 +1,2 @@
|
|||
//#include "Point.h"
|
||||
|
21
BeefySysLib/util/Point.h
Normal file
21
BeefySysLib/util/Point.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
#pragma once
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
NS_BF_BEGIN;
|
||||
|
||||
class Point2D
|
||||
{
|
||||
public:
|
||||
float mX;
|
||||
float mY;
|
||||
|
||||
public:
|
||||
Point2D(float x = 0, float y = 0)
|
||||
{
|
||||
mX = x;
|
||||
mY = y;
|
||||
}
|
||||
};
|
||||
|
||||
NS_BF_END;
|
67
BeefySysLib/util/PolySpline.cpp
Normal file
67
BeefySysLib/util/PolySpline.cpp
Normal file
|
@ -0,0 +1,67 @@
|
|||
#include "PolySpline.h"
|
||||
|
||||
USING_NS_BF;
|
||||
|
||||
PolySpline2D::PolySpline2D()
|
||||
{
|
||||
mCoefs = NULL;
|
||||
}
|
||||
|
||||
PolySpline2D::~PolySpline2D()
|
||||
{
|
||||
delete mCoefs;
|
||||
}
|
||||
|
||||
void PolySpline2D::AddPt(float x, float y)
|
||||
{
|
||||
delete mCoefs;
|
||||
mCoefs = NULL;
|
||||
|
||||
|
||||
|
||||
mInputPoints.push_back(Point2D(x, y));
|
||||
}
|
||||
|
||||
int PolySpline2D::GetLength()
|
||||
{
|
||||
return (int) mInputPoints.size();
|
||||
}
|
||||
|
||||
void PolySpline2D::Calculate()
|
||||
{
|
||||
int n = (int) mInputPoints.size();
|
||||
float* mat = new float[n*n];
|
||||
mCoefs = new float[n];
|
||||
|
||||
for (int j=0; j<n; j++)
|
||||
mat[j*n] = mInputPoints[j].mY;
|
||||
for (int i=1; i<n; i++)
|
||||
{
|
||||
for (int j=0; j<n-i; j++)
|
||||
mat[i+j*n]=(mat[(i-1)+j*n]-mat[(i-1)+(j+1)*n])/(mInputPoints[j].mX-mInputPoints[j+i].mX);
|
||||
}
|
||||
|
||||
for (int i=0; i<n; i++)
|
||||
mCoefs[i] = mat[i];
|
||||
|
||||
delete mat;
|
||||
}
|
||||
|
||||
float PolySpline2D::Evaluate(float x)
|
||||
{
|
||||
if (mCoefs == NULL)
|
||||
Calculate();
|
||||
|
||||
float result = mCoefs[0];
|
||||
int n = (int) mInputPoints.size();
|
||||
for (int i = 1; i < n; i++)
|
||||
{
|
||||
float add = mCoefs[i];
|
||||
for (int j = 0; j < i; j++)
|
||||
add *= (x - mInputPoints[j].mX);
|
||||
result += add;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
30
BeefySysLib/util/PolySpline.h
Normal file
30
BeefySysLib/util/PolySpline.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
|
||||
#include "Common.h"
|
||||
#include "Point.h"
|
||||
#include <vector>
|
||||
|
||||
NS_BF_BEGIN;
|
||||
|
||||
class PolySpline2D
|
||||
{
|
||||
public:
|
||||
std::vector<Point2D> mInputPoints;
|
||||
|
||||
|
||||
public:
|
||||
float* mCoefs;
|
||||
|
||||
public:
|
||||
PolySpline2D();
|
||||
~PolySpline2D();
|
||||
|
||||
void AddPt(float x, float y);
|
||||
int GetLength();
|
||||
|
||||
void Calculate();
|
||||
float Evaluate(float x);
|
||||
};
|
||||
|
||||
|
||||
NS_BF_END;
|
53
BeefySysLib/util/Quaternion.cpp
Normal file
53
BeefySysLib/util/Quaternion.cpp
Normal file
|
@ -0,0 +1,53 @@
|
|||
#include "Quaternion.h"
|
||||
|
||||
USING_NS_BF;
|
||||
|
||||
Quaternion Beefy::operator* (float fScalar, const Quaternion& rkQ)
|
||||
{
|
||||
return Quaternion(fScalar*rkQ.mX, fScalar*rkQ.mY, fScalar*rkQ.mZ, fScalar*rkQ.mW);
|
||||
}
|
||||
|
||||
Beefy::Quaternion Beefy::Quaternion::Slerp(float fT, const Quaternion& rkP, const Quaternion& rkQ, bool shortestPath)
|
||||
{
|
||||
float fCos = Dot(rkP, rkQ);
|
||||
Quaternion rkT;
|
||||
|
||||
// Do we need to invert rotation?
|
||||
if (fCos < 0.0f && shortestPath)
|
||||
{
|
||||
fCos = -fCos;
|
||||
rkT = -rkQ;
|
||||
}
|
||||
else
|
||||
{
|
||||
rkT = rkQ;
|
||||
}
|
||||
|
||||
if ((fabs(fCos) < 1.0f - BF_MS_EPSILON) && (false))
|
||||
{
|
||||
// Standard case (slerp)
|
||||
float fSin = sqrt(1.0f - (fCos * fCos));
|
||||
float fAngle = atan2(fSin, fCos);
|
||||
float fInvSin = 1.0f / fSin;
|
||||
float fCoeff0 = sin((1.0f - fT) * fAngle) * fInvSin;
|
||||
float fCoeff1 = sin(fT * fAngle) * fInvSin;
|
||||
return fCoeff0 * rkP + fCoeff1 * rkT;
|
||||
}
|
||||
else
|
||||
{
|
||||
// There are two situations:
|
||||
// 1. "rkP" and "rkQ" are very close (fCos ~= +1), so we can do a linear
|
||||
// interpolation safely.
|
||||
// 2. "rkP" and "rkQ" are almost inverse of each other (fCos ~= -1), there
|
||||
// are an infinite number of possibilities interpolation. but we haven't
|
||||
// have method to fix this case, so just use linear interpolation here.
|
||||
Quaternion t = (1.0f - fT) * rkP + fT * rkT;
|
||||
// taking the complement requires renormalisation
|
||||
t = Normalise(t);
|
||||
|
||||
//OutputDebugStrF("%0.3f %0.3f %0.3f %0.3f\n", t.mX, t.mY, t.mZ, t.mW);
|
||||
return t;
|
||||
|
||||
//return (fT < 0.5f) ? rkP : rkQ;
|
||||
}
|
||||
}
|
124
BeefySysLib/util/Quaternion.h
Normal file
124
BeefySysLib/util/Quaternion.h
Normal file
|
@ -0,0 +1,124 @@
|
|||
#pragma once
|
||||
|
||||
#include "Common.h"
|
||||
#include "Matrix4.h"
|
||||
#include <math.h>
|
||||
|
||||
NS_BF_BEGIN;
|
||||
|
||||
const float BF_MS_EPSILON = 1e-03f;
|
||||
|
||||
class Quaternion
|
||||
{
|
||||
public:
|
||||
float mX, mY, mZ, mW;
|
||||
|
||||
public:
|
||||
Quaternion()
|
||||
{}
|
||||
|
||||
Quaternion(float x, float y, float z, float w) :
|
||||
mX(x), mY(y), mZ(z), mW(w)
|
||||
{
|
||||
}
|
||||
|
||||
Matrix4 ToMatrix2() const
|
||||
{
|
||||
// source -> http://content.gpwiki.org/index.php/OpenGL:Tutorials:Using_Quaternions_to_represent_rotation#Quaternion_to_Matrix
|
||||
float x2 = mX * mX;
|
||||
float y2 = mY * mY;
|
||||
float z2 = mZ * mZ;
|
||||
float xy = mX * mY;
|
||||
float xz = mX * mZ;
|
||||
float yz = mY * mZ;
|
||||
float wx = mW * mX;
|
||||
float wy = mW * mY;
|
||||
float wz = mW * mZ;
|
||||
|
||||
// This calculation would be a lot more complicated for non-unit length quaternions
|
||||
// Note: The constructor of Matrix4 expects the Matrix in column-major format like expected by
|
||||
// OpenGL
|
||||
return Matrix4(
|
||||
1.0f - 2.0f * (y2 + z2), 2.0f * (xy - wz), 2.0f * (xz + wy), 0.0f,
|
||||
2.0f * (xy + wz), 1.0f - 2.0f * (x2 + z2), 2.0f * (yz - wx), 0.0f,
|
||||
2.0f * (xz - wy), 2.0f * (yz + wx), 1.0f - 2.0f * (x2 + y2), 0.0f,
|
||||
2.0f * (xz - wy), 2.0f * (yz + wx), 1.0f - 2.0f * (x2 + y2), 0.0f);
|
||||
}
|
||||
|
||||
Matrix4 ToMatrix() const
|
||||
{
|
||||
Matrix4 result;
|
||||
float fTx = mX + mX;
|
||||
float fTy = mY + mY;
|
||||
float fTz = mZ + mZ;
|
||||
float fTwx = fTx*mW;
|
||||
float fTwy = fTy*mW;
|
||||
float fTwz = fTz*mW;
|
||||
float fTxx = fTx*mX;
|
||||
float fTxy = fTy*mX;
|
||||
float fTxz = fTz*mX;
|
||||
float fTyy = fTy*mY;
|
||||
float fTyz = fTz*mY;
|
||||
float fTzz = fTz*mZ;
|
||||
|
||||
result.m00 = 1.0f - (fTyy + fTzz);
|
||||
result.m01 = fTxy - fTwz;
|
||||
result.m02 = fTxz + fTwy;
|
||||
result.m03 = 0;
|
||||
|
||||
result.m10 = fTxy + fTwz;
|
||||
result.m11 = 1.0f - (fTxx + fTzz);
|
||||
result.m12 = fTyz - fTwx;
|
||||
result.m13 = 0;
|
||||
|
||||
result.m20 = fTxz - fTwy;
|
||||
result.m21 = fTyz + fTwx;
|
||||
result.m22 = 1.0f - (fTxx + fTyy);
|
||||
result.m23 = 0;
|
||||
|
||||
result.m30 = 0;
|
||||
result.m31 = 0;
|
||||
result.m32 = 0;
|
||||
result.m33 = 1.0f;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static float Dot(const Quaternion& rkP, const Quaternion& rkQ)
|
||||
{
|
||||
return rkP.mX*rkQ.mX + rkP.mY*rkQ.mY + rkP.mZ*rkQ.mZ + rkP.mW*rkQ.mW;
|
||||
}
|
||||
|
||||
Quaternion operator- () const
|
||||
{
|
||||
return Quaternion(-mX, -mY, -mZ, -mW);
|
||||
}
|
||||
|
||||
Quaternion operator* (float fScalar) const
|
||||
{
|
||||
return Quaternion(fScalar*mX, fScalar*mY, fScalar*mZ, fScalar*mW);
|
||||
}
|
||||
|
||||
Quaternion operator+ (const Quaternion& rkQ) const
|
||||
{
|
||||
return Quaternion(mX + rkQ.mX, mY + rkQ.mY, mZ + rkQ.mZ, mW + rkQ.mW);
|
||||
}
|
||||
|
||||
static Quaternion Slerp(float fT, const Quaternion& rkP, const Quaternion& rkQ, bool shortestPath);
|
||||
|
||||
float Norm() const
|
||||
{
|
||||
return mX*mX + mY*mY + mZ*mZ + mW*mW;
|
||||
}
|
||||
|
||||
static Quaternion Normalise(const Quaternion& quat)
|
||||
{
|
||||
float len = quat.Norm();
|
||||
float factor = 1.0f / sqrt(len);
|
||||
return quat * factor;
|
||||
}
|
||||
};
|
||||
|
||||
Quaternion operator*(float fScalar, const Quaternion& rkQ);
|
||||
|
||||
NS_BF_END;
|
17
BeefySysLib/util/Queue.h
Normal file
17
BeefySysLib/util/Queue.h
Normal file
|
@ -0,0 +1,17 @@
|
|||
#pragma once
|
||||
|
||||
#include "../Common.h"
|
||||
|
||||
NS_BF_BEGIN;
|
||||
|
||||
template <typename T>
|
||||
class Queue
|
||||
{
|
||||
public:
|
||||
T* mBuffer;
|
||||
|
||||
|
||||
public:
|
||||
};
|
||||
|
||||
NS_BF_END;
|
30
BeefySysLib/util/Rect.h
Normal file
30
BeefySysLib/util/Rect.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
NS_BF_BEGIN;
|
||||
|
||||
class Rect
|
||||
{
|
||||
public:
|
||||
float mX;
|
||||
float mY;
|
||||
float mWidth;
|
||||
float mHeight;
|
||||
|
||||
public:
|
||||
Rect()
|
||||
{
|
||||
mX = 0;
|
||||
mY = 0;
|
||||
mWidth = 0;
|
||||
mHeight = 0;
|
||||
}
|
||||
|
||||
bool operator!=(const Rect& r2)
|
||||
{
|
||||
return (mX != r2.mX) || (mY != r2.mY) || (mWidth != r2.mWidth) || (mHeight != r2.mHeight);
|
||||
}
|
||||
};
|
||||
|
||||
NS_BF_END;
|
256
BeefySysLib/util/SLIList.h
Normal file
256
BeefySysLib/util/SLIList.h
Normal file
|
@ -0,0 +1,256 @@
|
|||
#pragma once
|
||||
|
||||
#include "../Common.h"
|
||||
|
||||
// Singly Linked Intrusive List (no 'node' wrapper, requires mNext member in T)
|
||||
|
||||
NS_BF_BEGIN;
|
||||
|
||||
template <typename T>
|
||||
class SLIList
|
||||
{
|
||||
public:
|
||||
T mHead;
|
||||
T mTail;
|
||||
|
||||
struct Iterator
|
||||
{
|
||||
public:
|
||||
T mMember;
|
||||
|
||||
public:
|
||||
Iterator(T member)
|
||||
{
|
||||
mMember = member;
|
||||
}
|
||||
|
||||
Iterator& operator++()
|
||||
{
|
||||
mMember = mMember->mNext;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator!=(const Iterator& itr) const
|
||||
{
|
||||
return itr.mMember != mMember;
|
||||
}
|
||||
|
||||
bool operator==(const Iterator& itr) const
|
||||
{
|
||||
return itr.mMember == mMember;
|
||||
}
|
||||
|
||||
T operator*()
|
||||
{
|
||||
return mMember;
|
||||
}
|
||||
};
|
||||
|
||||
struct RemovableIterator
|
||||
{
|
||||
public:
|
||||
T mPrevVal;
|
||||
T* mPrevNextPtr;
|
||||
|
||||
public:
|
||||
RemovableIterator(T* headPtr)
|
||||
{
|
||||
mPrevVal = NULL;
|
||||
mPrevNextPtr = headPtr;
|
||||
}
|
||||
|
||||
RemovableIterator& operator++()
|
||||
{
|
||||
T newNode = *mPrevNextPtr;
|
||||
mPrevVal = newNode;
|
||||
if (newNode != NULL)
|
||||
{
|
||||
mPrevNextPtr = &newNode->mNext;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator!=(const RemovableIterator& itr) const
|
||||
{
|
||||
if (itr.mPrevNextPtr == NULL)
|
||||
return *mPrevNextPtr != NULL;
|
||||
return *itr.mPrevNextPtr != *mPrevNextPtr;
|
||||
}
|
||||
|
||||
bool operator==(const RemovableIterator& itr) const
|
||||
{
|
||||
if (itr.mPrevNextPtr == NULL)
|
||||
return *mPrevNextPtr == NULL;
|
||||
return *itr.mPrevNextPtr == *mPrevNextPtr;
|
||||
}
|
||||
|
||||
T operator*()
|
||||
{
|
||||
return *mPrevNextPtr;
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
SLIList()
|
||||
{
|
||||
mHead = NULL;
|
||||
mTail = NULL;
|
||||
}
|
||||
|
||||
int Size()
|
||||
{
|
||||
int size = 0;
|
||||
T checkNode = mHead;
|
||||
while (checkNode != NULL)
|
||||
{
|
||||
size++;
|
||||
checkNode = checkNode->mNext;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
T operator[](int idx)
|
||||
{
|
||||
int curIdx = 0;
|
||||
T checkNode = mHead;
|
||||
while (checkNode != NULL)
|
||||
{
|
||||
if (idx == curIdx)
|
||||
return checkNode;
|
||||
T next = checkNode->mNext;
|
||||
curIdx++;
|
||||
checkNode = next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void PushBack(T node)
|
||||
{
|
||||
BF_ASSERT(node->mNext == NULL);
|
||||
|
||||
if (mHead == NULL)
|
||||
mHead = node;
|
||||
else
|
||||
mTail->mNext = node;
|
||||
mTail = node;
|
||||
}
|
||||
|
||||
void PushFront(T node)
|
||||
{
|
||||
BF_ASSERT(node->mNext == NULL);
|
||||
|
||||
if (mHead == NULL)
|
||||
mTail = node;
|
||||
else
|
||||
node->mNext = mHead;
|
||||
mHead = node;
|
||||
}
|
||||
|
||||
T PopFront()
|
||||
{
|
||||
T node = mHead;
|
||||
mHead = node->mNext;
|
||||
node->mNext = NULL;
|
||||
return node;
|
||||
}
|
||||
|
||||
void Remove(T node, T prevNode)
|
||||
{
|
||||
if (prevNode != NULL)
|
||||
prevNode->mNext = node->mNext;
|
||||
else
|
||||
mHead = node->mNext;
|
||||
if (mTail == node)
|
||||
mTail = prevNode;
|
||||
node->mNext = NULL;
|
||||
}
|
||||
|
||||
void Remove(T node)
|
||||
{
|
||||
T prevNode = NULL;
|
||||
T checkNode = mHead;
|
||||
while (checkNode != NULL)
|
||||
{
|
||||
if (checkNode == node)
|
||||
{
|
||||
Remove(node, prevNode);
|
||||
return;
|
||||
}
|
||||
prevNode = checkNode;
|
||||
checkNode = checkNode->mNext;
|
||||
}
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
T checkNode = mHead;
|
||||
while (checkNode != NULL)
|
||||
{
|
||||
T next = checkNode->mNext;
|
||||
checkNode->mNext = NULL;
|
||||
checkNode = next;
|
||||
}
|
||||
|
||||
mHead = NULL;
|
||||
mTail = NULL;
|
||||
}
|
||||
|
||||
void ClearFast()
|
||||
{
|
||||
mHead = NULL;
|
||||
mTail = NULL;
|
||||
}
|
||||
|
||||
void DeleteAll()
|
||||
{
|
||||
T checkNode = mHead;
|
||||
while (checkNode != NULL)
|
||||
{
|
||||
T next = checkNode->mNext;
|
||||
delete checkNode;
|
||||
checkNode = next;
|
||||
}
|
||||
mHead = NULL;
|
||||
mTail = NULL;
|
||||
}
|
||||
|
||||
|
||||
bool IsEmpty()
|
||||
{
|
||||
return mHead == NULL;
|
||||
}
|
||||
|
||||
T front()
|
||||
{
|
||||
return mHead;
|
||||
}
|
||||
|
||||
T back()
|
||||
{
|
||||
return mTail;
|
||||
}
|
||||
|
||||
RemovableIterator begin()
|
||||
{
|
||||
return RemovableIterator(&mHead);
|
||||
}
|
||||
|
||||
RemovableIterator end()
|
||||
{
|
||||
return RemovableIterator(NULL);
|
||||
}
|
||||
|
||||
RemovableIterator erase(RemovableIterator itr)
|
||||
{
|
||||
T removedVal = *itr;
|
||||
(*itr.mPrevNextPtr) = (*itr)->mNext;
|
||||
if (removedVal == mTail)
|
||||
{
|
||||
mTail = itr.mPrevVal;
|
||||
}
|
||||
|
||||
return itr;
|
||||
}
|
||||
};
|
||||
|
||||
NS_BF_END;
|
1076
BeefySysLib/util/SizedArray.h
Normal file
1076
BeefySysLib/util/SizedArray.h
Normal file
File diff suppressed because it is too large
Load diff
72
BeefySysLib/util/StackHelper.cpp
Normal file
72
BeefySysLib/util/StackHelper.cpp
Normal file
|
@ -0,0 +1,72 @@
|
|||
#include "StackHelper.h"
|
||||
#include "BeefPerf.h"
|
||||
#include "ThreadPool.h"
|
||||
|
||||
USING_NS_BF;
|
||||
|
||||
// Debug code takes more stack space - allow more there
|
||||
#ifdef _DEBUG
|
||||
#define MAX_STACK_SIZE 32*1024*1024
|
||||
#else
|
||||
#define MAX_STACK_SIZE 16*1024*1024
|
||||
#endif
|
||||
|
||||
//#define MAX_STACK_SIZE 1*1024*1024 // Testing
|
||||
|
||||
static ThreadPool gThreadPool(8, MAX_STACK_SIZE);
|
||||
|
||||
StackHelper::StackHelper()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool StackHelper::CanStackExpand(int wantBytes)
|
||||
{
|
||||
intptr stackBase = 0;
|
||||
int stackLimit = 0;
|
||||
BfpThreadResult threadResult;
|
||||
BfpThread_GetStackInfo(NULL, &stackBase, &stackLimit, &threadResult);
|
||||
if (threadResult != BfpThreadResult_Ok)
|
||||
return true;
|
||||
|
||||
//printf("StackInfo: %p %p %d\n", stackBase, &wantBytes, stackLimit);
|
||||
|
||||
intptr expectedStackPtr = (intptr)(void*)&wantBytes - wantBytes;
|
||||
int resultSize = (int)(stackBase - expectedStackPtr);
|
||||
|
||||
return resultSize <= stackLimit;
|
||||
}
|
||||
|
||||
struct StackHelperExecuter
|
||||
{
|
||||
const std::function<void()>* mFunc;
|
||||
SyncEvent mDoneEvent;
|
||||
|
||||
static void BFP_CALLTYPE Proc(void* threadParam)
|
||||
{
|
||||
auto _this = (StackHelperExecuter*)threadParam;
|
||||
(*_this->mFunc)();
|
||||
_this->mDoneEvent.Set();
|
||||
}
|
||||
|
||||
void Wait()
|
||||
{
|
||||
mDoneEvent.WaitFor();
|
||||
}
|
||||
};
|
||||
|
||||
bool StackHelper::Execute(const std::function<void()>& func)
|
||||
{
|
||||
// Only allow one threaded continuation
|
||||
if (gThreadPool.IsInJob())
|
||||
return false;
|
||||
|
||||
StackHelperExecuter executer;
|
||||
executer.mFunc = &func;
|
||||
|
||||
//printf("StackHelper executing!\n");
|
||||
|
||||
gThreadPool.AddJob(StackHelperExecuter::Proc, (void*)&executer, 1);
|
||||
executer.Wait();
|
||||
return true;
|
||||
}
|
18
BeefySysLib/util/StackHelper.h
Normal file
18
BeefySysLib/util/StackHelper.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
#pragma once
|
||||
|
||||
#include "../Common.h"
|
||||
|
||||
NS_BF_BEGIN
|
||||
|
||||
class StackHelper
|
||||
{
|
||||
public:
|
||||
StackHelper();
|
||||
|
||||
bool CanStackExpand(int wantBytes = 32*1024);
|
||||
bool Execute(const std::function<void()>& func); // Can fail if the job thread overflows stack - must check result
|
||||
};
|
||||
|
||||
|
||||
|
||||
NS_BF_END
|
795
BeefySysLib/util/String.cpp
Normal file
795
BeefySysLib/util/String.cpp
Normal file
|
@ -0,0 +1,795 @@
|
|||
#include "String.h"
|
||||
|
||||
USING_NS_BF;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
StringView::StringView(const StringImpl& str)
|
||||
{
|
||||
mPtr = str.GetPtr();
|
||||
mLength = str.mLength;
|
||||
}
|
||||
|
||||
StringView::StringView(const StringImpl& str, int offset)
|
||||
{
|
||||
mPtr = str.GetPtr() + offset;
|
||||
mLength = str.mLength - offset;
|
||||
}
|
||||
|
||||
StringView::StringView(const StringImpl& str, int offset, int length)
|
||||
{
|
||||
mPtr = str.GetPtr() + offset;
|
||||
mLength = length;
|
||||
}
|
||||
|
||||
StringView& StringView::operator=(const StringImpl& str)
|
||||
{
|
||||
mPtr = str.GetPtr();
|
||||
mLength = str.mLength;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool StringView::operator==(const StringImpl& strB) const
|
||||
{
|
||||
if (this->mLength != strB.mLength)
|
||||
return false;
|
||||
return strncmp(this->mPtr, strB.GetPtr(), this->mLength) == 0;
|
||||
}
|
||||
|
||||
bool StringView::operator!=(const StringImpl& strB) const
|
||||
{
|
||||
if (this->mLength != strB.mLength)
|
||||
return true;
|
||||
return strncmp(this->mPtr, strB.GetPtr(), this->mLength) != 0;
|
||||
}
|
||||
|
||||
intptr StringView::IndexOf(const StringView& subStr, bool ignoreCase) const
|
||||
{
|
||||
for (intptr ofs = 0; ofs <= mLength - subStr.mLength; ofs++)
|
||||
{
|
||||
if (String::Compare(*this, ofs, subStr, 0, subStr.mLength, ignoreCase) == 0)
|
||||
return ofs;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
intptr StringView::IndexOf(const StringView& subStr, int32 startIdx) const
|
||||
{
|
||||
return IndexOf(subStr, (int64)startIdx);
|
||||
}
|
||||
|
||||
intptr StringView::IndexOf(const StringView& subStr, int64 startIdx) const
|
||||
{
|
||||
const char* ptr = mPtr;
|
||||
const char* subStrPtr = subStr.mPtr;
|
||||
for (intptr ofs = (intptr)startIdx; ofs <= (intptr)(mLength - subStr.mLength); ofs++)
|
||||
{
|
||||
if (strncmp(ptr + ofs, subStrPtr, subStr.mLength) == 0)
|
||||
return ofs;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
intptr StringView::IndexOf(char c, intptr startIdx) const
|
||||
{
|
||||
auto ptr = mPtr;
|
||||
for (intptr i = startIdx; i < mLength; i++)
|
||||
if (ptr[i] == c)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
intptr StringView::LastIndexOf(char c) const
|
||||
{
|
||||
auto ptr = mPtr;
|
||||
for (intptr i = mLength - 1; i >= 0; i--)
|
||||
if (ptr[i] == c)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
intptr StringView::LastIndexOf(char c, intptr startCheck) const
|
||||
{
|
||||
auto ptr = mPtr;
|
||||
for (intptr i = startCheck; i >= 0; i--)
|
||||
if (ptr[i] == c)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
String StringView::ToString() const
|
||||
{
|
||||
return String(this->mPtr, this->mLength);
|
||||
}
|
||||
|
||||
void StringView::ToString(StringImpl& str) const
|
||||
{
|
||||
str.Append(mPtr, mLength);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
String Beefy::operator+(const StringImpl& lhs, const StringImpl& rhs)
|
||||
{
|
||||
String str;
|
||||
str.Reserve(lhs.mLength + rhs.mLength + 1);
|
||||
str.Append(lhs);
|
||||
str.Append(rhs);
|
||||
return str;
|
||||
}
|
||||
|
||||
String Beefy::operator+(const StringImpl& lhs, const StringView& rhs)
|
||||
{
|
||||
String str;
|
||||
str.Reserve(lhs.mLength + rhs.mLength + 1);
|
||||
str.Append(lhs);
|
||||
str.Append(rhs);
|
||||
return str;
|
||||
}
|
||||
|
||||
String Beefy::operator+(const StringImpl& lhs, char rhs)
|
||||
{
|
||||
String str;
|
||||
str.Reserve(lhs.mLength + 1 + 1);
|
||||
str.Append(lhs);
|
||||
str.Append(rhs);
|
||||
return str;
|
||||
}
|
||||
|
||||
bool Beefy::operator==(const char* lhs, const StringImpl& rhs)
|
||||
{
|
||||
return rhs == lhs;
|
||||
}
|
||||
|
||||
bool Beefy::operator!=(const char* lhs, const StringImpl& rhs)
|
||||
{
|
||||
return rhs != lhs;
|
||||
}
|
||||
|
||||
// bool Beefy::operator==(const StringView& lhs, const StringImpl& rhs)
|
||||
// {
|
||||
// if (lhs.mLength != rhs.mLength)
|
||||
// return false;
|
||||
// return strncmp(lhs.mPtr, rhs.GetPtr(), lhs.mLength) == 0;
|
||||
// }
|
||||
//
|
||||
// bool Beefy::operator!=(const StringView& lhs, const StringImpl& rhs)
|
||||
// {
|
||||
// if (lhs.mLength != rhs.mLength)
|
||||
// return true;
|
||||
// return strncmp(lhs.mPtr, rhs.GetPtr(), lhs.mLength) != 0;
|
||||
// }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
StringImpl::StringImpl(const StringView& str)
|
||||
{
|
||||
Init(str.mPtr, str.mLength);
|
||||
}
|
||||
|
||||
void StringImpl::Reference(const char* str)
|
||||
{
|
||||
Reference(str, strlen(str));
|
||||
}
|
||||
|
||||
void StringImpl::Reference(const char* str, intptr length)
|
||||
{
|
||||
if (IsDynAlloc())
|
||||
DeletePtr();
|
||||
mPtr = (char*)str;
|
||||
mLength = (int_strsize)length;
|
||||
mAllocSizeAndFlags = mLength | StrPtrFlag;
|
||||
}
|
||||
|
||||
void StringImpl::Reference(const StringView& strView)
|
||||
{
|
||||
Reference(strView.mPtr, strView.mLength);
|
||||
}
|
||||
|
||||
String StringImpl::CreateReference(const StringView& strView)
|
||||
{
|
||||
String str;
|
||||
str.Reference(strView);
|
||||
return str;
|
||||
}
|
||||
|
||||
intptr StringImpl::CalcNewSize(intptr minSize)
|
||||
{
|
||||
// Grow factor is 1.5
|
||||
intptr bumpSize = GetAllocSize();
|
||||
bumpSize += bumpSize / 2;
|
||||
return (bumpSize > minSize) ? bumpSize : minSize;
|
||||
}
|
||||
|
||||
void StringImpl::Realloc(intptr newSize, bool copyStr)
|
||||
{
|
||||
BF_ASSERT((uint32)newSize < 0x40000000);
|
||||
char* newPtr = AllocPtr(newSize);
|
||||
if (copyStr)
|
||||
memcpy(newPtr, GetPtr(), mLength + 1);
|
||||
if (IsDynAlloc())
|
||||
DeletePtr();
|
||||
mPtr = newPtr;
|
||||
mAllocSizeAndFlags = (uint32)newSize | DynAllocFlag | StrPtrFlag;
|
||||
}
|
||||
|
||||
void StringImpl::Realloc(char* newPtr, intptr newSize)
|
||||
{
|
||||
BF_ASSERT((uint32)newSize < 0x40000000);
|
||||
// We purposely don't copy the terminating NULL here, it's assumed the caller will do so
|
||||
memcpy(newPtr, GetPtr(), mLength);
|
||||
if (IsDynAlloc())
|
||||
DeletePtr();
|
||||
mPtr = newPtr;
|
||||
mAllocSizeAndFlags = (uint32)newSize | DynAllocFlag | StrPtrFlag;
|
||||
}
|
||||
|
||||
void StringImpl::Reserve(intptr newSize)
|
||||
{
|
||||
if (GetAllocSize() < newSize)
|
||||
Realloc(newSize, true);
|
||||
}
|
||||
|
||||
bool StringImpl::EqualsHelper(const char * a, const char * b, intptr length)
|
||||
{
|
||||
return strncmp(a, b, length) == 0;
|
||||
}
|
||||
|
||||
bool StringImpl::EqualsIgnoreCaseHelper(const char * a, const char * b, int length)
|
||||
{
|
||||
const char* curA = a;
|
||||
const char* curB = b;
|
||||
int curLength = length;
|
||||
|
||||
/*Contract.Requires(strA != null);
|
||||
Contract.Requires(strB != null);
|
||||
Contract.EndContractBlock();*/
|
||||
while (curLength != 0)
|
||||
{
|
||||
int_strsize char8A = (int_strsize)*curA;
|
||||
int_strsize char8B = (int_strsize)*curB;
|
||||
|
||||
//Contract.Assert((char8A | char8B) <= 0x7F, "strings have to be ASCII");
|
||||
|
||||
// uppercase both char8s - notice that we need just one compare per char8
|
||||
if ((uint32)(char8A - 'a') <= (uint32)('z' - 'a')) char8A -= 0x20;
|
||||
if ((uint32)(char8B - 'a') <= (uint32)('z' - 'a')) char8B -= 0x20;
|
||||
|
||||
//Return the (case-insensitive) difference between them.
|
||||
if (char8A != char8B)
|
||||
return false;
|
||||
|
||||
// Next char8
|
||||
curA++; curB++;
|
||||
curLength--;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int StringImpl::CompareOrdinalIgnoreCaseHelper(const StringImpl & strA, const StringImpl & strB)
|
||||
{
|
||||
/*Contract.Requires(strA != null);
|
||||
Contract.Requires(strB != null);
|
||||
Contract.EndContractBlock();*/
|
||||
int_strsize length = BF_MIN(strA.mLength, strB.mLength);
|
||||
|
||||
const char* a = strA.GetPtr();
|
||||
const char* b = strB.GetPtr();
|
||||
|
||||
while (length != 0)
|
||||
{
|
||||
int_strsize char8A = (int_strsize)*a;
|
||||
int_strsize char8B = (int_strsize)*b;
|
||||
|
||||
//Contract.Assert((char8A | char8B) <= 0x7F, "strings have to be ASCII");
|
||||
|
||||
// uppercase both char8s - notice that we need just one compare per char8
|
||||
if ((uint32)(char8A - 'a') <= (uint32)('z' - 'a')) char8A -= 0x20;
|
||||
if ((uint32)(char8B - 'a') <= (uint32)('z' - 'a')) char8B -= 0x20;
|
||||
|
||||
//Return the (case-insensitive) difference between them.
|
||||
if (char8A != char8B)
|
||||
return char8A - char8B;
|
||||
|
||||
// Next char8
|
||||
a++; b++;
|
||||
length--;
|
||||
}
|
||||
|
||||
return strA.mLength - strB.mLength;
|
||||
}
|
||||
|
||||
intptr StringImpl::CompareOrdinalIgnoreCaseHelper(const char * strA, intptr lengthA, const char * strB, intptr lengthB)
|
||||
{
|
||||
const char* a = strA;
|
||||
const char* b = strB;
|
||||
intptr length = BF_MIN(lengthA, lengthB);
|
||||
|
||||
while (length != 0)
|
||||
{
|
||||
int_strsize char8A = (int_strsize)*a;
|
||||
int_strsize char8B = (int_strsize)*b;
|
||||
|
||||
//Contract.Assert((char8A | char8B) <= 0x7F, "strings have to be ASCII");
|
||||
// uppercase both char8s - notice that we need just one compare per char8
|
||||
if ((uint32)(char8A - 'a') <= (uint32)('z' - 'a')) char8A -= 0x20;
|
||||
if ((uint32)(char8B - 'a') <= (uint32)('z' - 'a')) char8B -= 0x20;
|
||||
|
||||
//Return the (case-insensitive) difference between them.
|
||||
if (char8A != char8B)
|
||||
return char8A - char8B;
|
||||
|
||||
// Next char8
|
||||
a++; b++;
|
||||
length--;
|
||||
}
|
||||
|
||||
return lengthA - lengthB;
|
||||
}
|
||||
|
||||
intptr StringImpl::CompareOrdinalIgnoreCaseHelper(const StringImpl & strA, intptr indexA, intptr lengthA, const StringImpl & strB, intptr indexB, intptr lengthB)
|
||||
{
|
||||
return CompareOrdinalIgnoreCaseHelper(strA.GetPtr() + indexA, lengthA, strB.GetPtr() + indexB, lengthB);
|
||||
}
|
||||
|
||||
intptr StringImpl::CompareOrdinalHelper(const char * strA, intptr lengthA, const char * strB, intptr lengthB)
|
||||
{
|
||||
const char* a = strA;
|
||||
const char* b = strB;
|
||||
intptr length = BF_MIN(lengthA, lengthB);
|
||||
|
||||
while (length != 0)
|
||||
{
|
||||
int_strsize char8A = (int_strsize)*a;
|
||||
int_strsize char8B = (int_strsize)*b;
|
||||
|
||||
//Return the (case-insensitive) difference between them.
|
||||
if (char8A != char8B)
|
||||
return char8A - char8B;
|
||||
|
||||
// Next char8
|
||||
a++; b++;
|
||||
length--;
|
||||
}
|
||||
|
||||
return lengthA - lengthB;
|
||||
}
|
||||
|
||||
intptr StringImpl::CompareOrdinalHelper(const StringImpl & strA, intptr indexA, intptr lengthA, const StringImpl & strB, intptr indexB, intptr lengthB)
|
||||
{
|
||||
return CompareOrdinalHelper(strA.GetPtr() + indexA, lengthA, strB.GetPtr() + indexB, lengthB);
|
||||
}
|
||||
|
||||
void StringImpl::Append(const char* appendPtr, intptr length)
|
||||
{
|
||||
intptr newCurrentIndex = mLength + length;
|
||||
char* ptr;
|
||||
if (newCurrentIndex >= GetAllocSize())
|
||||
{
|
||||
// This handles appending to ourselves, we invalidate 'ptr' after calling Realloc
|
||||
intptr newSize = CalcNewSize(newCurrentIndex + 1);
|
||||
char* newPtr = AllocPtr(newSize);
|
||||
memcpy(newPtr + mLength, appendPtr, length);
|
||||
Realloc(newPtr, newSize);
|
||||
ptr = newPtr;
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr = GetMutablePtr();
|
||||
memcpy(ptr + mLength, appendPtr, length);
|
||||
}
|
||||
mLength = (int_strsize)newCurrentIndex;
|
||||
ptr[mLength] = 0;
|
||||
}
|
||||
|
||||
void StringImpl::Append(const StringView& value)
|
||||
{
|
||||
//Contract.Ensures(Contract.Result<String>() != null);
|
||||
Append(value.mPtr, value.mLength);
|
||||
}
|
||||
|
||||
void StringImpl::Append(const StringImpl& value)
|
||||
{
|
||||
//Contract.Ensures(Contract.Result<String>() != null);
|
||||
Append(value.GetPtr(), value.mLength);
|
||||
}
|
||||
|
||||
void StringImpl::Append(const StringImpl& str, const StringImpl& str2)
|
||||
{
|
||||
Append(str.GetPtr(), str.mLength);
|
||||
Append(str2.GetPtr(), str2.mLength);
|
||||
}
|
||||
|
||||
void StringImpl::Append(const StringImpl& str, const StringImpl& str2, const StringImpl& str3)
|
||||
{
|
||||
Append(str.GetPtr(), str.mLength);
|
||||
Append(str2.GetPtr(), str2.mLength);
|
||||
Append(str3.GetPtr(), str3.mLength);
|
||||
}
|
||||
|
||||
void StringImpl::Append(char c, int count)
|
||||
{
|
||||
if (count == 0)
|
||||
return;
|
||||
|
||||
if (mLength + count >= GetAllocSize())
|
||||
Realloc(CalcNewSize(mLength + count + 1));
|
||||
auto ptr = GetMutablePtr();
|
||||
for (int_strsize i = 0; i < count; i++)
|
||||
ptr[mLength++] = c;
|
||||
ptr[mLength] = 0;
|
||||
BF_ASSERT(mLength < GetAllocSize());
|
||||
}
|
||||
|
||||
String StringImpl::Substring(intptr startIdx) const
|
||||
{
|
||||
BF_ASSERT((uintptr)startIdx <= (uintptr)mLength);
|
||||
return String(GetPtr() + startIdx, mLength - startIdx);
|
||||
}
|
||||
|
||||
String StringImpl::Substring(intptr startIdx, intptr length) const
|
||||
{
|
||||
BF_ASSERT((startIdx >= 0) && (length >= 0) && (startIdx + length <= mLength));
|
||||
return String(GetPtr() + startIdx, length);
|
||||
}
|
||||
|
||||
void StringImpl::Remove(intptr startIdx, intptr length)
|
||||
{
|
||||
BF_ASSERT((startIdx >= 0) && (length >= 0) && (startIdx + length <= mLength));
|
||||
intptr moveCount = mLength - startIdx - length;
|
||||
auto ptr = GetMutablePtr();
|
||||
if (moveCount > 0)
|
||||
memmove(ptr + startIdx, ptr + startIdx + length, mLength - startIdx - length);
|
||||
mLength -= (int_strsize)length;
|
||||
ptr[mLength] = 0;
|
||||
}
|
||||
|
||||
void StringImpl::Remove(intptr char8Idx)
|
||||
{
|
||||
Remove(char8Idx, 1);
|
||||
}
|
||||
|
||||
void StringImpl::RemoveToEnd(intptr startIdx)
|
||||
{
|
||||
Remove(startIdx, mLength - startIdx);
|
||||
}
|
||||
|
||||
void StringImpl::Insert(intptr idx, const char* str, intptr length)
|
||||
{
|
||||
BF_ASSERT(idx >= 0);
|
||||
|
||||
int_strsize newLength = mLength + (int_strsize)length;
|
||||
if (newLength >= GetAllocSize())
|
||||
{
|
||||
intptr newSize = max((int_strsize)GetAllocSize() * 2, newLength + 1);
|
||||
Realloc(newSize);
|
||||
}
|
||||
|
||||
auto moveChars = mLength - idx;
|
||||
auto ptr = GetMutablePtr();
|
||||
if (moveChars > 0)
|
||||
memmove(ptr + idx + length, ptr + idx, moveChars);
|
||||
memcpy(ptr + idx, str, length);
|
||||
mLength = newLength;
|
||||
ptr[mLength] = 0;
|
||||
}
|
||||
|
||||
void StringImpl::Insert(intptr idx, const StringImpl& addString)
|
||||
{
|
||||
BF_ASSERT(idx >= 0);
|
||||
|
||||
int_strsize length = addString.mLength;
|
||||
int_strsize newLength = mLength + length;
|
||||
if (newLength >= GetAllocSize())
|
||||
{
|
||||
intptr newSize = max((int_strsize)GetAllocSize() * 2, newLength + 1);
|
||||
Realloc(newSize);
|
||||
}
|
||||
|
||||
auto moveChars = mLength - idx;
|
||||
auto ptr = GetMutablePtr();
|
||||
if (moveChars > 0)
|
||||
memmove(ptr + idx + length, ptr + idx, moveChars);
|
||||
memcpy(ptr + idx, addString.GetPtr(), length);
|
||||
mLength = newLength;
|
||||
ptr[mLength] = 0;
|
||||
}
|
||||
|
||||
void StringImpl::Insert(intptr idx, char c)
|
||||
{
|
||||
BF_ASSERT(idx >= 0);
|
||||
|
||||
int_strsize newLength = mLength + 1;
|
||||
if (newLength >= GetAllocSize())
|
||||
{
|
||||
int newSize = max((int_strsize)GetAllocSize() * 2, newLength + 1);
|
||||
Realloc(newSize);
|
||||
}
|
||||
|
||||
auto moveChars = mLength - idx;
|
||||
auto ptr = GetMutablePtr();
|
||||
if (moveChars > 0)
|
||||
memmove(ptr + idx + 1, ptr + idx, moveChars);
|
||||
ptr[idx] = c;
|
||||
mLength = newLength;
|
||||
ptr[mLength] = 0;
|
||||
}
|
||||
|
||||
intptr StringImpl::Compare(const StringImpl & strA, intptr indexA, const StringImpl & strB, intptr indexB, intptr length, bool ignoreCase)
|
||||
{
|
||||
intptr lengthA = length;
|
||||
intptr lengthB = length;
|
||||
|
||||
if (strA.GetLength() - indexA < lengthA)
|
||||
{
|
||||
lengthA = (strA.GetLength() - indexA);
|
||||
}
|
||||
|
||||
if (strB.GetLength() - indexB < lengthB)
|
||||
{
|
||||
lengthB = (strB.GetLength() - indexB);
|
||||
}
|
||||
|
||||
if (ignoreCase)
|
||||
return CompareOrdinalIgnoreCaseHelper(strA, indexA, lengthA, strB, indexB, lengthB);
|
||||
return CompareOrdinalHelper(strA, indexA, lengthA, strB, indexB, lengthB);
|
||||
}
|
||||
|
||||
void StringImpl::ReplaceLargerHelper(const StringImpl & find, const StringImpl & replace)
|
||||
{
|
||||
Array<int> replaceEntries;
|
||||
|
||||
int_strsize moveOffset = replace.mLength - find.mLength;
|
||||
|
||||
for (int startIdx = 0; startIdx < mLength - find.mLength; startIdx++)
|
||||
{
|
||||
if (EqualsHelper(GetPtr() + startIdx, find.GetPtr(), find.mLength))
|
||||
{
|
||||
replaceEntries.Add(startIdx);
|
||||
startIdx += find.mLength - 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (replaceEntries.size() == 0)
|
||||
return;
|
||||
|
||||
intptr destLength = mLength + moveOffset * replaceEntries.size();
|
||||
intptr needSize = destLength + 1;
|
||||
if (needSize > GetAllocSize())
|
||||
Realloc((int_strsize)needSize);
|
||||
|
||||
auto replacePtr = replace.GetPtr();
|
||||
auto ptr = GetMutablePtr();
|
||||
|
||||
intptr lastDestStartIdx = destLength;
|
||||
for (intptr moveIdx = replaceEntries.size() - 1; moveIdx >= 0; moveIdx--)
|
||||
{
|
||||
intptr srcStartIdx = replaceEntries[moveIdx];
|
||||
intptr srcEndIdx = srcStartIdx + find.mLength;
|
||||
intptr destStartIdx = srcStartIdx + moveIdx * moveOffset;
|
||||
intptr destEndIdx = destStartIdx + replace.mLength;
|
||||
|
||||
for (intptr i = lastDestStartIdx - destEndIdx - 1; i >= 0; i--)
|
||||
ptr[destEndIdx + i] = ptr[srcEndIdx + i];
|
||||
|
||||
for (intptr i = 0; i < replace.mLength; i++)
|
||||
ptr[destStartIdx + i] = replacePtr[i];
|
||||
|
||||
lastDestStartIdx = destStartIdx;
|
||||
}
|
||||
|
||||
ptr[destLength] = 0;
|
||||
mLength = (int_strsize)destLength;
|
||||
}
|
||||
|
||||
void StringImpl::Replace(const StringImpl & find, const StringImpl & replace)
|
||||
{
|
||||
if (replace.mLength > find.mLength)
|
||||
{
|
||||
ReplaceLargerHelper(find, replace);
|
||||
return;
|
||||
}
|
||||
|
||||
auto ptr = GetMutablePtr();
|
||||
auto findPtr = find.GetPtr();
|
||||
auto replacePtr = replace.GetPtr();
|
||||
|
||||
int_strsize inIdx = 0;
|
||||
int_strsize outIdx = 0;
|
||||
|
||||
while (inIdx < mLength - find.mLength)
|
||||
{
|
||||
if (EqualsHelper(ptr + inIdx, findPtr, find.mLength))
|
||||
{
|
||||
for (int_strsize i = 0; i < replace.mLength; i++)
|
||||
ptr[outIdx++] = replacePtr[i];
|
||||
|
||||
inIdx += find.mLength;
|
||||
}
|
||||
else if (inIdx == outIdx)
|
||||
{
|
||||
++inIdx;
|
||||
++outIdx;
|
||||
}
|
||||
else // We need to physically move char8acters once we've found an equal span
|
||||
{
|
||||
ptr[outIdx++] = ptr[inIdx++];
|
||||
}
|
||||
}
|
||||
|
||||
while (inIdx < mLength)
|
||||
{
|
||||
if (inIdx == outIdx)
|
||||
{
|
||||
++inIdx;
|
||||
++outIdx;
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr[outIdx++] = ptr[inIdx++];
|
||||
}
|
||||
}
|
||||
|
||||
ptr[outIdx] = 0;
|
||||
mLength = outIdx;
|
||||
}
|
||||
|
||||
void StringImpl::TrimEnd()
|
||||
{
|
||||
auto ptr = GetPtr();
|
||||
for (intptr i = mLength - 1; i >= 0; i--)
|
||||
{
|
||||
char c = ptr[i];
|
||||
if (!iswspace(c))
|
||||
{
|
||||
if (i < mLength - 1)
|
||||
RemoveToEnd(i + 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
Clear();
|
||||
}
|
||||
|
||||
void StringImpl::TrimStart()
|
||||
{
|
||||
auto ptr = GetPtr();
|
||||
for (intptr i = 0; i < mLength; i++)
|
||||
{
|
||||
char c = ptr[i];
|
||||
if (!iswspace(c))
|
||||
{
|
||||
if (i > 0)
|
||||
Remove(0, i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
Clear();
|
||||
}
|
||||
|
||||
void StringImpl::Trim()
|
||||
{
|
||||
TrimStart();
|
||||
TrimEnd();
|
||||
}
|
||||
|
||||
bool StringImpl::IsWhitespace() const
|
||||
{
|
||||
auto ptr = GetPtr();
|
||||
for (intptr i = 0; i < mLength; i++)
|
||||
if (!iswspace(ptr[i]))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StringImpl::HasMultibyteChars()
|
||||
{
|
||||
auto ptr = GetPtr();
|
||||
for (int i = 0; i < (int)mLength; i++)
|
||||
if (ptr[i] >= 0x80)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
intptr StringImpl::IndexOf(const StringImpl& subStr, bool ignoreCase) const
|
||||
{
|
||||
for (intptr ofs = 0; ofs <= mLength - subStr.mLength; ofs++)
|
||||
{
|
||||
if (Compare(*this, ofs, subStr, 0, subStr.mLength, ignoreCase) == 0)
|
||||
return ofs;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
intptr StringImpl::IndexOf(const StringImpl& subStr, int32 startIdx) const
|
||||
{
|
||||
return IndexOf(subStr, (int64)startIdx);
|
||||
}
|
||||
|
||||
intptr StringImpl::IndexOf(const StringImpl& subStr, int64 startIdx) const
|
||||
{
|
||||
const char* ptr = GetPtr();
|
||||
const char* subStrPtr = subStr.GetPtr();
|
||||
for (intptr ofs = (intptr)startIdx; ofs <= mLength - subStr.mLength; ofs++)
|
||||
{
|
||||
if (strncmp(ptr + ofs, subStrPtr, subStr.mLength) == 0)
|
||||
return ofs;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
intptr StringImpl::IndexOf(char c, intptr startIdx) const
|
||||
{
|
||||
auto ptr = GetPtr();
|
||||
for (intptr i = startIdx; i < mLength; i++)
|
||||
if (ptr[i] == c)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
intptr StringImpl::LastIndexOf(char c) const
|
||||
{
|
||||
auto ptr = GetPtr();
|
||||
for (intptr i = mLength - 1; i >= 0; i--)
|
||||
if (ptr[i] == c)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
intptr StringImpl::LastIndexOf(char c, intptr startCheck) const
|
||||
{
|
||||
auto ptr = GetPtr();
|
||||
for (intptr i = startCheck; i >= 0; i--)
|
||||
if (ptr[i] == c)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
UTF16String::UTF16String()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
UTF16String::UTF16String(const wchar_t* str)
|
||||
{
|
||||
Set(str);
|
||||
}
|
||||
|
||||
UTF16String::UTF16String(const wchar_t* str, int len)
|
||||
{
|
||||
Set(str, len);
|
||||
}
|
||||
|
||||
void UTF16String::Set(const wchar_t* str, int len)
|
||||
{
|
||||
Clear();
|
||||
ResizeRaw(len + 1);
|
||||
memcpy(mVals, str, len * 2);
|
||||
mVals[len] = 0;
|
||||
}
|
||||
|
||||
void UTF16String::Set(const wchar_t* str)
|
||||
{
|
||||
return Set(str, (int)wcslen(str));
|
||||
}
|
||||
|
||||
const wchar_t* UTF16String::c_str() const
|
||||
{
|
||||
if (mVals == NULL)
|
||||
return L"";
|
||||
mVals[mSize - 1] = 0; // Re-terminate in case we modified the string
|
||||
return (wchar_t*)mVals;
|
||||
}
|
||||
|
||||
size_t UTF16String::length() const
|
||||
{
|
||||
if (mSize == 0)
|
||||
return 0;
|
||||
return mSize - 1;
|
||||
}
|
966
BeefySysLib/util/String.h
Normal file
966
BeefySysLib/util/String.h
Normal file
|
@ -0,0 +1,966 @@
|
|||
#pragma once
|
||||
|
||||
#include "BFPlatform.h"
|
||||
#include "Array.h"
|
||||
|
||||
NS_BF_BEGIN;
|
||||
|
||||
template <const int TBufSize>
|
||||
class StringT;
|
||||
|
||||
typedef StringT<16> String;
|
||||
|
||||
class StringView
|
||||
{
|
||||
public:
|
||||
const char* mPtr;
|
||||
int mLength;
|
||||
|
||||
public:
|
||||
StringView()
|
||||
{
|
||||
this->mPtr = NULL;
|
||||
this->mLength = 0;
|
||||
}
|
||||
|
||||
StringView(const StringView& sv)
|
||||
{
|
||||
this->mPtr = sv.mPtr;
|
||||
this->mLength = sv.mLength;
|
||||
}
|
||||
|
||||
StringView(const StringImpl& str);
|
||||
StringView(const StringImpl& str, int offset);
|
||||
StringView(const StringImpl& str, int offset, int length);
|
||||
|
||||
StringView(const char* ptr, int length)
|
||||
{
|
||||
this->mPtr = ptr;
|
||||
this->mLength = length;
|
||||
}
|
||||
|
||||
const char& operator[](intptr idx) const
|
||||
{
|
||||
BF_ASSERT((uintptr)idx < (uintptr)this->mLength);
|
||||
return this->mPtr[idx];
|
||||
}
|
||||
|
||||
StringView& operator=(const StringImpl& str);
|
||||
|
||||
StringView& operator=(const StringView& str)
|
||||
{
|
||||
this->mPtr = str.mPtr;
|
||||
this->mLength = str.mLength;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const StringImpl& strB) const;
|
||||
|
||||
bool operator!=(const StringImpl& strB) const;
|
||||
|
||||
bool operator==(const StringView& strB) const
|
||||
{
|
||||
if (this->mLength != strB.mLength)
|
||||
return false;
|
||||
return strncmp(this->mPtr, strB.mPtr, this->mLength) == 0;
|
||||
}
|
||||
|
||||
bool operator!=(const StringView& strB) const
|
||||
{
|
||||
if (this->mLength != strB.mLength)
|
||||
return true;
|
||||
return strncmp(this->mPtr, strB.mPtr, this->mLength) != 0;
|
||||
}
|
||||
|
||||
bool operator==(const char* strB) const
|
||||
{
|
||||
if (strncmp(mPtr, strB, mLength) != 0)
|
||||
return false;
|
||||
return strB[mLength] == 0;
|
||||
}
|
||||
|
||||
bool operator!=(const char* strB) const
|
||||
{
|
||||
if (strncmp(mPtr, strB, mLength) != 0)
|
||||
return true;
|
||||
return strB[mLength] != 0;
|
||||
}
|
||||
|
||||
intptr IndexOf(const StringView& subStr, bool ignoreCase = false) const;
|
||||
intptr IndexOf(const StringView& subStr, int32 startIdx) const;
|
||||
intptr IndexOf(const StringView& subStr, int64 startIdx) const;
|
||||
intptr IndexOf(char c, intptr startIdx = 0) const;
|
||||
intptr LastIndexOf(char c) const;
|
||||
intptr LastIndexOf(char c, intptr startCheck) const;
|
||||
String ToString() const;
|
||||
void ToString(StringImpl& str) const;
|
||||
|
||||
bool Contains(char c) const
|
||||
{
|
||||
return IndexOf(c) != -1;
|
||||
}
|
||||
|
||||
bool Contains(const StringView& str) const
|
||||
{
|
||||
return IndexOf(str) != -1;
|
||||
}
|
||||
};
|
||||
|
||||
class StringImpl
|
||||
{
|
||||
public:
|
||||
enum CompareKind
|
||||
{
|
||||
CompareKind_CurrentCulture = 0,
|
||||
CompareKind_CurrentCultureIgnoreCase = 1,
|
||||
CompareKind_InvariantCulture = 2,
|
||||
CompareKind_InvariantCultureIgnoreCase = 3,
|
||||
CompareKind_Ordinal = 4,
|
||||
CompareKind_OrdinalIgnoreCase = 5,
|
||||
};
|
||||
|
||||
public:
|
||||
typedef int int_strsize;
|
||||
|
||||
const static uint32 SizeFlags = 0x3FFFFFFF;
|
||||
const static uint32 DynAllocFlag = 0x80000000;
|
||||
const static uint32 StrPtrFlag = 0x40000000;
|
||||
const static uint32 AttrFlags = 0xC0000000;
|
||||
|
||||
int mLength;
|
||||
uint32 mAllocSizeAndFlags;
|
||||
char* mPtr;
|
||||
|
||||
public:
|
||||
struct iterator
|
||||
{
|
||||
public:
|
||||
typedef std::random_access_iterator_tag iterator_category;
|
||||
typedef char value_type;
|
||||
typedef intptr difference_type;
|
||||
|
||||
typedef char* pointer;
|
||||
typedef char& reference;
|
||||
|
||||
public:
|
||||
char* mPtr;
|
||||
|
||||
public:
|
||||
iterator(char* ptr)
|
||||
{
|
||||
mPtr = ptr;
|
||||
}
|
||||
|
||||
iterator& operator++()
|
||||
{
|
||||
mPtr++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator operator++(int)
|
||||
{
|
||||
auto prevVal = *this;
|
||||
mPtr++;
|
||||
return prevVal;
|
||||
}
|
||||
|
||||
bool operator!=(const iterator& itr) const
|
||||
{
|
||||
return itr.mPtr != mPtr;
|
||||
}
|
||||
|
||||
bool operator==(const iterator& itr) const
|
||||
{
|
||||
return itr.mPtr == mPtr;
|
||||
}
|
||||
|
||||
intptr operator-(const iterator& itr) const
|
||||
{
|
||||
return mPtr - itr.mPtr;
|
||||
}
|
||||
|
||||
iterator operator+(intptr offset) const
|
||||
{
|
||||
iterator itr(mPtr + offset);
|
||||
return itr;
|
||||
}
|
||||
|
||||
char& operator*()
|
||||
{
|
||||
return *mPtr;
|
||||
}
|
||||
|
||||
char* operator->()
|
||||
{
|
||||
return mPtr;
|
||||
}
|
||||
|
||||
bool operator<(const iterator& val2)
|
||||
{
|
||||
return mPtr < val2.mPtr;
|
||||
}
|
||||
};
|
||||
|
||||
struct const_iterator
|
||||
{
|
||||
public:
|
||||
typedef std::random_access_iterator_tag iterator_category;
|
||||
typedef char value_type;
|
||||
typedef intptr difference_type;
|
||||
|
||||
typedef const char* pointer;
|
||||
typedef const char& reference;
|
||||
|
||||
public:
|
||||
const char* mPtr;
|
||||
|
||||
public:
|
||||
const_iterator(const char* ptr)
|
||||
{
|
||||
mPtr = ptr;
|
||||
}
|
||||
|
||||
const_iterator& operator++()
|
||||
{
|
||||
mPtr++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator operator++(int)
|
||||
{
|
||||
auto prevVal = *this;
|
||||
mPtr++;
|
||||
return prevVal;
|
||||
}
|
||||
|
||||
bool operator!=(const const_iterator& itr) const
|
||||
{
|
||||
return itr.mPtr != mPtr;
|
||||
}
|
||||
|
||||
bool operator==(const const_iterator& itr) const
|
||||
{
|
||||
return itr.mPtr == mPtr;
|
||||
}
|
||||
|
||||
intptr operator-(const iterator& itr) const
|
||||
{
|
||||
return mPtr - itr.mPtr;
|
||||
}
|
||||
|
||||
const_iterator operator+(intptr offset) const
|
||||
{
|
||||
const_iterator itr(mPtr + offset);
|
||||
return itr;
|
||||
}
|
||||
|
||||
const char& operator*()
|
||||
{
|
||||
return *mPtr;
|
||||
}
|
||||
|
||||
const char* operator->()
|
||||
{
|
||||
return mPtr;
|
||||
}
|
||||
|
||||
bool operator<(const const_iterator& val2)
|
||||
{
|
||||
return mPtr < val2.mPtr;
|
||||
}
|
||||
};
|
||||
|
||||
protected:
|
||||
void EnsureMutable()
|
||||
{
|
||||
if ((mAllocSizeAndFlags & AttrFlags) == StrPtrFlag)
|
||||
{
|
||||
// It's a reference
|
||||
char* newPtr = AllocPtr(this->mLength);
|
||||
memcpy(newPtr, this->mPtr, this->mLength + 1);
|
||||
this->mPtr = newPtr;
|
||||
mAllocSizeAndFlags = this->mLength | DynAllocFlag | StrPtrFlag;
|
||||
}
|
||||
}
|
||||
|
||||
char* AllocPtr(intptr size)
|
||||
{
|
||||
return (char*)malloc(size);
|
||||
}
|
||||
|
||||
void DeletePtr()
|
||||
{
|
||||
free(this->mPtr);
|
||||
}
|
||||
|
||||
intptr CalcNewSize(intptr minSize);
|
||||
void Realloc(intptr newSize, bool copyStr = true);
|
||||
void Realloc(char* newPtr, intptr newSize);
|
||||
static bool EqualsHelper(const char* a, const char* b, intptr length);
|
||||
static bool EqualsIgnoreCaseHelper(const char* a, const char* b, int length);
|
||||
static int CompareOrdinalIgnoreCaseHelper(const StringImpl& strA, const StringImpl& strB);
|
||||
static intptr CompareOrdinalIgnoreCaseHelper(const char* strA, intptr lengthA, const char* strB, intptr lengthB);
|
||||
static intptr CompareOrdinalIgnoreCaseHelper(const StringImpl& strA, intptr indexA, intptr lengthA, const StringImpl& strB, intptr indexB, intptr lengthB);
|
||||
static intptr CompareOrdinalHelper(const char* strA, intptr lengthA, const char* strB, intptr lengthB);
|
||||
static intptr CompareOrdinalHelper(const StringImpl& strA, intptr indexA, intptr lengthA, const StringImpl& strB, intptr indexB, intptr lengthB);
|
||||
|
||||
void Init(const char* charPtr, int_strsize count)
|
||||
{
|
||||
int_strsize internalSize = (int_strsize)(sizeof(StringImpl) - offsetof(StringImpl, mPtr));
|
||||
int_strsize allocSize = count + 1;
|
||||
|
||||
if (allocSize <= internalSize)
|
||||
{
|
||||
// Fits
|
||||
auto ptr = (char*)&this->mPtr;
|
||||
memcpy(ptr, charPtr, count);
|
||||
ptr[count] = 0;
|
||||
mAllocSizeAndFlags = internalSize;
|
||||
this->mLength = count;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Too big, must alloc
|
||||
auto ptr = AllocPtr(allocSize);
|
||||
memcpy(ptr, charPtr, count);
|
||||
ptr[count] = 0;
|
||||
this->mPtr = ptr;
|
||||
mAllocSizeAndFlags = allocSize | DynAllocFlag | StrPtrFlag;
|
||||
this->mLength = count;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
// Use "String" or "StringT<>"
|
||||
StringImpl()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
StringImpl(const char* charPtr)
|
||||
{
|
||||
// This is just a ref - called when we pass a literal to a method (for example)
|
||||
this->mPtr = (char*)charPtr;
|
||||
this->mLength = (int_strsize)strlen(charPtr);
|
||||
mAllocSizeAndFlags = this->mLength | StrPtrFlag;
|
||||
}
|
||||
|
||||
StringImpl(const char* charPtr, const char* charPtrEnd)
|
||||
{
|
||||
Init(charPtr, (int_strsize)(charPtrEnd - charPtr));
|
||||
}
|
||||
|
||||
StringImpl(const char* charPtr, intptr length)
|
||||
{
|
||||
Init(charPtr, (int_strsize)length);
|
||||
}
|
||||
|
||||
StringImpl(const StringImpl& str)
|
||||
{
|
||||
Init(str.GetPtr(), str.mLength);
|
||||
}
|
||||
|
||||
StringImpl(const StringView& str);
|
||||
|
||||
StringImpl(const StringImpl& str, intptr offset)
|
||||
{
|
||||
Init(str.GetPtr() + offset, (int_strsize)(str.mLength - offset));
|
||||
}
|
||||
|
||||
StringImpl(const StringImpl& str, intptr offset, intptr length)
|
||||
{
|
||||
Init(str.GetPtr() + offset, (int_strsize)length);
|
||||
}
|
||||
|
||||
StringImpl(const std::string& str)
|
||||
{
|
||||
// This is just a ref - called when we pass a std::string to a method (for example)
|
||||
this->mPtr = (char*)str.c_str();
|
||||
this->mLength = (int_strsize)str.length();
|
||||
mAllocSizeAndFlags = this->mLength | StrPtrFlag;
|
||||
}
|
||||
|
||||
StringImpl(StringImpl&& str)
|
||||
{
|
||||
if ((str.mAllocSizeAndFlags & StrPtrFlag) != 0)
|
||||
{
|
||||
this->mPtr = str.mPtr;
|
||||
mAllocSizeAndFlags = str.mAllocSizeAndFlags;
|
||||
this->mLength = str.mLength;
|
||||
|
||||
str.mAllocSizeAndFlags = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If there's an internal buffer then we have to copy
|
||||
int_strsize count = (int_strsize)str.mLength;
|
||||
int_strsize allocSize = count + 1;
|
||||
auto ptr = AllocPtr(allocSize);
|
||||
memcpy(ptr, str.GetPtr(), count + 1);
|
||||
ptr[count] = 0;
|
||||
this->mPtr = ptr;
|
||||
mAllocSizeAndFlags = allocSize | DynAllocFlag | StrPtrFlag;
|
||||
this->mLength = count;
|
||||
}
|
||||
}
|
||||
|
||||
~StringImpl()
|
||||
{
|
||||
if (IsDynAlloc())
|
||||
DeletePtr();
|
||||
}
|
||||
|
||||
char& operator[](intptr idx)
|
||||
{
|
||||
BF_ASSERT((uintptr)idx < (uintptr)this->mLength);
|
||||
switch (mAllocSizeAndFlags & AttrFlags)
|
||||
{
|
||||
case 0:
|
||||
return ((char*)&this->mPtr)[idx];
|
||||
case StrPtrFlag:
|
||||
EnsureMutable();
|
||||
default:
|
||||
return this->mPtr[idx];
|
||||
}
|
||||
}
|
||||
|
||||
const char& operator[](intptr idx) const
|
||||
{
|
||||
BF_ASSERT((uintptr)idx < (uintptr)this->mLength);
|
||||
return ((mAllocSizeAndFlags & StrPtrFlag) != 0) ? this->mPtr[idx] : ((char*)&this->mPtr)[idx];
|
||||
}
|
||||
|
||||
bool operator==(const StringImpl& strB) const
|
||||
{
|
||||
if (this->mLength != strB.mLength)
|
||||
return false;
|
||||
return strncmp(GetPtr(), strB.GetPtr(), this->mLength) == 0;
|
||||
}
|
||||
|
||||
bool operator!=(const StringImpl& strB) const
|
||||
{
|
||||
if (this->mLength != strB.mLength)
|
||||
return true;
|
||||
return strncmp(GetPtr(), strB.GetPtr(), this->mLength) != 0;
|
||||
}
|
||||
|
||||
bool operator==(const StringView& strB) const
|
||||
{
|
||||
if (this->mLength != strB.mLength)
|
||||
return false;
|
||||
return strncmp(GetPtr(), strB.mPtr, this->mLength) == 0;
|
||||
}
|
||||
|
||||
bool operator!=(const StringView& strB) const
|
||||
{
|
||||
if (this->mLength != strB.mLength)
|
||||
return true;
|
||||
return strncmp(GetPtr(), strB.mPtr, this->mLength) != 0;
|
||||
}
|
||||
|
||||
bool operator==(const char* strB) const
|
||||
{
|
||||
return strcmp(GetPtr(), strB) == 0;
|
||||
}
|
||||
|
||||
bool operator!=(const char* strB) const
|
||||
{
|
||||
return strcmp(GetPtr(), strB) != 0;
|
||||
}
|
||||
|
||||
bool operator<(const StringImpl& strB) const
|
||||
{
|
||||
return strcmp(GetPtr(), strB.GetPtr()) < 0;
|
||||
}
|
||||
|
||||
bool operator>(const StringImpl& strB) const
|
||||
{
|
||||
return strcmp(GetPtr(), strB.GetPtr()) > 0;
|
||||
}
|
||||
|
||||
StringImpl& operator=(const StringImpl& str)
|
||||
{
|
||||
if (&str != this)
|
||||
{
|
||||
this->mLength = 0;
|
||||
Append(str.GetPtr(), str.mLength);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
StringImpl& operator=(StringImpl&& str)
|
||||
{
|
||||
if ((str.mAllocSizeAndFlags & StrPtrFlag) != 0)
|
||||
{
|
||||
if (IsDynAlloc())
|
||||
DeletePtr();
|
||||
|
||||
this->mPtr = str.mPtr;
|
||||
mAllocSizeAndFlags = str.mAllocSizeAndFlags;
|
||||
this->mLength = str.mLength;
|
||||
|
||||
str.mAllocSizeAndFlags = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If there's an internal buffer then we have to copy
|
||||
int_strsize count = (int_strsize)str.mLength;
|
||||
int_strsize allocSize = count + 1;
|
||||
if (allocSize > GetAllocSize())
|
||||
Realloc(allocSize, false);
|
||||
auto ptr = GetMutablePtr();
|
||||
memcpy(ptr, str.GetPtr(), count + 1);
|
||||
ptr[count] = 0;
|
||||
this->mLength = count;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
StringImpl& operator=(const std::string& str)
|
||||
{
|
||||
this->mLength = 0;
|
||||
Append(str.c_str(), (intptr)str.length());
|
||||
return *this;
|
||||
}
|
||||
|
||||
StringImpl& operator=(const char* str)
|
||||
{
|
||||
this->mLength = 0;
|
||||
Append(str, strlen(str));
|
||||
return *this;
|
||||
}
|
||||
|
||||
StringImpl& operator=(char c)
|
||||
{
|
||||
this->mLength = 0;
|
||||
Append(c);
|
||||
return *this;
|
||||
}
|
||||
|
||||
StringImpl& operator+=(const StringImpl& str)
|
||||
{
|
||||
Append(str.GetPtr(), str.mLength);
|
||||
return *this;
|
||||
}
|
||||
|
||||
StringImpl& operator+=(const StringView& str)
|
||||
{
|
||||
Append(str.mPtr, str.mLength);
|
||||
return *this;
|
||||
}
|
||||
|
||||
StringImpl& operator+=(const char* str)
|
||||
{
|
||||
Append(str, strlen(str));
|
||||
return *this;
|
||||
}
|
||||
|
||||
StringImpl& operator+=(char c)
|
||||
{
|
||||
Append(c);
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator std::string() const
|
||||
{
|
||||
return std::string(GetPtr(), GetPtr() + this->mLength);
|
||||
}
|
||||
|
||||
intptr length() const
|
||||
{
|
||||
return this->mLength;
|
||||
}
|
||||
|
||||
int GetLength() const
|
||||
{
|
||||
return this->mLength;
|
||||
}
|
||||
|
||||
intptr GetAllocSize() const
|
||||
{
|
||||
return (int_strsize)(mAllocSizeAndFlags & SizeFlags);
|
||||
}
|
||||
|
||||
bool IsDynAlloc() const
|
||||
{
|
||||
return (mAllocSizeAndFlags & DynAllocFlag) != 0;
|
||||
}
|
||||
|
||||
const char* GetPtr() const
|
||||
{
|
||||
return ((mAllocSizeAndFlags & StrPtrFlag) != 0) ? this->mPtr : (char*)&this->mPtr;
|
||||
}
|
||||
|
||||
const char* c_str() const
|
||||
{
|
||||
return ((mAllocSizeAndFlags & StrPtrFlag) != 0) ? this->mPtr : (char*)&this->mPtr;
|
||||
}
|
||||
|
||||
char* GetMutablePtr()
|
||||
{
|
||||
switch (mAllocSizeAndFlags & AttrFlags)
|
||||
{
|
||||
case 0:
|
||||
return ((char*)&this->mPtr);
|
||||
case StrPtrFlag:
|
||||
EnsureMutable();
|
||||
default:
|
||||
return this->mPtr;
|
||||
}
|
||||
}
|
||||
|
||||
void Reference(const char* str);
|
||||
void Reference(const char* str, intptr length);
|
||||
void Reference(const StringView& strView);
|
||||
static String CreateReference(const StringView& strView);
|
||||
void Reserve(intptr newSize);
|
||||
|
||||
void Append(const char* appendPtr, intptr length);
|
||||
void Append(const StringView& str);
|
||||
void Append(const StringImpl& str);
|
||||
void Append(const StringImpl& str, const StringImpl& str2);
|
||||
void Append(const StringImpl& str, const StringImpl& str2, const StringImpl& str3);
|
||||
void Append(char c, int count = 1);
|
||||
|
||||
void Clear()
|
||||
{
|
||||
this->mLength = 0;
|
||||
GetMutablePtr()[0] = 0;
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
this->mLength = 0;
|
||||
GetMutablePtr()[0] = 0;
|
||||
}
|
||||
|
||||
String Substring(intptr startIdx) const;
|
||||
String Substring(intptr startIdx, intptr length) const;
|
||||
|
||||
void Remove(intptr startIdx, intptr length);
|
||||
void Remove(intptr char8Idx);
|
||||
void RemoveToEnd(intptr startIdx);
|
||||
void Insert(intptr idx, const StringImpl& addString);
|
||||
void Insert(intptr idx, const char* str, intptr len);
|
||||
void Insert(intptr idx, char c);
|
||||
|
||||
intptr CompareTo(const StringImpl& strB, bool ignoreCase = false) const
|
||||
{
|
||||
return Compare(*this, 0, strB, 0, strB.GetLength(), ignoreCase);
|
||||
}
|
||||
|
||||
static intptr Compare(const StringImpl& strA, const StringImpl& strB, bool ignoreCase)
|
||||
{
|
||||
return Compare(strA, 0, strB, 0, strB.GetLength(), ignoreCase);
|
||||
}
|
||||
|
||||
static intptr Compare(const StringImpl& strA, intptr indexA, const StringImpl& strB, intptr indexB, intptr length, bool ignoreCase);
|
||||
|
||||
bool Equals(const StringImpl& b, CompareKind comparisonType = CompareKind_Ordinal) const
|
||||
{
|
||||
return Equals(*this, b, comparisonType);
|
||||
}
|
||||
|
||||
static bool Equals(const StringImpl& a, const StringImpl& b, CompareKind comparisonType = CompareKind_Ordinal)
|
||||
{
|
||||
if (a.mLength != b.mLength)
|
||||
return false;
|
||||
if (comparisonType == CompareKind_OrdinalIgnoreCase)
|
||||
return EqualsIgnoreCaseHelper(a.GetPtr(), b.GetPtr(), a.mLength);
|
||||
return EqualsHelper(a.GetPtr(), b.GetPtr(), a.mLength);
|
||||
}
|
||||
|
||||
bool StartsWith(const StringImpl& b, CompareKind comparisonType = CompareKind_Ordinal) const
|
||||
{
|
||||
if (this->mLength < b.mLength)
|
||||
return false;
|
||||
if (comparisonType == CompareKind_OrdinalIgnoreCase)
|
||||
return EqualsIgnoreCaseHelper(this->GetPtr(), b.GetPtr(), b.mLength);
|
||||
return EqualsHelper(this->GetPtr(), b.GetPtr(), b.mLength);
|
||||
}
|
||||
|
||||
bool EndsWith(const StringImpl& b, CompareKind comparisonType = CompareKind_Ordinal) const
|
||||
{
|
||||
if (this->mLength < b.mLength)
|
||||
return false;
|
||||
if (comparisonType == CompareKind_OrdinalIgnoreCase)
|
||||
return EqualsIgnoreCaseHelper(this->GetPtr() + this->mLength - b.mLength, b.GetPtr(), b.mLength);
|
||||
return EqualsHelper(this->GetPtr() + this->mLength - b.mLength, b.GetPtr(), b.mLength);
|
||||
}
|
||||
|
||||
bool StartsWith(char c) const
|
||||
{
|
||||
if (this->mLength == 0)
|
||||
return false;
|
||||
return GetPtr()[0] == c;
|
||||
}
|
||||
|
||||
bool EndsWith(char c) const
|
||||
{
|
||||
if (this->mLength == 0)
|
||||
return false;
|
||||
return GetPtr()[this->mLength - 1] == c;
|
||||
}
|
||||
|
||||
void ReplaceLargerHelper(const StringImpl& find, const StringImpl& replace);
|
||||
void Replace(const StringImpl& find, const StringImpl& replace);
|
||||
void TrimEnd();
|
||||
void TrimStart();
|
||||
void Trim();
|
||||
bool IsWhitespace() const;
|
||||
bool IsEmpty() const
|
||||
{
|
||||
return this->mLength == 0;
|
||||
}
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
return this->mLength == 0;
|
||||
}
|
||||
|
||||
bool HasMultibyteChars();
|
||||
intptr IndexOf(const StringImpl& subStr, bool ignoreCase = false) const;
|
||||
intptr IndexOf(const StringImpl& subStr, int32 startIdx) const;
|
||||
intptr IndexOf(const StringImpl& subStr, int64 startIdx) const;
|
||||
intptr IndexOf(char c, intptr startIdx = 0) const;
|
||||
intptr LastIndexOf(char c) const;
|
||||
intptr LastIndexOf(char c, intptr startCheck) const;
|
||||
|
||||
bool Contains(char c) const
|
||||
{
|
||||
return IndexOf(c) != -1;
|
||||
}
|
||||
|
||||
bool Contains(const StringImpl& str) const
|
||||
{
|
||||
return IndexOf(str) != -1;
|
||||
}
|
||||
|
||||
const_iterator begin() const
|
||||
{
|
||||
return GetPtr();
|
||||
}
|
||||
|
||||
const_iterator end() const
|
||||
{
|
||||
return GetPtr() + this->mLength;
|
||||
}
|
||||
|
||||
iterator begin()
|
||||
{
|
||||
return GetMutablePtr();
|
||||
}
|
||||
|
||||
iterator end()
|
||||
{
|
||||
return GetMutablePtr() + this->mLength;
|
||||
}
|
||||
};
|
||||
|
||||
template <const int TBufSize = 16>
|
||||
class StringT : public StringImpl
|
||||
{
|
||||
protected:
|
||||
void Init(const char* charPtr, int_strsize count)
|
||||
{
|
||||
int_strsize internalSize = (int_strsize)(sizeof(StringT) - offsetof(StringImpl, mPtr));
|
||||
int_strsize allocSize = count + 1;
|
||||
|
||||
if (allocSize <= internalSize)
|
||||
{
|
||||
// Fits
|
||||
auto ptr = (char*)&this->mPtr;
|
||||
memcpy(ptr, charPtr, count);
|
||||
ptr[count] = 0;
|
||||
mAllocSizeAndFlags = internalSize;
|
||||
this->mLength = count;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Too big, must alloc
|
||||
auto ptr = AllocPtr(allocSize);
|
||||
memcpy(ptr, charPtr, count);
|
||||
ptr[count] = 0;
|
||||
this->mPtr = ptr;
|
||||
mAllocSizeAndFlags = allocSize | DynAllocFlag | StrPtrFlag;
|
||||
this->mLength = count;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
char mInternalBuffer[TBufSize - sizeof(intptr)];
|
||||
|
||||
using StringImpl::operator=;
|
||||
|
||||
StringT()
|
||||
{
|
||||
int_strsize internalSize = (int_strsize)(sizeof(StringT) - offsetof(StringImpl, mPtr));
|
||||
this->mPtr = 0;
|
||||
mAllocSizeAndFlags = internalSize;
|
||||
this->mLength = 0;
|
||||
}
|
||||
|
||||
StringT(const char* charPtr)
|
||||
{
|
||||
Init(charPtr, (int_strsize)strlen(charPtr));
|
||||
}
|
||||
|
||||
StringT(const char* charPtr, const char* charPtrEnd)
|
||||
{
|
||||
Init(charPtr, (int_strsize)(charPtrEnd - charPtr));
|
||||
}
|
||||
|
||||
StringT(const char* charPtr, intptr length)
|
||||
{
|
||||
Init(charPtr, (int_strsize)length);
|
||||
}
|
||||
|
||||
StringT(const StringT& str)
|
||||
{
|
||||
Init(str.GetPtr(), str.mLength);
|
||||
}
|
||||
|
||||
StringT(StringT&& str)
|
||||
{
|
||||
if ((str.mAllocSizeAndFlags & StrPtrFlag) != 0)
|
||||
{
|
||||
this->mPtr = str.mPtr;
|
||||
mAllocSizeAndFlags = str.mAllocSizeAndFlags;
|
||||
this->mLength = str.mLength;
|
||||
|
||||
str.mAllocSizeAndFlags = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If there's an internal buffer then we have to copy
|
||||
Init(str.GetPtr(), str.mLength);
|
||||
}
|
||||
}
|
||||
|
||||
StringT(const StringT& str, intptr offset)
|
||||
{
|
||||
Init(str.GetPtr() + offset, (int_strsize)(str.mLength - offset));
|
||||
}
|
||||
|
||||
StringT(const StringT& str, intptr offset, intptr length)
|
||||
{
|
||||
BF_ASSERT(offset >= 0);
|
||||
BF_ASSERT((uintptr)offset + (uintptr)length <= (uintptr)str.mLength);
|
||||
Init(str.GetPtr() + offset, (int_strsize)length);
|
||||
}
|
||||
|
||||
StringT(const StringImpl& str)
|
||||
{
|
||||
Init(str.GetPtr(), str.mLength);
|
||||
}
|
||||
|
||||
StringT(const std::string& str)
|
||||
{
|
||||
Init(str.c_str(), (int_strsize)str.length());
|
||||
}
|
||||
|
||||
StringT(StringImpl&& str)
|
||||
{
|
||||
if ((str.mAllocSizeAndFlags & StrPtrFlag) != 0)
|
||||
{
|
||||
this->mPtr = str.mPtr;
|
||||
mAllocSizeAndFlags = str.mAllocSizeAndFlags;
|
||||
this->mLength = str.mLength;
|
||||
|
||||
str.mAllocSizeAndFlags = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If there's an internal buffer then we have to copy
|
||||
Init(str.GetPtr(), str.mLength);
|
||||
}
|
||||
}
|
||||
|
||||
StringImpl& operator=(StringT&& str)
|
||||
{
|
||||
return StringImpl::operator=(std::move(str));
|
||||
}
|
||||
|
||||
StringImpl& operator=(const StringT& str)
|
||||
{
|
||||
return StringImpl::operator=(str);
|
||||
}
|
||||
};
|
||||
|
||||
class UTF16String : public Array<uint16>
|
||||
{
|
||||
public:
|
||||
UTF16String();
|
||||
UTF16String(const wchar_t* str);
|
||||
UTF16String(const wchar_t* str, int len);
|
||||
|
||||
void Set(const wchar_t* str, int len);
|
||||
void Set(const wchar_t* str);
|
||||
const wchar_t* c_str() const;
|
||||
size_t length() const;
|
||||
};
|
||||
|
||||
#define BF_SPECIALIZE_STR(size) \
|
||||
template <> class StringT<size> : public StringImpl \
|
||||
{ \
|
||||
public: \
|
||||
using StringImpl::StringImpl; \
|
||||
using StringImpl::operator=; \
|
||||
StringT() { mPtr = NULL; mLength = 0; mAllocSizeAndFlags = 0; } \
|
||||
StringT(const char* str) { Init(str, (int_strsize)strlen(str)); } \
|
||||
StringT(const std::string& str) { Init(str.c_str(), (int_strsize)str.length()); } \
|
||||
StringT(const StringImpl& str) : StringImpl(str) {} \
|
||||
StringT(StringImpl&& str) : StringImpl(std::move(str)) {} \
|
||||
};
|
||||
BF_SPECIALIZE_STR(0)
|
||||
BF_SPECIALIZE_STR(1)
|
||||
BF_SPECIALIZE_STR(2)
|
||||
BF_SPECIALIZE_STR(3)
|
||||
#ifdef BF64
|
||||
BF_SPECIALIZE_STR(4)
|
||||
BF_SPECIALIZE_STR(5)
|
||||
BF_SPECIALIZE_STR(6)
|
||||
#endif
|
||||
#undef BF_SPECIALIZE_STR
|
||||
|
||||
String operator+(const StringImpl& lhs, const StringImpl& rhs);
|
||||
String operator+(const StringImpl& lhs, const StringView& rhs);
|
||||
String operator+(const StringImpl& lhs, char rhs);
|
||||
bool operator==(const char* lhs, const StringImpl& rhs);
|
||||
bool operator!=(const char* lhs, const StringImpl& rhs);
|
||||
// bool operator==(const StringView& lhs, const StringImpl& rhs);
|
||||
// bool operator!=(const StringView& lhs, const StringImpl& rhs);
|
||||
|
||||
NS_BF_END;
|
||||
|
||||
namespace std
|
||||
{
|
||||
template<>
|
||||
struct hash<Beefy::StringImpl>
|
||||
{
|
||||
size_t operator()(const Beefy::StringImpl& val) const
|
||||
{
|
||||
return HashBytes((const uint8*)val.GetPtr(), val.mLength);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct hash<Beefy::StringView>
|
||||
{
|
||||
size_t operator()(const Beefy::StringView& val) const
|
||||
{
|
||||
return HashBytes((const uint8*)val.mPtr, val.mLength);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct hash<Beefy::String>
|
||||
{
|
||||
size_t operator()(const Beefy::String& val) const
|
||||
{
|
||||
return HashBytes((const uint8*)val.GetPtr(), val.mLength);
|
||||
}
|
||||
};
|
||||
}
|
133
BeefySysLib/util/TLSingleton.h
Normal file
133
BeefySysLib/util/TLSingleton.h
Normal file
|
@ -0,0 +1,133 @@
|
|||
#pragma once
|
||||
|
||||
#include "../Common.h"
|
||||
#include "CritSect.h"
|
||||
|
||||
NS_BF_BEGIN;
|
||||
|
||||
#ifdef BF_PLATFORM_WINDOWS
|
||||
|
||||
template <typename T>
|
||||
class TLSingleton
|
||||
{
|
||||
public:
|
||||
uint32 mFlsValue;
|
||||
|
||||
public:
|
||||
TLSingleton()
|
||||
{
|
||||
this->mFlsValue = FlsAlloc(&this->FlsFreeFunc);
|
||||
}
|
||||
|
||||
~TLSingleton()
|
||||
{
|
||||
FlsFree(this->mFlsValue);
|
||||
}
|
||||
|
||||
static void NTAPI FlsFreeFunc(void* ptr)
|
||||
{
|
||||
delete (T*)ptr;
|
||||
}
|
||||
|
||||
T* Get()
|
||||
{
|
||||
T* val = (T*)FlsGetValue(this->mFlsValue);
|
||||
if (val == NULL)
|
||||
{
|
||||
val = new T();
|
||||
FlsSetValue(this->mFlsValue, val);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
};
|
||||
|
||||
class TLSDtor
|
||||
{
|
||||
public:
|
||||
typedef void (NTAPI *CallbackFunction)(void* lpFlsData);
|
||||
|
||||
public:
|
||||
DWORD mFlsValue;
|
||||
|
||||
TLSDtor(CallbackFunction callbackFunction)
|
||||
{
|
||||
mFlsValue = FlsAlloc(callbackFunction);
|
||||
}
|
||||
|
||||
~TLSDtor()
|
||||
{
|
||||
FlsFree(mFlsValue);
|
||||
}
|
||||
|
||||
void Add(void* data)
|
||||
{
|
||||
FlsSetValue(mFlsValue, data);
|
||||
}
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
template <typename T>
|
||||
class TLSingleton
|
||||
{
|
||||
public:
|
||||
pthread_key_t mTlsKey;
|
||||
|
||||
public:
|
||||
TLSingleton()
|
||||
{
|
||||
mTlsKey = 0;
|
||||
pthread_key_create(&mTlsKey, TlsFreeFunc);
|
||||
}
|
||||
|
||||
~TLSingleton()
|
||||
{
|
||||
pthread_key_delete(mTlsKey);
|
||||
}
|
||||
|
||||
static void NTAPI TlsFreeFunc(void* ptr)
|
||||
{
|
||||
delete (T*)ptr;
|
||||
}
|
||||
|
||||
T* Get()
|
||||
{
|
||||
T* val = (T*)pthread_getspecific(this->mTlsKey);
|
||||
if (val == NULL)
|
||||
{
|
||||
val = new T();
|
||||
pthread_setspecific(this->mTlsKey, (void*)val);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
};
|
||||
|
||||
class TLSDtor
|
||||
{
|
||||
public:
|
||||
typedef void (*CallbackFunction)(void* lpFlsData);
|
||||
|
||||
public:
|
||||
pthread_key_t mTlsKey;
|
||||
|
||||
TLSDtor(CallbackFunction callbackFunction)
|
||||
{
|
||||
mTlsKey = 0;
|
||||
pthread_key_create(&mTlsKey, callbackFunction);
|
||||
}
|
||||
|
||||
~TLSDtor()
|
||||
{
|
||||
pthread_key_delete(mTlsKey);
|
||||
}
|
||||
|
||||
void Add(void* data)
|
||||
{
|
||||
pthread_setspecific(mTlsKey, data);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
NS_BF_END;
|
179
BeefySysLib/util/ThreadPool.cpp
Normal file
179
BeefySysLib/util/ThreadPool.cpp
Normal file
|
@ -0,0 +1,179 @@
|
|||
#include "ThreadPool.h"
|
||||
#include "BeefPerf.h"
|
||||
|
||||
USING_NS_BF;
|
||||
|
||||
static BF_TLS_DECLSPEC ThreadPool* gPoolParent;
|
||||
|
||||
ThreadPool::Thread::Thread()
|
||||
{
|
||||
mCurJobThreadId = -1;
|
||||
}
|
||||
|
||||
ThreadPool::Thread::~Thread()
|
||||
{
|
||||
BfpThread_Release(mBfpThread);
|
||||
}
|
||||
|
||||
void ThreadPool::Thread::Proc()
|
||||
{
|
||||
bool isWorking = true;
|
||||
|
||||
BpSetThreadName("ThreadPoolProc");
|
||||
BfpThread_SetName(NULL, "ThreadPoolProc", NULL);
|
||||
|
||||
gPoolParent = mThreadPool;
|
||||
|
||||
while (true)
|
||||
{
|
||||
Job* job = NULL;
|
||||
//
|
||||
{
|
||||
AutoCrit autoCrit(mThreadPool->mCritSect);
|
||||
if (!mThreadPool->mJobs.IsEmpty())
|
||||
{
|
||||
job = mThreadPool->mJobs[0];
|
||||
job->mProcessing = true;
|
||||
mThreadPool->mJobs.RemoveAt(0);
|
||||
}
|
||||
|
||||
if (job == NULL)
|
||||
mCurJobThreadId = -1;
|
||||
else
|
||||
mCurJobThreadId = job->mFromThreadId;
|
||||
|
||||
bool hasWork = job != NULL;
|
||||
if (hasWork != isWorking)
|
||||
{
|
||||
isWorking = hasWork;
|
||||
if (isWorking)
|
||||
mThreadPool->mFreeThreads--;
|
||||
else
|
||||
mThreadPool->mFreeThreads++;
|
||||
}
|
||||
}
|
||||
|
||||
if ((job == NULL) && (mThreadPool->mShuttingDown))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
bool didCancel = false;
|
||||
if ((mThreadPool->mShuttingDown) && (job->Cancel()))
|
||||
didCancel = true;
|
||||
|
||||
if (job == NULL)
|
||||
{
|
||||
mThreadPool->mEvent.WaitFor();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!didCancel)
|
||||
{
|
||||
BP_ZONE("ThreadProc:Job");
|
||||
job->Perform();
|
||||
}
|
||||
|
||||
// Run dtor synchronized
|
||||
AutoCrit autoCrit(mThreadPool->mCritSect);
|
||||
delete job;
|
||||
}
|
||||
}
|
||||
|
||||
ThreadPool::ThreadPool(int maxThreads, int stackSize)
|
||||
{
|
||||
mMaxThreads = maxThreads;
|
||||
mShuttingDown = false;
|
||||
mStackSize = stackSize;
|
||||
mFreeThreads = 0;
|
||||
mRunningThreads = 0;
|
||||
}
|
||||
|
||||
ThreadPool::~ThreadPool()
|
||||
{
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
void ThreadPool::Shutdown()
|
||||
{
|
||||
mShuttingDown = true;
|
||||
mEvent.Set(true);
|
||||
|
||||
while (true)
|
||||
{
|
||||
Thread* thread = NULL;
|
||||
//
|
||||
{
|
||||
AutoCrit autoCrit(mCritSect);
|
||||
if (mThreads.IsEmpty())
|
||||
break;
|
||||
thread = mThreads.back();
|
||||
mThreads.pop_back();
|
||||
}
|
||||
|
||||
if (!BfpThread_WaitFor(thread->mBfpThread, -1))
|
||||
break;
|
||||
|
||||
AutoCrit autoCrit(mCritSect);
|
||||
delete thread;
|
||||
mRunningThreads--;
|
||||
}
|
||||
|
||||
BF_ASSERT(mRunningThreads == 0);
|
||||
|
||||
for (auto job : mJobs)
|
||||
delete job;
|
||||
mJobs.Clear();
|
||||
}
|
||||
|
||||
void ThreadPool::SetStackSize(int stackSize)
|
||||
{
|
||||
mStackSize = stackSize;
|
||||
}
|
||||
|
||||
static void WorkerProc(void* param)
|
||||
{
|
||||
((ThreadPool::Thread*)param)->Proc();
|
||||
}
|
||||
|
||||
void ThreadPool::AddJob(Job* job, int maxWorkersPerProviderThread)
|
||||
{
|
||||
AutoCrit autoCrit(mCritSect);
|
||||
|
||||
BfpThreadId curThreadId = BfpThread_GetCurrentId();
|
||||
|
||||
job->mFromThreadId = curThreadId;
|
||||
mJobs.Add(job);
|
||||
mEvent.Set();
|
||||
|
||||
if (((int)mThreads.size() < mMaxThreads) && (mFreeThreads == 0))
|
||||
{
|
||||
int workersForUs = 0;
|
||||
for (auto thread : mThreads)
|
||||
{
|
||||
if (thread->mCurJobThreadId == curThreadId)
|
||||
workersForUs++;
|
||||
}
|
||||
if (workersForUs >= maxWorkersPerProviderThread)
|
||||
return;
|
||||
|
||||
mRunningThreads++;
|
||||
Thread* thread = new Thread();
|
||||
thread->mThreadPool = this;
|
||||
thread->mBfpThread = BfpThread_Create(WorkerProc, (void*)thread, mStackSize, BfpThreadCreateFlag_StackSizeReserve);
|
||||
mThreads.Add(thread);
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadPool::AddJob(BfpThreadStartProc proc, void* param, int maxWorkersPerProviderThread)
|
||||
{
|
||||
ProcJob* job = new ProcJob();
|
||||
job->mProc = proc;
|
||||
job->mParam = param;
|
||||
AddJob(job, maxWorkersPerProviderThread);
|
||||
}
|
||||
|
||||
bool ThreadPool::IsInJob()
|
||||
{
|
||||
return gPoolParent == this;
|
||||
}
|
90
BeefySysLib/util/ThreadPool.h
Normal file
90
BeefySysLib/util/ThreadPool.h
Normal file
|
@ -0,0 +1,90 @@
|
|||
#pragma once
|
||||
|
||||
#include "../Common.h"
|
||||
#include "CritSect.h"
|
||||
#include "Deque.h"
|
||||
|
||||
NS_BF_BEGIN
|
||||
|
||||
class ThreadPool
|
||||
{
|
||||
public:
|
||||
class Thread
|
||||
{
|
||||
public:
|
||||
ThreadPool* mThreadPool;
|
||||
BfpThread* mBfpThread;
|
||||
BfpThreadId mCurJobThreadId;
|
||||
|
||||
public:
|
||||
Thread();
|
||||
~Thread();
|
||||
void Proc();
|
||||
};
|
||||
|
||||
class Job
|
||||
{
|
||||
public:
|
||||
BfpThreadId mFromThreadId;
|
||||
bool mProcessing;
|
||||
|
||||
Job()
|
||||
{
|
||||
mFromThreadId = 0;
|
||||
mProcessing = false;
|
||||
}
|
||||
|
||||
// By default don't allow cancelling
|
||||
virtual bool Cancel()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual ~Job()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void Perform() = 0;
|
||||
};
|
||||
|
||||
class ProcJob : public Job
|
||||
{
|
||||
public:
|
||||
BfpThreadStartProc mProc;
|
||||
void* mParam;
|
||||
|
||||
ProcJob()
|
||||
{
|
||||
mProc = NULL;
|
||||
mParam = NULL;
|
||||
}
|
||||
|
||||
void Perform() override
|
||||
{
|
||||
mProc(mParam);
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
int mStackSize;
|
||||
int mMaxThreads;
|
||||
CritSect mCritSect;
|
||||
SyncEvent mEvent;
|
||||
Deque<Job*> mJobs;
|
||||
Array<Thread*> mThreads;
|
||||
int mFreeThreads;
|
||||
int mRunningThreads;
|
||||
bool mShuttingDown;
|
||||
|
||||
public:
|
||||
ThreadPool(int maxThreads = 4, int stackSize = 1024 * 1024);
|
||||
~ThreadPool();
|
||||
|
||||
void Shutdown();
|
||||
void SetStackSize(int stackSize);
|
||||
void AddJob(Job* job, int maxWorkersPerProviderThread = 0x7FFFFFFF);
|
||||
void AddJob(BfpThreadStartProc proc, void* param, int maxWorkersPerProviderThread = 0x7FFFFFFF);
|
||||
bool IsInJob();
|
||||
};
|
||||
|
||||
NS_BF_END
|
641
BeefySysLib/util/UTF8.cpp
Normal file
641
BeefySysLib/util/UTF8.cpp
Normal file
|
@ -0,0 +1,641 @@
|
|||
#pragma warning(disable:4333)
|
||||
|
||||
/*
|
||||
Basic UTF-8 manipulation routines
|
||||
by Jeff Bezanson
|
||||
placed in the public domain Fall 2005
|
||||
|
||||
This code is designed to provide the utilities you need to manipulate
|
||||
UTF-8 as an internal string encoding. These functions do not perform the
|
||||
error checking normally needed when handling UTF-8 data, so if you happen
|
||||
to be from the Unicode Consortium you will want to flay me alive.
|
||||
I do this because error checking can be performed at the boundaries (I/O),
|
||||
with these routines reserved for higher performance on data known to be
|
||||
valid.
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#ifdef WIN32
|
||||
#include <malloc.h>
|
||||
#else
|
||||
//#include <alloca.h>
|
||||
#endif
|
||||
|
||||
#include "util/UTF8.h"
|
||||
|
||||
USING_NS_BF;
|
||||
|
||||
static const uint32 offsetsFromUTF8[6] = {
|
||||
0x00000000UL, 0x00003080UL, 0x000E2080UL,
|
||||
0x03C82080UL, 0xFA082080UL, 0x82082080UL
|
||||
};
|
||||
|
||||
static const char trailingBytesForUTF8[256] = {
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
|
||||
};
|
||||
|
||||
/* returns length of next utf-8 sequence */
|
||||
int Beefy::u8_seqlen(char *s)
|
||||
{
|
||||
return trailingBytesForUTF8[(unsigned int)(unsigned char)s[0]] + 1;
|
||||
}
|
||||
|
||||
int Beefy::u8_seqlen(uint32 ch)
|
||||
{
|
||||
if (ch < 0x80) {
|
||||
return 1;
|
||||
}
|
||||
else if (ch < 0x800) {
|
||||
return 2;
|
||||
}
|
||||
else if (ch < 0x10000) {
|
||||
return 3;
|
||||
}
|
||||
else if (ch < 0x110000) {
|
||||
return 4;
|
||||
}
|
||||
return 5;
|
||||
}
|
||||
|
||||
/* conversions without error checking
|
||||
only works for valid UTF-8, i.e. no 5- or 6-byte sequences
|
||||
srcsz = source size in bytes, or -1 if 0-terminated
|
||||
sz = dest size in # of wide characters
|
||||
|
||||
returns # characters converted
|
||||
dest will always be L'\0'-terminated, even if there isn't enough room
|
||||
for all the characters.
|
||||
if sz = srcsz+1 (i.e. 4*srcsz+4 bytes), there will always be enough space.
|
||||
*/
|
||||
int Beefy::u8_toucs(wchar_t *dest, int sz, char *src, int srcsz)
|
||||
{
|
||||
wchar_t ch;
|
||||
char *src_end = src + srcsz;
|
||||
int nb;
|
||||
int i=0;
|
||||
|
||||
while (i < sz-1) {
|
||||
nb = trailingBytesForUTF8[(unsigned char)*src];
|
||||
if (srcsz == -1) {
|
||||
if (*src == 0)
|
||||
goto done_toucs;
|
||||
}
|
||||
else {
|
||||
if (src + nb >= src_end)
|
||||
goto done_toucs;
|
||||
}
|
||||
ch = 0;
|
||||
switch (nb) {
|
||||
/* these fall through deliberately */
|
||||
case 3: ch += (unsigned char)*src++; ch <<= 6;
|
||||
case 2: ch += (unsigned char)*src++; ch <<= 6;
|
||||
case 1: ch += (unsigned char)*src++; ch <<= 6;
|
||||
case 0: ch += (unsigned char)*src++;
|
||||
}
|
||||
ch -= offsetsFromUTF8[nb];
|
||||
dest[i++] = ch;
|
||||
}
|
||||
done_toucs:
|
||||
dest[i] = 0;
|
||||
return i;
|
||||
}
|
||||
|
||||
uint32 Beefy::u8_toucs(const char* src, int srcsz, int* outLen)
|
||||
{
|
||||
const char *src_end = src + srcsz;
|
||||
int nb = trailingBytesForUTF8[(unsigned char)*src];
|
||||
if (outLen != NULL)
|
||||
*outLen = nb + 1;
|
||||
if (srcsz == -1) {
|
||||
if (*src == 0)
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
if (src + nb >= src_end)
|
||||
return 0;
|
||||
}
|
||||
uint32 ch = 0;
|
||||
switch (nb) {
|
||||
/* these fall through deliberately */
|
||||
case 3: ch += (unsigned char)*src++; ch <<= 6;
|
||||
case 2: ch += (unsigned char)*src++; ch <<= 6;
|
||||
case 1: ch += (unsigned char)*src++; ch <<= 6;
|
||||
case 0: ch += (unsigned char)*src++;
|
||||
}
|
||||
ch -= offsetsFromUTF8[nb];
|
||||
return ch;
|
||||
}
|
||||
|
||||
/* srcsz = number of source characters, or -1 if 0-terminated
|
||||
sz = size of dest buffer in bytes
|
||||
|
||||
returns # characters converted
|
||||
dest will only be '\0'-terminated if there is enough space. this is
|
||||
for consistency; imagine there are 2 bytes of space left, but the next
|
||||
character requires 3 bytes. in this case we could NUL-terminate, but in
|
||||
general we can't when there's insufficient space. therefore this function
|
||||
only NUL-terminates if all the characters fit, and there's space for
|
||||
the NUL as well.
|
||||
the destination string will never be bigger than the source string.
|
||||
*/
|
||||
int Beefy::u8_toutf8(char *dest, int sz, wchar_t *src, int srcsz)
|
||||
{
|
||||
wchar_t ch;
|
||||
int i = 0;
|
||||
char *dest_end = dest + sz;
|
||||
|
||||
while (srcsz<0 ? src[i]!=0 : i < srcsz) {
|
||||
ch = src[i];
|
||||
if (ch < 0x80) {
|
||||
if (dest >= dest_end)
|
||||
return i;
|
||||
*dest++ = (char)ch;
|
||||
}
|
||||
else if (ch < 0x800) {
|
||||
if (dest >= dest_end-1)
|
||||
return i;
|
||||
*dest++ = (ch>>6) | 0xC0;
|
||||
*dest++ = (ch & 0x3F) | 0x80;
|
||||
}
|
||||
else if (ch < 0x10000) {
|
||||
if (dest >= dest_end-2)
|
||||
return i;
|
||||
*dest++ = (ch>>12) | 0xE0;
|
||||
*dest++ = ((ch>>6) & 0x3F) | 0x80;
|
||||
*dest++ = (ch & 0x3F) | 0x80;
|
||||
}
|
||||
else if (ch < 0x110000) {
|
||||
if (dest >= dest_end-3)
|
||||
return i;
|
||||
*dest++ = (ch>>18) | 0xF0;
|
||||
*dest++ = ((ch>>12) & 0x3F) | 0x80;
|
||||
*dest++ = ((ch>>6) & 0x3F) | 0x80;
|
||||
*dest++ = (ch & 0x3F) | 0x80;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (dest < dest_end)
|
||||
*dest = '\0';
|
||||
return i;
|
||||
}
|
||||
|
||||
int Beefy::u8_toutf8(char *dest, int sz, uint32 ch)
|
||||
{
|
||||
char *dest_end = dest + sz;
|
||||
int len = 0;
|
||||
if (ch < 0x80) {
|
||||
if (dest >= dest_end)
|
||||
return 1;
|
||||
len = 1;
|
||||
*dest++ = (char)ch;
|
||||
}
|
||||
else if (ch < 0x800) {
|
||||
if (dest >= dest_end - 1)
|
||||
return 2;
|
||||
len = 2;
|
||||
*dest++ = (ch >> 6) | 0xC0;
|
||||
*dest++ = (ch & 0x3F) | 0x80;
|
||||
}
|
||||
else if (ch < 0x10000) {
|
||||
if (dest >= dest_end - 2)
|
||||
return 3;
|
||||
len = 3;
|
||||
*dest++ = (ch >> 12) | 0xE0;
|
||||
*dest++ = ((ch >> 6) & 0x3F) | 0x80;
|
||||
*dest++ = (ch & 0x3F) | 0x80;
|
||||
}
|
||||
else if (ch < 0x110000) {
|
||||
if (dest >= dest_end - 3)
|
||||
return 4;
|
||||
len = 4;
|
||||
*dest++ = (ch >> 18) | 0xF0;
|
||||
*dest++ = ((ch >> 12) & 0x3F) | 0x80;
|
||||
*dest++ = ((ch >> 6) & 0x3F) | 0x80;
|
||||
*dest++ = (ch & 0x3F) | 0x80;
|
||||
}
|
||||
if (dest < dest_end)
|
||||
*dest = '\0';
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int Beefy::u8_wc_toutf8(char *dest, uint32 ch)
|
||||
{
|
||||
if (ch < 0x80) {
|
||||
dest[0] = (char)ch;
|
||||
return 1;
|
||||
}
|
||||
if (ch < 0x800) {
|
||||
dest[0] = (ch>>6) | 0xC0;
|
||||
dest[1] = (ch & 0x3F) | 0x80;
|
||||
return 2;
|
||||
}
|
||||
if (ch < 0x10000) {
|
||||
dest[0] = (ch>>12) | 0xE0;
|
||||
dest[1] = ((ch>>6) & 0x3F) | 0x80;
|
||||
dest[2] = (ch & 0x3F) | 0x80;
|
||||
return 3;
|
||||
}
|
||||
if (ch < 0x110000) {
|
||||
dest[0] = (ch>>18) | 0xF0;
|
||||
dest[1] = ((ch>>12) & 0x3F) | 0x80;
|
||||
dest[2] = ((ch>>6) & 0x3F) | 0x80;
|
||||
dest[3] = (ch & 0x3F) | 0x80;
|
||||
return 4;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* charnum => byte offset */
|
||||
int Beefy::u8_offset(char *str, int charnum)
|
||||
{
|
||||
int offs=0;
|
||||
|
||||
while (charnum > 0 && str[offs]) {
|
||||
(void)(isutf(str[++offs]) || isutf(str[++offs]) ||
|
||||
isutf(str[++offs]) || ++offs);
|
||||
charnum--;
|
||||
}
|
||||
return offs;
|
||||
}
|
||||
|
||||
/* byte offset => charnum */
|
||||
int Beefy::u8_charnum(char *s, int offset)
|
||||
{
|
||||
int charnum = 0, offs=0;
|
||||
|
||||
while (offs < offset && s[offs]) {
|
||||
(void)(isutf(s[++offs]) || isutf(s[++offs]) ||
|
||||
isutf(s[++offs]) || ++offs);
|
||||
charnum++;
|
||||
}
|
||||
return charnum;
|
||||
}
|
||||
|
||||
/* number of characters */
|
||||
int Beefy::u8_strlen(char *s)
|
||||
{
|
||||
int count = 0;
|
||||
int i = 0;
|
||||
|
||||
while (u8_nextchar(s, &i) != 0)
|
||||
count++;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/* reads the next utf-8 sequence out of a string, updating an index */
|
||||
uint32 Beefy::u8_nextchar(char *s, int *i)
|
||||
{
|
||||
uint32 ch = 0;
|
||||
int sz = 0;
|
||||
|
||||
do {
|
||||
ch <<= 6;
|
||||
ch += (unsigned char)s[(*i)++];
|
||||
sz++;
|
||||
} while ((ch != 0) && s[*i] && !isutf(s[*i]));
|
||||
ch -= offsetsFromUTF8[sz-1];
|
||||
|
||||
return ch;
|
||||
}
|
||||
|
||||
void Beefy::u8_inc(char *s, int *i)
|
||||
{
|
||||
(void)(isutf(s[++(*i)]) || isutf(s[++(*i)]) ||
|
||||
isutf(s[++(*i)]) || ++(*i));
|
||||
}
|
||||
|
||||
void Beefy::u8_dec(char *s, int *i)
|
||||
{
|
||||
(void)(isutf(s[--(*i)]) || isutf(s[--(*i)]) ||
|
||||
isutf(s[--(*i)]) || --(*i));
|
||||
}
|
||||
|
||||
int Beefy::octal_digit(char c)
|
||||
{
|
||||
return (c >= '0' && c <= '7');
|
||||
}
|
||||
|
||||
int Beefy::hex_digit(char c)
|
||||
{
|
||||
return ((c >= '0' && c <= '9') ||
|
||||
(c >= 'A' && c <= 'F') ||
|
||||
(c >= 'a' && c <= 'f'));
|
||||
}
|
||||
|
||||
/* assumes that src points to the character after a backslash
|
||||
returns number of input characters processed */
|
||||
int Beefy::u8_read_escape_sequence(char *str, uint32 *dest)
|
||||
{
|
||||
uint32 ch;
|
||||
char digs[9]="\0\0\0\0\0\0\0\0";
|
||||
int dno=0, i=1;
|
||||
|
||||
ch = (uint32)str[0]; /* take literal character */
|
||||
if (str[0] == 'n')
|
||||
ch = L'\n';
|
||||
else if (str[0] == 't')
|
||||
ch = L'\t';
|
||||
else if (str[0] == 'r')
|
||||
ch = L'\r';
|
||||
else if (str[0] == 'b')
|
||||
ch = L'\b';
|
||||
else if (str[0] == 'f')
|
||||
ch = L'\f';
|
||||
else if (str[0] == 'v')
|
||||
ch = L'\v';
|
||||
else if (str[0] == 'a')
|
||||
ch = L'\a';
|
||||
else if (octal_digit(str[0])) {
|
||||
i = 0;
|
||||
do {
|
||||
digs[dno++] = str[i++];
|
||||
} while (octal_digit(str[i]) && dno < 3);
|
||||
ch = strtol(digs, NULL, 8);
|
||||
}
|
||||
else if (str[0] == 'x') {
|
||||
while (hex_digit(str[i]) && dno < 2) {
|
||||
digs[dno++] = str[i++];
|
||||
}
|
||||
if (dno > 0)
|
||||
ch = strtol(digs, NULL, 16);
|
||||
}
|
||||
else if (str[0] == 'u') {
|
||||
while (hex_digit(str[i]) && dno < 4) {
|
||||
digs[dno++] = str[i++];
|
||||
}
|
||||
if (dno > 0)
|
||||
ch = strtol(digs, NULL, 16);
|
||||
}
|
||||
else if (str[0] == 'U') {
|
||||
while (hex_digit(str[i]) && dno < 8) {
|
||||
digs[dno++] = str[i++];
|
||||
}
|
||||
if (dno > 0)
|
||||
ch = strtol(digs, NULL, 16);
|
||||
}
|
||||
*dest = ch;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/* convert a string with literal \uxxxx or \Uxxxxxxxx characters to UTF-8
|
||||
example: u8_unescape(mybuf, 256, "hello\\u220e")
|
||||
note the double backslash is needed if called on a C string literal */
|
||||
int Beefy::u8_unescape(char *buf, int sz, char *src)
|
||||
{
|
||||
int c=0, amt;
|
||||
uint32 ch;
|
||||
char temp[4];
|
||||
|
||||
while (*src && c < sz) {
|
||||
if (*src == '\\') {
|
||||
src++;
|
||||
amt = u8_read_escape_sequence(src, &ch);
|
||||
}
|
||||
else {
|
||||
ch = (uint32)*src;
|
||||
amt = 1;
|
||||
}
|
||||
src += amt;
|
||||
amt = u8_wc_toutf8(temp, ch);
|
||||
if (amt > sz-c)
|
||||
break;
|
||||
memcpy(&buf[c], temp, amt);
|
||||
c += amt;
|
||||
}
|
||||
if (c < sz)
|
||||
buf[c] = '\0';
|
||||
return c;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
#define snprintf _snprintf
|
||||
#pragma warning (disable:4996)
|
||||
#endif
|
||||
|
||||
int Beefy::u8_escape_wchar(char *buf, int sz, uint32 ch)
|
||||
{
|
||||
if (ch == L'\n')
|
||||
return snprintf(buf, sz, "\\n");
|
||||
else if (ch == L'\t')
|
||||
return snprintf(buf, sz, "\\t");
|
||||
else if (ch == L'\r')
|
||||
return snprintf(buf, sz, "\\r");
|
||||
else if (ch == L'\b')
|
||||
return snprintf(buf, sz, "\\b");
|
||||
else if (ch == L'\f')
|
||||
return snprintf(buf, sz, "\\f");
|
||||
else if (ch == L'\v')
|
||||
return snprintf(buf, sz, "\\v");
|
||||
else if (ch == L'\a')
|
||||
return snprintf(buf, sz, "\\a");
|
||||
else if (ch == L'\\')
|
||||
return snprintf(buf, sz, "\\\\");
|
||||
else if (ch < 32 || ch == 0x7f)
|
||||
return snprintf(buf, sz, "\\x%hhX", (unsigned char)ch);
|
||||
else if (ch > 0xFFFF)
|
||||
return snprintf(buf, sz, "\\U%.8X", (uint32)ch);
|
||||
else if (ch >= 0x80 && ch <= 0xFFFF)
|
||||
return snprintf(buf, sz, "\\u%.4hX", (unsigned short)ch);
|
||||
|
||||
return snprintf(buf, sz, "%c", (char)ch);
|
||||
}
|
||||
|
||||
int Beefy::u8_escape(char *buf, int sz, char *src, int escape_quotes)
|
||||
{
|
||||
int c=0, i=0, amt;
|
||||
|
||||
while (src[i] && c < sz) {
|
||||
if (escape_quotes && src[i] == '"') {
|
||||
amt = snprintf(buf, sz - c, "\\\"");
|
||||
i++;
|
||||
}
|
||||
else {
|
||||
amt = u8_escape_wchar(buf, sz - c, u8_nextchar(src, &i));
|
||||
}
|
||||
c += amt;
|
||||
buf += amt;
|
||||
}
|
||||
if (c < sz)
|
||||
*buf = '\0';
|
||||
return c;
|
||||
}
|
||||
|
||||
char* Beefy::u8_strchr(char *s, uint32 ch, int *charn)
|
||||
{
|
||||
int i = 0, lasti=0;
|
||||
uint32 c;
|
||||
|
||||
*charn = 0;
|
||||
while (s[i]) {
|
||||
c = u8_nextchar(s, &i);
|
||||
if (c == ch) {
|
||||
return &s[lasti];
|
||||
}
|
||||
lasti = i;
|
||||
(*charn)++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char* Beefy::u8_memchr(char *s, uint32 ch, size_t sz, int *charn)
|
||||
{
|
||||
int i = 0, lasti=0;
|
||||
uint32 c;
|
||||
int csz;
|
||||
|
||||
*charn = 0;
|
||||
while (i < (int)sz) {
|
||||
c = csz = 0;
|
||||
do {
|
||||
c <<= 6;
|
||||
c += (unsigned char)s[i++];
|
||||
csz++;
|
||||
} while (i < (int)sz && !isutf(s[i]));
|
||||
c -= offsetsFromUTF8[csz-1];
|
||||
|
||||
if (c == ch) {
|
||||
return &s[lasti];
|
||||
}
|
||||
lasti = i;
|
||||
(*charn)++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int Beefy::u8_is_locale_utf8(char *locale)
|
||||
{
|
||||
/* this code based on libutf8 */
|
||||
const char* cp = locale;
|
||||
|
||||
for (; *cp != '\0' && *cp != '@' && *cp != '+' && *cp != ','; cp++) {
|
||||
if (*cp == '.') {
|
||||
const char* encoding = ++cp;
|
||||
for (; *cp != '\0' && *cp != '@' && *cp != '+' && *cp != ','; cp++)
|
||||
;
|
||||
if ((cp-encoding == 5 && !strncmp(encoding, "UTF-8", 5))
|
||||
|| (cp-encoding == 4 && !strncmp(encoding, "utf8", 4)))
|
||||
return 1; /* it's UTF-8 */
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Beefy::u8_vprintf(char *fmt, va_list ap)
|
||||
{
|
||||
int cnt, sz=0;
|
||||
char *buf;
|
||||
wchar_t *wcs;
|
||||
|
||||
sz = 512;
|
||||
buf = (char*)alloca(sz);
|
||||
try_print:
|
||||
cnt = vsnprintf(buf, sz, fmt, ap);
|
||||
if (cnt >= sz) {
|
||||
buf = (char*)alloca(cnt - sz + 1);
|
||||
sz = cnt + 1;
|
||||
goto try_print;
|
||||
}
|
||||
wcs = (wchar_t*)alloca((cnt+1) * sizeof(wchar_t));
|
||||
cnt = u8_toucs(wcs, cnt+1, buf, cnt);
|
||||
printf("%ls", (wchar_t*)wcs);
|
||||
return cnt;
|
||||
}
|
||||
|
||||
int Beefy::u8_printf(char *fmt, ...)
|
||||
{
|
||||
int cnt;
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
|
||||
cnt = u8_vprintf(fmt, args);
|
||||
|
||||
va_end(args);
|
||||
return cnt;
|
||||
}
|
||||
|
||||
bool Beefy::UTF8IsCombiningMark(uint32 c)
|
||||
{
|
||||
return ((c >= 0x0300) && (c <= 0x036F)) || ((c >= 0x1DC0) && (c <= 0x1DFF));
|
||||
}
|
||||
|
||||
bool Beefy::UTF8GetGraphemeClusterSpan(const char* str, int strLength, int idx, int& startIdx, int& spanLength)
|
||||
{
|
||||
const char* ptr = str;
|
||||
|
||||
// Move to start of char
|
||||
while (startIdx >= 0)
|
||||
{
|
||||
char c = ptr[startIdx];
|
||||
if (((uint8)c & 0x80) == 0)
|
||||
{
|
||||
// This is the simple and fast case - ASCII followed by the string end or more ASCII
|
||||
if ((startIdx == strLength - 1) || (((uint8)ptr[startIdx + 1] & 0x80) == 0))
|
||||
{
|
||||
spanLength = 1;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (((uint8)c & 0xC0) != 0x80)
|
||||
{
|
||||
uint32 c32 = u8_toucs(ptr + startIdx, strLength - startIdx);
|
||||
|
||||
if (!UTF8IsCombiningMark(c32))
|
||||
break;
|
||||
}
|
||||
startIdx--;
|
||||
}
|
||||
|
||||
int curIdx = startIdx;
|
||||
while (true)
|
||||
{
|
||||
int cLen = 0;
|
||||
uint32 c32 = u8_toucs(ptr + startIdx, strLength - startIdx, &cLen);
|
||||
|
||||
int nextIdx = curIdx + cLen;
|
||||
if ((curIdx != startIdx) && (!UTF8IsCombiningMark(c32)))
|
||||
{
|
||||
spanLength = curIdx - startIdx;
|
||||
return true;
|
||||
}
|
||||
if (nextIdx == strLength)
|
||||
{
|
||||
spanLength = nextIdx - startIdx;
|
||||
return false;
|
||||
}
|
||||
curIdx = nextIdx;
|
||||
}
|
||||
}
|
||||
|
||||
void Beefy::UTF8Categorize(const char* str, int strLength, int& numCodePoints, int& numCombiningMarks)
|
||||
{
|
||||
numCodePoints = 0;
|
||||
numCombiningMarks = 0;
|
||||
|
||||
int offset = 0;
|
||||
while (offset < strLength)
|
||||
{
|
||||
int cLen = 0;
|
||||
uint32 c32 = u8_toucs(str + offset, strLength - offset, &cLen);
|
||||
|
||||
numCodePoints++;
|
||||
if (UTF8IsCombiningMark(c32))
|
||||
numCombiningMarks++;
|
||||
|
||||
offset += cLen;
|
||||
}
|
||||
}
|
88
BeefySysLib/util/UTF8.h
Normal file
88
BeefySysLib/util/UTF8.h
Normal file
|
@ -0,0 +1,88 @@
|
|||
#pragma once
|
||||
|
||||
#include "../Common.h"
|
||||
|
||||
NS_BF_BEGIN;
|
||||
|
||||
/* is c the start of a utf8 sequence? */
|
||||
#define isutf(c) (((c)&0xC0)!=0x80)
|
||||
|
||||
/* convert UTF-8 data to wide character */
|
||||
int u8_toucs(wchar_t *dest, int sz, char *src, int srcsz);
|
||||
|
||||
uint32 u8_toucs(const char* src, int srcsz, int* outLen = NULL);
|
||||
|
||||
/* the opposite conversion */
|
||||
int u8_toutf8(char *dest, int sz, wchar_t *src, int srcsz);
|
||||
|
||||
/* the opposite conversion */
|
||||
int u8_toutf8(char *dest, int sz, uint32 ch);
|
||||
|
||||
/* single character to UTF-8 */
|
||||
int u8_wc_toutf8(char *dest, uint32 ch);
|
||||
|
||||
/* character number to byte offset */
|
||||
int u8_offset(char *str, int charnum);
|
||||
|
||||
/* byte offset to character number */
|
||||
int u8_charnum(char *s, int offset);
|
||||
|
||||
/* return next character, updating an index variable */
|
||||
uint32 u8_nextchar(char *s, int *i);
|
||||
|
||||
/* move to next character */
|
||||
void u8_inc(char *s, int *i);
|
||||
|
||||
/* move to previous character */
|
||||
void u8_dec(char *s, int *i);
|
||||
|
||||
/* returns length of next utf-8 sequence */
|
||||
int u8_seqlen(char *s);
|
||||
|
||||
int u8_seqlen(uint32 ch);
|
||||
|
||||
/* assuming src points to the character after a backslash, read an
|
||||
escape sequence, storing the result in dest and returning the number of
|
||||
input characters processed */
|
||||
int u8_read_escape_sequence(char *src, uint32 *dest);
|
||||
|
||||
/* given a wide character, convert it to an ASCII escape sequence stored in
|
||||
buf, where buf is "sz" bytes. returns the number of characters output. */
|
||||
int u8_escape_wchar(char *buf, int sz, uint32 ch);
|
||||
|
||||
/* convert a string "src" containing escape sequences to UTF-8 */
|
||||
int u8_unescape(char *buf, int sz, char *src);
|
||||
|
||||
/* convert UTF-8 "src" to ASCII with escape sequences.
|
||||
if escape_quotes is nonzero, quote characters will be preceded by
|
||||
backslashes as well. */
|
||||
int u8_escape(char *buf, int sz, char *src, int escape_quotes);
|
||||
|
||||
/* utility predicates used by the above */
|
||||
int octal_digit(char c);
|
||||
int hex_digit(char c);
|
||||
|
||||
/* return a pointer to the first occurrence of ch in s, or NULL if not
|
||||
found. character index of found character returned in *charn. */
|
||||
char *u8_strchr(char *s, uint32 ch, int *charn);
|
||||
|
||||
/* same as the above, but searches a buffer of a given size instead of
|
||||
a NUL-terminated string. */
|
||||
char *u8_memchr(char *s, uint32 ch, size_t sz, int *charn);
|
||||
|
||||
/* count the number of characters in a UTF-8 string */
|
||||
int u8_strlen(char *s);
|
||||
|
||||
int u8_is_locale_utf8(char *locale);
|
||||
|
||||
/* printf where the format string and arguments may be in UTF-8.
|
||||
you can avoid this function and just use ordinary printf() if the current
|
||||
locale is UTF-8. */
|
||||
int u8_vprintf(char *fmt, va_list ap);
|
||||
int u8_printf(char *fmt, ...);
|
||||
|
||||
bool UTF8IsCombiningMark(uint32 c);
|
||||
bool UTF8GetGraphemeClusterSpan(const char* str, int strLength, int idx, int& startIdx, int& spanLength);
|
||||
void UTF8Categorize(const char* str, int strLength, int& numCodePoints, int& numCombiningMarks);
|
||||
|
||||
NS_BF_END;
|
78
BeefySysLib/util/Vector.cpp
Normal file
78
BeefySysLib/util/Vector.cpp
Normal file
|
@ -0,0 +1,78 @@
|
|||
#include "Vector.h"
|
||||
#include "Matrix4.h"
|
||||
#include "Quaternion.h"
|
||||
|
||||
USING_NS_BF;
|
||||
|
||||
Vector3::Vector3(float x, float y, float z)
|
||||
{
|
||||
mX = x;
|
||||
mY = y;
|
||||
mZ = z;
|
||||
}
|
||||
|
||||
float Vector3::GetMagnitude() const
|
||||
{
|
||||
return sqrt(mX*mX + mY*mY + mZ*mZ);
|
||||
}
|
||||
|
||||
Vector3 Vector3::Normalize(const Vector3& vec)
|
||||
{
|
||||
float mag = vec.GetMagnitude();
|
||||
return Vector3(
|
||||
vec.mX / mag,
|
||||
vec.mY / mag,
|
||||
vec.mZ / mag);
|
||||
}
|
||||
|
||||
float Vector3::Dot(const Vector3& vec, const Vector3& vec2)
|
||||
{
|
||||
return vec.mX*vec2.mX + vec.mY*vec2.mY + vec.mZ*vec2.mZ;
|
||||
}
|
||||
|
||||
Vector3 Vector3::CrossProduct(const Vector3& vec1, const Vector3& vec2)
|
||||
{
|
||||
return Vector3(
|
||||
vec1.mY * vec2.mZ - vec1.mZ * vec2.mY,
|
||||
vec1.mZ * vec2.mX - vec1.mX * vec2.mZ,
|
||||
vec1.mX * vec2.mY - vec1.mY * vec2.mX);
|
||||
}
|
||||
|
||||
Vector3 Vector3::Transform(const Vector3& vec, const Matrix4& matrix)
|
||||
{
|
||||
float fInvW = 1.0f / (matrix.m30 * vec.mX + matrix.m31 * vec.mY + matrix.m32 * vec.mZ + matrix.m33);
|
||||
|
||||
return Vector3(
|
||||
(matrix.m00 * vec.mX + matrix.m01 * vec.mY + matrix.m02 * vec.mZ + matrix.m03) * fInvW,
|
||||
(matrix.m10 * vec.mX + matrix.m11 * vec.mY + matrix.m12 * vec.mZ + matrix.m13) * fInvW,
|
||||
(matrix.m20 * vec.mX + matrix.m21 * vec.mY + matrix.m22 * vec.mZ + matrix.m23) * fInvW);
|
||||
}
|
||||
|
||||
Vector3 Vector3::Transform(const Vector3& vec, const Quaternion& quat)
|
||||
{
|
||||
Vector3 result;
|
||||
|
||||
Vector3 uv, uuv;
|
||||
Vector3 qvec(quat.mX, quat.mY, quat.mZ);
|
||||
uv = Vector3::CrossProduct(qvec, vec);
|
||||
uuv = Vector3::CrossProduct(qvec, uv);
|
||||
uv *= (2.0f * quat.mW);
|
||||
uuv *= 2.0f;
|
||||
|
||||
return vec + uv + uuv;
|
||||
}
|
||||
|
||||
Vector3 Vector3::Transform2(const Vector3& vec, const Quaternion& quat)
|
||||
{
|
||||
Vector3 result;
|
||||
|
||||
float x = 2 * (quat.mY * vec.mZ - quat.mZ * vec.mY);
|
||||
float y = 2 * (quat.mZ * vec.mX - quat.mX * vec.mZ);
|
||||
float z = 2 * (quat.mX * vec.mY - quat.mY * vec.mX);
|
||||
|
||||
result.mX = vec.mX + x * quat.mW + (quat.mY * z - quat.mZ * y);
|
||||
result.mY = vec.mY + y * quat.mW + (quat.mZ * x - quat.mX * z);
|
||||
result.mZ = vec.mZ + z * quat.mW + (quat.mX * y - quat.mY * x);
|
||||
|
||||
return result;
|
||||
}
|
97
BeefySysLib/util/Vector.h
Normal file
97
BeefySysLib/util/Vector.h
Normal file
|
@ -0,0 +1,97 @@
|
|||
#pragma once
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
NS_BF_BEGIN;
|
||||
|
||||
class Matrix4;
|
||||
class Quaternion;
|
||||
|
||||
class TexCoords
|
||||
{
|
||||
public:
|
||||
float mU;
|
||||
float mV;
|
||||
|
||||
public:
|
||||
TexCoords()
|
||||
{
|
||||
}
|
||||
|
||||
TexCoords(float u, float v) : mU(u), mV(v)
|
||||
{
|
||||
}
|
||||
|
||||
static TexCoords FlipV(const TexCoords& texCoords)
|
||||
{
|
||||
return TexCoords(texCoords.mU, 1.0f - texCoords.mV);
|
||||
}
|
||||
};
|
||||
|
||||
class Vector3
|
||||
{
|
||||
public:
|
||||
float mX;
|
||||
float mY;
|
||||
float mZ;
|
||||
|
||||
public:
|
||||
Vector3(float x = 0, float y = 0, float z = 0);
|
||||
|
||||
float GetMagnitude() const;
|
||||
static Vector3 Normalize(const Vector3& vec);
|
||||
static float Dot(const Vector3& vec1, const Vector3& vec2);
|
||||
static Vector3 CrossProduct(const Vector3& vec1, const Vector3& vec2);
|
||||
|
||||
bool operator==(const Vector3& check) const
|
||||
{
|
||||
return (mX == check.mX) && (mY == check.mY) && (mZ == check.mZ);
|
||||
}
|
||||
|
||||
bool operator!=(const Vector3& check) const
|
||||
{
|
||||
return (mX != check.mX) || (mY != check.mY) || (mZ != check.mZ);
|
||||
}
|
||||
|
||||
static Vector3 Transform(const Vector3& vec, const Matrix4& matrix);
|
||||
static Vector3 Transform(const Vector3& vec, const Quaternion& quat);
|
||||
static Vector3 Transform2(const Vector3& vec, const Quaternion& quat);
|
||||
|
||||
static Vector3 Scale(const Vector3& vec, float scale)
|
||||
{
|
||||
return Vector3(vec.mX * scale, vec.mY * scale, vec.mZ * scale);
|
||||
}
|
||||
|
||||
Vector3 operator +(const Vector3& v2) const
|
||||
{
|
||||
return Vector3(mX + v2.mX, mY + v2.mY, mZ + v2.mZ);
|
||||
}
|
||||
|
||||
Vector3 operator *(const Vector3& v2) const
|
||||
{
|
||||
return Vector3(mX * v2.mX, mY * v2.mY, mZ * v2.mZ);
|
||||
}
|
||||
|
||||
Vector3 operator *(float scale) const
|
||||
{
|
||||
return Vector3(mX * scale, mY * scale, mZ * scale);
|
||||
}
|
||||
|
||||
inline Vector3& operator -= (const Vector3& vec)
|
||||
{
|
||||
mX -= vec.mX;
|
||||
mY -= vec.mY;
|
||||
mZ -= vec.mZ;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Vector3& operator *= (const Vector3& vec)
|
||||
{
|
||||
mX *= vec.mX;
|
||||
mY *= vec.mY;
|
||||
mZ *= vec.mZ;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
NS_BF_END;
|
59
BeefySysLib/util/WorkThread.cpp
Normal file
59
BeefySysLib/util/WorkThread.cpp
Normal file
|
@ -0,0 +1,59 @@
|
|||
#include "WorkThread.h"
|
||||
|
||||
USING_NS_BF;
|
||||
|
||||
WorkThread::WorkThread()
|
||||
{
|
||||
mThread = NULL;
|
||||
}
|
||||
|
||||
WorkThread::~WorkThread()
|
||||
{
|
||||
if (mThread != NULL)
|
||||
Stop();
|
||||
|
||||
BfpThread_Release(mThread);
|
||||
mThread = NULL;
|
||||
}
|
||||
|
||||
static void WorkThreadStub(void* param)
|
||||
{
|
||||
((WorkThread*)param)->Run();
|
||||
}
|
||||
|
||||
void WorkThread::Start()
|
||||
{
|
||||
mThread = BfpThread_Create(WorkThreadStub, (void*)this, 256 * 1024, BfpThreadCreateFlag_StackSizeReserve);
|
||||
}
|
||||
|
||||
void WorkThread::Stop()
|
||||
{
|
||||
WaitForFinish();
|
||||
}
|
||||
|
||||
void WorkThread::WaitForFinish()
|
||||
{
|
||||
if (mThread == NULL)
|
||||
return;
|
||||
|
||||
BfpThread_WaitFor(mThread, -1);
|
||||
}
|
||||
|
||||
bool WorkThread::WaitForFinish(int waitMS)
|
||||
{
|
||||
if (BfpThread_WaitFor(mThread, waitMS))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void WorkThreadFunc::Start(void(*func)(void*), void* param)
|
||||
{
|
||||
mParam = param;
|
||||
mFunc = func;
|
||||
WorkThread::Start();
|
||||
}
|
||||
|
||||
void WorkThreadFunc::Run()
|
||||
{
|
||||
mFunc(mParam);
|
||||
}
|
37
BeefySysLib/util/WorkThread.h
Normal file
37
BeefySysLib/util/WorkThread.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
#pragma once
|
||||
|
||||
#include "../Common.h"
|
||||
|
||||
NS_BF_BEGIN
|
||||
|
||||
class WorkThread
|
||||
{
|
||||
public:
|
||||
BfpThread* mThread;
|
||||
|
||||
public:
|
||||
WorkThread();
|
||||
virtual ~WorkThread();
|
||||
|
||||
virtual void Start();
|
||||
virtual void Stop();
|
||||
virtual void WaitForFinish();
|
||||
virtual bool WaitForFinish(int waitMS);
|
||||
|
||||
virtual void Run() = 0;
|
||||
};
|
||||
|
||||
class WorkThreadFunc : public WorkThread
|
||||
{
|
||||
public:
|
||||
void (*mFunc)(void*);
|
||||
void* mParam;
|
||||
|
||||
public:
|
||||
// Note: this startProc signature does not match BfpThreadStartProc -- here we abstract out the calling convention to be default
|
||||
// on all platforms (cdecl)
|
||||
void Start(void (*func)(void*), void* param);
|
||||
virtual void Run() override;
|
||||
};
|
||||
|
||||
NS_BF_END
|
Loading…
Add table
Add a link
Reference in a new issue