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.cpp
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#include "stdafx.h" // Standard include.
17#include <stgpool.h> // Our interface definitions.
18#include <posterror.h> // Error handling.
19#include <safemath.h> // CLRSafeInt integer overflow checking
20#include "../md/inc/streamutil.h"
21
22#include "ex.h"
23
24#ifdef FEATURE_PREJIT
25#include <corcompile.h>
26#endif
27
28using namespace StreamUtil;
29
30#define MAX_CHAIN_LENGTH 20 // Max chain length before rehashing.
31
32//
33//
34// StgPool
35//
36//
37
38
39//*****************************************************************************
40// Free any memory we allocated.
41//*****************************************************************************
42StgPool::~StgPool()
43{
44 WRAPPER_NO_CONTRACT;
45
46 Uninit();
47} // StgPool::~StgPool()
48
49
50//*****************************************************************************
51// Init the pool for use. This is called for both the create empty case.
52//*****************************************************************************
53__checkReturn
54HRESULT
55StgPool::InitNew(
56 ULONG cbSize, // Estimated size.
57 ULONG cItems) // Estimated item count.
58{
59 CONTRACTL
60 {
61 NOTHROW;
62 INJECT_FAULT(return E_OUTOFMEMORY);
63 }
64 CONTRACTL_END
65
66 // Make sure we aren't stomping anything and are properly initialized.
67 _ASSERTE(m_pSegData == m_zeros);
68 _ASSERTE(m_pNextSeg == 0);
69 _ASSERTE(m_pCurSeg == this);
70 _ASSERTE(m_cbCurSegOffset == 0);
71 _ASSERTE(m_cbSegSize == 0);
72 _ASSERTE(m_cbSegNext == 0);
73
74 m_bReadOnly = false;
75 m_bFree = false;
76
77 return S_OK;
78} // StgPool::InitNew
79
80//*****************************************************************************
81// Init the pool from existing data.
82//*****************************************************************************
83__checkReturn
84HRESULT
85StgPool::InitOnMem(
86 void *pData, // Predefined data.
87 ULONG iSize, // Size of data.
88 int bReadOnly) // true if append is forbidden.
89{
90 CONTRACTL
91 {
92 NOTHROW;
93 INJECT_FAULT(return E_OUTOFMEMORY;);
94 }
95 CONTRACTL_END
96
97 // Make sure we aren't stomping anything and are properly initialized.
98 _ASSERTE(m_pSegData == m_zeros);
99 _ASSERTE(m_pNextSeg == 0);
100 _ASSERTE(m_pCurSeg == this);
101 _ASSERTE(m_cbCurSegOffset == 0);
102
103 // Create case requires no further action.
104 if (!pData)
105 return (E_INVALIDARG);
106
107 // Might we be extending this heap?
108 m_bReadOnly = bReadOnly;
109
110
111 m_pSegData = reinterpret_cast<BYTE*>(pData);
112 m_cbSegSize = iSize;
113 m_cbSegNext = iSize;
114
115 m_bFree = false;
116
117 return (S_OK);
118} // StgPool::InitOnMem
119
120//*****************************************************************************
121// Called when the pool must stop accessing memory passed to InitOnMem().
122//*****************************************************************************
123__checkReturn
124HRESULT
125StgPool::TakeOwnershipOfInitMem()
126{
127 CONTRACTL
128 {
129 NOTHROW;
130 INJECT_FAULT(return E_OUTOFMEMORY;);
131 }
132 CONTRACTL_END
133
134 // If the pool doesn't have a pointer to non-owned memory, done.
135 if (m_bFree)
136 return (S_OK);
137
138 // If the pool doesn't have a pointer to memory at all, done.
139 if (m_pSegData == m_zeros)
140 {
141 _ASSERTE(m_cbSegSize == 0);
142 return (S_OK);
143 }
144
145 // Get some memory to keep.
146 BYTE *pData = new (nothrow) BYTE[m_cbSegSize+4];
147 if (pData == 0)
148 return (PostError(OutOfMemory()));
149
150 // Copy the old data to the new memory.
151 memcpy(pData, m_pSegData, m_cbSegSize);
152 m_pSegData = pData;
153 m_bFree = true;
154
155 return (S_OK);
156} // StgPool::TakeOwnershipOfInitMem
157
158//*****************************************************************************
159// Clear out this pool. Cannot use until you call InitNew.
160//*****************************************************************************
161void StgPool::Uninit()
162{
163 CONTRACTL
164 {
165 NOTHROW;
166 FORBID_FAULT;
167 }
168 CONTRACTL_END
169
170 // Free base segment, if appropriate.
171 if (m_bFree && (m_pSegData != m_zeros))
172 {
173 delete [] m_pSegData;
174 m_bFree = false;
175 }
176
177 // Free chain, if any.
178 StgPoolSeg *pSeg = m_pNextSeg;
179 while (pSeg)
180 {
181 StgPoolSeg *pNext = pSeg->m_pNextSeg;
182 delete [] (BYTE*)pSeg;
183 pSeg = pNext;
184 }
185
186 // Clear vars.
187 m_pSegData = (BYTE*)m_zeros;
188 m_cbSegSize = m_cbSegNext = 0;
189 m_pNextSeg = 0;
190 m_pCurSeg = this;
191 m_cbCurSegOffset = 0;
192} // StgPool::Uninit
193
194//*****************************************************************************
195// Called to copy the pool to writable memory, reset the r/o bit.
196//*****************************************************************************
197__checkReturn
198HRESULT
199StgPool::ConvertToRW()
200{
201 CONTRACTL
202 {
203 NOTHROW;
204 INJECT_FAULT(return E_OUTOFMEMORY;);
205 }
206 CONTRACTL_END
207
208 HRESULT hr; // A result.
209 IfFailRet(TakeOwnershipOfInitMem());
210
211 IfFailRet(SetHash(true));
212
213 m_bReadOnly = false;
214
215 return S_OK;
216} // StgPool::ConvertToRW
217
218//*****************************************************************************
219// Turn hashing off or on. Real implementation as required in subclass.
220//*****************************************************************************
221__checkReturn
222HRESULT
223StgPool::SetHash(int bHash)
224{
225 CONTRACTL
226 {
227 NOTHROW;
228 INJECT_FAULT(return E_OUTOFMEMORY;);
229 }
230 CONTRACTL_END
231
232 return S_OK;
233} // StgPool::SetHash
234
235//*****************************************************************************
236// Trim any empty final segment.
237//*****************************************************************************
238void StgPool::Trim()
239{
240 CONTRACTL
241 {
242 NOTHROW;
243 FORBID_FAULT;
244 }
245 CONTRACTL_END
246
247 // If no chained segments, nothing to do.
248 if (m_pNextSeg == 0)
249 return;
250
251 // Handle special case for a segment that was completely unused.
252 if (m_pCurSeg->m_cbSegNext == 0)
253 {
254 // Find the segment which points to the empty segment.
255 StgPoolSeg *pPrev;
256 for (pPrev = this; pPrev && pPrev->m_pNextSeg != m_pCurSeg; pPrev = pPrev->m_pNextSeg);
257 _ASSERTE(pPrev && pPrev->m_pNextSeg == m_pCurSeg);
258
259 // Free the empty segment.
260 delete [] (BYTE*) m_pCurSeg;
261
262 // Fix the pCurSeg pointer.
263 pPrev->m_pNextSeg = 0;
264 m_pCurSeg = pPrev;
265
266 // Adjust the base offset, because the PREVIOUS seg is now current.
267 _ASSERTE(m_pCurSeg->m_cbSegNext <= m_cbCurSegOffset);
268 m_cbCurSegOffset = m_cbCurSegOffset - m_pCurSeg->m_cbSegNext;
269 }
270} // StgPool::Trim
271
272//*****************************************************************************
273// Allocate memory if we don't have any, or grow what we have. If successful,
274// then at least iRequired bytes will be allocated.
275//*****************************************************************************
276bool StgPool::Grow( // true if successful.
277 ULONG iRequired) // Min required bytes to allocate.
278{
279 CONTRACTL
280 {
281 NOTHROW;
282 INJECT_FAULT(return FALSE;);
283 }
284 CONTRACTL_END
285
286 ULONG iNewSize; // New size we want.
287 StgPoolSeg *pNew; // Temp pointer for malloc.
288
289 _ASSERTE(!m_bReadOnly);
290
291 // Would this put the pool over 2GB?
292 if ((m_cbCurSegOffset + iRequired) > INT_MAX)
293 return (false);
294
295 // Adjust grow size as a ratio to avoid too many reallocs.
296 if ((m_pCurSeg->m_cbSegNext + m_cbCurSegOffset) / m_ulGrowInc >= 3)
297 m_ulGrowInc *= 2;
298
299 // NOTE: MD\DataSource\RemoteMDInternalRWSource has taken a dependency that there
300 // won't be more than 1000 segments. Given the current exponential growth algorithm
301 // we'll never get anywhere close to that, but if the algorithm changes to allow for
302 // many segments, please update that source as well.
303
304 // If first time, handle specially.
305 if (m_pSegData == m_zeros)
306 {
307 // Allocate the buffer.
308 iNewSize = max(m_ulGrowInc, iRequired);
309 BYTE *pSegData = new (nothrow) BYTE[iNewSize + 4];
310 if (pSegData == NULL)
311 return false;
312 m_pSegData = pSegData;
313
314 // Will need to delete it.
315 m_bFree = true;
316
317 // How big is this initial segment?
318 m_cbSegSize = iNewSize;
319
320 // Do some validation of var fields.
321 _ASSERTE(m_cbSegNext == 0);
322 _ASSERTE(m_pCurSeg == this);
323 _ASSERTE(m_pNextSeg == NULL);
324
325 return true;
326 }
327
328 // Allocate the new space enough for header + data.
329 iNewSize = (ULONG)(max(m_ulGrowInc, iRequired) + sizeof(StgPoolSeg));
330 pNew = (StgPoolSeg *)new (nothrow) BYTE[iNewSize+4];
331 if (pNew == NULL)
332 return false;
333
334 // Set the fields in the new segment.
335 pNew->m_pSegData = reinterpret_cast<BYTE*>(pNew) + sizeof(StgPoolSeg);
336 _ASSERTE(ALIGN4BYTE(reinterpret_cast<ULONG_PTR>(pNew->m_pSegData)) == reinterpret_cast<ULONG_PTR>(pNew->m_pSegData));
337 pNew->m_pNextSeg = 0;
338 pNew->m_cbSegSize = iNewSize - sizeof(StgPoolSeg);
339 pNew->m_cbSegNext = 0;
340
341 // Calculate the base offset of the new segment.
342 m_cbCurSegOffset = m_cbCurSegOffset + m_pCurSeg->m_cbSegNext;
343
344 // Handle special case for a segment that was completely unused.
345 //<TODO>@todo: Trim();</TODO>
346 if (m_pCurSeg->m_cbSegNext == 0)
347 {
348 // Find the segment which points to the empty segment.
349 StgPoolSeg *pPrev;
350 for (pPrev = this; pPrev && pPrev->m_pNextSeg != m_pCurSeg; pPrev = pPrev->m_pNextSeg);
351 _ASSERTE(pPrev && pPrev->m_pNextSeg == m_pCurSeg);
352
353 // Free the empty segment.
354 delete [] (BYTE *) m_pCurSeg;
355
356 // Link in the new segment.
357 pPrev->m_pNextSeg = pNew;
358 m_pCurSeg = pNew;
359
360 return true;
361 }
362
363 // Fix the size of the old segment.
364 m_pCurSeg->m_cbSegSize = m_pCurSeg->m_cbSegNext;
365
366 // Link the new segment into the chain.
367 m_pCurSeg->m_pNextSeg = pNew;
368 m_pCurSeg = pNew;
369
370 return true;
371} // StgPool::Grow
372
373//*****************************************************************************
374// Add a segment to the chain of segments.
375//*****************************************************************************
376__checkReturn
377HRESULT
378StgPool::AddSegment(
379 const void *pData, // The data.
380 ULONG cbData, // Size of the data.
381 bool bCopy) // If true, make a copy of the data.
382{
383 CONTRACTL
384 {
385 NOTHROW;
386 INJECT_FAULT(return E_OUTOFMEMORY;);
387 }
388 CONTRACTL_END
389
390 StgPoolSeg *pNew; // Temp pointer for malloc.
391
392
393 // If we need to copy the data, just grow the heap by enough to take the
394 // the new data, and copy it in.
395 if (bCopy)
396 {
397 void *pDataToAdd = new (nothrow) BYTE[cbData];
398 IfNullRet(pDataToAdd);
399 memcpy(pDataToAdd, pData, cbData);
400 pData = pDataToAdd;
401 }
402
403 // If first time, handle specially.
404 if (m_pSegData == m_zeros)
405 { // Data was passed in.
406 m_pSegData = reinterpret_cast<BYTE*>(const_cast<void*>(pData));
407 m_cbSegSize = cbData;
408 m_cbSegNext = cbData;
409 _ASSERTE(m_pNextSeg == NULL);
410
411 // Will not delete it.
412 m_bFree = false;
413
414 return S_OK;
415 }
416
417 // Not first time. Handle a completely empty tail segment.
418 Trim();
419
420 // Abandon any space past the end of the current live data.
421 _ASSERTE(m_pCurSeg->m_cbSegSize >= m_pCurSeg->m_cbSegNext);
422 m_pCurSeg->m_cbSegSize = m_pCurSeg->m_cbSegNext;
423
424 // Allocate a new segment header.
425 pNew = (StgPoolSeg *) new (nothrow) BYTE[sizeof(StgPoolSeg)];
426 IfNullRet(pNew);
427
428 // Set the fields in the new segment.
429 pNew->m_pSegData = reinterpret_cast<BYTE*>(const_cast<void*>(pData));
430 pNew->m_pNextSeg = NULL;
431 pNew->m_cbSegSize = cbData;
432 pNew->m_cbSegNext = cbData;
433
434 // Calculate the base offset of the new segment.
435 m_cbCurSegOffset = m_cbCurSegOffset + m_pCurSeg->m_cbSegNext;
436
437 // Link the segment into the chain.
438 _ASSERTE(m_pCurSeg->m_pNextSeg == NULL);
439 m_pCurSeg->m_pNextSeg = pNew;
440 m_pCurSeg = pNew;
441
442 return S_OK;
443} // StgPool::AddSegment
444
445#ifndef DACCESS_COMPILE
446//*****************************************************************************
447// The entire string pool is written to the given stream. The stream is aligned
448// to a 4 byte boundary.
449//*****************************************************************************
450__checkReturn
451HRESULT
452StgPool::PersistToStream(
453 IStream *pIStream) // The stream to write to.
454{
455 CONTRACTL
456 {
457 NOTHROW;
458 INJECT_FAULT(return E_OUTOFMEMORY);
459 }
460 CONTRACTL_END
461
462 HRESULT hr = S_OK;
463 ULONG cbTotal; // Total bytes written.
464 StgPoolSeg *pSeg; // A segment being written.
465
466 _ASSERTE(m_pSegData != m_zeros);
467
468 // Start with the base segment.
469 pSeg = this;
470 cbTotal = 0;
471
472 EX_TRY
473 {
474 // As long as there is data, write it.
475 while (pSeg != NULL)
476 {
477 // If there is data in the segment . . .
478 if (pSeg->m_cbSegNext)
479 { // . . . write and count the data.
480 if (FAILED(hr = pIStream->Write(pSeg->m_pSegData, pSeg->m_cbSegNext, 0)))
481 break;
482 cbTotal += pSeg->m_cbSegNext;
483 }
484
485 // Get the next segment.
486 pSeg = pSeg->m_pNextSeg;
487 }
488
489 if (SUCCEEDED(hr))
490 {
491 // Align to variable (0-4 byte) boundary.
492 UINT32 cbTotalAligned;
493 if (FAILED(Align(cbTotal, &cbTotalAligned)))
494 {
495 hr = COR_E_BADIMAGEFORMAT;
496 }
497 else
498 {
499 if (cbTotalAligned > cbTotal)
500 {
501 _ASSERTE(sizeof(hr) >= 3);
502 hr = 0;
503 hr = pIStream->Write(&hr, cbTotalAligned - cbTotal, 0);
504 }
505 }
506 }
507 }
508 EX_CATCH
509 {
510 hr = E_FAIL;
511 }
512 EX_END_CATCH(SwallowAllExceptions);
513
514 return hr;
515} // StgPool::PersistToStream
516#endif //!DACCESS_COMPILE
517
518//*****************************************************************************
519// The entire string pool is written to the given stream. The stream is aligned
520// to a 4 byte boundary.
521//*****************************************************************************
522__checkReturn
523HRESULT
524StgPool::PersistPartialToStream(
525 IStream *pIStream, // The stream to write to.
526 ULONG iOffset) // Starting offset.
527{
528 CONTRACTL
529 {
530 NOTHROW;
531 INJECT_FAULT(return E_OUTOFMEMORY);
532 }
533 CONTRACTL_END
534
535 HRESULT hr = S_OK; // A result.
536 ULONG cbTotal; // Total bytes written.
537 StgPoolSeg *pSeg; // A segment being written.
538
539 _ASSERTE(m_pSegData != m_zeros);
540
541 // Start with the base segment.
542 pSeg = this;
543 cbTotal = 0;
544
545 // As long as there is data, write it.
546 while (pSeg != NULL)
547 {
548 // If there is data in the segment . . .
549 if (pSeg->m_cbSegNext)
550 { // If this data should be skipped...
551 if (iOffset >= pSeg->m_cbSegNext)
552 { // Skip it
553 iOffset -= pSeg->m_cbSegNext;
554 }
555 else
556 { // At least some data should be written, so write and count the data.
557 IfFailRet(pIStream->Write(pSeg->m_pSegData+iOffset, pSeg->m_cbSegNext-iOffset, 0));
558 cbTotal += pSeg->m_cbSegNext-iOffset;
559 iOffset = 0;
560 }
561 }
562
563 // Get the next segment.
564 pSeg = pSeg->m_pNextSeg;
565 }
566
567 // Align to variable (0-4 byte) boundary.
568 UINT32 cbTotalAligned;
569 if (FAILED(Align(cbTotal, &cbTotalAligned)))
570 {
571 return COR_E_BADIMAGEFORMAT;
572 }
573 if (cbTotalAligned > cbTotal)
574 {
575 _ASSERTE(sizeof(hr) >= 3);
576 hr = 0;
577 hr = pIStream->Write(&hr, cbTotalAligned - cbTotal, 0);
578 }
579
580 return hr;
581} // StgPool::PersistPartialToStream
582
583// Copies data from pSourcePool starting at index nStartSourceIndex.
584__checkReturn
585HRESULT
586StgPool::CopyPool(
587 UINT32 nStartSourceIndex,
588 const StgPool *pSourcePool)
589{
590 HRESULT hr;
591 UINT32 cbDataSize;
592 BYTE *pbData = NULL;
593
594 if (nStartSourceIndex == pSourcePool->GetRawSize())
595 { // There's nothing to copy
596 return S_OK;
597 }
598 if (nStartSourceIndex > pSourcePool->GetRawSize())
599 { // Invalid input
600 Debug_ReportInternalError("The caller should not pass invalid start index in the pool.");
601 IfFailGo(METADATA_E_INDEX_NOTFOUND);
602 }
603
604 // Allocate new segment
605 cbDataSize = pSourcePool->GetRawSize() - nStartSourceIndex;
606 pbData = new (nothrow) BYTE[cbDataSize];
607 IfNullGo(pbData);
608
609 // Copy data to the new segment
610 UINT32 cbCopiedDataSize;
611 IfFailGo(pSourcePool->CopyData(
612 nStartSourceIndex,
613 pbData,
614 cbDataSize,
615 &cbCopiedDataSize));
616 // Check that we copied everything
617 if (cbDataSize != cbCopiedDataSize)
618 {
619 Debug_ReportInternalError("It is expected to copy everything from the source pool.");
620 IfFailGo(E_FAIL);
621 }
622
623 // Add the newly allocated segment to the pool
624 IfFailGo(AddSegment(
625 pbData,
626 cbDataSize,
627 false)); // fCopyData
628
629ErrExit:
630 if (FAILED(hr))
631 {
632 if (pbData != NULL)
633 {
634 delete [] pbData;
635 }
636 }
637 return hr;
638} // StgPool::CopyPool
639
640// Copies data from the pool into a buffer. It will correctly walk all segments for the copy.
641__checkReturn
642HRESULT
643StgPool::CopyData(
644 UINT32 nOffset,
645 BYTE *pBuffer,
646 UINT32 cbBuffer,
647 UINT32 *pcbWritten) const
648{
649 CONTRACTL
650 {
651 NOTHROW;
652 PRECONDITION(CheckPointer(pBuffer));
653 PRECONDITION(CheckPointer(pcbWritten));
654 }
655 CONTRACTL_END
656
657 HRESULT hr = S_OK;
658 const StgPoolSeg *pSeg; // A segment being written.
659
660 _ASSERTE(m_pSegData != m_zeros);
661
662 // Start with the base segment.
663 pSeg = this;
664 *pcbWritten = 0;
665
666 // As long as there is data, write it.
667 while (pSeg != NULL)
668 {
669 // If there is data in the segment . . .
670 if (pSeg->m_cbSegNext)
671 { // If this data should be skipped...
672 if (nOffset >= pSeg->m_cbSegNext)
673 { // Skip it
674 nOffset -= pSeg->m_cbSegNext;
675 }
676 else
677 {
678 ULONG nNumBytesToCopy = pSeg->m_cbSegNext - nOffset;
679 if (nNumBytesToCopy > (cbBuffer - *pcbWritten))
680 {
681 _ASSERTE(!"Buffer isn't big enough to copy everything!");
682 nNumBytesToCopy = cbBuffer - *pcbWritten;
683 }
684
685 memcpy(pBuffer + *pcbWritten, pSeg->m_pSegData+nOffset, nNumBytesToCopy);
686
687 *pcbWritten += nNumBytesToCopy;
688 nOffset = 0;
689 }
690 }
691
692 // Get the next segment.
693 pSeg = pSeg->m_pNextSeg;
694 }
695
696 return hr;
697} // StgPool::CopyData
698
699//*****************************************************************************
700// Get a pointer to the data at some offset. May require traversing the
701// chain of extensions. It is the caller's responsibility not to attempt
702// to access data beyond the end of a segment.
703// This is an internal accessor, and should only be called when the data
704// is not in the base segment.
705//*****************************************************************************
706__checkReturn
707HRESULT
708StgPool::GetData_i(
709 UINT32 nOffset,
710 MetaData::DataBlob *pData)
711{
712 LIMITED_METHOD_CONTRACT;
713
714 // Shouldn't be called on base segment.
715 _ASSERTE(nOffset >= m_cbSegNext);
716 StgPoolSeg *pSeg = this;
717
718 while ((nOffset > 0) && (nOffset >= pSeg->m_cbSegNext))
719 {
720 // On to next segment.
721 nOffset -= pSeg->m_cbSegNext;
722 pSeg = pSeg->m_pNextSeg;
723
724 // Is there a next?
725 if (pSeg == NULL)
726 {
727 Debug_ReportError("Invalid offset passed - reached end of pool.");
728 pData->Clear();
729 return CLDB_E_INDEX_NOTFOUND;
730 }
731 }
732
733 // For the case where we want to read the first item and the pool is empty.
734 if (nOffset == pSeg->m_cbSegNext)
735 { // Can only be if both == 0
736 Debug_ReportError("Invalid offset passed - it is at the end of pool.");
737 pData->Clear();
738 return CLDB_E_INDEX_NOTFOUND;
739 }
740
741 pData->Init(pSeg->m_pSegData + nOffset, pSeg->m_cbSegNext - nOffset);
742
743 return S_OK;
744} // StgPool::GetData_i
745
746//
747//
748// StgStringPool
749//
750//
751
752
753//*****************************************************************************
754// Create a new, empty string pool.
755//*****************************************************************************
756__checkReturn
757HRESULT
758StgStringPool::InitNew(
759 ULONG cbSize, // Estimated size.
760 ULONG cItems) // Estimated item count.
761{
762 CONTRACTL
763 {
764 NOTHROW;
765 INJECT_FAULT(return E_OUTOFMEMORY);
766 }
767 CONTRACTL_END
768
769 HRESULT hr;
770 UINT32 nEmptyStringOffset;
771
772 // Let base class intialize.
773 IfFailRet(StgPool::InitNew());
774
775 // Set initial table sizes, if specified.
776 if (cbSize > 0)
777 {
778 if (!Grow(cbSize))
779 {
780 return E_OUTOFMEMORY;
781 }
782 }
783 if (cItems > 0)
784 {
785 m_Hash.SetBuckets(cItems);
786 }
787
788 // Init with empty string.
789 IfFailRet(AddString("", &nEmptyStringOffset));
790 // Empty string had better be at offset 0.
791 _ASSERTE(nEmptyStringOffset == 0);
792
793 return hr;
794} // StgStringPool::InitNew
795
796//*****************************************************************************
797// Load a string heap from persisted memory. If a copy of the data is made
798// (so that it may be updated), then a new hash table is generated which can
799// be used to elminate duplicates with new strings.
800//*****************************************************************************
801__checkReturn
802HRESULT
803StgStringPool::InitOnMem(
804 void *pData, // Predefined data.
805 ULONG iSize, // Size of data.
806 int bReadOnly) // true if append is forbidden.
807{
808 CONTRACTL
809 {
810 NOTHROW;
811 INJECT_FAULT(return E_OUTOFMEMORY);
812 }
813 CONTRACTL_END
814
815 HRESULT hr = S_OK;
816
817 // There may be up to three extra '\0' characters appended for padding. Trim them.
818 char *pchData = reinterpret_cast<char*>(pData);
819 while (iSize > 1 && pchData[iSize-1] == 0 && pchData[iSize-2] == 0)
820 --iSize;
821
822 // Let base class init our memory structure.
823 IfFailRet(StgPool::InitOnMem(pData, iSize, bReadOnly));
824
825 //<TODO>@todo: defer this until we hand out a pointer.</TODO>
826 if (!bReadOnly)
827 {
828 IfFailRet(TakeOwnershipOfInitMem());
829 IfFailRet(RehashStrings());
830 }
831
832 return hr;
833} // StgStringPool::InitOnMem
834
835//*****************************************************************************
836// Clears the hash table then calls the base class.
837//*****************************************************************************
838void StgStringPool::Uninit()
839{
840 CONTRACTL
841 {
842 NOTHROW;
843 FORBID_FAULT;
844 }
845 CONTRACTL_END
846
847 // Clear the hash table.
848 m_Hash.Clear();
849
850 // Let base class clean up.
851 StgPool::Uninit();
852} // StgStringPool::Uninit
853
854//*****************************************************************************
855// Turn hashing off or on. If you turn hashing on, then any existing data is
856// thrown away and all data is rehashed during this call.
857//*****************************************************************************
858__checkReturn
859HRESULT
860StgStringPool::SetHash(int bHash)
861{
862 CONTRACTL
863 {
864 NOTHROW;
865 INJECT_FAULT(return E_OUTOFMEMORY;);
866 }
867 CONTRACTL_END
868
869 HRESULT hr = S_OK;
870
871 // If turning on hash again, need to rehash all strings.
872 if (bHash)
873 hr = RehashStrings();
874
875 m_bHash = bHash;
876 return (hr);
877} // StgStringPool::SetHash
878
879//*****************************************************************************
880// The string will be added to the pool. The offset of the string in the pool
881// is returned in *piOffset. If the string is already in the pool, then the
882// offset will be to the existing copy of the string.
883//*****************************************************************************
884__checkReturn
885HRESULT
886StgStringPool::AddString(
887 LPCSTR szString, // The string to add to pool.
888 UINT32 *pnOffset) // Return offset of string here.
889{
890 CONTRACTL
891 {
892 NOTHROW;
893 INJECT_FAULT(return E_OUTOFMEMORY;);
894 }
895 CONTRACTL_END
896
897 STRINGHASH *pHash; // Hash item for add.
898 ULONG iLen; // To handle non-null strings.
899 LPSTR pData; // Pointer to location for new string.
900 HRESULT hr;
901
902 _ASSERTE(!m_bReadOnly);
903
904 // Null pointer is an error.
905 if (szString == 0)
906 return (PostError(E_INVALIDARG));
907
908 // Find the real length we need in buffer.
909 iLen = (ULONG)(strlen(szString) + 1);
910
911 // Where to put the new string?
912 if (iLen > GetCbSegAvailable())
913 {
914 if (!Grow(iLen))
915 return (PostError(OutOfMemory()));
916 }
917 pData = reinterpret_cast<LPSTR>(GetNextLocation());
918
919 // Copy the data into the buffer.
920 strcpy_s(pData, iLen, szString);
921
922 // If the hash table is to be kept built (default).
923 if (m_bHash)
924 {
925 // Find or add the entry.
926 pHash = m_Hash.Find(pData, true);
927 if (!pHash)
928 return (PostError(OutOfMemory()));
929
930 // If the entry was new, keep the new string.
931 if (pHash->iOffset == 0xffffffff)
932 {
933 *pnOffset = pHash->iOffset = GetNextOffset();
934 SegAllocate(iLen);
935
936 // Check for hash chains that are too long.
937 if (m_Hash.MaxChainLength() > MAX_CHAIN_LENGTH)
938 {
939 IfFailRet(RehashStrings());
940 }
941 }
942 // Else use the old one.
943 else
944 {
945 *pnOffset = pHash->iOffset;
946 }
947 }
948 // Probably an import which defers the hash table for speed.
949 else
950 {
951 *pnOffset = GetNextOffset();
952 SegAllocate(iLen);
953 }
954 return S_OK;
955} // StgStringPool::AddString
956
957//*****************************************************************************
958// Add a string to the pool with Unicode to UTF8 conversion.
959//*****************************************************************************
960__checkReturn
961HRESULT
962StgStringPool::AddStringW(
963 LPCWSTR szString, // The string to add to pool.
964 UINT32 *pnOffset) // Return offset of string here.
965{
966 CONTRACTL
967 {
968 NOTHROW;
969 INJECT_FAULT(return E_OUTOFMEMORY;);
970 }
971 CONTRACTL_END
972
973 STRINGHASH *pHash; // Hash item for add.
974 ULONG iLen; // Correct length after conversion.
975 LPSTR pData; // Pointer to location for new string.
976
977 _ASSERTE(!m_bReadOnly);
978
979 // Null pointer is an error.
980 if (szString == 0)
981 return (PostError(E_INVALIDARG));
982
983 // Special case empty string.
984 if (*szString == '\0')
985 {
986 *pnOffset = 0;
987 return (S_OK);
988 }
989
990 // How many bytes will be required in the heap?
991 iLen = ::WszWideCharToMultiByte(
992 CP_UTF8,
993 0,
994 szString,
995 -1, // null-terminated string
996 NULL,
997 0,
998 NULL,
999 NULL);
1000 // WCTMB includes trailing 0 if (when passing parameter #4 (length) -1.
1001
1002 // Check for room.
1003 if (iLen > GetCbSegAvailable())
1004 {
1005 if (!Grow(iLen))
1006 return (PostError(OutOfMemory()));
1007 }
1008 pData = reinterpret_cast<LPSTR>(GetNextLocation());
1009
1010 // Convert the data in place to the correct location.
1011 iLen = ::WszWideCharToMultiByte(
1012 CP_UTF8,
1013 0,
1014 szString,
1015 -1,
1016 pData,
1017 GetCbSegAvailable(),
1018 NULL,
1019 NULL);
1020 if (iLen == 0)
1021 return (BadError(HRESULT_FROM_NT(GetLastError())));
1022
1023 // If the hash table is to be kept built (default).
1024 if (m_bHash)
1025 {
1026 // Find or add the entry.
1027 pHash = m_Hash.Find(pData, true);
1028 if (!pHash)
1029 return (PostError(OutOfMemory()));
1030
1031 // If the entry was new, keep the new string.
1032 if (pHash->iOffset == 0xffffffff)
1033 {
1034 *pnOffset = pHash->iOffset = GetNextOffset();
1035 SegAllocate(iLen);
1036 }
1037 // Else use the old one.
1038 else
1039 {
1040 *pnOffset = pHash->iOffset;
1041 }
1042 }
1043 // Probably an import which defers the hash table for speed.
1044 else
1045 {
1046 *pnOffset = GetNextOffset();
1047 SegAllocate(iLen);
1048 }
1049 return (S_OK);
1050} // StgStringPool::AddStringW
1051
1052
1053//*****************************************************************************
1054// Clears out the existing hash table used to eliminate duplicates. Then
1055// rebuilds the hash table from scratch based on the current data.
1056//*****************************************************************************
1057__checkReturn
1058HRESULT
1059StgStringPool::RehashStrings()
1060{
1061 CONTRACTL
1062 {
1063 NOTHROW;
1064 INJECT_FAULT(return E_OUTOFMEMORY;);
1065 }
1066 CONTRACTL_END
1067
1068 ULONG iOffset; // Loop control.
1069 ULONG iMax; // End of loop.
1070 ULONG iSeg; // Location within segment.
1071 StgPoolSeg *pSeg = this; // To loop over segments.
1072 STRINGHASH *pHash; // Hash item for add.
1073 LPCSTR pString; // A string;
1074 ULONG iLen; // The string's length.
1075 int iBuckets; // Buckets in the hash.
1076 int iCount; // Items in the hash.
1077 int iNewBuckets; // New count of buckets in the hash.
1078
1079 // Determine the new bucket size.
1080 iBuckets = m_Hash.Buckets();
1081 iCount = m_Hash.Count();
1082 iNewBuckets = max(iCount, iBuckets+iBuckets/2+1);
1083
1084 // Remove any stale data.
1085 m_Hash.Clear();
1086 m_Hash.SetBuckets(iNewBuckets);
1087
1088 // How far should the loop go.
1089 iMax = GetNextOffset();
1090
1091 // Go through each string, skipping initial empty string.
1092 for (iSeg=iOffset=1; iOffset < iMax; )
1093 {
1094 // Get the string from the pool.
1095 pString = reinterpret_cast<LPCSTR>(pSeg->m_pSegData + iSeg);
1096 // Add the string to the hash table.
1097 if ((pHash = m_Hash.Add(pString)) == 0)
1098 return (PostError(OutOfMemory()));
1099 pHash->iOffset = iOffset;
1100
1101 // Move to next string.
1102 iLen = (ULONG)(strlen(pString) + 1);
1103 iOffset += iLen;
1104 iSeg += iLen;
1105 if (iSeg >= pSeg->m_cbSegNext)
1106 {
1107 pSeg = pSeg->m_pNextSeg;
1108 iSeg = 0;
1109 }
1110 }
1111 return (S_OK);
1112} // StgStringPool::RehashStrings
1113
1114//
1115//
1116// StgGuidPool
1117//
1118//
1119
1120__checkReturn
1121HRESULT
1122StgGuidPool::InitNew(
1123 ULONG cbSize, // Estimated size.
1124 ULONG cItems) // Estimated item count.
1125{
1126 CONTRACTL
1127 {
1128 NOTHROW;
1129 INJECT_FAULT(return E_OUTOFMEMORY;);
1130 }
1131 CONTRACTL_END
1132
1133 HRESULT hr; // A result.
1134
1135 if (FAILED(hr = StgPool::InitNew()))
1136 return (hr);
1137
1138 // Set initial table sizes, if specified.
1139 if (cbSize)
1140 if (!Grow(cbSize))
1141 return E_OUTOFMEMORY;
1142 if (cItems)
1143 m_Hash.SetBuckets(cItems);
1144
1145 return (S_OK);
1146} // StgGuidPool::InitNew
1147
1148//*****************************************************************************
1149// Load a Guid heap from persisted memory. If a copy of the data is made
1150// (so that it may be updated), then a new hash table is generated which can
1151// be used to elminate duplicates with new Guids.
1152//*****************************************************************************
1153__checkReturn
1154HRESULT
1155StgGuidPool::InitOnMem(
1156 void *pData, // Predefined data.
1157 ULONG iSize, // Size of data.
1158 int bReadOnly) // true if append is forbidden.
1159{
1160 CONTRACTL
1161 {
1162 NOTHROW;
1163 INJECT_FAULT(return E_OUTOFMEMORY;);
1164 }
1165 CONTRACTL_END
1166
1167 HRESULT hr;
1168
1169 // Let base class init our memory structure.
1170 IfFailRet(StgPool::InitOnMem(pData, iSize, bReadOnly));
1171
1172 // For init on existing mem case.
1173 if (pData && iSize)
1174 {
1175 // If we cannot update, then we don't need a hash table.
1176 if (bReadOnly)
1177 return S_OK;
1178
1179 //<TODO>@todo: defer this until we hand out a pointer.</TODO>
1180 IfFailRet(TakeOwnershipOfInitMem());
1181
1182 // Build the hash table on the data.
1183 if (FAILED(hr = RehashGuids()))
1184 {
1185 Uninit();
1186 return hr;
1187 }
1188 }
1189
1190 return S_OK;
1191} // StgGuidPool::InitOnMem
1192
1193//*****************************************************************************
1194// Clears the hash table then calls the base class.
1195//*****************************************************************************
1196void StgGuidPool::Uninit()
1197{
1198 CONTRACTL
1199 {
1200 NOTHROW;
1201 FORBID_FAULT;
1202 }
1203 CONTRACTL_END
1204
1205 // Clear the hash table.
1206 m_Hash.Clear();
1207
1208 // Let base class clean up.
1209 StgPool::Uninit();
1210} // StgGuidPool::Uninit
1211
1212//*****************************************************************************
1213// Add a segment to the chain of segments.
1214//*****************************************************************************
1215__checkReturn
1216HRESULT
1217StgGuidPool::AddSegment(
1218 const void *pData, // The data.
1219 ULONG cbData, // Size of the data.
1220 bool bCopy) // If true, make a copy of the data.
1221{
1222 CONTRACTL
1223 {
1224 NOTHROW;
1225 INJECT_FAULT(return E_OUTOFMEMORY;);
1226 }
1227 CONTRACTL_END
1228
1229 // Want an integeral number of GUIDs.
1230 _ASSERTE((cbData % sizeof(GUID)) == 0);
1231
1232 return StgPool::AddSegment(pData, cbData, bCopy);
1233
1234} // StgGuidPool::AddSegment
1235
1236//*****************************************************************************
1237// Turn hashing off or on. If you turn hashing on, then any existing data is
1238// thrown away and all data is rehashed during this call.
1239//*****************************************************************************
1240__checkReturn
1241HRESULT
1242StgGuidPool::SetHash(int bHash)
1243{
1244 CONTRACTL
1245 {
1246 NOTHROW;
1247 INJECT_FAULT(return E_OUTOFMEMORY;);
1248 }
1249 CONTRACTL_END
1250
1251 HRESULT hr = S_OK;
1252
1253 // If turning on hash again, need to rehash all guids.
1254 if (bHash)
1255 hr = RehashGuids();
1256
1257 m_bHash = bHash;
1258 return (hr);
1259} // StgGuidPool::SetHash
1260
1261//*****************************************************************************
1262// The Guid will be added to the pool. The index of the Guid in the pool
1263// is returned in *piIndex. If the Guid is already in the pool, then the
1264// index will be to the existing copy of the Guid.
1265//*****************************************************************************
1266__checkReturn
1267HRESULT
1268StgGuidPool::AddGuid(
1269 const GUID *pGuid, // The Guid to add to pool.
1270 UINT32 *pnIndex) // Return 1-based index of Guid here.
1271{
1272 CONTRACTL
1273 {
1274 NOTHROW;
1275 INJECT_FAULT(return E_OUTOFMEMORY;);
1276 }
1277 CONTRACTL_END
1278
1279 GUIDHASH *pHash = NULL; // Hash item for add.
1280
1281 GUID guid = *pGuid;
1282 SwapGuid(&guid);
1283
1284 // Special case for GUID_NULL
1285 if (guid == GUID_NULL)
1286 {
1287 *pnIndex = 0;
1288 return S_OK;
1289 }
1290
1291 // If the hash table is to be kept built (default).
1292 if (m_bHash)
1293 {
1294 // Find or add the entry.
1295 pHash = m_Hash.Find(&guid, true);
1296 if (!pHash)
1297 return (PostError(OutOfMemory()));
1298
1299 // If the guid was found, just use it.
1300 if (pHash->iIndex != 0xffffffff)
1301 { // Return 1-based index.
1302 *pnIndex = pHash->iIndex;
1303 return S_OK;
1304 }
1305 }
1306
1307 // Space on heap for new guid?
1308 if (sizeof(GUID) > GetCbSegAvailable())
1309 {
1310 if (!Grow(sizeof(GUID)))
1311 return (PostError(OutOfMemory()));
1312 }
1313
1314 // Copy the guid to the heap.
1315 *reinterpret_cast<GUID*>(GetNextLocation()) = guid;
1316
1317 // Give the 1-based index back to caller.
1318 *pnIndex = (GetNextOffset() / sizeof(GUID)) + 1;
1319
1320 // If hashing, save the 1-based index in the hash.
1321 if (m_bHash)
1322 pHash->iIndex = *pnIndex;
1323
1324 // Update heap counters.
1325 SegAllocate(sizeof(GUID));
1326
1327 return S_OK;
1328} // StgGuidPool::AddGuid
1329
1330//*****************************************************************************
1331// Recompute the hashes for the pool.
1332//*****************************************************************************
1333__checkReturn
1334HRESULT
1335StgGuidPool::RehashGuids()
1336{
1337 CONTRACTL
1338 {
1339 NOTHROW;
1340 INJECT_FAULT(return E_OUTOFMEMORY;);
1341 }
1342 CONTRACTL_END
1343
1344 ULONG iOffset; // Loop control.
1345 ULONG iMax; // End of loop.
1346 ULONG iSeg; // Location within segment.
1347 StgPoolSeg *pSeg = this; // To loop over segments.
1348 GUIDHASH *pHash; // Hash item for add.
1349 GUID *pGuid; // A guid;
1350
1351 // Remove any stale data.
1352 m_Hash.Clear();
1353
1354 // How far should the loop go.
1355 iMax = GetNextOffset();
1356
1357 // Go through each guid.
1358 for (iSeg=iOffset=0; iOffset < iMax; )
1359 {
1360 // Get a pointer to the guid.
1361 pGuid = reinterpret_cast<GUID*>(pSeg->m_pSegData + iSeg);
1362 // Add the guid to the hash table.
1363 if ((pHash = m_Hash.Add(pGuid)) == 0)
1364 return (PostError(OutOfMemory()));
1365 pHash->iIndex = iOffset / sizeof(GUID);
1366
1367 // Move to next Guid.
1368 iOffset += sizeof(GUID);
1369 iSeg += sizeof(GUID);
1370 if (iSeg > pSeg->m_cbSegNext)
1371 {
1372 pSeg = pSeg->m_pNextSeg;
1373 iSeg = 0;
1374 }
1375 }
1376 return (S_OK);
1377} // StgGuidPool::RehashGuids
1378
1379//
1380//
1381// StgBlobPool
1382//
1383//
1384
1385
1386
1387//*****************************************************************************
1388// Create a new, empty blob pool.
1389//*****************************************************************************
1390__checkReturn
1391HRESULT
1392StgBlobPool::InitNew(
1393 ULONG cbSize, // Estimated size.
1394 ULONG cItems, // Estimated item count.
1395 BOOL fAddEmptryItem) // Should we add an empty item at offset 0
1396{
1397 CONTRACTL
1398 {
1399 NOTHROW;
1400 INJECT_FAULT(return E_OUTOFMEMORY;);
1401 }
1402 CONTRACTL_END
1403
1404 HRESULT hr;
1405
1406 // Let base class intialize.
1407 IfFailRet(StgPool::InitNew());
1408
1409 // Set initial table sizes, if specified.
1410 if (cbSize > 0)
1411 {
1412 if (!Grow(cbSize))
1413 return E_OUTOFMEMORY;
1414 }
1415 if (cItems > 0)
1416 m_Hash.SetBuckets(cItems);
1417
1418 // Init with empty blob.
1419
1420 // Normally must do this, regardless if we currently have anything in the pool.
1421 // If we don't do this, the first blob that gets added to the pool will
1422 // have an offset of 0. This will cause this blob to have a token of
1423 // 0x70000000, which is considered a nil string token.
1424 //
1425 // By inserting a zero length blob into the pool the being with, we're
1426 // assured that the first blob added to the pool will have an offset
1427 // of 1 and a token of 0x70000001, which is a valid token.
1428 //
1429 // The only time we wouldn't want to do this is if we're reading in a delta metadata.
1430 // Then, we don't care if the first string is at offset 0... when the delta gets applied,
1431 // the string will get moved to the appropriate offset.
1432 if (fAddEmptryItem)
1433 {
1434 MetaData::DataBlob emptyBlob(NULL, 0);
1435 UINT32 nIndex_Ignore;
1436 IfFailRet(AddBlob(&emptyBlob, &nIndex_Ignore));
1437 // Empty blob better be at offset 0.
1438 _ASSERTE(nIndex_Ignore == 0);
1439 }
1440 return hr;
1441} // StgBlobPool::InitNew
1442
1443//*****************************************************************************
1444// Init the blob pool for use. This is called for both create and read case.
1445// If there is existing data and bCopyData is true, then the data is rehashed
1446// to eliminate dupes in future adds.
1447//*****************************************************************************
1448__checkReturn
1449HRESULT
1450StgBlobPool::InitOnMem(
1451 void *pBuf, // Predefined data.
1452 ULONG iBufSize, // Size of data.
1453 int bReadOnly) // true if append is forbidden.
1454{
1455 CONTRACTL
1456 {
1457 NOTHROW;
1458 INJECT_FAULT(return E_OUTOFMEMORY;);
1459 }
1460 CONTRACTL_END
1461
1462 HRESULT hr;
1463
1464 // Let base class init our memory structure.
1465 IfFailRet(StgPool::InitOnMem(pBuf, iBufSize, bReadOnly));
1466
1467 // Init hash table from existing data.
1468 // If we cannot update, we don't need a hash table.
1469 if (bReadOnly)
1470 {
1471 return S_OK;
1472 }
1473
1474 //<TODO>@todo: defer this until we hand out a pointer.</TODO>
1475 IfFailRet(TakeOwnershipOfInitMem());
1476
1477 UINT32 nMaxOffset = GetNextOffset();
1478 for (UINT32 nOffset = 0; nOffset < nMaxOffset; )
1479 {
1480 MetaData::DataBlob blob;
1481 BLOBHASH *pHash;
1482
1483 IfFailRet(GetBlobWithSizePrefix(nOffset, &blob));
1484
1485 // Add the blob to the hash table.
1486 if ((pHash = m_Hash.Add(blob.GetDataPointer())) == NULL)
1487 {
1488 Uninit();
1489 return E_OUTOFMEMORY;
1490 }
1491 pHash->iOffset = nOffset;
1492
1493 nOffset += blob.GetSize();
1494 }
1495 return S_OK;
1496} // StgBlobPool::InitOnMem
1497
1498//*****************************************************************************
1499// Clears the hash table then calls the base class.
1500//*****************************************************************************
1501void StgBlobPool::Uninit()
1502{
1503 CONTRACTL
1504 {
1505 NOTHROW;
1506 FORBID_FAULT;
1507 }
1508 CONTRACTL_END
1509
1510 // Clear the hash table.
1511 m_Hash.Clear();
1512
1513 // Let base class clean up.
1514 StgPool::Uninit();
1515} // StgBlobPool::Uninit
1516
1517
1518//*****************************************************************************
1519// The blob will be added to the pool. The offset of the blob in the pool
1520// is returned in *piOffset. If the blob is already in the pool, then the
1521// offset will be to the existing copy of the blob.
1522//*****************************************************************************
1523__checkReturn
1524HRESULT
1525StgBlobPool::AddBlob(
1526 const MetaData::DataBlob *pData,
1527 UINT32 *pnOffset) // Return offset of blob here.
1528{
1529 BLOBHASH *pHash; // Hash item for add.
1530 void *pBytes; // Working pointer.
1531 BYTE *pStartLoc; // Location to write real blob
1532 ULONG iRequired; // How much buffer for this blob?
1533 ULONG iFillerLen; // space to fill to make byte-aligned
1534 HRESULT hr;
1535
1536 CONTRACTL
1537 {
1538 NOTHROW;
1539 INJECT_FAULT(return E_OUTOFMEMORY;);
1540 }
1541 CONTRACTL_END
1542
1543 // Can we handle this blob?
1544 if (pData->GetSize() > CPackedLen::MAX_LEN)
1545 return (PostError(CLDB_E_TOO_BIG));
1546
1547 // worst case is we need three more bytes to ensure byte-aligned, hence the 3
1548 iRequired = pData->GetSize() + CPackedLen::Size(pData->GetSize()) + 3;
1549 if (iRequired > GetCbSegAvailable())
1550 {
1551 if (!Grow(iRequired))
1552 return (PostError(OutOfMemory()));
1553 }
1554
1555 // unless changed due to alignment, the location of the blob is just
1556 // the value returned by GetNextLocation(), which is also a iFillerLen of
1557 // 0
1558
1559 pStartLoc = (BYTE *)GetNextLocation();
1560 iFillerLen = 0;
1561
1562 // technichally, only the data portion must be DWORD-aligned. So, if the
1563 // data length is zero, we don't need to worry about alignment.
1564
1565 // Pack in the length at pStartLoc (the start location)
1566 pBytes = CPackedLen::PutLength(pStartLoc, pData->GetSize());
1567
1568 // Put the bytes themselves.
1569 memcpy(pBytes, pData->GetDataPointer(), pData->GetSize());
1570
1571 // Find or add the entry.
1572 if ((pHash = m_Hash.Find(GetNextLocation() + iFillerLen, true)) == NULL)
1573 return (PostError(OutOfMemory()));
1574
1575 // If the entry was new, keep the new blob.
1576 if (pHash->iOffset == 0xffffffff)
1577 {
1578 // this blob's offset is increased by iFillerLen bytes
1579 pHash->iOffset = *pnOffset = GetNextOffset() + iFillerLen;
1580 // only SegAllocate what we actually used, rather than what we requested
1581 SegAllocate(pData->GetSize() + CPackedLen::Size(pData->GetSize()) + iFillerLen);
1582
1583 // Check for hash chains that are too long.
1584 if (m_Hash.MaxChainLength() > MAX_CHAIN_LENGTH)
1585 {
1586 IfFailRet(RehashBlobs());
1587 }
1588 }
1589 // Else use the old one.
1590 else
1591 {
1592 *pnOffset = pHash->iOffset;
1593 }
1594
1595 return S_OK;
1596} // StgBlobPool::AddBlob
1597
1598//*****************************************************************************
1599// Return a pointer to a blob, and the size of the blob.
1600//*****************************************************************************
1601__checkReturn
1602HRESULT
1603StgBlobPool::GetBlob(
1604 UINT32 nOffset, // Offset of blob in pool.
1605 MetaData::DataBlob *pData)
1606{
1607 STATIC_CONTRACT_NOTHROW;
1608 STATIC_CONTRACT_FORBID_FAULT;
1609
1610 HRESULT hr;
1611
1612 if (nOffset == 0)
1613 {
1614 // TODO: It would be nice to remove it, but people read behind the end of buffer,
1615 // e.g. VBC reads 2 zeros even though the size is 0 when it's storing string in the blob.
1616 // Nice to have: Move this to the public API only as a compat layer.
1617 pData->Init((BYTE *)m_zeros, 0);
1618 return S_OK;
1619 }
1620
1621 IfFailGo(StgPool::GetData(nOffset, pData));
1622
1623 UINT32 cbBlobContentSize;
1624 if (!pData->GetCompressedU(&cbBlobContentSize))
1625 {
1626 IfFailGo(COR_E_BADIMAGEFORMAT);
1627 }
1628 if (!pData->TruncateToExactSize(cbBlobContentSize))
1629 {
1630 IfFailGo(COR_E_BADIMAGEFORMAT);
1631 }
1632
1633 return S_OK;
1634ErrExit:
1635 pData->Clear();
1636 return hr;
1637} // StgBlobPool::GetBlob
1638
1639//*****************************************************************************
1640// Return a pointer to a blob, and the size of the blob.
1641//*****************************************************************************
1642__checkReturn
1643HRESULT
1644StgBlobPool::GetBlobWithSizePrefix(
1645 UINT32 nOffset, // Offset of blob in pool.
1646 MetaData::DataBlob *pData)
1647{
1648 STATIC_CONTRACT_NOTHROW;
1649 STATIC_CONTRACT_FORBID_FAULT;
1650
1651 HRESULT hr;
1652
1653 if (nOffset == 0)
1654 {
1655 // TODO: Should be a static empty blob once we get rid of m_zeros
1656 pData->Init((BYTE *)m_zeros, 1);
1657 return S_OK;
1658 }
1659
1660 IfFailGo(StgPool::GetData(nOffset, pData));
1661
1662 UINT32 cbBlobContentSize;
1663 UINT32 cbBlobSizePrefixSize;
1664 if (!pData->PeekCompressedU(&cbBlobContentSize, &cbBlobSizePrefixSize))
1665 {
1666 IfFailGo(COR_E_BADIMAGEFORMAT);
1667 }
1668 //_ASSERTE(cbBlobSizePrefixSize <= 4);
1669 //_ASSERTE(cbBlobContentSize <= CompressedInteger::const_Max);
1670
1671 // Cannot overflow, because previous asserts hold (in comments)
1672 UINT32 cbBlobSize;
1673 cbBlobSize = cbBlobContentSize + cbBlobSizePrefixSize;
1674 if (!pData->TruncateToExactSize(cbBlobSize))
1675 {
1676 IfFailGo(COR_E_BADIMAGEFORMAT);
1677 }
1678
1679 return S_OK;
1680ErrExit:
1681 pData->Clear();
1682 return hr;
1683} // StgBlobPool::GetBlob
1684
1685//*****************************************************************************
1686// Turn hashing off or on. If you turn hashing on, then any existing data is
1687// thrown away and all data is rehashed during this call.
1688//*****************************************************************************
1689__checkReturn
1690HRESULT
1691StgBlobPool::SetHash(int bHash)
1692{
1693 CONTRACTL
1694 {
1695 NOTHROW;
1696 INJECT_FAULT(return E_OUTOFMEMORY;);
1697 }
1698 CONTRACTL_END
1699
1700 HRESULT hr = S_OK;
1701
1702 // If turning on hash again, need to rehash all Blobs.
1703 if (bHash)
1704 hr = RehashBlobs();
1705
1706 //<TODO>@todo: m_bHash = bHash;</TODO>
1707 return (hr);
1708} // StgBlobPool::SetHash
1709
1710//*****************************************************************************
1711// Clears out the existing hash table used to eliminate duplicates. Then
1712// rebuilds the hash table from scratch based on the current data.
1713//*****************************************************************************
1714__checkReturn
1715HRESULT
1716StgBlobPool::RehashBlobs()
1717{
1718 CONTRACTL
1719 {
1720 NOTHROW;
1721 INJECT_FAULT(return E_OUTOFMEMORY;);
1722 }
1723 CONTRACTL_END
1724
1725 void const *pBlob; // Pointer to a given blob.
1726 ULONG cbBlob; // Length of a blob.
1727 int iSizeLen = 0; // Size of an encoded length.
1728 ULONG iOffset; // Location within iteration.
1729 ULONG iMax; // End of loop.
1730 ULONG iSeg; // Location within segment.
1731 StgPoolSeg *pSeg = this; // To loop over segments.
1732 BLOBHASH *pHash; // Hash item for add.
1733 int iBuckets; // Buckets in the hash.
1734 int iCount; // Items in the hash.
1735 int iNewBuckets; // New count of buckets in the hash.
1736
1737 // Determine the new bucket size.
1738 iBuckets = m_Hash.Buckets();
1739 iCount = m_Hash.Count();
1740 iNewBuckets = max(iCount, iBuckets+iBuckets/2+1);
1741
1742 // Remove any stale data.
1743 m_Hash.Clear();
1744 m_Hash.SetBuckets(iNewBuckets);
1745
1746 // How far should the loop go.
1747 iMax = GetNextOffset();
1748
1749 // Go through each string, skipping initial empty string.
1750 for (iSeg=iOffset=0; iOffset < iMax; )
1751 {
1752 // Get the string from the pool.
1753 pBlob = pSeg->m_pSegData + iSeg;
1754
1755 cbBlob = CPackedLen::GetLength(pBlob, &iSizeLen);
1756 if (cbBlob == (ULONG)-1)
1757 { // Invalid blob size encoding
1758
1759 //#GarbageInBlobHeap
1760 // Note that this is allowed in ECMA spec (see chapter "#US and #Blob heaps"):
1761 // Both these heaps can contain garbage, as long as any part that is reachable from any of
1762 // the tables contains a valid 'blob'.
1763
1764 // The hash is incomplete, which means that we might emit duplicate blob entries ... that is fine
1765 return S_OK;
1766 }
1767 //_ASSERTE((iSizeLen >= 1) && (iSizeLen <= 4) && (cbBlob <= 0x1fffffff));
1768
1769 // Make it blob size incl. its size encoding (cannot integer overflow)
1770 cbBlob += iSizeLen;
1771 // Check for integer overflow and that the entire blob entry is in this segment
1772 if ((iSeg > (iSeg + cbBlob)) || ((iSeg + cbBlob) > pSeg->m_cbSegNext))
1773 { // Invalid blob size
1774
1775 // See code:#GarbageInBlobHeap
1776 // The hash is incomplete, which means that we might emit duplicate blob entries ... that is fine
1777 return S_OK;
1778 }
1779
1780 // Add the blob to the hash table.
1781 if ((pHash = m_Hash.Add(pBlob)) == 0)
1782 {
1783 Uninit();
1784 return (E_OUTOFMEMORY);
1785 }
1786 pHash->iOffset = iOffset;
1787
1788 // Move to next blob.
1789 iOffset += cbBlob;
1790 iSeg += cbBlob;
1791 if (iSeg >= pSeg->m_cbSegNext)
1792 {
1793 pSeg = pSeg->m_pNextSeg;
1794 iSeg = 0;
1795 }
1796 }
1797 return (S_OK);
1798} // StgBlobPool::RehashBlobs
1799
1800
1801//
1802// CInMemoryStream
1803//
1804
1805
1806ULONG
1807STDMETHODCALLTYPE CInMemoryStream::Release()
1808{
1809 CONTRACTL
1810 {
1811 NOTHROW;
1812 FORBID_FAULT;
1813 SUPPORTS_DAC_HOST_ONLY;
1814 }
1815 CONTRACTL_END
1816
1817 ULONG cRef = InterlockedDecrement(&m_cRef);
1818 if (cRef == 0)
1819 {
1820 if (m_dataCopy != NULL)
1821 delete [] m_dataCopy;
1822
1823 delete this;
1824 }
1825 return (cRef);
1826} // CInMemoryStream::Release
1827
1828HRESULT
1829STDMETHODCALLTYPE
1830CInMemoryStream::QueryInterface(REFIID riid, PVOID *ppOut)
1831{
1832 CONTRACTL
1833 {
1834 NOTHROW;
1835 INJECT_FAULT(return E_OUTOFMEMORY;);
1836 }
1837 CONTRACTL_END
1838
1839 if (!ppOut)
1840 {
1841 return E_POINTER;
1842 }
1843
1844 *ppOut = NULL;
1845 if (riid == IID_IStream || riid == IID_ISequentialStream || riid == IID_IUnknown)
1846 {
1847 *ppOut = this;
1848 AddRef();
1849 return (S_OK);
1850 }
1851
1852 return E_NOINTERFACE;
1853
1854} // CInMemoryStream::QueryInterface
1855
1856HRESULT
1857STDMETHODCALLTYPE
1858CInMemoryStream::Read(
1859 void *pv,
1860 ULONG cb,
1861 ULONG *pcbRead)
1862{
1863 STATIC_CONTRACT_NOTHROW;
1864 STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY;
1865
1866 ULONG cbRead = min(cb, m_cbSize - m_cbCurrent);
1867
1868 if (cbRead == 0)
1869 return (S_FALSE);
1870 memcpy(pv, (void *) ((ULONG_PTR) m_pMem + m_cbCurrent), cbRead);
1871 if (pcbRead)
1872 *pcbRead = cbRead;
1873 m_cbCurrent += cbRead;
1874 return (S_OK);
1875} // CInMemoryStream::Read
1876
1877HRESULT
1878STDMETHODCALLTYPE
1879CInMemoryStream::Write(
1880 const void *pv,
1881 ULONG cb,
1882 ULONG *pcbWritten)
1883{
1884 STATIC_CONTRACT_NOTHROW;
1885 STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY;
1886
1887 if (ovadd_gt(m_cbCurrent, cb, m_cbSize))
1888 return (OutOfMemory());
1889
1890 memcpy((BYTE *) m_pMem + m_cbCurrent, pv, cb);
1891 m_cbCurrent += cb;
1892 if (pcbWritten) *pcbWritten = cb;
1893 return (S_OK);
1894} // CInMemoryStream::Write
1895
1896HRESULT
1897STDMETHODCALLTYPE
1898CInMemoryStream::Seek(
1899 LARGE_INTEGER dlibMove,
1900 DWORD dwOrigin,
1901 ULARGE_INTEGER *plibNewPosition)
1902{
1903 STATIC_CONTRACT_NOTHROW;
1904 STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY;
1905
1906 _ASSERTE(dwOrigin == STREAM_SEEK_SET || dwOrigin == STREAM_SEEK_CUR);
1907 _ASSERTE(dlibMove.QuadPart <= static_cast<LONGLONG>(ULONG_MAX));
1908
1909 if (dwOrigin == STREAM_SEEK_SET)
1910 {
1911 m_cbCurrent = (ULONG) dlibMove.QuadPart;
1912 }
1913 else
1914 if (dwOrigin == STREAM_SEEK_CUR)
1915 {
1916 m_cbCurrent+= (ULONG)dlibMove.QuadPart;
1917 }
1918
1919 if (plibNewPosition)
1920 {
1921 plibNewPosition->QuadPart = m_cbCurrent;
1922 }
1923
1924 return (m_cbCurrent < m_cbSize) ? (S_OK) : E_FAIL;
1925} // CInMemoryStream::Seek
1926
1927HRESULT
1928STDMETHODCALLTYPE
1929CInMemoryStream::CopyTo(
1930 IStream *pstm,
1931 ULARGE_INTEGER cb,
1932 ULARGE_INTEGER *pcbRead,
1933 ULARGE_INTEGER *pcbWritten)
1934{
1935 STATIC_CONTRACT_NOTHROW;
1936 STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY;
1937
1938 HRESULT hr;
1939 // We don't handle pcbRead or pcbWritten.
1940 _ASSERTE(pcbRead == 0);
1941 _ASSERTE(pcbWritten == 0);
1942
1943 _ASSERTE(cb.QuadPart <= ULONG_MAX);
1944 ULONG cbTotal = min(static_cast<ULONG>(cb.QuadPart), m_cbSize - m_cbCurrent);
1945 ULONG cbRead=min(1024, cbTotal);
1946 CQuickBytes rBuf;
1947 void *pBuf = rBuf.AllocNoThrow(cbRead);
1948 if (pBuf == 0)
1949 return (PostError(OutOfMemory()));
1950
1951 while (cbTotal)
1952 {
1953 if (cbRead > cbTotal)
1954 cbRead = cbTotal;
1955 if (FAILED(hr=Read(pBuf, cbRead, 0)))
1956 return (hr);
1957 if (FAILED(hr=pstm->Write(pBuf, cbRead, 0)))
1958 return (hr);
1959 cbTotal -= cbRead;
1960 }
1961
1962 // Adjust seek pointer to the end.
1963 m_cbCurrent = m_cbSize;
1964
1965 return (S_OK);
1966} // CInMemoryStream::CopyTo
1967
1968HRESULT
1969CInMemoryStream::CreateStreamOnMemory(
1970 void *pMem, // Memory to create stream on.
1971 ULONG cbSize, // Size of data.
1972 IStream **ppIStream, // Return stream object here.
1973 BOOL fDeleteMemoryOnRelease)
1974{
1975 CONTRACTL
1976 {
1977 NOTHROW;
1978 INJECT_FAULT(return E_OUTOFMEMORY;);
1979 }
1980 CONTRACTL_END
1981
1982 CInMemoryStream *pIStream; // New stream object.
1983 if ((pIStream = new (nothrow) CInMemoryStream) == 0)
1984 return (PostError(OutOfMemory()));
1985 pIStream->InitNew(pMem, cbSize);
1986 if (fDeleteMemoryOnRelease)
1987 {
1988 // make sure this memory is allocated using new
1989 pIStream->m_dataCopy = (BYTE *)pMem;
1990 }
1991 *ppIStream = pIStream;
1992 return (S_OK);
1993} // CInMemoryStream::CreateStreamOnMemory
1994
1995HRESULT
1996CInMemoryStream::CreateStreamOnMemoryCopy(
1997 void *pMem,
1998 ULONG cbSize,
1999 IStream **ppIStream)
2000{
2001 CONTRACTL
2002 {
2003 NOTHROW;
2004 INJECT_FAULT(return E_OUTOFMEMORY;);
2005 }
2006 CONTRACTL_END
2007
2008 CInMemoryStream *pIStream; // New stream object.
2009 if ((pIStream = new (nothrow) CInMemoryStream) == 0)
2010 return (PostError(OutOfMemory()));
2011
2012 // Init the stream.
2013 pIStream->m_cbCurrent = 0;
2014 pIStream->m_cbSize = cbSize;
2015
2016 // Copy the data.
2017 pIStream->m_dataCopy = new (nothrow) BYTE[cbSize];
2018
2019 if (pIStream->m_dataCopy == NULL)
2020 {
2021 delete pIStream;
2022 return (PostError(OutOfMemory()));
2023 }
2024
2025 pIStream->m_pMem = pIStream->m_dataCopy;
2026 memcpy(pIStream->m_dataCopy, pMem, cbSize);
2027
2028 *ppIStream = pIStream;
2029 return (S_OK);
2030} // CInMemoryStream::CreateStreamOnMemoryCopy
2031
2032//---------------------------------------------------------------------------
2033// CGrowableStream is a simple IStream implementation that grows as
2034// its written to. All the memory is contigious, so read access is
2035// fast. A grow does a realloc, so be aware of that if you're going to
2036// use this.
2037//---------------------------------------------------------------------------
2038
2039//Constructs a new GrowableStream
2040// multiplicativeGrowthRate - when the stream grows it will be at least this
2041// multiple of its old size. Values greater than 1 ensure O(N) amortized
2042// performance growing the stream to size N, 1 ensures O(N^2) amortized perf
2043// but gives the tightest memory usage. Valid range is [1.0, 2.0].
2044// additiveGrowthRate - when the stream grows it will increase in size by at least
2045// this number of bytes. Larger numbers cause fewer re-allocations at the cost of
2046// increased memory usage.
2047CGrowableStream::CGrowableStream(float multiplicativeGrowthRate, DWORD additiveGrowthRate)
2048{
2049 CONTRACTL
2050 {
2051 NOTHROW;
2052 FORBID_FAULT;
2053 }
2054 CONTRACTL_END
2055
2056 m_swBuffer = NULL;
2057 m_dwBufferSize = 0;
2058 m_dwBufferIndex = 0;
2059 m_dwStreamLength = 0;
2060 m_cRef = 1;
2061
2062 // Lets make sure these values stay somewhat sane... if you adjust the limits
2063 // make sure you also write correct overflow checking code in EnsureCapcity
2064 _ASSERTE(multiplicativeGrowthRate >= 1.0F && multiplicativeGrowthRate <= 2.0F);
2065 m_multiplicativeGrowthRate = min(max(1.0F, multiplicativeGrowthRate), 2.0F);
2066
2067 _ASSERTE(additiveGrowthRate >= 1);
2068 m_additiveGrowthRate = max(1, additiveGrowthRate);
2069} // CGrowableStream::CGrowableStream
2070
2071#ifndef DACCESS_COMPILE
2072
2073CGrowableStream::~CGrowableStream()
2074{
2075 CONTRACTL
2076 {
2077 NOTHROW;
2078 FORBID_FAULT;
2079 }
2080 CONTRACTL_END
2081
2082 // Destroy the buffer.
2083 if (m_swBuffer != NULL)
2084 delete [] m_swBuffer;
2085
2086 m_swBuffer = NULL;
2087 m_dwBufferSize = 0;
2088} // CGrowableStream::~CGrowableStream
2089
2090// Grows the stream and optionally the internal buffer to ensure it is at least
2091// newLogicalSize
2092HRESULT CGrowableStream::EnsureCapacity(DWORD newLogicalSize)
2093{
2094 _ASSERTE(m_dwBufferSize >= m_dwStreamLength);
2095
2096 // If there is no enough space left in the buffer, grow it
2097 if (newLogicalSize > m_dwBufferSize)
2098 {
2099 // Grow to max of newLogicalSize, m_dwBufferSize*multiplicativeGrowthRate, and
2100 // m_dwBufferSize+m_additiveGrowthRate
2101 S_UINT32 addSize = S_UINT32(m_dwBufferSize) + S_UINT32(m_additiveGrowthRate);
2102 if (addSize.IsOverflow())
2103 {
2104 addSize = S_UINT32(UINT_MAX);
2105 }
2106
2107 // this should have been enforced in the constructor too
2108 _ASSERTE(m_multiplicativeGrowthRate <= 2.0 && m_multiplicativeGrowthRate >= 1.0);
2109
2110 // 2*UINT_MAX doesn't overflow a float so this certain to be safe
2111 float multSizeF = (float)m_dwBufferSize * m_multiplicativeGrowthRate;
2112 DWORD multSize;
2113 if(multSizeF > (float)UINT_MAX)
2114 {
2115 multSize = UINT_MAX;
2116 }
2117 else
2118 {
2119 multSize = (DWORD)multSizeF;
2120 }
2121
2122 DWORD newBufferSize = max(max(newLogicalSize, multSize), addSize.Value());
2123
2124 char *tmp = new (nothrow) char[newBufferSize];
2125 if(tmp == NULL)
2126 {
2127 return E_OUTOFMEMORY;
2128 }
2129
2130 if (m_swBuffer) {
2131 memcpy (tmp, m_swBuffer, m_dwBufferSize);
2132 delete [] m_swBuffer;
2133 }
2134 m_swBuffer = (BYTE *)tmp;
2135 m_dwBufferSize = newBufferSize;
2136 }
2137
2138 _ASSERTE(m_dwBufferSize >= newLogicalSize);
2139 // the internal buffer is big enough, might have to increase logical size
2140 // though
2141 if(newLogicalSize > m_dwStreamLength)
2142 {
2143 m_dwStreamLength = newLogicalSize;
2144 }
2145
2146 _ASSERTE(m_dwBufferSize >= m_dwStreamLength);
2147 return S_OK;
2148}
2149
2150ULONG
2151STDMETHODCALLTYPE
2152CGrowableStream::Release()
2153{
2154 STATIC_CONTRACT_NOTHROW;
2155 STATIC_CONTRACT_FORBID_FAULT;
2156
2157 ULONG cRef = InterlockedDecrement(&m_cRef);
2158
2159 if (cRef == 0)
2160 delete this;
2161
2162 return cRef;
2163} // CGrowableStream::Release
2164
2165HRESULT
2166STDMETHODCALLTYPE
2167CGrowableStream::QueryInterface(
2168 REFIID riid,
2169 PVOID *ppOut)
2170{
2171 STATIC_CONTRACT_NOTHROW;
2172 STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY
2173
2174 if (riid != IID_IUnknown && riid!=IID_ISequentialStream && riid!=IID_IStream)
2175 return E_NOINTERFACE;
2176
2177 *ppOut = this;
2178 AddRef();
2179 return (S_OK);
2180} // CGrowableStream::QueryInterface
2181
2182HRESULT
2183CGrowableStream::Read(
2184 void *pv,
2185 ULONG cb,
2186 ULONG *pcbRead)
2187{
2188 STATIC_CONTRACT_NOTHROW;
2189 STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY
2190
2191 HRESULT hr = S_OK;
2192 DWORD dwCanReadBytes = 0;
2193
2194 if (NULL == pv)
2195 return E_POINTER;
2196
2197 // short-circuit a zero-length read or see if we are at the end
2198 if (cb == 0 || m_dwBufferIndex >= m_dwStreamLength)
2199 {
2200 if (pcbRead != NULL)
2201 *pcbRead = 0;
2202
2203 return S_OK;
2204 }
2205
2206 // Figure out if we have enough room in the stream (excluding any
2207 // unused space at the end of the buffer)
2208 dwCanReadBytes = cb;
2209
2210 S_UINT32 dwNewIndex = S_UINT32(dwCanReadBytes) + S_UINT32(m_dwBufferIndex);
2211 if (dwNewIndex.IsOverflow() || (dwNewIndex.Value() > m_dwStreamLength))
2212 {
2213 // Only read whatever is left in the buffer (if any)
2214 dwCanReadBytes = (m_dwStreamLength - m_dwBufferIndex);
2215 }
2216
2217 // copy from our buffer to caller's buffer
2218 memcpy(pv, &m_swBuffer[m_dwBufferIndex], dwCanReadBytes);
2219
2220 // adjust our current position
2221 m_dwBufferIndex += dwCanReadBytes;
2222
2223 // if they want the info, tell them how many byte we read for them
2224 if (pcbRead != NULL)
2225 *pcbRead = dwCanReadBytes;
2226
2227 return hr;
2228} // CGrowableStream::Read
2229
2230HRESULT
2231CGrowableStream::Write(
2232 const void *pv,
2233 ULONG cb,
2234 ULONG *pcbWritten)
2235{
2236 STATIC_CONTRACT_NOTHROW;
2237 STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY
2238
2239 HRESULT hr = S_OK;
2240 DWORD dwActualWrite = 0;
2241
2242 // avoid NULL write
2243 if (cb == 0)
2244 {
2245 hr = S_OK;
2246 goto Error;
2247 }
2248
2249 // Check if our buffer is large enough
2250 _ASSERTE(m_dwBufferIndex <= m_dwStreamLength);
2251 _ASSERTE(m_dwStreamLength <= m_dwBufferSize);
2252
2253 // If there is no enough space left in the buffer, grow it
2254 if (cb > (m_dwStreamLength - m_dwBufferIndex))
2255 {
2256 // Determine the new size needed
2257 S_UINT32 size = S_UINT32(m_dwBufferSize) + S_UINT32(cb);
2258 if (size.IsOverflow())
2259 {
2260 hr = HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
2261 goto Error;
2262 }
2263
2264 hr = EnsureCapacity(size.Value());
2265 if(FAILED(hr))
2266 {
2267 goto Error;
2268 }
2269 }
2270
2271 if ((pv != NULL) && (cb > 0))
2272 {
2273 // write to current position in the buffer
2274 memcpy(&m_swBuffer[m_dwBufferIndex], pv, cb);
2275
2276 // now update our current index
2277 m_dwBufferIndex += cb;
2278
2279 // in case they want to know the number of bytes written
2280 dwActualWrite = cb;
2281 }
2282
2283Error:
2284 if (pcbWritten)
2285 *pcbWritten = dwActualWrite;
2286
2287 return hr;
2288} // CGrowableStream::Write
2289
2290STDMETHODIMP
2291CGrowableStream::Seek(
2292 LARGE_INTEGER dlibMove,
2293 DWORD dwOrigin,
2294 ULARGE_INTEGER *plibNewPosition)
2295{
2296 STATIC_CONTRACT_NOTHROW;
2297 STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY
2298
2299 // a Seek() call on STREAM_SEEK_CUR and a dlibMove == 0 is a
2300 // request to get the current seek position.
2301 if ((dwOrigin == STREAM_SEEK_CUR && dlibMove.u.LowPart == 0) &&
2302 (dlibMove.u.HighPart == 0) &&
2303 (NULL != plibNewPosition))
2304 {
2305 goto Error;
2306 }
2307
2308 // we only support STREAM_SEEK_SET (beginning of buffer)
2309 if (dwOrigin != STREAM_SEEK_SET)
2310 return E_NOTIMPL;
2311
2312 // did they ask to seek past end of stream? If so we're supposed to
2313 // extend with zeros. But we've never supported that.
2314 if (dlibMove.u.LowPart > m_dwStreamLength)
2315 return E_UNEXPECTED;
2316
2317 // we ignore the high part of the large integer
2318 SIMPLIFYING_ASSUMPTION(dlibMove.u.HighPart == 0);
2319 m_dwBufferIndex = dlibMove.u.LowPart;
2320
2321Error:
2322 if (NULL != plibNewPosition)
2323 {
2324 plibNewPosition->u.HighPart = 0;
2325 plibNewPosition->u.LowPart = m_dwBufferIndex;
2326 }
2327
2328 return S_OK;
2329} // CGrowableStream::Seek
2330
2331STDMETHODIMP
2332CGrowableStream::SetSize(
2333 ULARGE_INTEGER libNewSize)
2334{
2335 STATIC_CONTRACT_NOTHROW;
2336 STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY
2337
2338 DWORD dwNewSize = libNewSize.u.LowPart;
2339
2340 _ASSERTE(libNewSize.u.HighPart == 0);
2341
2342 // we don't support large allocations
2343 if (libNewSize.u.HighPart > 0)
2344 return E_OUTOFMEMORY;
2345
2346 HRESULT hr = EnsureCapacity(dwNewSize);
2347 if(FAILED(hr))
2348 {
2349 return hr;
2350 }
2351
2352 // EnsureCapacity doesn't shrink the logicalSize if dwNewSize is smaller
2353 // and SetSize is allowed to shrink the stream too. Note that we won't
2354 // release physical memory here, we just appear to get smaller
2355 m_dwStreamLength = dwNewSize;
2356
2357 return S_OK;
2358} // CGrowableStream::SetSize
2359
2360STDMETHODIMP
2361CGrowableStream::Stat(
2362 STATSTG *pstatstg,
2363 DWORD grfStatFlag)
2364{
2365 STATIC_CONTRACT_NOTHROW;
2366 STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY
2367
2368 if (NULL == pstatstg)
2369 return E_POINTER;
2370
2371 // this is the only useful information we hand out - the length of the stream
2372 pstatstg->cbSize.u.HighPart = 0;
2373 pstatstg->cbSize.u.LowPart = m_dwStreamLength;
2374 pstatstg->type = STGTY_STREAM;
2375
2376 // we ignore the grfStatFlag - we always assume STATFLAG_NONAME
2377 pstatstg->pwcsName = NULL;
2378
2379 pstatstg->grfMode = 0;
2380 pstatstg->grfLocksSupported = 0;
2381 pstatstg->clsid = CLSID_NULL;
2382 pstatstg->grfStateBits = 0;
2383
2384 return S_OK;
2385} // CGrowableStream::Stat
2386
2387//
2388// Clone - Make a deep copy of the stream into a new cGrowableStream instance
2389//
2390// Arguments:
2391// ppStream - required output parameter for the new stream instance
2392//
2393// Returns:
2394// S_OK on succeess, or an error code on failure.
2395//
2396HRESULT
2397CGrowableStream::Clone(
2398 IStream **ppStream)
2399{
2400 STATIC_CONTRACT_NOTHROW;
2401 STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY
2402
2403 if (NULL == ppStream)
2404 return E_POINTER;
2405
2406 // Copy our entire buffer into the new stream
2407 CGrowableStream * newStream = new (nothrow) CGrowableStream();
2408 if (newStream == NULL)
2409 {
2410 return E_OUTOFMEMORY;
2411 }
2412
2413 HRESULT hr = newStream->Write(m_swBuffer, m_dwStreamLength, NULL);
2414 if (FAILED(hr))
2415 {
2416 delete newStream;
2417 return hr;
2418 }
2419
2420 *ppStream = newStream;
2421 return S_OK;
2422} // CGrowableStream::Clone
2423
2424#endif // !DACCESS_COMPILE
2425