1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-07-06 16:25:59 +02:00

Initial checkin

This commit is contained in:
Brian Fiete 2019-08-23 11:56:54 -07:00
parent c74712dad9
commit 078564ac9e
3242 changed files with 1616395 additions and 0 deletions

View 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);
}

View 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

File diff suppressed because it is too large Load diff

View 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;
}

View 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;

File diff suppressed because it is too large Load diff

331
BeefySysLib/util/BeefPerf.h Normal file
View 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);

View 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

View 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

View 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

View 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);
}

View 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;

View 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;
}

View 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;

View 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
View 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;

View 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 );
}

View 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;

View 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));
}

View 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
View 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

File diff suppressed because it is too large Load diff

View 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;

View 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;
}

View 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

File diff suppressed because it is too large Load diff

140
BeefySysLib/util/Hash.h Normal file
View 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
View 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
View 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
View 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

View 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

View 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

View 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
View 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;

View 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;

View 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

View 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;

View file

@ -0,0 +1,2 @@
//#include "Point.h"

21
BeefySysLib/util/Point.h Normal file
View 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;

View 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;
}

View 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;

View 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;
}
}

View 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
View 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
View 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
View 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;

File diff suppressed because it is too large Load diff

View 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;
}

View 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
View 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
View 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);
}
};
}

View 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;

View 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;
}

View 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
View 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
View 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;

View 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
View 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;

View 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);
}

View 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