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 ******************************************************** |
43 | const int DFT_STRING_HEAP_SIZE = 1024; |
44 | const int DFT_GUID_HEAP_SIZE = 32; |
45 | const int DFT_BLOB_HEAP_SIZE = 1024; |
46 | const int DFT_VARIANT_HEAP_SIZE = 512; |
47 | const int DFT_CODE_HEAP_SIZE = 8192; |
48 | |
49 | |
50 | |
51 | // Forwards. |
52 | class StgStringPool; |
53 | class StgBlobPool; |
54 | class StgCodePool; |
55 | class CorProfileData; |
56 | |
57 | // Perform binary search on index table. |
58 | // |
59 | class RIDBinarySearch : public CBinarySearch<UINT32> |
60 | { |
61 | public: |
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 | //***************************************************************************** |
87 | class StgPoolSeg |
88 | { |
89 | friend class VerifyLayoutsMD; |
90 | public: |
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); } |
99 | protected: |
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 | |
115 | public: |
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 | |
129 | namespace 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 | //***************************************************************************** |
145 | class StgPoolReadOnly : public StgPoolSeg |
146 | { |
147 | friend class CBlobPoolHash; |
148 | friend class MetaData::StringHeapRO; |
149 | friend class MetaData::StringHeapRW; |
150 | friend class MetaData::BlobHeapRO; |
151 | friend class VerifyLayoutsMD; |
152 | |
153 | public: |
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 | |
321 | protected: |
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 | |
381 | private: |
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 | //***************************************************************************** |
397 | class StgBlobPoolReadOnly : public StgPoolReadOnly |
398 | { |
399 | public: |
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 | |
408 | protected: |
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 | //***************************************************************************** |
434 | class StgPool : public StgPoolReadOnly |
435 | { |
436 | friend class StgStringPool; |
437 | friend class StgBlobPool; |
438 | friend class RecordPool; |
439 | friend class CBlobPoolHash; |
440 | friend class VerifyLayoutsMD; |
441 | |
442 | public: |
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 | |
456 | protected: |
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 | |
469 | public: |
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 | //***************************************************************************** |
681 | private: |
682 | __checkReturn |
683 | HRESULT CopyData( |
684 | UINT32 nOffset, |
685 | BYTE *pBuffer, |
686 | UINT32 cbBuffer, |
687 | UINT32 *pcbWritten) const; |
688 | |
689 | public: |
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 | |
705 | protected: |
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 | //***************************************************************************** |
778 | class StgStringPool : public StgPool |
779 | { |
780 | friend class VerifyLayoutsMD; |
781 | public: |
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 | |
918 | private: |
919 | __checkReturn |
920 | HRESULT RehashStrings(); |
921 | |
922 | private: |
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 | //***************************************************************************** |
939 | class StgGuidPool : public StgPool |
940 | { |
941 | friend class VerifyLayoutsMD; |
942 | public: |
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 | |
1065 | private: |
1066 | |
1067 | __checkReturn |
1068 | HRESULT RehashGuids(); |
1069 | |
1070 | |
1071 | private: |
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 | //***************************************************************************** |
1090 | class StgBlobPool : public StgPool |
1091 | { |
1092 | friend class VerifyLayoutsMD; |
1093 | |
1094 | using StgPool::InitNew; |
1095 | using StgPool::InitOnMem; |
1096 | |
1097 | public: |
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 | |
1206 | protected: |
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 | |
1220 | private: |
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 | //***************************************************************************** |
1239 | class CInMemoryStream : public IStream |
1240 | { |
1241 | public: |
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 | |
1378 | private: |
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. |
1394 | typedef DPTR(class CGrowableStream) PTR_CGrowableStream; |
1395 | |
1396 | class CGrowableStream : public IStream |
1397 | { |
1398 | public: |
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 | |
1424 | private: |
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 |
1449 | public: |
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 | |