Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

633 lines
16 KiB

  1. /*
  2. * B U F F E R . H
  3. *
  4. * Data buffer processing
  5. *
  6. * Copyright 1986-1997 Microsoft Corporation, All Rights Reserved
  7. */
  8. #ifndef _EX_BUFFER_H_
  9. #define _EX_BUFFER_H_
  10. // Alignment macros ----------------------------------------------------------
  11. //
  12. #include <align.h>
  13. // Safe allocators -----------------------------------------------------------
  14. //
  15. #include <ex\exmem.h>
  16. // Stack buffers -------------------------------------------------------------
  17. //
  18. #include <ex\stackbuf.h>
  19. // StringSize usage ----------------------------------------------------------
  20. //
  21. // CbStringSize is the size of the string without the NULL termination
  22. // CbStringSizeNull is the size of the string with the NULL termination
  23. // CchStringLength is the length of the string without the NULL termination
  24. // CchzStringLength is the length of the string with the NULL termination
  25. //
  26. template<class X>
  27. inline int WINAPI CbStringSize(const X* const pszText)
  28. {
  29. int cch;
  30. Assert (pszText);
  31. cch = (sizeof(X) == sizeof(WCHAR))
  32. ? wcslen(reinterpret_cast<const WCHAR* const>(pszText))
  33. : strlen(reinterpret_cast<const CHAR* const>(pszText));
  34. return cch * sizeof(X);
  35. }
  36. template<class X>
  37. inline int WINAPI CbStringSizeNull(const X* const pszText)
  38. {
  39. int cch;
  40. Assert (pszText);
  41. cch = (sizeof(X) == sizeof(WCHAR))
  42. ? wcslen(reinterpret_cast<const WCHAR* const>(pszText))
  43. : strlen(reinterpret_cast<const CHAR* const>(pszText));
  44. return (cch + 1) * sizeof(X);
  45. }
  46. template<class X>
  47. inline int WINAPI CchStringLength(const X* const pszText)
  48. {
  49. int cch;
  50. Assert (pszText);
  51. cch = (sizeof(X) == sizeof(WCHAR))
  52. ? wcslen(reinterpret_cast<const WCHAR* const>(pszText))
  53. : strlen(reinterpret_cast<const CHAR* const>(pszText));
  54. return cch;
  55. }
  56. template<class X>
  57. inline int WINAPI CchzStringLength(const X* const pszText)
  58. {
  59. int cch;
  60. Assert (pszText);
  61. cch = (sizeof(X) == sizeof(WCHAR))
  62. ? wcslen(reinterpret_cast<const WCHAR* const>(pszText))
  63. : strlen(reinterpret_cast<const CHAR* const>(pszText));
  64. return cch + 1;
  65. }
  66. // StringBuffer vs ChainedStringBuffer usage ---------------------------------
  67. //
  68. // When should you use which one?
  69. // StringBuffer characteristics:
  70. // o Data stored in one contiguous memory block.
  71. // o Memory may be realloc'd.
  72. // o Only offsets (ib) to strings are returned.
  73. // ChainedStringBuffer characteristics:
  74. // o Memory not contiguous. Multiple chained buffers.
  75. // o Memory is never realloc'd.
  76. // o String pointers directly into chained buffers are returned.
  77. // Both have logarithmic allocation behavior (order log(n) alloc operations
  78. // will be done, where n is the max size of the data). This behavior is
  79. // governed by the m_cbChunkSize starting size and increments.
  80. //
  81. // StringBuffer template class -----------------------------------------------
  82. //
  83. // A simple variable-size, demand paged buffer abstraction.
  84. //
  85. template<class T>
  86. class StringBuffer
  87. {
  88. T * m_pData;
  89. UINT m_cbAllocated;
  90. UINT m_cbUsed;
  91. UINT m_cbChunkSize; // Count of bytes to alloc (dynamic).
  92. enum { CHUNKSIZE_START = 64 }; // Default starting chunk size (in bytes).
  93. // Memory allocation mechanism -------------------------------------------
  94. //
  95. UINT Alloc( UINT ibLoc, UINT cbAppend )
  96. {
  97. // Grow the data buffer if necessary
  98. //
  99. if ( ibLoc + cbAppend > m_cbAllocated )
  100. {
  101. T* pData;
  102. // Alloc the buffer.
  103. //
  104. UINT cbSize = max( m_cbChunkSize, cbAppend );
  105. if (m_pData)
  106. {
  107. pData = static_cast<T*>
  108. (ExRealloc( m_pData, m_cbAllocated + cbSize ));
  109. }
  110. else
  111. {
  112. pData = static_cast<T*>
  113. (ExAlloc( m_cbAllocated + cbSize ));
  114. }
  115. // When we are in the context of the server, our allocators
  116. // can fail without throwing. Bubble the error out.
  117. //
  118. if (NULL == pData)
  119. return static_cast<UINT>(-1);
  120. m_cbAllocated += cbSize;
  121. m_pData = pData;
  122. // Increase the chunk size, to get "logarithmic allocation behavior"
  123. //
  124. m_cbChunkSize *= 2;
  125. }
  126. return cbAppend;
  127. }
  128. // non-implemented operators
  129. //
  130. StringBuffer(const StringBuffer& );
  131. StringBuffer& operator=(const StringBuffer& );
  132. public:
  133. StringBuffer( ULONG cbChunkSize = CHUNKSIZE_START ) :
  134. m_pData(NULL),
  135. m_cbAllocated(0),
  136. m_cbUsed(0),
  137. m_cbChunkSize(cbChunkSize)
  138. {
  139. }
  140. ~StringBuffer()
  141. {
  142. ExFree( m_pData );
  143. }
  144. // There is no reason to make it constant on relinquish
  145. // we do not own the memory any more
  146. //
  147. T * relinquish()
  148. {
  149. T * tRet = m_pData;
  150. m_pData = NULL;
  151. m_cbUsed = 0;
  152. m_cbAllocated = 0;
  153. return tRet;
  154. }
  155. const T * PContents() const { return m_pData; }
  156. UINT CbSize() const { return m_cbUsed; }
  157. UINT CchSize() const { return m_cbUsed/sizeof(T); }
  158. VOID Reset() { m_cbUsed = 0; }
  159. // Counted type appends --------------------------------------------------
  160. //
  161. UINT AppendAt( UINT ibLoc, UINT cbAppend, const T * pAppend)
  162. {
  163. UINT cb;
  164. // Ensure there is enough memory to hold what is needed
  165. //
  166. cb = Alloc( ibLoc, cbAppend );
  167. // When we are in the context of the server, our allocators
  168. // can fail without throwing. Bubble the error out.
  169. //
  170. if (cb != cbAppend)
  171. return cb;
  172. // Append the data to the buffer
  173. //
  174. CopyMemory( reinterpret_cast<LPBYTE>(m_pData) + ibLoc,
  175. pAppend,
  176. cbAppend );
  177. m_cbUsed = ibLoc + cbAppend;
  178. return cbAppend;
  179. }
  180. UINT Append( UINT cbAppend, const T * pAppend )
  181. {
  182. return AppendAt( CbSize(), cbAppend, pAppend );
  183. }
  184. // Uncounted appends -----------------------------------------------------
  185. //
  186. UINT AppendAt( UINT ibLoc, const T * const pszText )
  187. {
  188. return AppendAt( ibLoc, CbStringSize<T>(pszText), pszText );
  189. }
  190. UINT Append( const T * const pszText )
  191. {
  192. return AppendAt( CbSize(), CbStringSize<T>(pszText), pszText );
  193. }
  194. BOOL FAppend( const T * const pszText )
  195. {
  196. if (AppendAt( CbSize(), CbStringSize<T>(pszText), pszText ) ==
  197. static_cast<UINT>(-1))
  198. {
  199. return FALSE;
  200. }
  201. return TRUE;
  202. }
  203. BOOL FTerminate()
  204. {
  205. T ch = 0;
  206. if (AppendAt(CbSize(), sizeof(T), &ch) == static_cast<UINT>(-1))
  207. return FALSE;
  208. return TRUE;
  209. }
  210. };
  211. // ChainedBuffer template class -----------------------------------------
  212. //
  213. // A variable-size, demand paged, non-realloc-ing buffer pool abstraction.
  214. // When would you use this guy? When you need to allocate heap memory for
  215. // many small data items and would rather do it in sizeable chunks rather
  216. // than allocate each small data item individually. You need the data to
  217. // to stay because you are going to point to it (no reallocs are allowed
  218. // under your feet).
  219. //
  220. // NOTE: Caller is required to allocate items to be properly aligned if
  221. // it the item being allocated is an element that requires a specific
  222. // alignment (ie. struct's).
  223. //
  224. template<class T>
  225. class ChainedBuffer
  226. {
  227. // CHAINBUF -- Hungarian hb
  228. //
  229. struct CHAINBUF
  230. {
  231. CHAINBUF * phbNext;
  232. UINT cbAllocated;
  233. UINT cbUsed;
  234. BYTE * pData;
  235. };
  236. CHAINBUF * m_phbData; // The data.
  237. CHAINBUF * m_phbCurrent; // The current buffer for appends.
  238. UINT m_cbChunkSizeInit; // Initial value of m_cbChunkSize
  239. UINT m_cbChunkSize; // Count of bytes to alloc (dynamic).
  240. // Alignments
  241. //
  242. UINT m_uAlign;
  243. // Destruction function
  244. //
  245. void FreeChainBuf( CHAINBUF * phbBuf )
  246. {
  247. while (phbBuf)
  248. {
  249. CHAINBUF * phbNext = phbBuf->phbNext;
  250. ExFree(phbBuf);
  251. phbBuf = phbNext;
  252. }
  253. }
  254. protected:
  255. enum { CHUNKSIZE_START = 64 }; // Default starting chunk size (in bytes).
  256. public:
  257. ChainedBuffer( ULONG cbChunkSize = CHUNKSIZE_START,
  258. UINT uAlign = ALIGN_NATURAL) :
  259. m_phbData(NULL),
  260. m_phbCurrent(NULL),
  261. m_cbChunkSizeInit(cbChunkSize),
  262. m_cbChunkSize(cbChunkSize),
  263. m_uAlign(uAlign)
  264. {
  265. }
  266. ~ChainedBuffer() { FreeChainBuf( m_phbData ); }
  267. // Alloc a fixed size buffer ---------------------------------------
  268. //
  269. T * Alloc( UINT cbAlloc )
  270. {
  271. BYTE * pbAdd;
  272. // So that we don't do anything stupid.... Make sure we allocate
  273. // stuff aligned for the template-parameterized type 'T'.
  274. //
  275. cbAlloc = AlignN(cbAlloc, m_uAlign);
  276. // Add another data buffer if necessary.
  277. //
  278. // It's necessary if we don't have a buffer, or
  279. // if the current buffer doesn't have enough free space.
  280. //
  281. if ( ( !m_phbCurrent ) ||
  282. ( m_phbCurrent->cbUsed + cbAlloc > m_phbCurrent->cbAllocated ) )
  283. {
  284. // Alloc the new buffer.
  285. //
  286. UINT cbSize = max(m_cbChunkSize, cbAlloc);
  287. CHAINBUF * phbNew = static_cast<CHAINBUF *>
  288. (ExAlloc( cbSize + sizeof(CHAINBUF) ));
  289. // When we are in the context of the server, our allocators
  290. // can fail without throwing. Bubble the error out.
  291. //
  292. if (NULL == phbNew)
  293. return NULL;
  294. // Fill in the header fields.
  295. //
  296. phbNew->phbNext = NULL;
  297. phbNew->cbAllocated = cbSize;
  298. phbNew->cbUsed = 0;
  299. phbNew->pData = reinterpret_cast<BYTE *>(phbNew) + sizeof(CHAINBUF);
  300. // Add the new buffer into the chain.
  301. //
  302. if ( !m_phbData )
  303. {
  304. Assert(!m_phbCurrent);
  305. m_phbData = phbNew;
  306. }
  307. else
  308. {
  309. Assert(m_phbCurrent);
  310. phbNew->phbNext = m_phbCurrent->phbNext;
  311. m_phbCurrent->phbNext = phbNew;
  312. }
  313. // Use the new buffer (it is now the current buffer).
  314. //
  315. m_phbCurrent = phbNew;
  316. // Increase the chunk size, to get "logarithmic allocation behavior".
  317. //
  318. m_cbChunkSize *= 2;
  319. }
  320. Assert(m_phbCurrent);
  321. Assert(m_phbCurrent->pData);
  322. // Find the correct starting spot in the current buffer.
  323. //
  324. pbAdd = m_phbCurrent->pData + m_phbCurrent->cbUsed;
  325. // Update our count of bytes actually used.
  326. //
  327. m_phbCurrent->cbUsed += cbAlloc;
  328. // Return the alloced data's starting point to the caller.
  329. //
  330. return reinterpret_cast<T *>(pbAdd);
  331. }
  332. // Clear all buffers -----------------------------------------------------
  333. //
  334. void Clear()
  335. {
  336. //
  337. // Clear out data from, but do not free, the buffers
  338. // in the chain. This allows a ChainedStringBuffer to be
  339. // reused without necessarily having to reallocate its
  340. // consituent buffers.
  341. //
  342. for ( CHAINBUF * phb = m_phbData; phb; phb = phb->phbNext )
  343. phb->cbUsed = 0;
  344. // Free any nodes after the first, they do not get reused
  345. // as you might expect.
  346. //
  347. if ( m_phbCurrent )
  348. {
  349. FreeChainBuf( m_phbCurrent->phbNext );
  350. m_phbCurrent->phbNext = NULL;
  351. }
  352. //
  353. // Reset the current buffer to the first one
  354. //
  355. m_phbCurrent = m_phbData;
  356. //
  357. // Reset the chunk size to the initial chunk size
  358. //
  359. m_cbChunkSize = m_cbChunkSizeInit;
  360. }
  361. // Get the total size of the buffer ---------------------------------------
  362. //
  363. DWORD CbBufferSize() const
  364. {
  365. DWORD cbTotal = 0;
  366. for ( CHAINBUF * phb = m_phbData; phb; phb = phb->phbNext )
  367. cbTotal += phb->cbUsed;
  368. return cbTotal;
  369. }
  370. // Dump the whole buffer contents into a contiguous buffer------------------
  371. //
  372. DWORD Dump(T *tBuffer, DWORD cbSize) const
  373. {
  374. BYTE *pbBuffer = NULL;
  375. Assert(tBuffer);
  376. Assert(cbSize >= CbBufferSize());
  377. pbBuffer = reinterpret_cast<PBYTE>(tBuffer);
  378. // walk thru the list and dump all the contents
  379. //
  380. for ( CHAINBUF * phb = m_phbData; phb; phb = phb->phbNext )
  381. {
  382. memcpy(pbBuffer, phb->pData, phb->cbUsed);
  383. pbBuffer += phb->cbUsed;
  384. }
  385. // return the actual size
  386. //
  387. return static_cast<DWORD>( (pbBuffer) - (reinterpret_cast<PBYTE>(tBuffer)) );
  388. }
  389. };
  390. // ChainedStringBuffer template class -----------------------------------------
  391. //
  392. // A variable-size, demand paged, non-realloc-ing string buffer pool abstraction.
  393. // Why would you use this guy instead of StringBuffer (above)?
  394. // If you want the strings to STAY, and you don't care about them being
  395. // in a contiguous block of memory.
  396. // NOTE: We still keep the data in order, it's just not all in one block.
  397. //
  398. // This template is only to be used for CHAR and WCHAR strings.
  399. // Use the ChainedBuffer template for other types.
  400. //
  401. template<class T>
  402. class ChainedStringBuffer : public ChainedBuffer<T>
  403. {
  404. // non-implemented operators
  405. //
  406. ChainedStringBuffer(const ChainedStringBuffer& );
  407. ChainedStringBuffer& operator=(const ChainedStringBuffer& );
  408. public:
  409. // Declare constructor inline (for efficiency) but do not provide
  410. // a definition here. Definitions for the two template paramater
  411. // types that we support (CHAR and WCHAR) are provided explicitly
  412. // below.
  413. //
  414. inline ChainedStringBuffer( ULONG cbChunkSize = CHUNKSIZE_START );
  415. // Counted append --------------------------------------------------
  416. //
  417. T * Append( UINT cbAppend, const T * pAppend )
  418. {
  419. T* pAdd;
  420. // Reserve the space
  421. //
  422. pAdd = Alloc( cbAppend );
  423. // When we are in the context of the server, our allocators
  424. // can fail without throwing. Bubble the error out.
  425. //
  426. if (NULL == pAdd)
  427. return NULL;
  428. // Append the data to the current buffer.
  429. //
  430. CopyMemory( pAdd, pAppend, cbAppend );
  431. // Return the data's starting point to the caller.
  432. //
  433. return pAdd;
  434. }
  435. // Uncounted append ------------------------------------------------------
  436. // NOTE: The append does NOT count the trailing NULL of the string!
  437. //
  438. T * Append( const T * const pszText )
  439. {
  440. return Append( CbStringSize<T>(pszText), pszText );
  441. }
  442. // Uncounted append with trailing NULL -----------------------------------
  443. //
  444. T * AppendWithNull( const T * const pszText )
  445. {
  446. return Append( CbStringSizeNull<T>(pszText), pszText );
  447. }
  448. };
  449. // Specialized ChainedStringBuffer constructor for CHAR ----------------------
  450. //
  451. // Pass ALIGN_NONE to the ChainedBuffer constructor because CHAR strings
  452. // do not require alignment.
  453. //
  454. // !!! DO NOT use ChainedStringBuffer<CHAR> for anything that must be aligned!
  455. //
  456. inline
  457. ChainedStringBuffer<CHAR>::ChainedStringBuffer( ULONG cbChunkSize )
  458. : ChainedBuffer<CHAR>(cbChunkSize, ALIGN_NONE )
  459. {
  460. }
  461. // Specialized ChainedStringBuffer constructor for WCHAR ---------------------
  462. //
  463. // Pass ALIGN_WORD to the ChainedBuffer constructor because WCHAR strings
  464. // require WORD alignment.
  465. //
  466. inline
  467. ChainedStringBuffer<WCHAR>::ChainedStringBuffer( ULONG cbChunkSize )
  468. : ChainedBuffer<WCHAR>(cbChunkSize, ALIGN_WORD )
  469. {
  470. }
  471. // LinkedBuffer template class -----------------------------------------------
  472. //
  473. // A variable-size, demand paged, non-realloc-ing buffer pool abstraction.
  474. // When would you use this guy? When you need to allocate heap memory for
  475. // many small data items and would rather do it in sizeable chunks rather
  476. // than allocate each small data item individually and the resulting pointer
  477. // you need to pass into the store needs to be "linked".
  478. //
  479. // IMPORTANT:
  480. //
  481. // Linked allocation mechanism is stolen from \store\src\_util\mdbmig.cxx
  482. // and needs to always match that mechanism.
  483. //
  484. PVOID ExAllocLinked(LPVOID pvLinked, UINT cb);
  485. VOID ExFreeLinked(LPVOID* ppv);
  486. template<class T>
  487. class LinkedBuffer
  488. {
  489. PVOID m_pvHead;
  490. PVOID PvAllocLinked(UINT cb)
  491. {
  492. PVOID pv = ExAllocLinked(m_pvHead, cb);
  493. if (NULL == m_pvHead)
  494. m_pvHead = pv;
  495. return pv;
  496. }
  497. public:
  498. LinkedBuffer() : m_pvHead(NULL)
  499. {
  500. }
  501. ~LinkedBuffer()
  502. {
  503. if (m_pvHead)
  504. ExFreeLinked(&m_pvHead);
  505. }
  506. // Alloc a fixed size buffer ---------------------------------------
  507. //
  508. T * Alloc( UINT cbAlloc )
  509. {
  510. return reinterpret_cast<T*>(PvAllocLinked (cbAlloc));
  511. }
  512. PVOID PvTop() { Assert (m_pvHead); return m_pvHead; }
  513. PVOID relinquish()
  514. {
  515. PVOID pv = m_pvHead;
  516. m_pvHead = NULL;
  517. return pv;
  518. }
  519. void clear()
  520. {
  521. if (m_pvHead)
  522. {
  523. ExFreeLinked(&m_pvHead);
  524. m_pvHead = NULL;
  525. }
  526. }
  527. void takeover ( LinkedBuffer<T> & lnkbufOldOwner )
  528. {
  529. m_pvHead = lnkbufOldOwner.m_pvHead;
  530. lnkbufOldOwner.m_pvHead = NULL;
  531. }
  532. };
  533. #endif // _EX_BUFFER_H_