1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4//*****************************************************************************
5// StgPool.h
6//
7
8//
9// Pools are used to reduce the amount of data actually required in the database.
10// This allows for duplicate string and binary values to be folded into one
11// copy shared by the rest of the database. Strings are tracked in a hash
12// table when insert/changing data to find duplicates quickly. The strings
13// are then persisted consecutively in a stream in the database format.
14//
15//*****************************************************************************
16
17#ifndef __StgPool_h__
18#define __StgPool_h__
19
20#ifdef _MSC_VER
21#pragma warning (disable : 4355) // warning C4355: 'this' : used in base member initializer list
22#endif
23
24#include "stgpooli.h" // Internal helpers.
25#include "corerror.h" // Error codes.
26#include "metadatatracker.h"
27#include "metamodelpub.h"
28#include "ex.h"
29#include "sarray.h"
30#include "memoryrange.h"
31#include "../md/hotdata/hotheap.h"
32
33#include "../md/debug_metadata.h"
34
35//*****************************************************************************
36// NOTE:
37// One limitation with the pools, we have no way to removing strings from
38// the pool. To remove, you need to know the ref count on the string, and
39// need the ability to compact the pool and reset all references.
40//*****************************************************************************
41
42//********** Constants ********************************************************
43const int DFT_STRING_HEAP_SIZE = 1024;
44const int DFT_GUID_HEAP_SIZE = 32;
45const int DFT_BLOB_HEAP_SIZE = 1024;
46const int DFT_VARIANT_HEAP_SIZE = 512;
47const int DFT_CODE_HEAP_SIZE = 8192;
48
49
50
51// Forwards.
52class StgStringPool;
53class StgBlobPool;
54class StgCodePool;
55class CorProfileData;
56
57// Perform binary search on index table.
58//
59class RIDBinarySearch : public CBinarySearch<UINT32>
60{
61public:
62 RIDBinarySearch(const UINT32 *pBase, int iCount) : CBinarySearch<UINT32>(pBase, iCount)
63 {
64 LIMITED_METHOD_CONTRACT;
65 } // RIDBinarySearch::RIDBinarySearch
66
67 int Compare(UINT32 const *pFirst, UINT32 const *pSecond)
68 {
69 LIMITED_METHOD_CONTRACT;
70
71 if (*pFirst < *pSecond)
72 return -1;
73
74 if (*pFirst > *pSecond)
75 return 1;
76
77 return 0;
78 } // RIDBinarySearch::Compare
79
80}; // class RIDBinarySearch
81
82//*****************************************************************************
83// This class provides common definitions for heap segments. It is both the
84// base class for the heap, and the class for heap extensions (additional
85// memory that must be allocated to grow the heap).
86//*****************************************************************************
87class StgPoolSeg
88{
89 friend class VerifyLayoutsMD;
90public:
91 StgPoolSeg() :
92 m_pSegData((BYTE*)m_zeros),
93 m_pNextSeg(NULL),
94 m_cbSegSize(0),
95 m_cbSegNext(0)
96 {LIMITED_METHOD_CONTRACT; }
97 ~StgPoolSeg()
98 { LIMITED_METHOD_CONTRACT; _ASSERTE(m_pSegData == m_zeros);_ASSERTE(m_pNextSeg == NULL); }
99protected:
100 BYTE *m_pSegData; // Pointer to the data.
101 StgPoolSeg *m_pNextSeg; // Pointer to next segment, or NULL.
102 // Size of the segment buffer. If this is last segment (code:m_pNextSeg is NULL), then it's the
103 // allocation size. If this is not the last segment, then this is shrinked to segment data size
104 // (code:m_cbSegNext).
105 ULONG m_cbSegSize;
106 ULONG m_cbSegNext; // Offset of next available byte in segment.
107 // Segment relative.
108
109 friend class StgPool;
110 friend class StgStringPool;
111 friend class StgGuidPool;
112 friend class StgBlobPool;
113 friend class RecordPool;
114
115public:
116 const BYTE *GetSegData() const { LIMITED_METHOD_CONTRACT; return m_pSegData; }
117 const StgPoolSeg* GetNextSeg() const { LIMITED_METHOD_CONTRACT; return m_pNextSeg; }
118 // Returns size of the segment. It can be bigger than the size of represented data by this segment if
119 // this is the last segment.
120 ULONG GetSegSize() const { LIMITED_METHOD_CONTRACT; return m_cbSegSize; }
121 // Returns size of represented data in this segment.
122 ULONG GetDataSize() const { LIMITED_METHOD_CONTRACT; return m_cbSegNext; }
123
124 static const BYTE m_zeros[64]; // array of zeros for "0" indices.
125 // The size should be at least maximum of all MD table record sizes
126 // (MD\Runtime\MDColumnDescriptors.cpp) which is currently 28 B.
127}; // class StgPoolSeg
128
129namespace MetaData
130{
131 // Forward declarations
132 class StringHeapRO;
133 class StringHeapRW;
134 class BlobHeapRO;
135}; // namespace MetaData
136
137//
138//
139// StgPoolReadOnly
140//
141//
142//*****************************************************************************
143// This is the read only StgPool class
144//*****************************************************************************
145class StgPoolReadOnly : public StgPoolSeg
146{
147friend class CBlobPoolHash;
148friend class MetaData::StringHeapRO;
149friend class MetaData::StringHeapRW;
150friend class MetaData::BlobHeapRO;
151friend class VerifyLayoutsMD;
152
153public:
154 StgPoolReadOnly()
155 { LIMITED_METHOD_CONTRACT; };
156
157 ~StgPoolReadOnly();
158
159
160//*****************************************************************************
161// Init the pool from existing data.
162//*****************************************************************************
163 __checkReturn
164 HRESULT InitOnMemReadOnly( // Return code.
165 void *pData, // Predefined data.
166 ULONG iSize); // Size of data.
167
168//*****************************************************************************
169// Prepare to shut down or reinitialize.
170//*****************************************************************************
171 virtual void Uninit();
172
173//*****************************************************************************
174// Return the size of the pool.
175//*****************************************************************************
176 virtual UINT32 GetPoolSize() const
177 { LIMITED_METHOD_CONTRACT; return m_cbSegSize; }
178
179//*****************************************************************************
180// Indicate if heap is empty.
181//*****************************************************************************
182 virtual int IsEmpty() // true if empty.
183 { LIMITED_METHOD_CONTRACT; _ASSERTE(!"This implementation should never be called!!!"); return FALSE; }
184
185//*****************************************************************************
186// true if the heap is read only.
187//*****************************************************************************
188 virtual int IsReadOnly() { LIMITED_METHOD_CONTRACT; return true ;};
189
190//*****************************************************************************
191// Is the given cookie a valid offset, index, etc?
192//*****************************************************************************
193 virtual int IsValidCookie(UINT32 nCookie)
194 { WRAPPER_NO_CONTRACT; return (IsValidOffset(nCookie)); }
195
196
197#ifdef _PREFAST_
198#pragma warning(push)
199#pragma warning(disable:6387) // Suppress PREFast warning: '*pszString' might be '0': this does not adhere to the specification for the function
200 // *pszString may be NULL only if method fails, but warning 6387 doesn't respect __success(SUCCEEDED(return)) which is part of HRESULT definition
201#endif
202//*****************************************************************************
203// Return a pointer to a null terminated string given an offset previously
204// handed out by AddString or FindString.
205//*****************************************************************************
206 __checkReturn
207 inline HRESULT GetString(
208 UINT32 nIndex,
209 __deref_out LPCSTR *pszString)
210 {
211 HRESULT hr;
212
213 // Size of the data in the heap will be ignored, because we have verified during creation of the string
214 // heap (code:Initialize) and when adding new strings (e.g. code:AddString,
215 // code:AddTemporaryStringBuffer), that the heap is null-terminated, therefore we don't have to check it
216 // for each string in the heap
217 MetaData::DataBlob stringData;
218
219 // Get data from the heap (clears stringData on error)
220 IfFailGo(GetData(
221 nIndex,
222 &stringData));
223 _ASSERTE(hr == S_OK);
224 // Raw data are always at least 1 byte long, otherwise it would be invalid offset and hr != S_OK
225 PREFAST_ASSUME(stringData.GetDataPointer() != NULL);
226 // Fills output string
227 *pszString = reinterpret_cast<LPSTR>(stringData.GetDataPointer());
228 //_ASSERTE(stringData.GetSize() > strlen(*pszString));
229
230 return hr;
231 ErrExit:
232 // Clears output string on error
233 *pszString = NULL;
234
235 return hr;
236 }
237#ifdef _PREFAST_
238#pragma warning(pop)
239#endif
240
241//*****************************************************************************
242// Convert a string to UNICODE into the caller's buffer.
243//*****************************************************************************
244 __checkReturn
245 virtual HRESULT GetStringW( // Return code.
246 ULONG iOffset, // Offset of string in pool.
247 __out_ecount(cchBuffer) LPWSTR szOut, // Output buffer for string.
248 int cchBuffer); // Size of output buffer.
249
250//*****************************************************************************
251// Copy a GUID into the caller's buffer.
252//*****************************************************************************
253 __checkReturn
254 HRESULT GetGuid(
255 UINT32 nIndex, // 1-based index of Guid in pool.
256 GUID UNALIGNED **ppGuid) // Output buffer for Guid.
257 {
258 STATIC_CONTRACT_NOTHROW;
259 STATIC_CONTRACT_FORBID_FAULT;
260
261 HRESULT hr;
262 MetaData::DataBlob heapData;
263
264 if (nIndex == 0)
265 {
266 *ppGuid = (GUID *)m_zeros;
267 return S_OK;
268 }
269
270 S_UINT32 nOffset = S_UINT32(nIndex - 1) * S_UINT32(sizeof(GUID));
271 if (nOffset.IsOverflow() || !IsValidOffset(nOffset.Value()))
272 {
273 Debug_ReportError("Invalid index passed - integer overflow.");
274 IfFailGo(CLDB_E_INDEX_NOTFOUND);
275 }
276 if (FAILED(GetData(nOffset.Value(), &heapData)))
277 {
278 if (nOffset.Value() == 0)
279 {
280 Debug_ReportError("Invalid index 0 passed.");
281 IfFailGo(CLDB_E_INDEX_NOTFOUND);
282 }
283 Debug_ReportInternalError("Invalid index passed.");
284 IfFailGo(CLDB_E_INTERNALERROR);
285 }
286 _ASSERTE(heapData.GetSize() >= sizeof(GUID));
287
288 *ppGuid = (GUID UNALIGNED *)heapData.GetDataPointer();
289 return S_OK;
290
291 ErrExit:
292 *ppGuid = (GUID *)m_zeros;
293 return hr;
294 } // StgPoolReadOnly::GetGuid
295
296//*****************************************************************************
297// Return a pointer to a null terminated blob given an offset previously
298// handed out by Addblob or Findblob.
299//*****************************************************************************
300 __checkReturn
301 virtual HRESULT GetBlob(
302 UINT32 nOffset, // Offset of blob in pool.
303 MetaData::DataBlob *pData);
304
305#ifdef FEATURE_PREJIT
306 // Initialize hot data structures.
307 // Method can be called multiple time, e.g. to disable usage of hot data structures in certain scenarios
308 // (see code:CMiniMd::DisableHotDataUsage).
309 void InitHotData(MetaData::HotHeap hotHeap)
310 {
311 LIMITED_METHOD_CONTRACT;
312
313#if !defined(FEATURE_UTILCODE_NO_DEPENDENCIES)
314 m_HotHeap = hotHeap;
315#else
316 _ASSERTE(!"InitHotData(): Not supposed to exist in RoMetaData.dll");
317#endif //!(defined(FEATURE_UTILCODE_NO_DEPENDENCIES))
318 }
319#endif //FEATURE_PREJIT
320
321protected:
322
323//*****************************************************************************
324// Check whether a given offset is valid in the pool.
325//*****************************************************************************
326 virtual int IsValidOffset(UINT32 nOffset)
327 {LIMITED_METHOD_CONTRACT; return (nOffset == 0) || ((m_pSegData != m_zeros) && (nOffset < m_cbSegSize)); }
328
329//*****************************************************************************
330// Get a pointer to an offset within the heap. Inline for base segment,
331// helper for extension segments.
332//*****************************************************************************
333 __checkReturn
334 FORCEINLINE HRESULT GetDataReadOnly(UINT32 nOffset, __inout MetaData::DataBlob *pData)
335 {
336 LIMITED_METHOD_CONTRACT;
337 _ASSERTE(IsReadOnly());
338
339 // If off the end of the heap, return the 'nul' item from the beginning.
340 if (nOffset >= m_cbSegSize)
341 {
342 Debug_ReportError("Invalid offset passed.");
343 pData->Clear();
344 return CLDB_E_INDEX_NOTFOUND;
345 }
346
347#if !defined(FEATURE_UTILCODE_NO_DEPENDENCIES)
348#ifdef FEATURE_PREJIT
349 // try hot data first
350 if (!m_HotHeap.IsEmpty())
351 {
352 HRESULT hr = m_HotHeap.GetData(nOffset, pData);
353 if ((hr == S_OK) || FAILED(hr))
354 {
355 return hr;
356 }
357 _ASSERTE(hr == S_FALSE);
358 }
359#endif //FEATURE_PREJIT
360#endif //!(defined(FEATURE_UTILCODE_NO_DEPENDENCIES))
361
362
363 pData->Init(m_pSegData + nOffset, m_cbSegSize - nOffset);
364
365 METADATATRACKER_ONLY(MetaDataTracker::NoteAccess((void *)pData->GetDataPointer()));
366
367 return S_OK;
368 } // StgPoolReadOnly::GetDataReadOnly
369
370//*****************************************************************************
371// Get a pointer to an offset within the heap. Inline for base segment,
372// helper for extension segments.
373//*****************************************************************************
374 __checkReturn
375 virtual HRESULT GetData(UINT32 nOffset, __inout MetaData::DataBlob *pData)
376 {
377 WRAPPER_NO_CONTRACT;
378 return GetDataReadOnly(nOffset, pData);
379 } // StgPoolReadOnly::GetData
380
381private:
382#if !defined(FEATURE_UTILCODE_NO_DEPENDENCIES)
383 // hot pool data
384 MetaData::HotHeap m_HotHeap;
385#endif //!(defined(FEATURE_UTILCODE_NO_DEPENDENCIES))
386
387}; // class StgPoolReadOnly
388
389//
390//
391// StgBlobPoolReadOnly
392//
393//
394//*****************************************************************************
395// This is the read only StgBlobPool class
396//*****************************************************************************
397class StgBlobPoolReadOnly : public StgPoolReadOnly
398{
399public:
400//*****************************************************************************
401// Return a pointer to a null terminated blob given an offset
402//*****************************************************************************
403 __checkReturn
404 virtual HRESULT GetBlob(
405 UINT32 nOffset, // Offset of blob in pool.
406 MetaData::DataBlob *pData);
407
408protected:
409
410//*****************************************************************************
411// Check whether a given offset is valid in the pool.
412//*****************************************************************************
413 virtual int IsValidOffset(UINT32 nOffset)
414 {
415 STATIC_CONTRACT_NOTHROW;
416 STATIC_CONTRACT_FORBID_FAULT;
417
418 MetaData::DataBlob data;
419 return (StgBlobPoolReadOnly::GetBlob(nOffset, &data) == S_OK);
420 }
421
422}; // class StgBlobPoolReadOnly
423
424//
425//
426// StgPool
427//
428//
429
430//*****************************************************************************
431// This base class provides common pool management code, such as allocation
432// of dynamic memory.
433//*****************************************************************************
434class StgPool : public StgPoolReadOnly
435{
436friend class StgStringPool;
437friend class StgBlobPool;
438friend class RecordPool;
439friend class CBlobPoolHash;
440friend class VerifyLayoutsMD;
441
442public:
443 StgPool(ULONG ulGrowInc=512, UINT32 nAlignment=4) :
444 m_ulGrowInc(ulGrowInc),
445 m_pCurSeg(this),
446 m_cbCurSegOffset(0),
447 m_bFree(true),
448 m_bReadOnly(false),
449 m_nVariableAlignmentMask(nAlignment-1),
450 m_cbStartOffsetOfEdit(0),
451 m_fValidOffsetOfEdit(0)
452 { LIMITED_METHOD_CONTRACT; }
453
454 virtual ~StgPool();
455
456protected:
457 HRESULT Align(UINT32 nValue, UINT32 *pnAlignedValue) const
458 {
459 LIMITED_METHOD_CONTRACT;
460
461 *pnAlignedValue = (nValue + m_nVariableAlignmentMask) & ~m_nVariableAlignmentMask;
462 if (*pnAlignedValue < nValue)
463 {
464 return COR_E_OVERFLOW;
465 }
466 return S_OK;
467 }
468
469public:
470//*****************************************************************************
471// Init the pool for use. This is called for the create empty case.
472//*****************************************************************************
473 __checkReturn
474 virtual HRESULT InitNew( // Return code.
475 ULONG cbSize=0, // Estimated size.
476 ULONG cItems=0); // Estimated item count.
477
478//*****************************************************************************
479// Init the pool from existing data.
480//*****************************************************************************
481 __checkReturn
482 virtual HRESULT InitOnMem( // Return code.
483 void *pData, // Predefined data.
484 ULONG iSize, // Size of data.
485 int bReadOnly); // true if append is forbidden.
486
487//*****************************************************************************
488// Called when the pool must stop accessing memory passed to InitOnMem().
489//*****************************************************************************
490 __checkReturn
491 virtual HRESULT TakeOwnershipOfInitMem();
492
493//*****************************************************************************
494// Clear out this pool. Cannot use until you call InitNew.
495//*****************************************************************************
496 virtual void Uninit();
497
498//*****************************************************************************
499// Called to copy the pool to writable memory, reset the r/o bit.
500//*****************************************************************************
501 __checkReturn
502 virtual HRESULT ConvertToRW();
503
504//*****************************************************************************
505// Turn hashing off or on. Implemented as required in subclass.
506//*****************************************************************************
507 __checkReturn
508 virtual HRESULT SetHash(int bHash);
509
510//*****************************************************************************
511// Allocate memory if we don't have any, or grow what we have. If successful,
512// then at least iRequired bytes will be allocated.
513//*****************************************************************************
514 bool Grow( // true if successful.
515 ULONG iRequired); // Min required bytes to allocate.
516
517//*****************************************************************************
518// Add a segment to the chain of segments.
519//*****************************************************************************
520 __checkReturn
521 virtual HRESULT AddSegment( // S_OK or error.
522 const void *pData, // The data.
523 ULONG cbData, // Size of the data.
524 bool bCopy); // If true, make a copy of the data.
525
526//*****************************************************************************
527// Trim any empty final segment.
528//*****************************************************************************
529 void Trim(); //
530
531//*****************************************************************************
532// Return the size in bytes of the persistent version of this pool. If
533// PersistToStream were the next call, the amount of bytes written to pIStream
534// has to be same as the return value from this function.
535//*****************************************************************************
536 __checkReturn
537 virtual HRESULT GetSaveSize(
538 UINT32 *pcbSaveSize) const
539 {
540 STATIC_CONTRACT_NOTHROW;
541 STATIC_CONTRACT_FORBID_FAULT;
542
543 _ASSERTE(pcbSaveSize != NULL);
544 // Size is offset of last seg + size of last seg.
545 UINT32 cbSize = m_pCurSeg->m_cbSegNext + m_cbCurSegOffset;
546
547 if (FAILED(Align(cbSize, pcbSaveSize)))
548 {
549 *pcbSaveSize = 0;
550 Debug_ReportInternalError("Aligned size of string heap overflows - we should prevent creating such heaps.");
551 return CLDB_E_INTERNALERROR;
552 }
553 return S_OK;
554 }
555
556//*****************************************************************************
557// Return the size in bytes of the edits contained in the persistent version of this pool.
558//*****************************************************************************
559 __checkReturn
560 HRESULT GetEditSaveSize(
561 UINT32 *pcbSaveSize) const // Return save size of this pool.
562 {
563 STATIC_CONTRACT_NOTHROW;
564 STATIC_CONTRACT_FORBID_FAULT;
565
566 _ASSERTE(pcbSaveSize != NULL);
567 UINT32 cbSize = 0;
568
569 if (HaveEdits())
570 {
571 // Size is offset of last seg + size of last seg.
572
573 // An offset of zero in the pool will give us a zero length blob. The first
574 // "real" user string is at offset 1. Wherever this delta gets applied, it will
575 // already have this zero length blob. Let's make sure we don't sent it another one.
576#ifdef _DEBUG
577 MetaData::DataBlob debug_data;
578 HRESULT hr = const_cast<StgPool *>(this)->GetData(0, &debug_data);
579 _ASSERTE(hr == S_OK);
580 _ASSERTE(debug_data.ContainsData(1));
581 _ASSERTE(*(debug_data.GetDataPointer()) == 0);
582#endif //_DEBUG
583 UINT32 nOffsetOfEdit = GetOffsetOfEdit();
584
585 if (nOffsetOfEdit == 0)
586 nOffsetOfEdit = 1;
587
588 cbSize = m_pCurSeg->m_cbSegNext + m_cbCurSegOffset - nOffsetOfEdit;
589 }
590
591 if (FAILED(Align(cbSize, pcbSaveSize)))
592 {
593 *pcbSaveSize = 0;
594 Debug_ReportInternalError("Aligned size of string heap overflows - we should prevent creating such heaps.");
595 return CLDB_E_INTERNALERROR;
596 }
597 return S_OK;
598 } // StgPool::GetEditSaveSize
599
600//*****************************************************************************
601// The entire pool is written to the given stream. The stream is aligned
602// to a 4 byte boundary.
603//*****************************************************************************
604 __checkReturn
605 virtual HRESULT PersistToStream( // Return code.
606 IStream *pIStream) // The stream to write to.
607 DAC_UNEXPECTED();
608
609//*****************************************************************************
610// A portion of the pool is written to the stream. Must not be optimized.
611//*****************************************************************************
612 __checkReturn
613 virtual HRESULT PersistPartialToStream( // Return code.
614 IStream *pIStream, // The stream to write to.
615 ULONG iOffset); // Starting byte.
616
617//*****************************************************************************
618// Get the size of the data block obtained from the pool.
619// Needed for generic persisting of data blocks.
620// Override in concrete pool classes to return the correct size.
621//*****************************************************************************
622 virtual ULONG GetSizeOfData( void const * data )
623 {
624 LIMITED_METHOD_CONTRACT;
625 return 0;
626 }
627
628//*****************************************************************************
629// Return the size of the pool.
630//*****************************************************************************
631 virtual UINT32 GetPoolSize() const
632 {LIMITED_METHOD_CONTRACT; return m_pCurSeg->m_cbSegNext + m_cbCurSegOffset; }
633
634//*****************************************************************************
635// Indicate if heap is empty.
636//*****************************************************************************
637 virtual int IsEmpty() // true if empty.
638 {LIMITED_METHOD_CONTRACT; return (m_pSegData == m_zeros); }
639
640//*****************************************************************************
641// true if the heap is read only.
642//*****************************************************************************
643 int IsReadOnly()
644 {LIMITED_METHOD_CONTRACT; return (m_bReadOnly == false); }
645
646//*****************************************************************************
647// Is the given cookie a valid offset, index, etc?
648//*****************************************************************************
649 virtual int IsValidCookie(UINT32 nCookie)
650 { WRAPPER_NO_CONTRACT; return (IsValidOffset(nCookie)); }
651
652//*****************************************************************************
653// Get a pointer to an offset within the heap. Inline for base segment,
654// helper for extension segments.
655//*****************************************************************************
656 __checkReturn
657 FORCEINLINE HRESULT GetData(UINT32 nOffset, MetaData::DataBlob *pData)
658 {
659 WRAPPER_NO_CONTRACT;
660 if (nOffset < m_cbSegNext)
661 {
662 pData->Init(m_pSegData + nOffset, m_cbSegNext - nOffset);
663 return S_OK;
664 }
665 else
666 {
667 return GetData_i(nOffset, pData);
668 }
669 } // StgPool::GetData
670
671 // Copies data from pSourcePool starting at index nStartSourceIndex.
672 __checkReturn
673 HRESULT CopyPool(
674 UINT32 nStartSourceIndex,
675 const StgPool *pSourcePool);
676
677//*****************************************************************************
678// Copies data from the pool into a buffer. It will correctly walk the different
679// segments for the copy
680//*****************************************************************************
681private:
682 __checkReturn
683 HRESULT CopyData(
684 UINT32 nOffset,
685 BYTE *pBuffer,
686 UINT32 cbBuffer,
687 UINT32 *pcbWritten) const;
688
689public:
690//*****************************************************************************
691// Helpers for dump utilities.
692//*****************************************************************************
693 UINT32 GetRawSize() const
694 {
695 LIMITED_METHOD_CONTRACT;
696
697 // Size is offset of last seg + size of last seg.
698 return m_pCurSeg->m_cbSegNext + m_cbCurSegOffset;
699 }
700
701 BOOL HaveEdits() const {LIMITED_METHOD_CONTRACT; return m_fValidOffsetOfEdit;}
702 UINT32 GetOffsetOfEdit() const {LIMITED_METHOD_CONTRACT; return m_cbStartOffsetOfEdit;}
703 void ResetOffsetOfEdit() {LIMITED_METHOD_CONTRACT; m_fValidOffsetOfEdit=FALSE;}
704
705protected:
706
707//*****************************************************************************
708// Check whether a given offset is valid in the pool.
709//*****************************************************************************
710 virtual int IsValidOffset(UINT32 nOffset)
711 { WRAPPER_NO_CONTRACT; return (nOffset == 0) || ((m_pSegData != m_zeros) && (nOffset < GetNextOffset())); }
712
713 // Following virtual because a) this header included outside the project, and
714 // non-virtual function call (in non-expanded inline function!!) generates
715 // an external def, which causes linkage errors.
716 __checkReturn
717 virtual HRESULT GetData_i(UINT32 nOffset, MetaData::DataBlob *pData);
718
719 // Get pointer to next location to which to write.
720 BYTE *GetNextLocation()
721 {LIMITED_METHOD_CONTRACT; return (m_pCurSeg->m_pSegData + m_pCurSeg->m_cbSegNext); }
722
723 // Get pool-relative offset of next location to which to write.
724 ULONG GetNextOffset()
725 {LIMITED_METHOD_CONTRACT; return (m_cbCurSegOffset + m_pCurSeg->m_cbSegNext); }
726
727 // Get count of bytes available in tail segment of pool.
728 ULONG GetCbSegAvailable()
729 {LIMITED_METHOD_CONTRACT; return (m_pCurSeg->m_cbSegSize - m_pCurSeg->m_cbSegNext); }
730
731 // Allocate space from the segment.
732
733 void SegAllocate(ULONG cb)
734 {
735 LIMITED_METHOD_CONTRACT;
736 _ASSERTE(cb <= GetCbSegAvailable());
737
738 if (!m_fValidOffsetOfEdit)
739 {
740 m_cbStartOffsetOfEdit = GetNextOffset();
741 m_fValidOffsetOfEdit = TRUE;
742 }
743
744 m_pCurSeg->m_cbSegNext += cb;
745 }// SegAllocate
746
747
748
749 ULONG m_ulGrowInc; // How many bytes at a time.
750 StgPoolSeg *m_pCurSeg; // Current seg for append -- end of chain.
751 ULONG m_cbCurSegOffset; // Base offset of current seg.
752
753 unsigned m_bFree : 1; // True if we should free base data.
754 // Extension data is always freed.
755 unsigned m_bReadOnly : 1; // True if we shouldn't append.
756
757 UINT32 m_nVariableAlignmentMask; // Alignment mask (variable 0, 1 or 3).
758 UINT32 m_cbStartOffsetOfEdit; // Place in the pool where edits started
759 BOOL m_fValidOffsetOfEdit; // Is the pool edit offset valid
760
761};
762
763
764//
765//
766// StgStringPool
767//
768//
769
770
771
772//*****************************************************************************
773// This string pool class collects user strings into a big consecutive heap.
774// Internally it manages this data in a hash table at run time to help throw
775// out duplicates. The list of strings is kept in memory while adding, and
776// finally flushed to a stream at the caller's request.
777//*****************************************************************************
778class StgStringPool : public StgPool
779{
780 friend class VerifyLayoutsMD;
781public:
782 StgStringPool() :
783 StgPool(DFT_STRING_HEAP_SIZE),
784 m_Hash(this),
785 m_bHash(true)
786 {
787 LIMITED_METHOD_CONTRACT;
788 // force some code in debug.
789 _ASSERTE(m_bHash);
790 }
791
792//*****************************************************************************
793// Create a new, empty string pool.
794//*****************************************************************************
795 __checkReturn
796 HRESULT InitNew( // Return code.
797 ULONG cbSize=0, // Estimated size.
798 ULONG cItems=0); // Estimated item count.
799
800//*****************************************************************************
801// Load a string heap from persisted memory. If a copy of the data is made
802// (so that it may be updated), then a new hash table is generated which can
803// be used to elminate duplicates with new strings.
804//*****************************************************************************
805 __checkReturn
806 HRESULT InitOnMem( // Return code.
807 void *pData, // Predefined data.
808 ULONG iSize, // Size of data.
809 int bReadOnly); // true if append is forbidden.
810
811//*****************************************************************************
812// Clears the hash table then calls the base class.
813//*****************************************************************************
814 void Uninit();
815
816//*****************************************************************************
817// Turn hashing off or on. If you turn hashing on, then any existing data is
818// thrown away and all data is rehashed during this call.
819//*****************************************************************************
820 __checkReturn
821 virtual HRESULT SetHash(int bHash);
822
823//*****************************************************************************
824// The string will be added to the pool. The offset of the string in the pool
825// is returned in *piOffset. If the string is already in the pool, then the
826// offset will be to the existing copy of the string.
827//
828// The first version essentially adds a zero-terminated sequence of bytes
829// to the pool. MBCS pairs will not be converted to the appropriate UTF8
830// sequence. The second version converts from Unicode.
831//*****************************************************************************
832 __checkReturn
833 HRESULT AddString(
834 LPCSTR szString, // The string to add to pool.
835 UINT32 *pnOffset); // Return offset of string here.
836
837 __checkReturn
838 HRESULT AddStringW(
839 LPCWSTR szString, // The string to add to pool.
840 UINT32 *pnOffset); // Return offset of string here.
841
842//*****************************************************************************
843// Look for the string and return its offset if found.
844//*****************************************************************************
845 __checkReturn
846 HRESULT FindString( // S_OK, S_FALSE.
847 LPCSTR szString, // The string to find in pool.
848 ULONG *piOffset) // Return offset of string here.
849 {
850 WRAPPER_NO_CONTRACT;
851
852 STRINGHASH *pHash; // Hash item for lookup.
853 if ((pHash = m_Hash.Find(szString)) == 0)
854 return (S_FALSE);
855 *piOffset = pHash->iOffset;
856 return (S_OK);
857 }
858
859//*****************************************************************************
860// How many objects are there in the pool? If the count is 0, you don't need
861// to persist anything at all to disk.
862//*****************************************************************************
863 int Count()
864 {
865 WRAPPER_NO_CONTRACT;
866 _ASSERTE(m_bHash);
867 return (m_Hash.Count()); }
868
869//*****************************************************************************
870// String heap is considered empty if the only thing it has is the initial
871// empty string, or if after organization, there are no strings.
872//*****************************************************************************
873 int IsEmpty() // true if empty.
874 {
875 WRAPPER_NO_CONTRACT;
876
877 return (GetNextOffset() <= 1);
878 }
879
880//*****************************************************************************
881// Return the size in bytes of the persistent version of this pool. If
882// PersistToStream were the next call, the amount of bytes written to pIStream
883// has to be same as the return value from this function.
884//*****************************************************************************
885 __checkReturn
886 virtual HRESULT GetSaveSize(
887 UINT32 *pcbSaveSize) const
888 {
889 LIMITED_METHOD_CONTRACT;
890
891 _ASSERTE(pcbSaveSize != NULL);
892
893 // Size is offset of last seg + size of last seg.
894 S_UINT32 cbSize = S_UINT32(m_pCurSeg->m_cbSegNext + m_cbCurSegOffset);
895
896 cbSize.AlignUp(4);
897
898 if (cbSize.IsOverflow())
899 {
900 *pcbSaveSize = 0;
901 Debug_ReportInternalError("Aligned size of string heap overflows - we should prevent creating such heaps.");
902 return CLDB_E_INTERNALERROR;
903 }
904 *pcbSaveSize = cbSize.Value();
905 return S_OK;
906 }
907
908//*****************************************************************************
909// Get the size of the string obtained from the pool.
910// Needed for generic persisting of data blocks.
911//*****************************************************************************
912 virtual ULONG GetSizeOfData( void const * data )
913 {
914 LIMITED_METHOD_CONTRACT;
915 return ULONG( strlen( reinterpret_cast< LPCSTR >( data ) ) + 1 ); // using strlen since the string is UTF8
916 }
917
918private:
919 __checkReturn
920 HRESULT RehashStrings();
921
922private:
923 CStringPoolHash m_Hash; // Hash table for lookups.
924 int m_bHash; // true to keep hash table.
925}; // class StgStringPool
926
927//
928//
929// StgGuidPool
930//
931//
932
933//*****************************************************************************
934// This Guid pool class collects user Guids into a big consecutive heap.
935// Internally it manages this data in a hash table at run time to help throw
936// out duplicates. The list of Guids is kept in memory while adding, and
937// finally flushed to a stream at the caller's request.
938//*****************************************************************************
939class StgGuidPool : public StgPool
940{
941 friend class VerifyLayoutsMD;
942public:
943 StgGuidPool() :
944 StgPool(DFT_GUID_HEAP_SIZE),
945 m_Hash(this),
946 m_bHash(true)
947 { LIMITED_METHOD_CONTRACT; }
948
949//*****************************************************************************
950// Init the pool for use. This is called for the create empty case.
951//*****************************************************************************
952 __checkReturn
953 HRESULT InitNew( // Return code.
954 ULONG cbSize=0, // Estimated size.
955 ULONG cItems=0); // Estimated item count.
956
957//*****************************************************************************
958// Load a Guid heap from persisted memory. If a copy of the data is made
959// (so that it may be updated), then a new hash table is generated which can
960// be used to elminate duplicates with new Guids.
961//*****************************************************************************
962 __checkReturn
963 HRESULT InitOnMem( // Return code.
964 void *pData, // Predefined data.
965 ULONG iSize, // Size of data.
966 int bReadOnly); // true if append is forbidden.
967
968//*****************************************************************************
969// Clears the hash table then calls the base class.
970//*****************************************************************************
971 void Uninit();
972
973//*****************************************************************************
974// Add a segment to the chain of segments.
975//*****************************************************************************
976 __checkReturn
977 virtual HRESULT AddSegment( // S_OK or error.
978 const void *pData, // The data.
979 ULONG cbData, // Size of the data.
980 bool bCopy); // If true, make a copy of the data.
981
982//*****************************************************************************
983// Turn hashing off or on. If you turn hashing on, then any existing data is
984// thrown away and all data is rehashed during this call.
985//*****************************************************************************
986 __checkReturn
987 virtual HRESULT SetHash(int bHash);
988
989//*****************************************************************************
990// The Guid will be added to the pool. The index of the Guid in the pool
991// is returned in *piIndex. If the Guid is already in the pool, then the
992// index will be to the existing copy of the Guid.
993//*****************************************************************************
994 __checkReturn
995 HRESULT AddGuid(
996 const GUID *pGuid, // The Guid to add to pool.
997 UINT32 *pnIndex); // Return index of Guid here.
998
999//*****************************************************************************
1000// Get the size of the GUID obtained from the pool.
1001// Needed for generic persisting of data blocks.
1002//*****************************************************************************
1003 virtual ULONG GetSizeOfData( void const * data )
1004 {
1005 LIMITED_METHOD_CONTRACT;
1006 return sizeof( GUID );
1007 }
1008
1009//*****************************************************************************
1010// How many objects are there in the pool? If the count is 0, you don't need
1011// to persist anything at all to disk.
1012//*****************************************************************************
1013 int Count()
1014 {
1015 WRAPPER_NO_CONTRACT;
1016 _ASSERTE(m_bHash);
1017 return (m_Hash.Count()); }
1018
1019//*****************************************************************************
1020// Indicate if heap is empty. This has to be based on the size of the data
1021// we are keeping. If you open in r/o mode on memory, there is no hash
1022// table.
1023//*****************************************************************************
1024 virtual int IsEmpty() // true if empty.
1025 {
1026 WRAPPER_NO_CONTRACT;
1027
1028 return (GetNextOffset() == 0);
1029 }
1030
1031//*****************************************************************************
1032// Is the index valid for the GUID?
1033//*****************************************************************************
1034 virtual int IsValidCookie(UINT32 nCookie)
1035 { WRAPPER_NO_CONTRACT; return ((nCookie == 0) || IsValidOffset((nCookie - 1) * sizeof(GUID))); }
1036
1037//*****************************************************************************
1038// Return the size of the heap.
1039//*****************************************************************************
1040 ULONG GetNextIndex()
1041 { LIMITED_METHOD_CONTRACT; return (GetNextOffset() / sizeof(GUID)); }
1042
1043//*****************************************************************************
1044// Return the size in bytes of the persistent version of this pool. If
1045// PersistToStream were the next call, the amount of bytes written to pIStream
1046// has to be same as the return value from this function.
1047//*****************************************************************************
1048 __checkReturn
1049 virtual HRESULT GetSaveSize(
1050 UINT32 *pcbSaveSize) const
1051 {
1052 STATIC_CONTRACT_NOTHROW;
1053 STATIC_CONTRACT_FORBID_FAULT;
1054
1055 _ASSERTE(pcbSaveSize != NULL);
1056
1057 // Size is offset of last seg + size of last seg.
1058 *pcbSaveSize = m_pCurSeg->m_cbSegNext + m_cbCurSegOffset;
1059
1060 // Should be aligned.
1061 _ASSERTE(*pcbSaveSize == ALIGN4BYTE(*pcbSaveSize));
1062 return S_OK;
1063 }
1064
1065private:
1066
1067 __checkReturn
1068 HRESULT RehashGuids();
1069
1070
1071private:
1072 CGuidPoolHash m_Hash; // Hash table for lookups.
1073 int m_bHash; // true to keep hash table.
1074}; // class StgGuidPool
1075
1076//
1077//
1078// StgBlobPool
1079//
1080//
1081
1082//*****************************************************************************
1083// Just like the string pool, this pool manages a list of items, throws out
1084// duplicates using a hash table, and can be persisted to a stream. The only
1085// difference is that instead of saving null terminated strings, this code
1086// manages binary values of up to 64K in size. Any data you have larger than
1087// this should be stored someplace else with a pointer in the record to the
1088// external source.
1089//*****************************************************************************
1090class StgBlobPool : public StgPool
1091{
1092 friend class VerifyLayoutsMD;
1093
1094 using StgPool::InitNew;
1095 using StgPool::InitOnMem;
1096
1097public:
1098 StgBlobPool(ULONG ulGrowInc=DFT_BLOB_HEAP_SIZE) :
1099 StgPool(ulGrowInc),
1100 m_Hash(this)
1101 { LIMITED_METHOD_CONTRACT; }
1102
1103//*****************************************************************************
1104// Init the pool for use. This is called for the create empty case.
1105//*****************************************************************************
1106 __checkReturn
1107 HRESULT InitNew( // Return code.
1108 ULONG cbSize=0, // Estimated size.
1109 ULONG cItems=0, // Estimated item count.
1110 BOOL fAddEmptryItem=TRUE); // Should we add an empty item at offset 0
1111
1112//*****************************************************************************
1113// Init the blob pool for use. This is called for both create and read case.
1114// If there is existing data and bCopyData is true, then the data is rehashed
1115// to eliminate dupes in future adds.
1116//*****************************************************************************
1117 __checkReturn
1118 HRESULT InitOnMem( // Return code.
1119 void *pData, // Predefined data.
1120 ULONG iSize, // Size of data.
1121 int bReadOnly); // true if append is forbidden.
1122
1123//*****************************************************************************
1124// Clears the hash table then calls the base class.
1125//*****************************************************************************
1126 void Uninit();
1127
1128//*****************************************************************************
1129// The blob will be added to the pool. The offset of the blob in the pool
1130// is returned in *piOffset. If the blob is already in the pool, then the
1131// offset will be to the existing copy of the blob.
1132//*****************************************************************************
1133 __checkReturn
1134 HRESULT AddBlob(
1135 const MetaData::DataBlob *pData,
1136 UINT32 *pnOffset);
1137
1138//*****************************************************************************
1139// Return a pointer to a null terminated blob given an offset previously
1140// handed out by Addblob or Findblob.
1141//*****************************************************************************
1142 __checkReturn
1143 virtual HRESULT GetBlob(
1144 UINT32 nOffset, // Offset of blob in pool.
1145 MetaData::DataBlob *pData);
1146
1147 __checkReturn
1148 HRESULT GetBlobWithSizePrefix(
1149 UINT32 nOffset, // Offset of blob in pool.
1150 MetaData::DataBlob *pData);
1151
1152//*****************************************************************************
1153// Turn hashing off or on. If you turn hashing on, then any existing data is
1154// thrown away and all data is rehashed during this call.
1155//*****************************************************************************
1156 __checkReturn
1157 virtual HRESULT SetHash(int bHash);
1158
1159//*****************************************************************************
1160// Get the size of the blob obtained from the pool.
1161// Needed for generic persisting of data blocks.
1162//*****************************************************************************
1163 virtual ULONG GetSizeOfData( void const * data )
1164 {
1165 WRAPPER_NO_CONTRACT;
1166
1167 void const * blobdata = 0;
1168 ULONG blobsize = CPackedLen::GetLength( data, & blobdata ); // the size is encoded at the beginning of the block
1169 return blobsize + static_cast< ULONG >( reinterpret_cast< BYTE const * >( blobdata ) - reinterpret_cast< BYTE const * >( data ) );
1170 }
1171
1172//*****************************************************************************
1173// How many objects are there in the pool? If the count is 0, you don't need
1174// to persist anything at all to disk.
1175//*****************************************************************************
1176 int Count()
1177 { WRAPPER_NO_CONTRACT; return (m_Hash.Count()); }
1178
1179//*****************************************************************************
1180// String heap is considered empty if the only thing it has is the initial
1181// empty string, or if after organization, there are no strings.
1182//*****************************************************************************
1183 virtual int IsEmpty() // true if empty.
1184 {
1185 STATIC_CONTRACT_NOTHROW;
1186 STATIC_CONTRACT_FORBID_FAULT;
1187
1188 return (GetNextOffset() <= 1);
1189 }
1190
1191//*****************************************************************************
1192// Return the size in bytes of the persistent version of this pool. If
1193// PersistToStream were the next call, the amount of bytes written to pIStream
1194// has to be same as the return value from this function.
1195//*****************************************************************************
1196 __checkReturn
1197 virtual HRESULT GetSaveSize(
1198 UINT32 *pcbSaveSize) const
1199 {
1200 STATIC_CONTRACT_NOTHROW;
1201 STATIC_CONTRACT_FORBID_FAULT;
1202
1203 return StgPool::GetSaveSize(pcbSaveSize);
1204 }
1205
1206protected:
1207
1208//*****************************************************************************
1209// Check whether a given offset is valid in the pool.
1210//*****************************************************************************
1211 virtual int IsValidOffset(UINT32 nOffset)
1212 {
1213 STATIC_CONTRACT_NOTHROW;
1214 STATIC_CONTRACT_FORBID_FAULT;
1215
1216 MetaData::DataBlob data;
1217 return (StgBlobPool::GetBlob(nOffset, &data) == S_OK);
1218 }
1219
1220private:
1221 __checkReturn
1222 HRESULT RehashBlobs();
1223
1224 CBlobPoolHash m_Hash; // Hash table for lookups.
1225}; // class StgBlobPool
1226
1227#ifdef _MSC_VER
1228#pragma warning (default : 4355)
1229#endif
1230
1231//*****************************************************************************
1232// Unfortunately the CreateStreamOnHGlobal is a little too smart in that
1233// it gets its size from GlobalSize. This means that even if you give it the
1234// memory for the stream, it has to be globally allocated. We don't want this
1235// because we have the stream read only in the middle of a memory mapped file.
1236// CreateStreamOnMemory and the corresponding, internal only stream object solves
1237// that problem.
1238//*****************************************************************************
1239class CInMemoryStream : public IStream
1240{
1241public:
1242 CInMemoryStream() :
1243 m_pMem(0),
1244 m_cbSize(0),
1245 m_cbCurrent(0),
1246 m_cRef(1),
1247 m_dataCopy(NULL)
1248 { LIMITED_METHOD_CONTRACT; }
1249
1250 virtual ~CInMemoryStream() {}
1251
1252 void InitNew(
1253 void *pMem,
1254 ULONG cbSize)
1255 {
1256 LIMITED_METHOD_CONTRACT;
1257
1258 m_pMem = pMem;
1259 m_cbSize = cbSize;
1260 m_cbCurrent = 0;
1261 }
1262
1263 ULONG STDMETHODCALLTYPE AddRef() {
1264 LIMITED_METHOD_CONTRACT;
1265 return InterlockedIncrement(&m_cRef);
1266 }
1267
1268
1269 ULONG STDMETHODCALLTYPE Release();
1270
1271 __checkReturn
1272 HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, PVOID *ppOut);
1273
1274 __checkReturn
1275 HRESULT STDMETHODCALLTYPE Read(void *pv, ULONG cb, ULONG *pcbRead);
1276
1277 __checkReturn
1278 HRESULT STDMETHODCALLTYPE Write(const void *pv, ULONG cb, ULONG *pcbWritten);
1279
1280 __checkReturn
1281 HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER dlibMove,DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition);
1282
1283 __checkReturn
1284 HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER libNewSize)
1285 {
1286 STATIC_CONTRACT_NOTHROW;
1287 STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY;
1288
1289 return (BadError(E_NOTIMPL));
1290 }
1291
1292 __checkReturn
1293 HRESULT STDMETHODCALLTYPE CopyTo(
1294 IStream *pstm,
1295 ULARGE_INTEGER cb,
1296 ULARGE_INTEGER *pcbRead,
1297 ULARGE_INTEGER *pcbWritten);
1298
1299 __checkReturn
1300 HRESULT STDMETHODCALLTYPE Commit(
1301 DWORD grfCommitFlags)
1302 {
1303 STATIC_CONTRACT_NOTHROW;
1304 STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY;
1305
1306 return (BadError(E_NOTIMPL));
1307 }
1308
1309 __checkReturn
1310 HRESULT STDMETHODCALLTYPE Revert()
1311 {
1312 STATIC_CONTRACT_NOTHROW;
1313 STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY;
1314
1315 return (BadError(E_NOTIMPL));
1316 }
1317
1318 __checkReturn
1319 HRESULT STDMETHODCALLTYPE LockRegion(
1320 ULARGE_INTEGER libOffset,
1321 ULARGE_INTEGER cb,
1322 DWORD dwLockType)
1323 {
1324 STATIC_CONTRACT_NOTHROW;
1325 STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY;
1326
1327 return (BadError(E_NOTIMPL));
1328 }
1329
1330 __checkReturn
1331 HRESULT STDMETHODCALLTYPE UnlockRegion(
1332 ULARGE_INTEGER libOffset,
1333 ULARGE_INTEGER cb,
1334 DWORD dwLockType)
1335 {
1336 STATIC_CONTRACT_NOTHROW;
1337 STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY;
1338
1339 return (BadError(E_NOTIMPL));
1340 }
1341
1342 __checkReturn
1343 HRESULT STDMETHODCALLTYPE Stat(
1344 STATSTG *pstatstg,
1345 DWORD grfStatFlag)
1346 {
1347 STATIC_CONTRACT_NOTHROW;
1348 STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY;
1349
1350 pstatstg->cbSize.QuadPart = m_cbSize;
1351 return (S_OK);
1352 }
1353
1354 __checkReturn
1355 HRESULT STDMETHODCALLTYPE Clone(
1356 IStream **ppstm)
1357 {
1358 STATIC_CONTRACT_NOTHROW;
1359 STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY;
1360
1361 return (BadError(E_NOTIMPL));
1362 }
1363
1364 __checkReturn
1365 static HRESULT CreateStreamOnMemory( // Return code.
1366 void *pMem, // Memory to create stream on.
1367 ULONG cbSize, // Size of data.
1368 IStream **ppIStream, // Return stream object here.
1369 BOOL fDeleteMemoryOnRelease = FALSE
1370 );
1371
1372 __checkReturn
1373 static HRESULT CreateStreamOnMemoryCopy(
1374 void *pMem,
1375 ULONG cbSize,
1376 IStream **ppIStream);
1377
1378private:
1379 void *m_pMem; // Memory for the read.
1380 ULONG m_cbSize; // Size of the memory.
1381 ULONG m_cbCurrent; // Current offset.
1382 LONG m_cRef; // Ref count.
1383 BYTE *m_dataCopy; // Optional copy of the data.
1384}; // class CInMemoryStream
1385
1386//*****************************************************************************
1387// CGrowableStream is a simple IStream implementation that grows as
1388// its written to. All the memory is contigious, so read access is
1389// fast. A grow does a realloc, so be aware of that if you're going to
1390// use this.
1391//*****************************************************************************
1392
1393// DPTR instead of VPTR because we don't actually call any of the virtuals.
1394typedef DPTR(class CGrowableStream) PTR_CGrowableStream;
1395
1396class CGrowableStream : public IStream
1397{
1398public:
1399 //Constructs a new GrowableStream
1400 // multiplicativeGrowthRate - when the stream grows it will be at least this
1401 // multiple of its old size. Values greater than 1 ensure O(N) amortized
1402 // performance growing the stream to size N, 1 ensures O(N^2) amortized perf
1403 // but gives the tightest memory usage. Valid range is [1.0, 2.0].
1404 // additiveGrowthRate - when the stream grows it will increase in size by at least
1405 // this number of bytes. Larger numbers cause fewer re-allocations at the cost of
1406 // increased memory usage.
1407 CGrowableStream(float multiplicativeGrowthRate = 2.0, DWORD additiveGrowthRate = 4096);
1408
1409#ifndef DACCESS_COMPILE
1410 virtual ~CGrowableStream();
1411#endif
1412
1413 // Expose the total raw buffer.
1414 // This can be used by DAC to get the raw contents.
1415 // This becomes potentiallyinvalid on the next call on the class, because the underlying storage can be
1416 // reallocated.
1417 MemoryRange GetRawBuffer() const
1418 {
1419 SUPPORTS_DAC;
1420 PTR_VOID p = m_swBuffer;
1421 return MemoryRange(p, m_dwBufferSize);
1422 }
1423
1424private:
1425 // Raw pointer to buffer. This may change as the buffer grows and gets reallocated.
1426 PTR_BYTE m_swBuffer;
1427
1428 // Total size of the buffer in bytes.
1429 DWORD m_dwBufferSize;
1430
1431 // Current index in the buffer. This can be moved around by Seek.
1432 DWORD m_dwBufferIndex;
1433
1434 // Logical length of the stream
1435 DWORD m_dwStreamLength;
1436
1437 // Reference count
1438 LONG m_cRef;
1439
1440 // growth rate parameters determine new stream size when it must grow
1441 float m_multiplicativeGrowthRate;
1442 int m_additiveGrowthRate;
1443
1444 // Ensures the stream is physically and logically at least newLogicalSize
1445 // in size
1446 HRESULT EnsureCapacity(DWORD newLogicalSize);
1447
1448 // IStream methods
1449public:
1450
1451#ifndef DACCESS_COMPILE
1452 ULONG STDMETHODCALLTYPE AddRef() {
1453 LIMITED_METHOD_CONTRACT;
1454 return InterlockedIncrement(&m_cRef);
1455 }
1456
1457
1458 ULONG STDMETHODCALLTYPE Release();
1459
1460 __checkReturn
1461 HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, PVOID *ppOut);
1462
1463 STDMETHOD(Read)(
1464 void * pv,
1465 ULONG cb,
1466 ULONG * pcbRead);
1467
1468 STDMETHOD(Write)(
1469 const void * pv,
1470 ULONG cb,
1471 ULONG * pcbWritten);
1472
1473 STDMETHOD(Seek)(
1474 LARGE_INTEGER dlibMove,
1475 DWORD dwOrigin,
1476 ULARGE_INTEGER * plibNewPosition);
1477
1478 STDMETHOD(SetSize)(ULARGE_INTEGER libNewSize);
1479
1480 STDMETHOD(CopyTo)(
1481 IStream * pstm,
1482 ULARGE_INTEGER cb,
1483 ULARGE_INTEGER * pcbRead,
1484 ULARGE_INTEGER * pcbWritten) { STATIC_CONTRACT_NOTHROW; STATIC_CONTRACT_FAULT; return E_NOTIMPL; }
1485
1486 STDMETHOD(Commit)(
1487 DWORD grfCommitFlags) { STATIC_CONTRACT_NOTHROW; STATIC_CONTRACT_FAULT; return NOERROR; }
1488
1489 STDMETHOD(Revert)( void) {STATIC_CONTRACT_NOTHROW; STATIC_CONTRACT_FAULT; return E_NOTIMPL; }
1490
1491 STDMETHOD(LockRegion)(
1492 ULARGE_INTEGER libOffset,
1493 ULARGE_INTEGER cb,
1494 DWORD dwLockType) { STATIC_CONTRACT_NOTHROW; STATIC_CONTRACT_FAULT; return E_NOTIMPL; }
1495
1496 STDMETHOD(UnlockRegion)(
1497 ULARGE_INTEGER libOffset,
1498 ULARGE_INTEGER cb,
1499 DWORD dwLockType) {STATIC_CONTRACT_NOTHROW; STATIC_CONTRACT_FAULT; return E_NOTIMPL; }
1500
1501 STDMETHOD(Stat)(
1502 STATSTG * pstatstg,
1503 DWORD grfStatFlag);
1504
1505 // Make a deep copy of the stream into a new CGrowableStream instance
1506 STDMETHOD(Clone)(
1507 IStream ** ppstm);
1508
1509#endif // DACCESS_COMPILE
1510}; // class CGrowableStream
1511
1512#endif // __StgPool_h__
1513