hstring.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526
  1. #include "cm_local.h"
  2. #include "hstring.h"
  3. #if defined (_DEBUG) && defined (_WIN32)
  4. #define WIN32_LEAN_AND_MEAN 1
  5. //#include <windows.h> // for Sleep for Z_Malloc recovery attempy
  6. #include "platform.h"
  7. #endif
  8. // mapPoolBlockCount is defined differently in the executable (sv_main.cpp) and the game dll (g_main.cpp) cuz
  9. //we likely don't need as many blocks in the executable as we do in the game
  10. extern int mapPoolBlockCount;
  11. // Used to fool optimizer during compilation of mem touch routines.
  12. int HaHaOptimizer2=0;
  13. #ifndef _XBOX
  14. CMapPoolLow &GetMapPool()
  15. {
  16. // this may need to be ifdefed to be different for different modules
  17. static CMapPoolLow thePool;
  18. return thePool;
  19. }
  20. #endif
  21. #define MAPBLOCK_SIZE_NODES (1024)
  22. #define MAPNODE_FREE (0xa1)
  23. #define MAPNODE_INUSE (0x94)
  24. struct SMapNode
  25. {
  26. unsigned char mData[MAP_NODE_SIZE-2];
  27. unsigned char mMapBlockNum;
  28. unsigned char mTag;
  29. };
  30. class CMapBlock
  31. {
  32. int mId;
  33. char mRaw[(MAPBLOCK_SIZE_NODES+1)*MAP_NODE_SIZE];
  34. SMapNode *mNodes;
  35. int mLastNode;
  36. public:
  37. CMapBlock(int id,vector <void *> &freeList) :
  38. mLastNode(0)
  39. {
  40. // Alloc node storage for MAPBLOCK_SIZE_NODES worth of nodes.
  41. mNodes=(SMapNode *)((((unsigned long)mRaw)+MAP_NODE_SIZE)&~(unsigned long)0x1f);
  42. // Set all nodes to initially be free.
  43. int i;
  44. for(i=0;i<MAPBLOCK_SIZE_NODES;i++)
  45. {
  46. mNodes[i].mMapBlockNum=id;
  47. mNodes[i].mTag=MAPNODE_FREE;
  48. freeList.push_back((void *)&mNodes[i]);
  49. }
  50. }
  51. bool bOwnsNode(void *node)
  52. {
  53. return((((SMapNode *)node)>=&mNodes[0])&&(((SMapNode *)node)<&mNodes[MAPBLOCK_SIZE_NODES]));
  54. }
  55. };
  56. #ifndef _XBOX
  57. CMapPoolLow::CMapPoolLow()
  58. {
  59. mLastBlockNum=-1;
  60. }
  61. CMapPoolLow::~CMapPoolLow()
  62. {
  63. #if _DEBUG
  64. char mess[1000];
  65. #if _GAME
  66. if(mFreeList.size()<mMapBlocks.size()*MAPBLOCK_SIZE_NODES)
  67. {
  68. sprintf(mess,"[MEM][GAME] !!!! Map Pool Leaked %d nodes\n",(MAPBLOCK_SIZE_NODES*mMapBlocks.size())-mFreeList.size());
  69. OutputDebugString(mess);
  70. }
  71. sprintf(mess, "[MEM][GAME] Map Pool max. mem used = %d\n",mMapBlocks.size()*MAPBLOCK_SIZE_NODES*MAP_NODE_SIZE);
  72. OutputDebugString(mess);
  73. #elif _CGAME
  74. if (mFreeList.size()<mMapBlocks.size()*MAPBLOCK_SIZE_NODES)
  75. {
  76. sprintf(mess, "[MEM][CGAME] !!!! Map Pool Leaked %d nodes\n",(MAPBLOCK_SIZE_NODES*mMapBlocks.size())-mFreeList.size());
  77. OutputDebugString(mess);
  78. }
  79. sprintf(mess, "[MEM][CGAME] Map Pool max. mem used = %d\n",mMapBlocks.size()*MAPBLOCK_SIZE_NODES*MAP_NODE_SIZE);
  80. OutputDebugString(mess);
  81. #else
  82. if (mFreeList.size()<mMapBlocks.size()*MAPBLOCK_SIZE_NODES)
  83. {
  84. sprintf(mess, "[MEM][EXE] !!!! Map Pool Leaked %d nodes\n",(MAPBLOCK_SIZE_NODES*mMapBlocks.size())-mFreeList.size());
  85. OutputDebugString(mess);
  86. }
  87. sprintf(mess, "[MEM][EXE] Map Pool max. mem used = %d\n",mMapBlocks.size()*MAPBLOCK_SIZE_NODES*MAP_NODE_SIZE);
  88. OutputDebugString(mess);
  89. #endif
  90. #endif
  91. int i;
  92. for (i=0;i<mMapBlocks.size();i++)
  93. {
  94. delete mMapBlocks[i];
  95. }
  96. }
  97. void *CMapPoolLow::Alloc()
  98. {
  99. // Try to request a node. First we look in the free-list, but if that
  100. // happens to be empty, we allocate more storage in the current CMapBlock.
  101. void *node=0;
  102. if(mFreeList.size())
  103. {
  104. // Retrieve the node to be recycled.
  105. node=(void *)mFreeList[mFreeList.size()-1];
  106. mFreeList.pop_back();
  107. }
  108. else
  109. {
  110. // None free, so alloc another block.
  111. CMapBlock *block=new CMapBlock(mLastBlockNum+1,mFreeList);
  112. assert(block);
  113. mMapBlocks.push_back(block);
  114. mLastBlockNum++;
  115. node=(void *)mFreeList[mFreeList.size()-1];
  116. mFreeList.pop_back();
  117. }
  118. // Validate we aren't somehow grabbing something that is already in use
  119. // and also that the end marker is intact.
  120. assert(((SMapNode *)node)->mTag==MAPNODE_FREE);
  121. assert((((SMapNode *)node)->mMapBlockNum)>=0);
  122. assert((((SMapNode *)node)->mMapBlockNum)<256);
  123. assert((((SMapNode *)node)->mMapBlockNum)<=mLastBlockNum);
  124. assert(mMapBlocks[((SMapNode *)node)->mMapBlockNum]->bOwnsNode(node));
  125. // Ok, mark the node as in use.
  126. ((SMapNode *)node)->mTag=MAPNODE_INUSE;
  127. return(node);
  128. }
  129. void CMapPoolLow::Free(void *p)
  130. {
  131. // Validate that someone isn't trying to double free this node and also
  132. // that the end marker is intact.
  133. assert(((SMapNode *)p)->mTag==MAPNODE_INUSE);
  134. assert((((SMapNode *)p)->mMapBlockNum)>=0);
  135. assert((((SMapNode *)p)->mMapBlockNum)<256);
  136. assert((((SMapNode *)p)->mMapBlockNum)<=mLastBlockNum);
  137. assert(mMapBlocks[((SMapNode *)p)->mMapBlockNum]->bOwnsNode(p));
  138. // Ok, mark the the node as free.
  139. ((SMapNode *)p)->mTag=MAPNODE_FREE;
  140. // Add a new freelist entry to point at this node.
  141. mFreeList.push_back(p);
  142. }
  143. void CMapPoolLow::TouchMem()
  144. {
  145. int i,j;
  146. unsigned char *memory;
  147. int totSize=0;
  148. for(i=0;i<mMapBlocks.size();i++)
  149. {
  150. memory=(unsigned char *)mMapBlocks[i];
  151. totSize+=sizeof(CMapBlock);
  152. for(j=0;j<sizeof(CMapBlock);j+=256)
  153. {
  154. HaHaOptimizer2+=memory[j];
  155. }
  156. }
  157. #ifdef _DEBUG
  158. Com_Printf("MapPool: Bytes touched %i\n",totSize);
  159. #endif
  160. }
  161. #endif // _XBOX
  162. ////////////
  163. // hString stuff
  164. ////////////
  165. #define MAX_HASH (65536*2)
  166. // Max number of strings we can ever deal with.
  167. #define MAX_HSTRINGS 100000
  168. // Size of a string storage block in bytes.
  169. #define BLOCK_SIZE 65536
  170. int HashFunction(const char *key)
  171. {
  172. long hash=0;
  173. int i=0;
  174. unsigned char letter;
  175. letter = (unsigned char)*key++;
  176. while (letter)
  177. {
  178. hash += (long)(letter) * (i + 119);
  179. i++;
  180. letter = (unsigned char)*key++;
  181. }
  182. hash &= MAX_HASH - 1;
  183. return (int)hash;
  184. }
  185. class CHashHelper
  186. {
  187. int mHashes[MAX_HASH];
  188. int mFindPtr;
  189. int mFindPtrStart;
  190. public:
  191. CHashHelper()
  192. {
  193. int i;
  194. for (i=0;i<MAX_HASH;i++)
  195. {
  196. mHashes[i]=0;
  197. }
  198. }
  199. void Add(int hash,int value)
  200. {
  201. assert(hash>=0&&hash<MAX_HASH);
  202. assert(value); // 0 is the empty marker
  203. int i=hash;
  204. while (mHashes[i])
  205. {
  206. assert(mHashes[i]!=value); //please don't insert things twice
  207. i=(i+1)&(MAX_HASH-1);
  208. assert(i!=hash); //hash table is full?
  209. }
  210. mHashes[i]=value;
  211. }
  212. int FindFirst(int hash)
  213. {
  214. mFindPtr=hash;
  215. mFindPtrStart=hash;
  216. return FindNext();
  217. }
  218. int FindNext()
  219. {
  220. assert(mFindPtr>=0&&mFindPtr<MAX_HASH);
  221. int val=mHashes[mFindPtr];
  222. mFindPtr=(mFindPtr+1)&(MAX_HASH-1);
  223. assert(mFindPtr!=mFindPtrStart); //hash table full?
  224. return val;
  225. }
  226. void TouchMem()
  227. {
  228. int i;
  229. for(i=0;i<sizeof(mHashes);i+=256)
  230. {
  231. HaHaOptimizer2+=((unsigned char *)mHashes)[i];
  232. }
  233. #ifdef _DEBUG
  234. Com_Printf("Hash helper: Bytes touched %i\n",sizeof(mHashes));
  235. #endif
  236. }
  237. };
  238. CHashHelper &HashHelper()
  239. {
  240. static CHashHelper It;
  241. return It;
  242. }
  243. static char *gCharPtrs[MAX_HSTRINGS];
  244. class CHSBlock
  245. {
  246. int mBytesUsed;
  247. char mRaw[BLOCK_SIZE];
  248. public:
  249. CHSBlock(void) :
  250. mBytesUsed(0)
  251. {
  252. // So we can do a comparison of blocks for debug purposes.
  253. memset(mRaw,0,BLOCK_SIZE);
  254. };
  255. char *Alloc(int sizeBytes)
  256. {
  257. // Remember to include 0 termintor in size.
  258. sizeBytes++;
  259. // Is it WAAAY to big? If so we complain loudly.
  260. assert(sizeBytes<=BLOCK_SIZE);
  261. // If we don't have space in the current block, return failure.
  262. if(sizeBytes>(BLOCK_SIZE-mBytesUsed))
  263. {
  264. return(0);
  265. }
  266. // Return the pointer to the start of allocated space.
  267. char *ret=&mRaw[mBytesUsed];
  268. mBytesUsed+=sizeBytes;
  269. return ret;
  270. }
  271. bool operator== (const CHSBlock &block) const
  272. {
  273. if(!memcmp(mRaw,block.mRaw,BLOCK_SIZE))
  274. {
  275. return(true);
  276. }
  277. return(false);
  278. }
  279. };
  280. class CPool
  281. {
  282. vector<CHSBlock *> mBlockVec;
  283. public:
  284. int mNextStringId;
  285. int mLastBlockNum;
  286. CPool(void) :
  287. mNextStringId(1),
  288. mLastBlockNum(-1)
  289. {
  290. memset(gCharPtrs,0,MAX_HSTRINGS*4);
  291. }
  292. ~CPool(void)
  293. {
  294. int i;
  295. for (i=0;i<mBlockVec.size();i++)
  296. {
  297. delete mBlockVec[i];
  298. }
  299. }
  300. char *Alloc(int sizeBytes,int &id)
  301. {
  302. // Can't alloc more than MAX_HSTRINGS.
  303. assert(mNextStringId<MAX_HSTRINGS);
  304. char *raw=0;
  305. if (mLastBlockNum>=0)
  306. {
  307. // Get the pointer to the start of allocated space in the current block.
  308. raw=mBlockVec[mLastBlockNum]->Alloc(sizeBytes);
  309. }
  310. if(!raw)
  311. {
  312. // Ok, make a new empty block and append it.
  313. CHSBlock *block=new(CHSBlock);
  314. mBlockVec.push_back(block);
  315. mLastBlockNum++;
  316. raw=mBlockVec[mLastBlockNum]->Alloc(sizeBytes);
  317. }
  318. // Should never really happen!!
  319. assert(raw);
  320. id=mNextStringId;
  321. gCharPtrs[mNextStringId]=raw;
  322. mNextStringId++;
  323. return(raw);
  324. }
  325. bool operator== (const CPool &pool) const
  326. {
  327. int i;
  328. for(i=0;i<mBlockVec.size();i++)
  329. {
  330. if(!(*mBlockVec[i]==*pool.mBlockVec[i]))
  331. {
  332. return(false);
  333. }
  334. }
  335. return(true);
  336. }
  337. void TouchMem()
  338. {
  339. int i,j;
  340. unsigned char *memory;
  341. int totSize=0;
  342. for(i=0;i<mBlockVec.size();i++)
  343. {
  344. memory=(unsigned char *)mBlockVec[i];
  345. totSize+=sizeof(CHSBlock);
  346. for(j=0;j<sizeof(CHSBlock);j+=256)
  347. {
  348. HaHaOptimizer2+=memory[j];
  349. }
  350. }
  351. #ifdef _DEBUG
  352. Com_Printf("String Pool: Bytes touched %i\n",totSize);
  353. #endif
  354. }
  355. };
  356. #ifdef _DEBUG
  357. CPool &TheDebugPool(void);
  358. CPool &ThePool(void);
  359. class CPoolChecker
  360. {
  361. public:
  362. CPoolChecker()
  363. {
  364. TheDebugPool();
  365. ThePool();
  366. }
  367. ~CPoolChecker()
  368. {
  369. #if 0
  370. int i;
  371. for (i=1;i<ThePool().mNextStringId;i++)
  372. {
  373. OutputDebugString(gCharPtrs[i]);
  374. OutputDebugString("\n");
  375. }
  376. #endif
  377. #if _DEBUG
  378. char mess[1000];
  379. #if _GAME
  380. sprintf(mess,"[MEM][GAME] String Pool %d unique strings, %dK\n",ThePool().mNextStringId,(ThePool().mLastBlockNum+1)*BLOCK_SIZE/1024);
  381. #elif _CGAME
  382. sprintf(mess,"[MEM][CGAME] String Pool %d unique strings, %dK\n",ThePool().mNextStringId,(ThePool().mLastBlockNum+1)*BLOCK_SIZE/1024);
  383. #else
  384. sprintf(mess,"[MEM][EXE] String Pool %d unique strings, %dK\n",ThePool().mNextStringId,(ThePool().mLastBlockNum+1)*BLOCK_SIZE/1024);
  385. #endif
  386. OutputDebugString(mess);
  387. #endif
  388. // if this fails it means the string storage is CORRUPTED, let someone know
  389. assert(TheDebugPool()==ThePool());
  390. }
  391. };
  392. static CPoolChecker TheCPoolChecker;
  393. CPool &TheDebugPool(void)
  394. {
  395. static CPool theDebugPool;
  396. return(theDebugPool);
  397. }
  398. #endif
  399. CPool &ThePool(void)
  400. {
  401. static CPool thePool;
  402. return(thePool);
  403. }
  404. void TouchStringPool(void)
  405. {
  406. ThePool().TouchMem();
  407. HashHelper().TouchMem();
  408. }
  409. //
  410. // Now the rest of the hString class.
  411. //
  412. #ifdef _XBOX
  413. namespace exeNamespace
  414. {
  415. #endif
  416. void hstring::Init(const char *str)
  417. {
  418. if(!str)
  419. {
  420. mId=0;
  421. return;
  422. }
  423. int hash=HashFunction(str);
  424. int id=HashHelper().FindFirst(hash);
  425. while (id)
  426. {
  427. assert(id>0&&id<ThePool().mNextStringId);
  428. if (!strcmp(str,gCharPtrs[id]))
  429. {
  430. mId=id;
  431. return;
  432. }
  433. id=HashHelper().FindNext();
  434. }
  435. char *raw=ThePool().Alloc(strlen(str),mId);
  436. strcpy(raw,str);
  437. HashHelper().Add(hash,mId);
  438. #ifdef _DEBUG
  439. int test;
  440. raw=TheDebugPool().Alloc(strlen(str),test);
  441. assert(test==mId);
  442. strcpy(raw,str);
  443. #endif
  444. }
  445. const char *hstring::c_str(void) const
  446. {
  447. if(!mId)
  448. {
  449. return("");
  450. }
  451. assert(mId>0&&mId<ThePool().mNextStringId);
  452. return(gCharPtrs[mId]);
  453. }
  454. string hstring::str(void) const
  455. {
  456. if(!mId)
  457. {
  458. return(string());
  459. }
  460. assert(mId>0&&mId<ThePool().mNextStringId);
  461. return string(gCharPtrs[mId]);
  462. }
  463. #ifdef _XBOX
  464. } // exeNamespace
  465. #endif