480 lines
13 KiB
C++
480 lines
13 KiB
C++
#if !defined(FUSION_ARRAYHELP_H_INCLUDED_)
|
|
#define FUSION_ARRAYHELP_H_INCLUDED_
|
|
|
|
#if _MSC_VER > 1000
|
|
#pragma once
|
|
#endif // _MSC_VER > 1000
|
|
|
|
#include <windows.h>
|
|
#include <oleauto.h>
|
|
#include "fusionheap.h"
|
|
#include "fusiontrace.h"
|
|
|
|
//
|
|
// arrayhelp.h
|
|
//
|
|
// Helper function(s) to deal with growable arrays.
|
|
//
|
|
// Users of this utility should provide explicit template
|
|
// specializations for classes for which you can safely (without
|
|
// possibility of failure) transfer the contens from a source
|
|
// instance to a destination instance, leaving the source "empty".
|
|
//
|
|
// If moving the data may fail, you must provide a specialization
|
|
// of FusionCopyContents() which returns an appropriate HRESULT
|
|
// on failure.
|
|
//
|
|
//
|
|
// C++ note:
|
|
//
|
|
// the C++ syntax for explicit function template specialization
|
|
// is:
|
|
//
|
|
// template <> BOOLEAN FusionCanMoveContents<CFoo>(CFoo *p) { UNUSED(p); return TRUE; }
|
|
//
|
|
|
|
#if !defined(FUSION_UNUSED)
|
|
#define FUSION_UNUSED(x) (x)
|
|
#endif
|
|
|
|
class CSaveErrorInfo
|
|
{
|
|
public:
|
|
CSaveErrorInfo() : m_pIErrorInfo(NULL) { ::GetErrorInfo(0, &m_pIErrorInfo); }
|
|
~CSaveErrorInfo() { ::SetErrorInfo(0, m_pIErrorInfo); if (m_pIErrorInfo != NULL) m_pIErrorInfo->Release(); }
|
|
private:
|
|
IErrorInfo *m_pIErrorInfo;
|
|
};
|
|
|
|
//
|
|
// Alternate CSaveErrorInfo implementation for templates which want to not require
|
|
// a link dependency on oleaut32.dll.
|
|
//
|
|
|
|
class CSaveErrorInfoNull
|
|
{
|
|
public:
|
|
CSaveErrorInfoNull() { }
|
|
~CSaveErrorInfoNull() { }
|
|
};
|
|
|
|
//
|
|
// The default implementation just does assignment which may not fail;
|
|
// you can (and must if assignment may fail) specialize as you like to
|
|
// do something that avoids data copies; you may assume that the source
|
|
// element will be destroyed momentarily.
|
|
//
|
|
|
|
//
|
|
// The FusionCanMemcpyContents() template function is used to determine
|
|
// if a class is trivial enough that a raw byte transfer of the old
|
|
// contents to the new contents is sufficient. The default is that the
|
|
// assignment operator is used as that is the only safe alternative.
|
|
//
|
|
|
|
template <typename T>
|
|
inline BOOLEAN
|
|
FusionCanMemcpyContents(
|
|
T *ptDummyRequired = NULL
|
|
)
|
|
{
|
|
FUSION_UNUSED(ptDummyRequired);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// The FusionCanMoveContents() template function is used by the array
|
|
// copy template function to optimize for the case that it should use
|
|
// FusionMoveContens<T>().
|
|
//
|
|
// When overriding this function, the general rule is that if the data
|
|
// movement may allocate memory etc. that will fail, we need to use the
|
|
// FusionCopyContens() member function instead.
|
|
//
|
|
// It takes a single parameter which is not used because a C++ template
|
|
// function must take at least one parameter using the template type so
|
|
// that the decorated name is unique.
|
|
//
|
|
|
|
template <typename T>
|
|
inline BOOLEAN
|
|
FusionCanMoveContents(
|
|
T *ptDummyRequired = NULL
|
|
)
|
|
{
|
|
FUSION_UNUSED(ptDummyRequired);
|
|
return FALSE;
|
|
}
|
|
|
|
template <> inline BOOLEAN
|
|
FusionCanMoveContents<LPWSTR>(LPWSTR *ptDummyRequired)
|
|
{
|
|
FUSION_UNUSED(ptDummyRequired);
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Override FusionMoveContents<T> to be a useful implementation which
|
|
// takes the contents of rtSource and transfers them to rtDestination.
|
|
// The transfer may not fail (returns VOID). The expectation is that
|
|
// any value that was stored in rtSource are moved to rtDestination
|
|
// and rtSource is left in a quiescent state. E.g. any pointers to
|
|
// objects can be simply assigned from rtSource to rtDestination and
|
|
// then set to NULL in rtSource. You may also assume that the destination
|
|
// element has only had the default constructor run on it, so you
|
|
// may choose to take shortcuts about not freeing non-NULL pointers
|
|
// in rtDestination if you see fit.
|
|
//
|
|
|
|
template <typename T>
|
|
inline VOID
|
|
FusionMoveContents(
|
|
T &rtDestination,
|
|
T &rtSource
|
|
)
|
|
{
|
|
rtDestination = rtSource;
|
|
}
|
|
|
|
template <> inline VOID
|
|
FusionMoveContents<LPWSTR>(
|
|
LPWSTR &rtDestination,
|
|
LPWSTR &rtSource
|
|
)
|
|
{
|
|
if ( rtDestination )
|
|
FUSION_DELETE_ARRAY(rtDestination);
|
|
|
|
rtDestination = rtSource;
|
|
rtSource = NULL;
|
|
}
|
|
|
|
//
|
|
// FusionCopyContents is a default implementation of the assignment
|
|
// operation from rtSource to rtDestination, except that it may return a
|
|
// failure status. Trivial classes which do define an assignment
|
|
// operator may just use the default definition, but any copy implementations
|
|
// which do anything non-trivial need to provide an explicit specialization
|
|
// of FusionCopyContents<T> for their class.
|
|
//
|
|
|
|
template <typename T>
|
|
inline HRESULT
|
|
FusionCopyContents(
|
|
T &rtDestination,
|
|
const T &rtSource
|
|
)
|
|
{
|
|
rtDestination = rtSource;
|
|
return NOERROR;
|
|
}
|
|
|
|
template <typename T>
|
|
inline BOOL
|
|
FusionWin32CopyContents(
|
|
T &rtDestination,
|
|
const T &rtSource
|
|
)
|
|
{
|
|
rtDestination = rtSource;
|
|
return TRUE;
|
|
}
|
|
|
|
template <> inline HRESULT
|
|
FusionCopyContents<LPWSTR>(
|
|
LPWSTR &rtDestination,
|
|
const LPWSTR &rtSource
|
|
)
|
|
{
|
|
SIZE_T cch = (SIZE_T)((rtSource == NULL) ? 0 : ::wcslen(rtSource));
|
|
|
|
if ( cch == 0) {
|
|
rtDestination = NULL;
|
|
return S_OK;
|
|
}
|
|
|
|
rtDestination = new WCHAR[cch];
|
|
if ( ! rtDestination)
|
|
return E_OUTOFMEMORY;
|
|
|
|
memcpy(rtDestination, rtSource, (cch+1)*sizeof(WCHAR));
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
//
|
|
// FusionAllocateArray() is a helper function that performs array allocation.
|
|
//
|
|
// It's a separate function so that users of these helpers may provide an
|
|
// explicit specialization of the allocation/default construction mechanism
|
|
// for an array without replacing all of FusionExpandArray().
|
|
//
|
|
|
|
template <typename T>
|
|
inline HRESULT
|
|
FusionAllocateArray(
|
|
SIZE_T nElements,
|
|
T *&rprgtElements
|
|
)
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
|
|
rprgtElements = NULL;
|
|
|
|
T *prgtElements = NULL;
|
|
|
|
if (nElements != 0) {
|
|
prgtElements = new T[nElements];
|
|
if (prgtElements == NULL) {
|
|
hr = E_OUTOFMEMORY;
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
rprgtElements = prgtElements;
|
|
hr = NOERROR;
|
|
|
|
Exit:
|
|
return hr;
|
|
}
|
|
|
|
template <typename T>
|
|
inline BOOL
|
|
FusionWin32AllocateArray(
|
|
SIZE_T nElements,
|
|
T *&rprgtElements
|
|
)
|
|
{
|
|
BOOL fSuccess = FALSE;
|
|
FN_TRACE_WIN32(fSuccess);
|
|
|
|
rprgtElements = NULL;
|
|
|
|
T *prgtElements = NULL;
|
|
|
|
if (nElements != 0)
|
|
IFALLOCFAILED_EXIT(prgtElements = new T[nElements]);
|
|
|
|
rprgtElements = prgtElements;
|
|
fSuccess = TRUE;
|
|
|
|
Exit:
|
|
return fSuccess;
|
|
}
|
|
|
|
template <> inline HRESULT FusionAllocateArray<LPWSTR>(SIZE_T nElements, LPWSTR *&rprgtElements)
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
SIZE_T i;
|
|
|
|
rprgtElements = NULL;
|
|
|
|
|
|
LPWSTR *prgtElements = NULL;
|
|
|
|
if (nElements != 0) {
|
|
prgtElements = new PWSTR[nElements];
|
|
if (prgtElements == NULL) {
|
|
hr = E_OUTOFMEMORY;
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
for ( i=0; i < nElements; i++)
|
|
prgtElements[i] = NULL ;
|
|
|
|
rprgtElements = prgtElements;
|
|
hr = NOERROR;
|
|
|
|
Exit:
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// FusionFreeArray() is a helper function that performs array deallocation.
|
|
//
|
|
// It's a separate function so that users of the array helper functions may
|
|
// provide an explicit specialization of the deallocation mechanism for an
|
|
// array of some particular type without replacing the whole of FusionExpandArray().
|
|
//
|
|
// We include nElements in the parameters so that overridden implementations
|
|
// may do something over the contents of the array before the deallocation.
|
|
// The default implementation just uses operator delete[], so nElements is
|
|
// unused.
|
|
//
|
|
|
|
template <typename T>
|
|
inline VOID
|
|
FusionFreeArray(
|
|
SIZE_T nElements,
|
|
T *prgtElements
|
|
)
|
|
{
|
|
FUSION_UNUSED(nElements);
|
|
|
|
ASSERT_NTC((nElements == 0) || (prgtElements != NULL));
|
|
|
|
if (nElements != 0)
|
|
FUSION_DELETE_ARRAY(prgtElements);
|
|
}
|
|
|
|
template <> inline VOID FusionFreeArray<LPWSTR>(SIZE_T nElements, LPWSTR *prgtElements)
|
|
{
|
|
FUSION_UNUSED(nElements);
|
|
|
|
ASSERT_NTC((nElements == 0) || (prgtElements != NULL));
|
|
|
|
for (SIZE_T i = 0; i < nElements; i++)
|
|
prgtElements[i] = NULL ;
|
|
|
|
if (nElements != 0)
|
|
FUSION_DELETE_ARRAY(prgtElements);
|
|
}
|
|
|
|
template <typename T>
|
|
inline HRESULT
|
|
FusionResizeArray(
|
|
T *&rprgtArrayInOut,
|
|
SIZE_T nOldSize,
|
|
SIZE_T nNewSize
|
|
)
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
FN_TRACE_HR(hr);
|
|
|
|
T *prgtTempNewArray = NULL;
|
|
|
|
//
|
|
// nMaxCopy is the number of elements currently in the array which
|
|
// need to have their values preserved. If we're actually shrinking
|
|
// the array, it's the new size; if we're expanding the array, it's
|
|
// the old size.
|
|
//
|
|
const SIZE_T nMaxCopy = (nOldSize > nNewSize) ? nNewSize : nOldSize;
|
|
|
|
if ((nOldSize != 0) && (rprgtArrayInOut == NULL))
|
|
{
|
|
hr = E_INVALIDARG;
|
|
goto Exit;
|
|
}
|
|
|
|
// If the resize is to the same size, complain in debug builds because
|
|
// the caller should have been smarter than to call us, but don't do
|
|
// any actual work.
|
|
ASSERT(nOldSize != nNewSize);
|
|
if (nOldSize == nNewSize)
|
|
{
|
|
hr = NOERROR;
|
|
goto Exit;
|
|
}
|
|
|
|
// Allocate the new array:
|
|
IFCOMFAILED_EXIT(::FusionAllocateArray(nNewSize, prgtTempNewArray));
|
|
|
|
if (::FusionCanMemcpyContents(rprgtArrayInOut)) {
|
|
memcpy(prgtTempNewArray, rprgtArrayInOut, sizeof(T) * nMaxCopy);
|
|
} else if (!::FusionCanMoveContents(rprgtArrayInOut)) {
|
|
// Copy the body of the array:
|
|
for (SIZE_T i=0; i<nMaxCopy; i++) {
|
|
IFCOMFAILED_EXIT(::FusionCopyContents(prgtTempNewArray[i], rprgtArrayInOut[i]));
|
|
}
|
|
} else {
|
|
// Move each of the elements:
|
|
for (SIZE_T i=0; i<nMaxCopy; i++) {
|
|
::FusionMoveContents(prgtTempNewArray[i], rprgtArrayInOut[i]);
|
|
}
|
|
}
|
|
|
|
// We're done. Blow away the old array and put the new one in its place.
|
|
::FusionFreeArray(nOldSize, rprgtArrayInOut);
|
|
rprgtArrayInOut = prgtTempNewArray;
|
|
prgtTempNewArray = NULL;
|
|
|
|
// Canonicalize the HRESULT we're returning so that we don't return random
|
|
// S_FALSE or other success HRESULTs from the allocator or copy functions.
|
|
hr = NOERROR;
|
|
|
|
Exit:
|
|
|
|
if (prgtTempNewArray != NULL)
|
|
::FusionFreeArray(nNewSize, prgtTempNewArray);
|
|
|
|
return hr;
|
|
}
|
|
|
|
template <typename T>
|
|
inline BOOL
|
|
FusionWin32ResizeArray(
|
|
T *&rprgtArrayInOut,
|
|
SIZE_T nOldSize,
|
|
SIZE_T nNewSize
|
|
)
|
|
{
|
|
BOOL fSuccess = FALSE;
|
|
FN_TRACE_WIN32(fSuccess);
|
|
|
|
T *prgtTempNewArray = NULL;
|
|
|
|
//
|
|
// nMaxCopy is the number of elements currently in the array which
|
|
// need to have their values preserved. If we're actually shrinking
|
|
// the array, it's the new size; if we're expanding the array, it's
|
|
// the old size.
|
|
//
|
|
const SIZE_T nMaxCopy = (nOldSize > nNewSize) ? nNewSize : nOldSize;
|
|
|
|
PARAMETER_CHECK((rprgtArrayInOut != NULL) || (nOldSize == 0));
|
|
|
|
// If the resize is to the same size, complain in debug builds because
|
|
// the caller should have been smarter than to call us, but don't do
|
|
// any actual work.
|
|
ASSERT(nOldSize != nNewSize);
|
|
if (nOldSize != nNewSize)
|
|
{
|
|
// Allocate the new array:
|
|
IFW32FALSE_EXIT(::FusionWin32AllocateArray(nNewSize, prgtTempNewArray));
|
|
|
|
if (::FusionCanMemcpyContents(rprgtArrayInOut))
|
|
{
|
|
memcpy(prgtTempNewArray, rprgtArrayInOut, sizeof(T) * nMaxCopy);
|
|
}
|
|
else if (!::FusionCanMoveContents(rprgtArrayInOut))
|
|
{
|
|
// Copy the body of the array:
|
|
for (SIZE_T i=0; i<nMaxCopy; i++)
|
|
IFW32FALSE_EXIT(::FusionWin32CopyContents(prgtTempNewArray[i], rprgtArrayInOut[i]));
|
|
}
|
|
else
|
|
{
|
|
// Move each of the elements:
|
|
for (SIZE_T i=0; i<nMaxCopy; i++)
|
|
{
|
|
::FusionWin32CopyContents(prgtTempNewArray[i], rprgtArrayInOut[i]);
|
|
}
|
|
}
|
|
|
|
// We're done. Blow away the old array and put the new one in its place.
|
|
::FusionFreeArray(nOldSize, rprgtArrayInOut);
|
|
rprgtArrayInOut = prgtTempNewArray;
|
|
prgtTempNewArray = NULL;
|
|
}
|
|
|
|
fSuccess = TRUE;
|
|
|
|
Exit:
|
|
if (prgtTempNewArray != NULL)
|
|
::FusionFreeArray(nNewSize, prgtTempNewArray);
|
|
|
|
return fSuccess;
|
|
}
|
|
|
|
#define MAKE_CFUSIONARRAY_READY(Typename, CopyFunc) \
|
|
template<> inline BOOL FusionWin32CopyContents<Typename>(Typename &rtDest, const Typename &rcSource) { \
|
|
FN_PROLOG_WIN32 IFW32FALSE_EXIT(rtDest.CopyFunc(rcSource)); FN_EPILOG } \
|
|
template<> inline HRESULT FusionCopyContents<Typename>(Typename &rtDest, const Typename &rcSource) { \
|
|
HRESULT hr = E_FAIL; FN_TRACE_HR(hr); IFW32FALSE_EXIT(::FusionWin32CopyContents<Typename>(rtDest, rcSource)); FN_EPILOG } \
|
|
template<> inline VOID FusionMoveContents<Typename>(Typename &rtDest, Typename &rcSource) { \
|
|
FN_TRACE(); HARD_ASSERT2_ACTION(FusionMoveContents<Typename>, "FusionMoveContents not allowed in 99.44% of cases."); }
|
|
|
|
#endif // !defined(FUSION_ARRAYHELP_H_INCLUDED_)
|