summaryrefslogtreecommitdiff
path: root/src/inc/stgpool.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/inc/stgpool.h')
-rw-r--r--src/inc/stgpool.h1515
1 files changed, 1515 insertions, 0 deletions
diff --git a/src/inc/stgpool.h b/src/inc/stgpool.h
new file mode 100644
index 0000000000..9eef70742e
--- /dev/null
+++ b/src/inc/stgpool.h
@@ -0,0 +1,1515 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// StgPool.h
+//
+
+//
+// Pools are used to reduce the amount of data actually required in the database.
+// This allows for duplicate string and binary values to be folded into one
+// copy shared by the rest of the database. Strings are tracked in a hash
+// table when insert/changing data to find duplicates quickly. The strings
+// are then persisted consecutively in a stream in the database format.
+//
+//*****************************************************************************
+
+#ifndef __StgPool_h__
+#define __StgPool_h__
+
+#ifdef _MSC_VER
+#pragma warning (disable : 4355) // warning C4355: 'this' : used in base member initializer list
+#endif
+
+#include "stgpooli.h" // Internal helpers.
+#include "corerror.h" // Error codes.
+#include "metadatatracker.h"
+#include "metamodelpub.h"
+#include "ex.h"
+#include "sarray.h"
+#include "memoryrange.h"
+#include "../md/hotdata/hotheap.h"
+
+#include "../md/debug_metadata.h"
+
+//*****************************************************************************
+// NOTE:
+// One limitation with the pools, we have no way to removing strings from
+// the pool. To remove, you need to know the ref count on the string, and
+// need the ability to compact the pool and reset all references.
+//*****************************************************************************
+
+//********** Constants ********************************************************
+const int DFT_STRING_HEAP_SIZE = 1024;
+const int DFT_GUID_HEAP_SIZE = 32;
+const int DFT_BLOB_HEAP_SIZE = 1024;
+const int DFT_VARIANT_HEAP_SIZE = 512;
+const int DFT_CODE_HEAP_SIZE = 8192;
+
+
+
+// Forwards.
+class StgStringPool;
+class StgBlobPool;
+class StgCodePool;
+class CorProfileData;
+
+// Perform binary search on index table.
+//
+class RIDBinarySearch : public CBinarySearch<UINT32>
+{
+public:
+ RIDBinarySearch(const UINT32 *pBase, int iCount) : CBinarySearch<UINT32>(pBase, iCount)
+ {
+ LIMITED_METHOD_CONTRACT;
+ } // RIDBinarySearch::RIDBinarySearch
+
+ int Compare(UINT32 const *pFirst, UINT32 const *pSecond)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ if (*pFirst < *pSecond)
+ return -1;
+
+ if (*pFirst > *pSecond)
+ return 1;
+
+ return 0;
+ } // RIDBinarySearch::Compare
+
+}; // class RIDBinarySearch
+
+//*****************************************************************************
+// This class provides common definitions for heap segments. It is both the
+// base class for the heap, and the class for heap extensions (additional
+// memory that must be allocated to grow the heap).
+//*****************************************************************************
+class StgPoolSeg
+{
+ friend class VerifyLayoutsMD;
+public:
+ StgPoolSeg() :
+ m_pSegData((BYTE*)m_zeros),
+ m_pNextSeg(NULL),
+ m_cbSegSize(0),
+ m_cbSegNext(0)
+ {LIMITED_METHOD_CONTRACT; }
+ ~StgPoolSeg()
+ { LIMITED_METHOD_CONTRACT; _ASSERTE(m_pSegData == m_zeros);_ASSERTE(m_pNextSeg == NULL); }
+protected:
+ BYTE *m_pSegData; // Pointer to the data.
+ StgPoolSeg *m_pNextSeg; // Pointer to next segment, or NULL.
+ // Size of the segment buffer. If this is last segment (code:m_pNextSeg is NULL), then it's the
+ // allocation size. If this is not the last segment, then this is shrinked to segment data size
+ // (code:m_cbSegNext).
+ ULONG m_cbSegSize;
+ ULONG m_cbSegNext; // Offset of next available byte in segment.
+ // Segment relative.
+
+ friend class StgPool;
+ friend class StgStringPool;
+ friend class StgGuidPool;
+ friend class StgBlobPool;
+ friend class RecordPool;
+
+public:
+ const BYTE *GetSegData() const { LIMITED_METHOD_CONTRACT; return m_pSegData; }
+ const StgPoolSeg* GetNextSeg() const { LIMITED_METHOD_CONTRACT; return m_pNextSeg; }
+ // Returns size of the segment. It can be bigger than the size of represented data by this segment if
+ // this is the last segment.
+ ULONG GetSegSize() const { LIMITED_METHOD_CONTRACT; return m_cbSegSize; }
+ // Returns size of represented data in this segment.
+ ULONG GetDataSize() const { LIMITED_METHOD_CONTRACT; return m_cbSegNext; }
+
+ static const BYTE m_zeros[64]; // array of zeros for "0" indices.
+ // The size should be at least maximum of all MD table record sizes
+ // (MD\Runtime\MDColumnDescriptors.cpp) which is currently 28 B.
+}; // class StgPoolSeg
+
+namespace MetaData
+{
+ // Forward declarations
+ class StringHeapRO;
+ class StringHeapRW;
+ class BlobHeapRO;
+}; // namespace MetaData
+
+//
+//
+// StgPoolReadOnly
+//
+//
+//*****************************************************************************
+// This is the read only StgPool class
+//*****************************************************************************
+class StgPoolReadOnly : public StgPoolSeg
+{
+friend class CBlobPoolHash;
+friend class MetaData::StringHeapRO;
+friend class MetaData::StringHeapRW;
+friend class MetaData::BlobHeapRO;
+friend class VerifyLayoutsMD;
+
+public:
+ StgPoolReadOnly()
+ { LIMITED_METHOD_CONTRACT; };
+
+ ~StgPoolReadOnly();
+
+
+//*****************************************************************************
+// Init the pool from existing data.
+//*****************************************************************************
+ __checkReturn
+ HRESULT InitOnMemReadOnly( // Return code.
+ void *pData, // Predefined data.
+ ULONG iSize); // Size of data.
+
+//*****************************************************************************
+// Prepare to shut down or reinitialize.
+//*****************************************************************************
+ virtual void Uninit();
+
+//*****************************************************************************
+// Return the size of the pool.
+//*****************************************************************************
+ virtual UINT32 GetPoolSize() const
+ { LIMITED_METHOD_CONTRACT; return m_cbSegSize; }
+
+//*****************************************************************************
+// Indicate if heap is empty.
+//*****************************************************************************
+ virtual int IsEmpty() // true if empty.
+ { LIMITED_METHOD_CONTRACT; _ASSERTE(!"This implementation should never be called!!!"); return FALSE; }
+
+//*****************************************************************************
+// true if the heap is read only.
+//*****************************************************************************
+ virtual int IsReadOnly() { LIMITED_METHOD_CONTRACT; return true ;};
+
+//*****************************************************************************
+// Is the given cookie a valid offset, index, etc?
+//*****************************************************************************
+ virtual int IsValidCookie(UINT32 nCookie)
+ { WRAPPER_NO_CONTRACT; return (IsValidOffset(nCookie)); }
+
+
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable:6387) // Suppress PREFast warning: '*pszString' might be '0': this does not adhere to the specification for the function
+ // *pszString may be NULL only if method fails, but warning 6387 doesn't respect __success(SUCCEEDED(return)) which is part of HRESULT definition
+#endif
+//*****************************************************************************
+// Return a pointer to a null terminated string given an offset previously
+// handed out by AddString or FindString.
+//*****************************************************************************
+ __checkReturn
+ inline HRESULT GetString(
+ UINT32 nIndex,
+ __deref_out LPCSTR *pszString)
+ {
+ HRESULT hr;
+
+ // Size of the data in the heap will be ignored, because we have verified during creation of the string
+ // heap (code:Initialize) and when adding new strings (e.g. code:AddString,
+ // code:AddTemporaryStringBuffer), that the heap is null-terminated, therefore we don't have to check it
+ // for each string in the heap
+ MetaData::DataBlob stringData;
+
+ // Get data from the heap (clears stringData on error)
+ IfFailGo(GetData(
+ nIndex,
+ &stringData));
+ _ASSERTE(hr == S_OK);
+ // Raw data are always at least 1 byte long, otherwise it would be invalid offset and hr != S_OK
+ PREFAST_ASSUME(stringData.GetDataPointer() != NULL);
+ // Fills output string
+ *pszString = reinterpret_cast<LPSTR>(stringData.GetDataPointer());
+ //_ASSERTE(stringData.GetSize() > strlen(*pszString));
+
+ return hr;
+ ErrExit:
+ // Clears output string on error
+ *pszString = NULL;
+
+ return hr;
+ }
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+
+//*****************************************************************************
+// Convert a string to UNICODE into the caller's buffer.
+//*****************************************************************************
+ __checkReturn
+ virtual HRESULT GetStringW( // Return code.
+ ULONG iOffset, // Offset of string in pool.
+ __out_ecount(cchBuffer) LPWSTR szOut, // Output buffer for string.
+ int cchBuffer); // Size of output buffer.
+
+//*****************************************************************************
+// Copy a GUID into the caller's buffer.
+//*****************************************************************************
+ __checkReturn
+ HRESULT GetGuid(
+ UINT32 nIndex, // 1-based index of Guid in pool.
+ GUID UNALIGNED **ppGuid) // Output buffer for Guid.
+ {
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ HRESULT hr;
+ MetaData::DataBlob heapData;
+
+ if (nIndex == 0)
+ {
+ *ppGuid = (GUID *)m_zeros;
+ return S_OK;
+ }
+
+ S_UINT32 nOffset = S_UINT32(nIndex - 1) * S_UINT32(sizeof(GUID));
+ if (nOffset.IsOverflow() || !IsValidOffset(nOffset.Value()))
+ {
+ Debug_ReportError("Invalid index passed - integer overflow.");
+ IfFailGo(CLDB_E_INDEX_NOTFOUND);
+ }
+ if (FAILED(GetData(nOffset.Value(), &heapData)))
+ {
+ if (nOffset.Value() == 0)
+ {
+ Debug_ReportError("Invalid index 0 passed.");
+ IfFailGo(CLDB_E_INDEX_NOTFOUND);
+ }
+ Debug_ReportInternalError("Invalid index passed.");
+ IfFailGo(CLDB_E_INTERNALERROR);
+ }
+ _ASSERTE(heapData.GetSize() >= sizeof(GUID));
+
+ *ppGuid = (GUID UNALIGNED *)heapData.GetDataPointer();
+ return S_OK;
+
+ ErrExit:
+ *ppGuid = (GUID *)m_zeros;
+ return hr;
+ } // StgPoolReadOnly::GetGuid
+
+//*****************************************************************************
+// Return a pointer to a null terminated blob given an offset previously
+// handed out by Addblob or Findblob.
+//*****************************************************************************
+ __checkReturn
+ virtual HRESULT GetBlob(
+ UINT32 nOffset, // Offset of blob in pool.
+ MetaData::DataBlob *pData);
+
+#ifdef FEATURE_PREJIT
+ // Initialize hot data structures.
+ // Method can be called multiple time, e.g. to disable usage of hot data structures in certain scenarios
+ // (see code:CMiniMd::DisableHotDataUsage).
+ void InitHotData(MetaData::HotHeap hotHeap)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+// @todo: Triton workaround: FEATURE_METADATA_STANDALNE_WINRT_RO is supposed to disable FEATURE_PREJIT - remove this #ifdef once we figure out how to get that working in the CoreClr build.
+#if !(defined(FEATURE_UTILCODE_NO_DEPENDENCIES) || defined(FEATURE_METADATA_STANDALNE_WINRT_RO))
+ m_HotHeap = hotHeap;
+#else
+ _ASSERTE(!"InitHotData(): Not supposed to exist in RoMetaData.dll");
+#endif //!(defined(FEATURE_UTILCODE_NO_DEPENDENCIES) || defined(FEATURE_METADATA_STANDALNE_WINRT_RO))
+ }
+#endif //FEATURE_PREJIT
+
+protected:
+
+//*****************************************************************************
+// Check whether a given offset is valid in the pool.
+//*****************************************************************************
+ virtual int IsValidOffset(UINT32 nOffset)
+ {LIMITED_METHOD_CONTRACT; return (nOffset == 0) || ((m_pSegData != m_zeros) && (nOffset < m_cbSegSize)); }
+
+//*****************************************************************************
+// Get a pointer to an offset within the heap. Inline for base segment,
+// helper for extension segments.
+//*****************************************************************************
+ __checkReturn
+ FORCEINLINE HRESULT GetDataReadOnly(UINT32 nOffset, __inout MetaData::DataBlob *pData)
+ {
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(IsReadOnly());
+
+ // If off the end of the heap, return the 'nul' item from the beginning.
+ if (nOffset >= m_cbSegSize)
+ {
+ Debug_ReportError("Invalid offset passed.");
+ pData->Clear();
+ return CLDB_E_INDEX_NOTFOUND;
+ }
+
+// @todo: Triton workaround: FEATURE_METADATA_STANDALNE_WINRT_RO is supposed to disable FEATURE_PREJIT - remove this #if once we figure out how to get that working in the CoreClr build.
+#if !(defined(FEATURE_UTILCODE_NO_DEPENDENCIES) || defined(FEATURE_METADATA_STANDALNE_WINRT_RO))
+#ifdef FEATURE_PREJIT
+ // try hot data first
+ if (!m_HotHeap.IsEmpty())
+ {
+ HRESULT hr = m_HotHeap.GetData(nOffset, pData);
+ if ((hr == S_OK) || FAILED(hr))
+ {
+ return hr;
+ }
+ _ASSERTE(hr == S_FALSE);
+ }
+#endif //FEATURE_PREJIT
+#endif //!(defined(FEATURE_UTILCODE_NO_DEPENDENCIES) || defined(FEATURE_METADATA_STANDALNE_WINRT_RO))
+
+
+ pData->Init(m_pSegData + nOffset, m_cbSegSize - nOffset);
+
+ METADATATRACKER_ONLY(MetaDataTracker::NoteAccess((void *)pData->GetDataPointer()));
+
+ return S_OK;
+ } // StgPoolReadOnly::GetDataReadOnly
+
+//*****************************************************************************
+// Get a pointer to an offset within the heap. Inline for base segment,
+// helper for extension segments.
+//*****************************************************************************
+ __checkReturn
+ virtual HRESULT GetData(UINT32 nOffset, __inout MetaData::DataBlob *pData)
+ {
+ WRAPPER_NO_CONTRACT;
+ return GetDataReadOnly(nOffset, pData);
+ } // StgPoolReadOnly::GetData
+
+private:
+// @todo: Triton workaround: FEATURE_METADATA_STANDALNE_WINRT_RO is supposed to disable FEATURE_PREJIT - remove this #if once we figure out how to get that working in the CoreClr build.
+#if !(defined(FEATURE_UTILCODE_NO_DEPENDENCIES) || defined(FEATURE_METADATA_STANDALNE_WINRT_RO))
+ // hot pool data
+ MetaData::HotHeap m_HotHeap;
+#endif //!(defined(FEATURE_UTILCODE_NO_DEPENDENCIES) || defined(FEATURE_METADATA_STANDALNE_WINRT_RO))
+
+}; // class StgPoolReadOnly
+
+//
+//
+// StgBlobPoolReadOnly
+//
+//
+//*****************************************************************************
+// This is the read only StgBlobPool class
+//*****************************************************************************
+class StgBlobPoolReadOnly : public StgPoolReadOnly
+{
+public:
+//*****************************************************************************
+// Return a pointer to a null terminated blob given an offset
+//*****************************************************************************
+ __checkReturn
+ virtual HRESULT GetBlob(
+ UINT32 nOffset, // Offset of blob in pool.
+ MetaData::DataBlob *pData);
+
+protected:
+
+//*****************************************************************************
+// Check whether a given offset is valid in the pool.
+//*****************************************************************************
+ virtual int IsValidOffset(UINT32 nOffset)
+ {
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ MetaData::DataBlob data;
+ return (StgBlobPoolReadOnly::GetBlob(nOffset, &data) == S_OK);
+ }
+
+}; // class StgBlobPoolReadOnly
+
+//
+//
+// StgPool
+//
+//
+
+//*****************************************************************************
+// This base class provides common pool management code, such as allocation
+// of dynamic memory.
+//*****************************************************************************
+class StgPool : public StgPoolReadOnly
+{
+friend class StgStringPool;
+friend class StgBlobPool;
+friend class RecordPool;
+friend class CBlobPoolHash;
+friend class VerifyLayoutsMD;
+
+public:
+ StgPool(ULONG ulGrowInc=512, UINT32 nAlignment=4) :
+ m_ulGrowInc(ulGrowInc),
+ m_pCurSeg(this),
+ m_cbCurSegOffset(0),
+ m_bFree(true),
+ m_bReadOnly(false),
+ m_nVariableAlignmentMask(nAlignment-1),
+ m_cbStartOffsetOfEdit(0),
+ m_fValidOffsetOfEdit(0)
+ { LIMITED_METHOD_CONTRACT; }
+
+ virtual ~StgPool();
+
+protected:
+ HRESULT Align(UINT32 nValue, UINT32 *pnAlignedValue) const
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ *pnAlignedValue = (nValue + m_nVariableAlignmentMask) & ~m_nVariableAlignmentMask;
+ if (*pnAlignedValue < nValue)
+ {
+ return COR_E_OVERFLOW;
+ }
+ return S_OK;
+ }
+
+public:
+//*****************************************************************************
+// Init the pool for use. This is called for the create empty case.
+//*****************************************************************************
+ __checkReturn
+ virtual HRESULT InitNew( // Return code.
+ ULONG cbSize=0, // Estimated size.
+ ULONG cItems=0); // Estimated item count.
+
+//*****************************************************************************
+// Init the pool from existing data.
+//*****************************************************************************
+ __checkReturn
+ virtual HRESULT InitOnMem( // Return code.
+ void *pData, // Predefined data.
+ ULONG iSize, // Size of data.
+ int bReadOnly); // true if append is forbidden.
+
+//*****************************************************************************
+// Called when the pool must stop accessing memory passed to InitOnMem().
+//*****************************************************************************
+ __checkReturn
+ virtual HRESULT TakeOwnershipOfInitMem();
+
+//*****************************************************************************
+// Clear out this pool. Cannot use until you call InitNew.
+//*****************************************************************************
+ virtual void Uninit();
+
+//*****************************************************************************
+// Called to copy the pool to writable memory, reset the r/o bit.
+//*****************************************************************************
+ __checkReturn
+ virtual HRESULT ConvertToRW();
+
+//*****************************************************************************
+// Turn hashing off or on. Implemented as required in subclass.
+//*****************************************************************************
+ __checkReturn
+ virtual HRESULT SetHash(int bHash);
+
+//*****************************************************************************
+// Allocate memory if we don't have any, or grow what we have. If successful,
+// then at least iRequired bytes will be allocated.
+//*****************************************************************************
+ bool Grow( // true if successful.
+ ULONG iRequired); // Min required bytes to allocate.
+
+//*****************************************************************************
+// Add a segment to the chain of segments.
+//*****************************************************************************
+ __checkReturn
+ virtual HRESULT AddSegment( // S_OK or error.
+ const void *pData, // The data.
+ ULONG cbData, // Size of the data.
+ bool bCopy); // If true, make a copy of the data.
+
+//*****************************************************************************
+// Trim any empty final segment.
+//*****************************************************************************
+ void Trim(); //
+
+//*****************************************************************************
+// Return the size in bytes of the persistent version of this pool. If
+// PersistToStream were the next call, the amount of bytes written to pIStream
+// has to be same as the return value from this function.
+//*****************************************************************************
+ __checkReturn
+ virtual HRESULT GetSaveSize(
+ UINT32 *pcbSaveSize) const
+ {
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ _ASSERTE(pcbSaveSize != NULL);
+ // Size is offset of last seg + size of last seg.
+ UINT32 cbSize = m_pCurSeg->m_cbSegNext + m_cbCurSegOffset;
+
+ if (FAILED(Align(cbSize, pcbSaveSize)))
+ {
+ *pcbSaveSize = 0;
+ Debug_ReportInternalError("Aligned size of string heap overflows - we should prevent creating such heaps.");
+ return CLDB_E_INTERNALERROR;
+ }
+ return S_OK;
+ }
+
+//*****************************************************************************
+// Return the size in bytes of the edits contained in the persistent version of this pool.
+//*****************************************************************************
+ __checkReturn
+ HRESULT GetEditSaveSize(
+ UINT32 *pcbSaveSize) const // Return save size of this pool.
+ {
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ _ASSERTE(pcbSaveSize != NULL);
+ UINT32 cbSize = 0;
+
+ if (HaveEdits())
+ {
+ // Size is offset of last seg + size of last seg.
+
+ // An offset of zero in the pool will give us a zero length blob. The first
+ // "real" user string is at offset 1. Wherever this delta gets applied, it will
+ // already have this zero length blob. Let's make sure we don't sent it another one.
+#ifdef _DEBUG
+ MetaData::DataBlob debug_data;
+ HRESULT hr = const_cast<StgPool *>(this)->GetData(0, &debug_data);
+ _ASSERTE(hr == S_OK);
+ _ASSERTE(debug_data.ContainsData(1));
+ _ASSERTE(*(debug_data.GetDataPointer()) == 0);
+#endif //_DEBUG
+ UINT32 nOffsetOfEdit = GetOffsetOfEdit();
+
+ if (nOffsetOfEdit == 0)
+ nOffsetOfEdit = 1;
+
+ cbSize = m_pCurSeg->m_cbSegNext + m_cbCurSegOffset - nOffsetOfEdit;
+ }
+
+ if (FAILED(Align(cbSize, pcbSaveSize)))
+ {
+ *pcbSaveSize = 0;
+ Debug_ReportInternalError("Aligned size of string heap overflows - we should prevent creating such heaps.");
+ return CLDB_E_INTERNALERROR;
+ }
+ return S_OK;
+ } // StgPool::GetEditSaveSize
+
+//*****************************************************************************
+// The entire pool is written to the given stream. The stream is aligned
+// to a 4 byte boundary.
+//*****************************************************************************
+ __checkReturn
+ virtual HRESULT PersistToStream( // Return code.
+ IStream *pIStream) // The stream to write to.
+ DAC_UNEXPECTED();
+
+//*****************************************************************************
+// A portion of the pool is written to the stream. Must not be optimized.
+//*****************************************************************************
+ __checkReturn
+ virtual HRESULT PersistPartialToStream( // Return code.
+ IStream *pIStream, // The stream to write to.
+ ULONG iOffset); // Starting byte.
+
+//*****************************************************************************
+// Get the size of the data block obtained from the pool.
+// Needed for generic persisting of data blocks.
+// Override in concrete pool classes to return the correct size.
+//*****************************************************************************
+ virtual ULONG GetSizeOfData( void const * data )
+ {
+ LIMITED_METHOD_CONTRACT;
+ return 0;
+ }
+
+//*****************************************************************************
+// Return the size of the pool.
+//*****************************************************************************
+ virtual UINT32 GetPoolSize() const
+ {LIMITED_METHOD_CONTRACT; return m_pCurSeg->m_cbSegNext + m_cbCurSegOffset; }
+
+//*****************************************************************************
+// Indicate if heap is empty.
+//*****************************************************************************
+ virtual int IsEmpty() // true if empty.
+ {LIMITED_METHOD_CONTRACT; return (m_pSegData == m_zeros); }
+
+//*****************************************************************************
+// true if the heap is read only.
+//*****************************************************************************
+ int IsReadOnly()
+ {LIMITED_METHOD_CONTRACT; return (m_bReadOnly == false); }
+
+//*****************************************************************************
+// Is the given cookie a valid offset, index, etc?
+//*****************************************************************************
+ virtual int IsValidCookie(UINT32 nCookie)
+ { WRAPPER_NO_CONTRACT; return (IsValidOffset(nCookie)); }
+
+//*****************************************************************************
+// Get a pointer to an offset within the heap. Inline for base segment,
+// helper for extension segments.
+//*****************************************************************************
+ __checkReturn
+ FORCEINLINE HRESULT GetData(UINT32 nOffset, MetaData::DataBlob *pData)
+ {
+ WRAPPER_NO_CONTRACT;
+ if (nOffset < m_cbSegNext)
+ {
+ pData->Init(m_pSegData + nOffset, m_cbSegNext - nOffset);
+ return S_OK;
+ }
+ else
+ {
+ return GetData_i(nOffset, pData);
+ }
+ } // StgPool::GetData
+
+ // Copies data from pSourcePool starting at index nStartSourceIndex.
+ __checkReturn
+ HRESULT CopyPool(
+ UINT32 nStartSourceIndex,
+ const StgPool *pSourcePool);
+
+//*****************************************************************************
+// Copies data from the pool into a buffer. It will correctly walk the different
+// segments for the copy
+//*****************************************************************************
+private:
+ __checkReturn
+ HRESULT CopyData(
+ UINT32 nOffset,
+ BYTE *pBuffer,
+ UINT32 cbBuffer,
+ UINT32 *pcbWritten) const;
+
+public:
+//*****************************************************************************
+// Helpers for dump utilities.
+//*****************************************************************************
+ UINT32 GetRawSize() const
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ // Size is offset of last seg + size of last seg.
+ return m_pCurSeg->m_cbSegNext + m_cbCurSegOffset;
+ }
+
+ BOOL HaveEdits() const {LIMITED_METHOD_CONTRACT; return m_fValidOffsetOfEdit;}
+ UINT32 GetOffsetOfEdit() const {LIMITED_METHOD_CONTRACT; return m_cbStartOffsetOfEdit;}
+ void ResetOffsetOfEdit() {LIMITED_METHOD_CONTRACT; m_fValidOffsetOfEdit=FALSE;}
+
+protected:
+
+//*****************************************************************************
+// Check whether a given offset is valid in the pool.
+//*****************************************************************************
+ virtual int IsValidOffset(UINT32 nOffset)
+ { WRAPPER_NO_CONTRACT; return (nOffset == 0) || ((m_pSegData != m_zeros) && (nOffset < GetNextOffset())); }
+
+ // Following virtual because a) this header included outside the project, and
+ // non-virtual function call (in non-expanded inline function!!) generates
+ // an external def, which causes linkage errors.
+ __checkReturn
+ virtual HRESULT GetData_i(UINT32 nOffset, MetaData::DataBlob *pData);
+
+ // Get pointer to next location to which to write.
+ BYTE *GetNextLocation()
+ {LIMITED_METHOD_CONTRACT; return (m_pCurSeg->m_pSegData + m_pCurSeg->m_cbSegNext); }
+
+ // Get pool-relative offset of next location to which to write.
+ ULONG GetNextOffset()
+ {LIMITED_METHOD_CONTRACT; return (m_cbCurSegOffset + m_pCurSeg->m_cbSegNext); }
+
+ // Get count of bytes available in tail segment of pool.
+ ULONG GetCbSegAvailable()
+ {LIMITED_METHOD_CONTRACT; return (m_pCurSeg->m_cbSegSize - m_pCurSeg->m_cbSegNext); }
+
+ // Allocate space from the segment.
+
+ void SegAllocate(ULONG cb)
+ {
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(cb <= GetCbSegAvailable());
+
+ if (!m_fValidOffsetOfEdit)
+ {
+ m_cbStartOffsetOfEdit = GetNextOffset();
+ m_fValidOffsetOfEdit = TRUE;
+ }
+
+ m_pCurSeg->m_cbSegNext += cb;
+ }// SegAllocate
+
+
+
+ ULONG m_ulGrowInc; // How many bytes at a time.
+ StgPoolSeg *m_pCurSeg; // Current seg for append -- end of chain.
+ ULONG m_cbCurSegOffset; // Base offset of current seg.
+
+ unsigned m_bFree : 1; // True if we should free base data.
+ // Extension data is always freed.
+ unsigned m_bReadOnly : 1; // True if we shouldn't append.
+
+ UINT32 m_nVariableAlignmentMask; // Alignment mask (variable 0, 1 or 3).
+ UINT32 m_cbStartOffsetOfEdit; // Place in the pool where edits started
+ BOOL m_fValidOffsetOfEdit; // Is the pool edit offset valid
+
+};
+
+
+//
+//
+// StgStringPool
+//
+//
+
+
+
+//*****************************************************************************
+// This string pool class collects user strings into a big consecutive heap.
+// Internally it manages this data in a hash table at run time to help throw
+// out duplicates. The list of strings is kept in memory while adding, and
+// finally flushed to a stream at the caller's request.
+//*****************************************************************************
+class StgStringPool : public StgPool
+{
+ friend class VerifyLayoutsMD;
+public:
+ StgStringPool() :
+ StgPool(DFT_STRING_HEAP_SIZE),
+ m_Hash(this),
+ m_bHash(true)
+ {
+ LIMITED_METHOD_CONTRACT;
+ // force some code in debug.
+ _ASSERTE(m_bHash);
+ }
+
+//*****************************************************************************
+// Create a new, empty string pool.
+//*****************************************************************************
+ __checkReturn
+ HRESULT InitNew( // Return code.
+ ULONG cbSize=0, // Estimated size.
+ ULONG cItems=0); // Estimated item count.
+
+//*****************************************************************************
+// Load a string heap from persisted memory. If a copy of the data is made
+// (so that it may be updated), then a new hash table is generated which can
+// be used to elminate duplicates with new strings.
+//*****************************************************************************
+ __checkReturn
+ HRESULT InitOnMem( // Return code.
+ void *pData, // Predefined data.
+ ULONG iSize, // Size of data.
+ int bReadOnly); // true if append is forbidden.
+
+//*****************************************************************************
+// Clears the hash table then calls the base class.
+//*****************************************************************************
+ void Uninit();
+
+//*****************************************************************************
+// Turn hashing off or on. If you turn hashing on, then any existing data is
+// thrown away and all data is rehashed during this call.
+//*****************************************************************************
+ __checkReturn
+ virtual HRESULT SetHash(int bHash);
+
+//*****************************************************************************
+// The string will be added to the pool. The offset of the string in the pool
+// is returned in *piOffset. If the string is already in the pool, then the
+// offset will be to the existing copy of the string.
+//
+// The first version essentially adds a zero-terminated sequence of bytes
+// to the pool. MBCS pairs will not be converted to the appropriate UTF8
+// sequence. The second version converts from Unicode.
+//*****************************************************************************
+ __checkReturn
+ HRESULT AddString(
+ LPCSTR szString, // The string to add to pool.
+ UINT32 *pnOffset); // Return offset of string here.
+
+ __checkReturn
+ HRESULT AddStringW(
+ LPCWSTR szString, // The string to add to pool.
+ UINT32 *pnOffset); // Return offset of string here.
+
+//*****************************************************************************
+// Look for the string and return its offset if found.
+//*****************************************************************************
+ __checkReturn
+ HRESULT FindString( // S_OK, S_FALSE.
+ LPCSTR szString, // The string to find in pool.
+ ULONG *piOffset) // Return offset of string here.
+ {
+ WRAPPER_NO_CONTRACT;
+
+ STRINGHASH *pHash; // Hash item for lookup.
+ if ((pHash = m_Hash.Find(szString)) == 0)
+ return (S_FALSE);
+ *piOffset = pHash->iOffset;
+ return (S_OK);
+ }
+
+//*****************************************************************************
+// How many objects are there in the pool? If the count is 0, you don't need
+// to persist anything at all to disk.
+//*****************************************************************************
+ int Count()
+ {
+ WRAPPER_NO_CONTRACT;
+ _ASSERTE(m_bHash);
+ return (m_Hash.Count()); }
+
+//*****************************************************************************
+// String heap is considered empty if the only thing it has is the initial
+// empty string, or if after organization, there are no strings.
+//*****************************************************************************
+ int IsEmpty() // true if empty.
+ {
+ WRAPPER_NO_CONTRACT;
+
+ return (GetNextOffset() <= 1);
+ }
+
+//*****************************************************************************
+// Return the size in bytes of the persistent version of this pool. If
+// PersistToStream were the next call, the amount of bytes written to pIStream
+// has to be same as the return value from this function.
+//*****************************************************************************
+ __checkReturn
+ virtual HRESULT GetSaveSize(
+ UINT32 *pcbSaveSize) const
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ _ASSERTE(pcbSaveSize != NULL);
+
+ // Size is offset of last seg + size of last seg.
+ S_UINT32 cbSize = S_UINT32(m_pCurSeg->m_cbSegNext + m_cbCurSegOffset);
+
+ cbSize.AlignUp(4);
+
+ if (cbSize.IsOverflow())
+ {
+ *pcbSaveSize = 0;
+ Debug_ReportInternalError("Aligned size of string heap overflows - we should prevent creating such heaps.");
+ return CLDB_E_INTERNALERROR;
+ }
+ *pcbSaveSize = cbSize.Value();
+ return S_OK;
+ }
+
+//*****************************************************************************
+// Get the size of the string obtained from the pool.
+// Needed for generic persisting of data blocks.
+//*****************************************************************************
+ virtual ULONG GetSizeOfData( void const * data )
+ {
+ LIMITED_METHOD_CONTRACT;
+ return ULONG( strlen( reinterpret_cast< LPCSTR >( data ) ) + 1 ); // using strlen since the string is UTF8
+ }
+
+private:
+ __checkReturn
+ HRESULT RehashStrings();
+
+private:
+ CStringPoolHash m_Hash; // Hash table for lookups.
+ int m_bHash; // true to keep hash table.
+}; // class StgStringPool
+
+//
+//
+// StgGuidPool
+//
+//
+
+//*****************************************************************************
+// This Guid pool class collects user Guids into a big consecutive heap.
+// Internally it manages this data in a hash table at run time to help throw
+// out duplicates. The list of Guids is kept in memory while adding, and
+// finally flushed to a stream at the caller's request.
+//*****************************************************************************
+class StgGuidPool : public StgPool
+{
+ friend class VerifyLayoutsMD;
+public:
+ StgGuidPool() :
+ StgPool(DFT_GUID_HEAP_SIZE),
+ m_Hash(this),
+ m_bHash(true)
+ { LIMITED_METHOD_CONTRACT; }
+
+//*****************************************************************************
+// Init the pool for use. This is called for the create empty case.
+//*****************************************************************************
+ __checkReturn
+ HRESULT InitNew( // Return code.
+ ULONG cbSize=0, // Estimated size.
+ ULONG cItems=0); // Estimated item count.
+
+//*****************************************************************************
+// Load a Guid heap from persisted memory. If a copy of the data is made
+// (so that it may be updated), then a new hash table is generated which can
+// be used to elminate duplicates with new Guids.
+//*****************************************************************************
+ __checkReturn
+ HRESULT InitOnMem( // Return code.
+ void *pData, // Predefined data.
+ ULONG iSize, // Size of data.
+ int bReadOnly); // true if append is forbidden.
+
+//*****************************************************************************
+// Clears the hash table then calls the base class.
+//*****************************************************************************
+ void Uninit();
+
+//*****************************************************************************
+// Add a segment to the chain of segments.
+//*****************************************************************************
+ __checkReturn
+ virtual HRESULT AddSegment( // S_OK or error.
+ const void *pData, // The data.
+ ULONG cbData, // Size of the data.
+ bool bCopy); // If true, make a copy of the data.
+
+//*****************************************************************************
+// Turn hashing off or on. If you turn hashing on, then any existing data is
+// thrown away and all data is rehashed during this call.
+//*****************************************************************************
+ __checkReturn
+ virtual HRESULT SetHash(int bHash);
+
+//*****************************************************************************
+// The Guid will be added to the pool. The index of the Guid in the pool
+// is returned in *piIndex. If the Guid is already in the pool, then the
+// index will be to the existing copy of the Guid.
+//*****************************************************************************
+ __checkReturn
+ HRESULT AddGuid(
+ const GUID *pGuid, // The Guid to add to pool.
+ UINT32 *pnIndex); // Return index of Guid here.
+
+//*****************************************************************************
+// Get the size of the GUID obtained from the pool.
+// Needed for generic persisting of data blocks.
+//*****************************************************************************
+ virtual ULONG GetSizeOfData( void const * data )
+ {
+ LIMITED_METHOD_CONTRACT;
+ return sizeof( GUID );
+ }
+
+//*****************************************************************************
+// How many objects are there in the pool? If the count is 0, you don't need
+// to persist anything at all to disk.
+//*****************************************************************************
+ int Count()
+ {
+ WRAPPER_NO_CONTRACT;
+ _ASSERTE(m_bHash);
+ return (m_Hash.Count()); }
+
+//*****************************************************************************
+// Indicate if heap is empty. This has to be based on the size of the data
+// we are keeping. If you open in r/o mode on memory, there is no hash
+// table.
+//*****************************************************************************
+ virtual int IsEmpty() // true if empty.
+ {
+ WRAPPER_NO_CONTRACT;
+
+ return (GetNextOffset() == 0);
+ }
+
+//*****************************************************************************
+// Is the index valid for the GUID?
+//*****************************************************************************
+ virtual int IsValidCookie(UINT32 nCookie)
+ { WRAPPER_NO_CONTRACT; return ((nCookie == 0) || IsValidOffset((nCookie - 1) * sizeof(GUID))); }
+
+//*****************************************************************************
+// Return the size of the heap.
+//*****************************************************************************
+ ULONG GetNextIndex()
+ { LIMITED_METHOD_CONTRACT; return (GetNextOffset() / sizeof(GUID)); }
+
+//*****************************************************************************
+// Return the size in bytes of the persistent version of this pool. If
+// PersistToStream were the next call, the amount of bytes written to pIStream
+// has to be same as the return value from this function.
+//*****************************************************************************
+ __checkReturn
+ virtual HRESULT GetSaveSize(
+ UINT32 *pcbSaveSize) const
+ {
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ _ASSERTE(pcbSaveSize != NULL);
+
+ // Size is offset of last seg + size of last seg.
+ *pcbSaveSize = m_pCurSeg->m_cbSegNext + m_cbCurSegOffset;
+
+ // Should be aligned.
+ _ASSERTE(*pcbSaveSize == ALIGN4BYTE(*pcbSaveSize));
+ return S_OK;
+ }
+
+private:
+
+ __checkReturn
+ HRESULT RehashGuids();
+
+
+private:
+ CGuidPoolHash m_Hash; // Hash table for lookups.
+ int m_bHash; // true to keep hash table.
+}; // class StgGuidPool
+
+//
+//
+// StgBlobPool
+//
+//
+
+//*****************************************************************************
+// Just like the string pool, this pool manages a list of items, throws out
+// duplicates using a hash table, and can be persisted to a stream. The only
+// difference is that instead of saving null terminated strings, this code
+// manages binary values of up to 64K in size. Any data you have larger than
+// this should be stored someplace else with a pointer in the record to the
+// external source.
+//*****************************************************************************
+class StgBlobPool : public StgPool
+{
+ friend class VerifyLayoutsMD;
+
+ using StgPool::InitNew;
+ using StgPool::InitOnMem;
+
+public:
+ StgBlobPool(ULONG ulGrowInc=DFT_BLOB_HEAP_SIZE) :
+ StgPool(ulGrowInc),
+ m_Hash(this)
+ { LIMITED_METHOD_CONTRACT; }
+
+//*****************************************************************************
+// Init the pool for use. This is called for the create empty case.
+//*****************************************************************************
+ __checkReturn
+ HRESULT InitNew( // Return code.
+ ULONG cbSize=0, // Estimated size.
+ ULONG cItems=0, // Estimated item count.
+ BOOL fAddEmptryItem=TRUE); // Should we add an empty item at offset 0
+
+//*****************************************************************************
+// Init the blob pool for use. This is called for both create and read case.
+// If there is existing data and bCopyData is true, then the data is rehashed
+// to eliminate dupes in future adds.
+//*****************************************************************************
+ __checkReturn
+ HRESULT InitOnMem( // Return code.
+ void *pData, // Predefined data.
+ ULONG iSize, // Size of data.
+ int bReadOnly); // true if append is forbidden.
+
+//*****************************************************************************
+// Clears the hash table then calls the base class.
+//*****************************************************************************
+ void Uninit();
+
+//*****************************************************************************
+// The blob will be added to the pool. The offset of the blob in the pool
+// is returned in *piOffset. If the blob is already in the pool, then the
+// offset will be to the existing copy of the blob.
+//*****************************************************************************
+ __checkReturn
+ HRESULT AddBlob(
+ const MetaData::DataBlob *pData,
+ UINT32 *pnOffset);
+
+//*****************************************************************************
+// Return a pointer to a null terminated blob given an offset previously
+// handed out by Addblob or Findblob.
+//*****************************************************************************
+ __checkReturn
+ virtual HRESULT GetBlob(
+ UINT32 nOffset, // Offset of blob in pool.
+ MetaData::DataBlob *pData);
+
+ __checkReturn
+ HRESULT GetBlobWithSizePrefix(
+ UINT32 nOffset, // Offset of blob in pool.
+ MetaData::DataBlob *pData);
+
+//*****************************************************************************
+// Turn hashing off or on. If you turn hashing on, then any existing data is
+// thrown away and all data is rehashed during this call.
+//*****************************************************************************
+ __checkReturn
+ virtual HRESULT SetHash(int bHash);
+
+//*****************************************************************************
+// Get the size of the blob obtained from the pool.
+// Needed for generic persisting of data blocks.
+//*****************************************************************************
+ virtual ULONG GetSizeOfData( void const * data )
+ {
+ WRAPPER_NO_CONTRACT;
+
+ void const * blobdata = 0;
+ ULONG blobsize = CPackedLen::GetLength( data, & blobdata ); // the size is encoded at the beginning of the block
+ return blobsize + static_cast< ULONG >( reinterpret_cast< BYTE const * >( blobdata ) - reinterpret_cast< BYTE const * >( data ) );
+ }
+
+//*****************************************************************************
+// How many objects are there in the pool? If the count is 0, you don't need
+// to persist anything at all to disk.
+//*****************************************************************************
+ int Count()
+ { WRAPPER_NO_CONTRACT; return (m_Hash.Count()); }
+
+//*****************************************************************************
+// String heap is considered empty if the only thing it has is the initial
+// empty string, or if after organization, there are no strings.
+//*****************************************************************************
+ virtual int IsEmpty() // true if empty.
+ {
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ return (GetNextOffset() <= 1);
+ }
+
+//*****************************************************************************
+// Return the size in bytes of the persistent version of this pool. If
+// PersistToStream were the next call, the amount of bytes written to pIStream
+// has to be same as the return value from this function.
+//*****************************************************************************
+ __checkReturn
+ virtual HRESULT GetSaveSize(
+ UINT32 *pcbSaveSize) const
+ {
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ return StgPool::GetSaveSize(pcbSaveSize);
+ }
+
+protected:
+
+//*****************************************************************************
+// Check whether a given offset is valid in the pool.
+//*****************************************************************************
+ virtual int IsValidOffset(UINT32 nOffset)
+ {
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ MetaData::DataBlob data;
+ return (StgBlobPool::GetBlob(nOffset, &data) == S_OK);
+ }
+
+private:
+ __checkReturn
+ HRESULT RehashBlobs();
+
+ CBlobPoolHash m_Hash; // Hash table for lookups.
+}; // class StgBlobPool
+
+#ifdef _MSC_VER
+#pragma warning (default : 4355)
+#endif
+
+//*****************************************************************************
+// Unfortunately the CreateStreamOnHGlobal is a little too smart in that
+// it gets its size from GlobalSize. This means that even if you give it the
+// memory for the stream, it has to be globally allocated. We don't want this
+// because we have the stream read only in the middle of a memory mapped file.
+// CreateStreamOnMemory and the corresponding, internal only stream object solves
+// that problem.
+//*****************************************************************************
+class CInMemoryStream : public IStream
+{
+public:
+ CInMemoryStream() :
+ m_pMem(0),
+ m_cbSize(0),
+ m_cbCurrent(0),
+ m_cRef(1),
+ m_dataCopy(NULL)
+ { LIMITED_METHOD_CONTRACT; }
+
+ virtual ~CInMemoryStream() {}
+
+ void InitNew(
+ void *pMem,
+ ULONG cbSize)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ m_pMem = pMem;
+ m_cbSize = cbSize;
+ m_cbCurrent = 0;
+ }
+
+ ULONG STDMETHODCALLTYPE AddRef() {
+ LIMITED_METHOD_CONTRACT;
+ return InterlockedIncrement(&m_cRef);
+ }
+
+
+ ULONG STDMETHODCALLTYPE Release();
+
+ __checkReturn
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, PVOID *ppOut);
+
+ __checkReturn
+ HRESULT STDMETHODCALLTYPE Read(void *pv, ULONG cb, ULONG *pcbRead);
+
+ __checkReturn
+ HRESULT STDMETHODCALLTYPE Write(const void *pv, ULONG cb, ULONG *pcbWritten);
+
+ __checkReturn
+ HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER dlibMove,DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition);
+
+ __checkReturn
+ HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER libNewSize)
+ {
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY;
+
+ return (BadError(E_NOTIMPL));
+ }
+
+ __checkReturn
+ HRESULT STDMETHODCALLTYPE CopyTo(
+ IStream *pstm,
+ ULARGE_INTEGER cb,
+ ULARGE_INTEGER *pcbRead,
+ ULARGE_INTEGER *pcbWritten);
+
+ __checkReturn
+ HRESULT STDMETHODCALLTYPE Commit(
+ DWORD grfCommitFlags)
+ {
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY;
+
+ return (BadError(E_NOTIMPL));
+ }
+
+ __checkReturn
+ HRESULT STDMETHODCALLTYPE Revert()
+ {
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY;
+
+ return (BadError(E_NOTIMPL));
+ }
+
+ __checkReturn
+ HRESULT STDMETHODCALLTYPE LockRegion(
+ ULARGE_INTEGER libOffset,
+ ULARGE_INTEGER cb,
+ DWORD dwLockType)
+ {
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY;
+
+ return (BadError(E_NOTIMPL));
+ }
+
+ __checkReturn
+ HRESULT STDMETHODCALLTYPE UnlockRegion(
+ ULARGE_INTEGER libOffset,
+ ULARGE_INTEGER cb,
+ DWORD dwLockType)
+ {
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY;
+
+ return (BadError(E_NOTIMPL));
+ }
+
+ __checkReturn
+ HRESULT STDMETHODCALLTYPE Stat(
+ STATSTG *pstatstg,
+ DWORD grfStatFlag)
+ {
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY;
+
+ pstatstg->cbSize.QuadPart = m_cbSize;
+ return (S_OK);
+ }
+
+ __checkReturn
+ HRESULT STDMETHODCALLTYPE Clone(
+ IStream **ppstm)
+ {
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY;
+
+ return (BadError(E_NOTIMPL));
+ }
+
+ __checkReturn
+ static HRESULT CreateStreamOnMemory( // Return code.
+ void *pMem, // Memory to create stream on.
+ ULONG cbSize, // Size of data.
+ IStream **ppIStream, // Return stream object here.
+ BOOL fDeleteMemoryOnRelease = FALSE
+ );
+
+ __checkReturn
+ static HRESULT CreateStreamOnMemoryCopy(
+ void *pMem,
+ ULONG cbSize,
+ IStream **ppIStream);
+
+private:
+ void *m_pMem; // Memory for the read.
+ ULONG m_cbSize; // Size of the memory.
+ ULONG m_cbCurrent; // Current offset.
+ LONG m_cRef; // Ref count.
+ BYTE *m_dataCopy; // Optional copy of the data.
+}; // class CInMemoryStream
+
+//*****************************************************************************
+// CGrowableStream is a simple IStream implementation that grows as
+// its written to. All the memory is contigious, so read access is
+// fast. A grow does a realloc, so be aware of that if you're going to
+// use this.
+//*****************************************************************************
+
+// DPTR instead of VPTR because we don't actually call any of the virtuals.
+typedef DPTR(class CGrowableStream) PTR_CGrowableStream;
+
+class CGrowableStream : public IStream
+{
+public:
+ //Constructs a new GrowableStream
+ // multiplicativeGrowthRate - when the stream grows it will be at least this
+ // multiple of its old size. Values greater than 1 ensure O(N) amortized
+ // performance growing the stream to size N, 1 ensures O(N^2) amortized perf
+ // but gives the tightest memory usage. Valid range is [1.0, 2.0].
+ // additiveGrowthRate - when the stream grows it will increase in size by at least
+ // this number of bytes. Larger numbers cause fewer re-allocations at the cost of
+ // increased memory usage.
+ CGrowableStream(float multiplicativeGrowthRate = 2.0, DWORD additiveGrowthRate = 4096);
+
+#ifndef DACCESS_COMPILE
+ virtual ~CGrowableStream();
+#endif
+
+ // Expose the total raw buffer.
+ // This can be used by DAC to get the raw contents.
+ // This becomes potentiallyinvalid on the next call on the class, because the underlying storage can be
+ // reallocated.
+ MemoryRange GetRawBuffer() const
+ {
+ SUPPORTS_DAC;
+ PTR_VOID p = m_swBuffer;
+ return MemoryRange(p, m_dwBufferSize);
+ }
+
+private:
+ // Raw pointer to buffer. This may change as the buffer grows and gets reallocated.
+ PTR_BYTE m_swBuffer;
+
+ // Total size of the buffer in bytes.
+ DWORD m_dwBufferSize;
+
+ // Current index in the buffer. This can be moved around by Seek.
+ DWORD m_dwBufferIndex;
+
+ // Logical length of the stream
+ DWORD m_dwStreamLength;
+
+ // Reference count
+ LONG m_cRef;
+
+ // growth rate parameters determine new stream size when it must grow
+ float m_multiplicativeGrowthRate;
+ int m_additiveGrowthRate;
+
+ // Ensures the stream is physically and logically at least newLogicalSize
+ // in size
+ HRESULT EnsureCapacity(DWORD newLogicalSize);
+
+ // IStream methods
+public:
+
+#ifndef DACCESS_COMPILE
+ ULONG STDMETHODCALLTYPE AddRef() {
+ LIMITED_METHOD_CONTRACT;
+ return InterlockedIncrement(&m_cRef);
+ }
+
+
+ ULONG STDMETHODCALLTYPE Release();
+
+ __checkReturn
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, PVOID *ppOut);
+
+ STDMETHOD(Read)(
+ void * pv,
+ ULONG cb,
+ ULONG * pcbRead);
+
+ STDMETHOD(Write)(
+ const void * pv,
+ ULONG cb,
+ ULONG * pcbWritten);
+
+ STDMETHOD(Seek)(
+ LARGE_INTEGER dlibMove,
+ DWORD dwOrigin,
+ ULARGE_INTEGER * plibNewPosition);
+
+ STDMETHOD(SetSize)(ULARGE_INTEGER libNewSize);
+
+ STDMETHOD(CopyTo)(
+ IStream * pstm,
+ ULARGE_INTEGER cb,
+ ULARGE_INTEGER * pcbRead,
+ ULARGE_INTEGER * pcbWritten) { STATIC_CONTRACT_NOTHROW; STATIC_CONTRACT_FAULT; return E_NOTIMPL; }
+
+ STDMETHOD(Commit)(
+ DWORD grfCommitFlags) { STATIC_CONTRACT_NOTHROW; STATIC_CONTRACT_FAULT; return NOERROR; }
+
+ STDMETHOD(Revert)( void) {STATIC_CONTRACT_NOTHROW; STATIC_CONTRACT_FAULT; return E_NOTIMPL; }
+
+ STDMETHOD(LockRegion)(
+ ULARGE_INTEGER libOffset,
+ ULARGE_INTEGER cb,
+ DWORD dwLockType) { STATIC_CONTRACT_NOTHROW; STATIC_CONTRACT_FAULT; return E_NOTIMPL; }
+
+ STDMETHOD(UnlockRegion)(
+ ULARGE_INTEGER libOffset,
+ ULARGE_INTEGER cb,
+ DWORD dwLockType) {STATIC_CONTRACT_NOTHROW; STATIC_CONTRACT_FAULT; return E_NOTIMPL; }
+
+ STDMETHOD(Stat)(
+ STATSTG * pstatstg,
+ DWORD grfStatFlag);
+
+ // Make a deep copy of the stream into a new CGrowableStream instance
+ STDMETHOD(Clone)(
+ IStream ** ppstm);
+
+#endif // DACCESS_COMPILE
+}; // class CGrowableStream
+
+#endif // __StgPool_h__