| 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 | |