Source code of Windows XP (NT5)
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.

479 lines
13 KiB

  1. #if !defined(FUSION_ARRAYHELP_H_INCLUDED_)
  2. #define FUSION_ARRAYHELP_H_INCLUDED_
  3. #if _MSC_VER > 1000
  4. #pragma once
  5. #endif // _MSC_VER > 1000
  6. #include <windows.h>
  7. #include <oleauto.h>
  8. #include "fusionheap.h"
  9. #include "fusiontrace.h"
  10. //
  11. // arrayhelp.h
  12. //
  13. // Helper function(s) to deal with growable arrays.
  14. //
  15. // Users of this utility should provide explicit template
  16. // specializations for classes for which you can safely (without
  17. // possibility of failure) transfer the contens from a source
  18. // instance to a destination instance, leaving the source "empty".
  19. //
  20. // If moving the data may fail, you must provide a specialization
  21. // of FusionCopyContents() which returns an appropriate HRESULT
  22. // on failure.
  23. //
  24. //
  25. // C++ note:
  26. //
  27. // the C++ syntax for explicit function template specialization
  28. // is:
  29. //
  30. // template <> BOOLEAN FusionCanMoveContents<CFoo>(CFoo *p) { UNUSED(p); return TRUE; }
  31. //
  32. #if !defined(FUSION_UNUSED)
  33. #define FUSION_UNUSED(x) (x)
  34. #endif
  35. class CSaveErrorInfo
  36. {
  37. public:
  38. CSaveErrorInfo() : m_pIErrorInfo(NULL) { ::GetErrorInfo(0, &m_pIErrorInfo); }
  39. ~CSaveErrorInfo() { ::SetErrorInfo(0, m_pIErrorInfo); if (m_pIErrorInfo != NULL) m_pIErrorInfo->Release(); }
  40. private:
  41. IErrorInfo *m_pIErrorInfo;
  42. };
  43. //
  44. // Alternate CSaveErrorInfo implementation for templates which want to not require
  45. // a link dependency on oleaut32.dll.
  46. //
  47. class CSaveErrorInfoNull
  48. {
  49. public:
  50. CSaveErrorInfoNull() { }
  51. ~CSaveErrorInfoNull() { }
  52. };
  53. //
  54. // The default implementation just does assignment which may not fail;
  55. // you can (and must if assignment may fail) specialize as you like to
  56. // do something that avoids data copies; you may assume that the source
  57. // element will be destroyed momentarily.
  58. //
  59. //
  60. // The FusionCanMemcpyContents() template function is used to determine
  61. // if a class is trivial enough that a raw byte transfer of the old
  62. // contents to the new contents is sufficient. The default is that the
  63. // assignment operator is used as that is the only safe alternative.
  64. //
  65. template <typename T>
  66. inline BOOLEAN
  67. FusionCanMemcpyContents(
  68. T *ptDummyRequired = NULL
  69. )
  70. {
  71. FUSION_UNUSED(ptDummyRequired);
  72. return FALSE;
  73. }
  74. //
  75. // The FusionCanMoveContents() template function is used by the array
  76. // copy template function to optimize for the case that it should use
  77. // FusionMoveContens<T>().
  78. //
  79. // When overriding this function, the general rule is that if the data
  80. // movement may allocate memory etc. that will fail, we need to use the
  81. // FusionCopyContens() member function instead.
  82. //
  83. // It takes a single parameter which is not used because a C++ template
  84. // function must take at least one parameter using the template type so
  85. // that the decorated name is unique.
  86. //
  87. template <typename T>
  88. inline BOOLEAN
  89. FusionCanMoveContents(
  90. T *ptDummyRequired = NULL
  91. )
  92. {
  93. FUSION_UNUSED(ptDummyRequired);
  94. return FALSE;
  95. }
  96. template <> inline BOOLEAN
  97. FusionCanMoveContents<LPWSTR>(LPWSTR *ptDummyRequired)
  98. {
  99. FUSION_UNUSED(ptDummyRequired);
  100. return TRUE;
  101. }
  102. //
  103. // Override FusionMoveContents<T> to be a useful implementation which
  104. // takes the contents of rtSource and transfers them to rtDestination.
  105. // The transfer may not fail (returns VOID). The expectation is that
  106. // any value that was stored in rtSource are moved to rtDestination
  107. // and rtSource is left in a quiescent state. E.g. any pointers to
  108. // objects can be simply assigned from rtSource to rtDestination and
  109. // then set to NULL in rtSource. You may also assume that the destination
  110. // element has only had the default constructor run on it, so you
  111. // may choose to take shortcuts about not freeing non-NULL pointers
  112. // in rtDestination if you see fit.
  113. //
  114. template <typename T>
  115. inline VOID
  116. FusionMoveContents(
  117. T &rtDestination,
  118. T &rtSource
  119. )
  120. {
  121. rtDestination = rtSource;
  122. }
  123. template <> inline VOID
  124. FusionMoveContents<LPWSTR>(
  125. LPWSTR &rtDestination,
  126. LPWSTR &rtSource
  127. )
  128. {
  129. if ( rtDestination )
  130. FUSION_DELETE_ARRAY(rtDestination);
  131. rtDestination = rtSource;
  132. rtSource = NULL;
  133. }
  134. //
  135. // FusionCopyContents is a default implementation of the assignment
  136. // operation from rtSource to rtDestination, except that it may return a
  137. // failure status. Trivial classes which do define an assignment
  138. // operator may just use the default definition, but any copy implementations
  139. // which do anything non-trivial need to provide an explicit specialization
  140. // of FusionCopyContents<T> for their class.
  141. //
  142. template <typename T>
  143. inline HRESULT
  144. FusionCopyContents(
  145. T &rtDestination,
  146. const T &rtSource
  147. )
  148. {
  149. rtDestination = rtSource;
  150. return NOERROR;
  151. }
  152. template <typename T>
  153. inline BOOL
  154. FusionWin32CopyContents(
  155. T &rtDestination,
  156. const T &rtSource
  157. )
  158. {
  159. rtDestination = rtSource;
  160. return TRUE;
  161. }
  162. template <> inline HRESULT
  163. FusionCopyContents<LPWSTR>(
  164. LPWSTR &rtDestination,
  165. const LPWSTR &rtSource
  166. )
  167. {
  168. SIZE_T cch = (SIZE_T)((rtSource == NULL) ? 0 : ::wcslen(rtSource));
  169. if ( cch == 0) {
  170. rtDestination = NULL;
  171. return S_OK;
  172. }
  173. rtDestination = new WCHAR[cch];
  174. if ( ! rtDestination)
  175. return E_OUTOFMEMORY;
  176. memcpy(rtDestination, rtSource, (cch+1)*sizeof(WCHAR));
  177. return NOERROR;
  178. }
  179. //
  180. // FusionAllocateArray() is a helper function that performs array allocation.
  181. //
  182. // It's a separate function so that users of these helpers may provide an
  183. // explicit specialization of the allocation/default construction mechanism
  184. // for an array without replacing all of FusionExpandArray().
  185. //
  186. template <typename T>
  187. inline HRESULT
  188. FusionAllocateArray(
  189. SIZE_T nElements,
  190. T *&rprgtElements
  191. )
  192. {
  193. HRESULT hr = NOERROR;
  194. rprgtElements = NULL;
  195. T *prgtElements = NULL;
  196. if (nElements != 0) {
  197. prgtElements = new T[nElements];
  198. if (prgtElements == NULL) {
  199. hr = E_OUTOFMEMORY;
  200. goto Exit;
  201. }
  202. }
  203. rprgtElements = prgtElements;
  204. hr = NOERROR;
  205. Exit:
  206. return hr;
  207. }
  208. template <typename T>
  209. inline BOOL
  210. FusionWin32AllocateArray(
  211. SIZE_T nElements,
  212. T *&rprgtElements
  213. )
  214. {
  215. BOOL fSuccess = FALSE;
  216. FN_TRACE_WIN32(fSuccess);
  217. rprgtElements = NULL;
  218. T *prgtElements = NULL;
  219. if (nElements != 0)
  220. IFALLOCFAILED_EXIT(prgtElements = new T[nElements]);
  221. rprgtElements = prgtElements;
  222. fSuccess = TRUE;
  223. Exit:
  224. return fSuccess;
  225. }
  226. template <> inline HRESULT FusionAllocateArray<LPWSTR>(SIZE_T nElements, LPWSTR *&rprgtElements)
  227. {
  228. HRESULT hr = NOERROR;
  229. SIZE_T i;
  230. rprgtElements = NULL;
  231. LPWSTR *prgtElements = NULL;
  232. if (nElements != 0) {
  233. prgtElements = new PWSTR[nElements];
  234. if (prgtElements == NULL) {
  235. hr = E_OUTOFMEMORY;
  236. goto Exit;
  237. }
  238. }
  239. for ( i=0; i < nElements; i++)
  240. prgtElements[i] = NULL ;
  241. rprgtElements = prgtElements;
  242. hr = NOERROR;
  243. Exit:
  244. return hr;
  245. }
  246. //
  247. // FusionFreeArray() is a helper function that performs array deallocation.
  248. //
  249. // It's a separate function so that users of the array helper functions may
  250. // provide an explicit specialization of the deallocation mechanism for an
  251. // array of some particular type without replacing the whole of FusionExpandArray().
  252. //
  253. // We include nElements in the parameters so that overridden implementations
  254. // may do something over the contents of the array before the deallocation.
  255. // The default implementation just uses operator delete[], so nElements is
  256. // unused.
  257. //
  258. template <typename T>
  259. inline VOID
  260. FusionFreeArray(
  261. SIZE_T nElements,
  262. T *prgtElements
  263. )
  264. {
  265. FUSION_UNUSED(nElements);
  266. ASSERT_NTC((nElements == 0) || (prgtElements != NULL));
  267. if (nElements != 0)
  268. FUSION_DELETE_ARRAY(prgtElements);
  269. }
  270. template <> inline VOID FusionFreeArray<LPWSTR>(SIZE_T nElements, LPWSTR *prgtElements)
  271. {
  272. FUSION_UNUSED(nElements);
  273. ASSERT_NTC((nElements == 0) || (prgtElements != NULL));
  274. for (SIZE_T i = 0; i < nElements; i++)
  275. prgtElements[i] = NULL ;
  276. if (nElements != 0)
  277. FUSION_DELETE_ARRAY(prgtElements);
  278. }
  279. template <typename T>
  280. inline HRESULT
  281. FusionResizeArray(
  282. T *&rprgtArrayInOut,
  283. SIZE_T nOldSize,
  284. SIZE_T nNewSize
  285. )
  286. {
  287. HRESULT hr = NOERROR;
  288. FN_TRACE_HR(hr);
  289. T *prgtTempNewArray = NULL;
  290. //
  291. // nMaxCopy is the number of elements currently in the array which
  292. // need to have their values preserved. If we're actually shrinking
  293. // the array, it's the new size; if we're expanding the array, it's
  294. // the old size.
  295. //
  296. const SIZE_T nMaxCopy = (nOldSize > nNewSize) ? nNewSize : nOldSize;
  297. if ((nOldSize != 0) && (rprgtArrayInOut == NULL))
  298. {
  299. hr = E_INVALIDARG;
  300. goto Exit;
  301. }
  302. // If the resize is to the same size, complain in debug builds because
  303. // the caller should have been smarter than to call us, but don't do
  304. // any actual work.
  305. ASSERT(nOldSize != nNewSize);
  306. if (nOldSize == nNewSize)
  307. {
  308. hr = NOERROR;
  309. goto Exit;
  310. }
  311. // Allocate the new array:
  312. IFCOMFAILED_EXIT(::FusionAllocateArray(nNewSize, prgtTempNewArray));
  313. if (::FusionCanMemcpyContents(rprgtArrayInOut)) {
  314. memcpy(prgtTempNewArray, rprgtArrayInOut, sizeof(T) * nMaxCopy);
  315. } else if (!::FusionCanMoveContents(rprgtArrayInOut)) {
  316. // Copy the body of the array:
  317. for (SIZE_T i=0; i<nMaxCopy; i++) {
  318. IFCOMFAILED_EXIT(::FusionCopyContents(prgtTempNewArray[i], rprgtArrayInOut[i]));
  319. }
  320. } else {
  321. // Move each of the elements:
  322. for (SIZE_T i=0; i<nMaxCopy; i++) {
  323. ::FusionMoveContents(prgtTempNewArray[i], rprgtArrayInOut[i]);
  324. }
  325. }
  326. // We're done. Blow away the old array and put the new one in its place.
  327. ::FusionFreeArray(nOldSize, rprgtArrayInOut);
  328. rprgtArrayInOut = prgtTempNewArray;
  329. prgtTempNewArray = NULL;
  330. // Canonicalize the HRESULT we're returning so that we don't return random
  331. // S_FALSE or other success HRESULTs from the allocator or copy functions.
  332. hr = NOERROR;
  333. Exit:
  334. if (prgtTempNewArray != NULL)
  335. ::FusionFreeArray(nNewSize, prgtTempNewArray);
  336. return hr;
  337. }
  338. template <typename T>
  339. inline BOOL
  340. FusionWin32ResizeArray(
  341. T *&rprgtArrayInOut,
  342. SIZE_T nOldSize,
  343. SIZE_T nNewSize
  344. )
  345. {
  346. BOOL fSuccess = FALSE;
  347. FN_TRACE_WIN32(fSuccess);
  348. T *prgtTempNewArray = NULL;
  349. //
  350. // nMaxCopy is the number of elements currently in the array which
  351. // need to have their values preserved. If we're actually shrinking
  352. // the array, it's the new size; if we're expanding the array, it's
  353. // the old size.
  354. //
  355. const SIZE_T nMaxCopy = (nOldSize > nNewSize) ? nNewSize : nOldSize;
  356. PARAMETER_CHECK((rprgtArrayInOut != NULL) || (nOldSize == 0));
  357. // If the resize is to the same size, complain in debug builds because
  358. // the caller should have been smarter than to call us, but don't do
  359. // any actual work.
  360. ASSERT(nOldSize != nNewSize);
  361. if (nOldSize != nNewSize)
  362. {
  363. // Allocate the new array:
  364. IFW32FALSE_EXIT(::FusionWin32AllocateArray(nNewSize, prgtTempNewArray));
  365. if (::FusionCanMemcpyContents(rprgtArrayInOut))
  366. {
  367. memcpy(prgtTempNewArray, rprgtArrayInOut, sizeof(T) * nMaxCopy);
  368. }
  369. else if (!::FusionCanMoveContents(rprgtArrayInOut))
  370. {
  371. // Copy the body of the array:
  372. for (SIZE_T i=0; i<nMaxCopy; i++)
  373. IFW32FALSE_EXIT(::FusionWin32CopyContents(prgtTempNewArray[i], rprgtArrayInOut[i]));
  374. }
  375. else
  376. {
  377. // Move each of the elements:
  378. for (SIZE_T i=0; i<nMaxCopy; i++)
  379. {
  380. ::FusionWin32CopyContents(prgtTempNewArray[i], rprgtArrayInOut[i]);
  381. }
  382. }
  383. // We're done. Blow away the old array and put the new one in its place.
  384. ::FusionFreeArray(nOldSize, rprgtArrayInOut);
  385. rprgtArrayInOut = prgtTempNewArray;
  386. prgtTempNewArray = NULL;
  387. }
  388. fSuccess = TRUE;
  389. Exit:
  390. if (prgtTempNewArray != NULL)
  391. ::FusionFreeArray(nNewSize, prgtTempNewArray);
  392. return fSuccess;
  393. }
  394. #define MAKE_CFUSIONARRAY_READY(Typename, CopyFunc) \
  395. template<> inline BOOL FusionWin32CopyContents<Typename>(Typename &rtDest, const Typename &rcSource) { \
  396. FN_PROLOG_WIN32 IFW32FALSE_EXIT(rtDest.CopyFunc(rcSource)); FN_EPILOG } \
  397. template<> inline HRESULT FusionCopyContents<Typename>(Typename &rtDest, const Typename &rcSource) { \
  398. HRESULT hr = E_FAIL; FN_TRACE_HR(hr); IFW32FALSE_EXIT(::FusionWin32CopyContents<Typename>(rtDest, rcSource)); FN_EPILOG } \
  399. template<> inline VOID FusionMoveContents<Typename>(Typename &rtDest, Typename &rcSource) { \
  400. FN_TRACE(); HARD_ASSERT2_ACTION(FusionMoveContents<Typename>, "FusionMoveContents not allowed in 99.44% of cases."); }
  401. #endif // !defined(FUSION_ARRAYHELP_H_INCLUDED_)