2 * Compound Storage (32 bit version)
3 * Stream implementation
5 * This file contains the implementation of the stream interface
6 * for streams contained in a compound storage.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Thuy Nguyen
21 #include "interfaces.h"
23 #include "storage32.h"
27 * Virtual function table for the StgStreamImpl class.
29 static ICOM_VTABLE(IStream32
) StgStreamImpl_Vtbl
=
33 VTABLE_FUNC(StgStreamImpl_QueryInterface
),
34 VTABLE_FUNC(StgStreamImpl_AddRef
),
35 VTABLE_FUNC(StgStreamImpl_Release
)
37 VTABLE_FUNC(StgStreamImpl_Read
),
38 VTABLE_FUNC(StgStreamImpl_Write
)
40 VTABLE_FUNC(StgStreamImpl_Seek
),
41 VTABLE_FUNC(StgStreamImpl_SetSize
),
42 VTABLE_FUNC(StgStreamImpl_CopyTo
),
43 VTABLE_FUNC(StgStreamImpl_Commit
),
44 VTABLE_FUNC(StgStreamImpl_Revert
),
45 VTABLE_FUNC(StgStreamImpl_LockRegion
),
46 VTABLE_FUNC(StgStreamImpl_UnlockRegion
),
47 VTABLE_FUNC(StgStreamImpl_Stat
),
48 VTABLE_FUNC(StgStreamImpl_Clone
)
51 /******************************************************************************
52 ** StgStreamImpl implementation
56 * This is the constructor for the StgStreamImpl class.
59 * parentStorage - Pointer to the storage that contains the stream to open
60 * ownerProperty - Index of the property that points to this stream.
62 StgStreamImpl
* StgStreamImpl_Construct(
63 Storage32BaseImpl
* parentStorage
,
66 StgStreamImpl
* newStream
;
68 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(StgStreamImpl
));
73 * Set-up the virtual function table and reference count.
75 newStream
->lpvtbl
= &StgStreamImpl_Vtbl
;
79 * We want to nail-down the reference to the storage in case the
80 * stream out-lives the storage in the client application.
82 newStream
->parentStorage
= parentStorage
;
83 IStorage32_AddRef(newStream
->parentStorage
);
85 newStream
->ownerProperty
= ownerProperty
;
88 * Start the stream at the begining.
90 newStream
->currentPosition
.HighPart
= 0;
91 newStream
->currentPosition
.LowPart
= 0;
94 * Initialize the rest of the data.
96 newStream
->streamSize
.HighPart
= 0;
97 newStream
->streamSize
.LowPart
= 0;
98 newStream
->bigBlockChain
= 0;
99 newStream
->smallBlockChain
= 0;
102 * Read the size from the property and determine if the blocks forming
103 * this stream are large or small.
105 StgStreamImpl_OpenBlockChain(newStream
);
112 * This is the destructor of the StgStreamImpl class.
114 * This method will clean-up all the resources used-up by the given StgStreamImpl
115 * class. The pointer passed-in to this function will be freed and will not
118 void StgStreamImpl_Destroy(StgStreamImpl
* This
)
121 * Release the reference we are holding on the parent storage.
123 IStorage32_Release(This
->parentStorage
);
124 This
->parentStorage
= 0;
127 * Make sure we clean-up the block chain stream objects that we were using.
129 if (This
->bigBlockChain
!= 0)
131 BlockChainStream_Destroy(This
->bigBlockChain
);
132 This
->bigBlockChain
= 0;
135 if (This
->smallBlockChain
!= 0)
137 SmallBlockChainStream_Destroy(This
->smallBlockChain
);
138 This
->smallBlockChain
= 0;
142 * Finally, free the memory used-up by the class.
144 HeapFree(GetProcessHeap(), 0, This
);
148 * This implements the IUnknown method QueryInterface for this
151 HRESULT WINAPI
StgStreamImpl_QueryInterface(
153 REFIID riid
, /* [in] */
154 void** ppvObject
) /* [iid_is][out] */
157 * Perform a sanity check on the parameters.
163 * Initialize the return parameter.
168 * Compare the riid with the interface IDs implemented by this object.
170 if (memcmp(&IID_IUnknown
, riid
, sizeof(IID_IUnknown
)) == 0)
172 *ppvObject
= (IStream32
*)This
;
174 else if (memcmp(&IID_IStorage
, riid
, sizeof(IID_IStream
)) == 0)
176 *ppvObject
= (IStream32
*)This
;
180 * Check that we obtained an interface.
183 return E_NOINTERFACE
;
186 * Query Interface always increases the reference count by one when it is
189 StgStreamImpl_AddRef(This
);
195 * This implements the IUnknown method AddRef for this
198 ULONG WINAPI
StgStreamImpl_AddRef(
207 * This implements the IUnknown method Release for this
210 ULONG WINAPI
StgStreamImpl_Release(
220 * If the reference count goes down to 0, perform suicide.
224 StgStreamImpl_Destroy(This
);
231 * This method will open the block chain pointed by the property
232 * that describes the stream.
233 * If the stream's size is null, no chain is opened.
235 void StgStreamImpl_OpenBlockChain(
238 StgProperty curProperty
;
239 BOOL32 readSucessful
;
242 * Make sure no old object is staying behind.
244 if (This
->smallBlockChain
!= 0)
246 SmallBlockChainStream_Destroy(This
->smallBlockChain
);
247 This
->smallBlockChain
= 0;
250 if (This
->bigBlockChain
!= 0)
252 BlockChainStream_Destroy(This
->bigBlockChain
);
253 This
->bigBlockChain
= 0;
257 * Read the information from the property.
259 readSucessful
= Storage32Impl_ReadProperty(This
->parentStorage
->ancestorStorage
,
265 This
->streamSize
= curProperty
.size
;
268 * This code supports only streams that are <32 bits in size.
270 assert(This
->streamSize
.HighPart
== 0);
272 if(curProperty
.startingBlock
== BLOCK_END_OF_CHAIN
)
274 assert( (This
->streamSize
.HighPart
== 0) && (This
->streamSize
.LowPart
== 0) );
278 if ( (This
->streamSize
.HighPart
== 0) &&
279 (This
->streamSize
.LowPart
< LIMIT_TO_USE_SMALL_BLOCK
) )
281 This
->smallBlockChain
= SmallBlockChainStream_Construct(
282 This
->parentStorage
->ancestorStorage
,
283 This
->ownerProperty
);
288 This
->bigBlockChain
= BlockChainStream_Construct(
289 This
->parentStorage
->ancestorStorage
,
291 This
->ownerProperty
);
298 * This method is part of the ISequentialStream interface.
300 * If reads a block of information from the stream at the current
301 * position. It then moves the current position at the end of the
304 * See the documentation of ISequentialStream for more info.
306 HRESULT WINAPI
StgStreamImpl_Read(
308 void* pv
, /* [length_is][size_is][out] */
310 ULONG
* pcbRead
) /* [out] */
312 ULONG bytesReadBuffer
;
313 ULONG bytesToReadFromBuffer
;
316 * If the caller is not interested in the nubmer of bytes read,
317 * we use another buffer to avoid "if" statements in the code.
320 pcbRead
= &bytesReadBuffer
;
323 * Using the known size of the stream, calculate the number of bytes
324 * to read from the block chain
326 bytesToReadFromBuffer
= MIN( This
->streamSize
.LowPart
- This
->currentPosition
.LowPart
, cb
);
329 * Depending on the type of chain that was opened when the stream was constructed,
330 * we delegate the work to the method that read the block chains.
332 if (This
->smallBlockChain
!=0)
334 SmallBlockChainStream_ReadAt(This
->smallBlockChain
,
335 This
->currentPosition
,
336 bytesToReadFromBuffer
,
341 else if (This
->bigBlockChain
!=0)
343 BlockChainStream_ReadAt(This
->bigBlockChain
,
344 This
->currentPosition
,
345 bytesToReadFromBuffer
,
353 * We should always be able to read the proper amount of data from the
356 assert(bytesToReadFromBuffer
== *pcbRead
);
359 * Advance the pointer for the number of positions read.
361 This
->currentPosition
.LowPart
+= *pcbRead
;
364 * The function returns S_OK if the buffer was filled completely
365 * it returns S_FALSE if the end of the stream is reached before the
375 * This method is part of the ISequentialStream interface.
377 * It writes a block of information to the stream at the current
378 * position. It then moves the current position at the end of the
379 * written block. If the stream is too small to fit the block,
380 * the stream is grown to fit.
382 * See the documentation of ISequentialStream for more info.
384 HRESULT WINAPI
StgStreamImpl_Write(
386 const void* pv
, /* [size_is][in] */
388 ULONG
* pcbWritten
) /* [out] */
390 ULARGE_INTEGER newSize
;
391 ULONG bytesWritten
= 0;
394 * If the caller is not interested in the number of bytes written,
395 * we use another buffer to avoid "if" statements in the code.
398 pcbWritten
= &bytesWritten
;
406 newSize
.HighPart
= 0;
407 newSize
.LowPart
= This
->currentPosition
.LowPart
+ cb
;
411 * Verify if we need to grow the stream
413 if (newSize
.LowPart
> This
->streamSize
.LowPart
)
416 StgStreamImpl_SetSize(This
, newSize
);
420 * Depending on the type of chain that was opened when the stream was constructed,
421 * we delegate the work to the method that readwrites to the block chains.
423 if (This
->smallBlockChain
!=0)
425 SmallBlockChainStream_WriteAt(This
->smallBlockChain
,
426 This
->currentPosition
,
432 else if (This
->bigBlockChain
!=0)
434 BlockChainStream_WriteAt(This
->bigBlockChain
,
435 This
->currentPosition
,
444 * Advance the position pointer for the number of positions written.
446 This
->currentPosition
.LowPart
+= *pcbWritten
;
452 * This method is part of the IStream interface.
454 * It will move the current stream pointer according to the parameters
457 * See the documentation of IStream for more info.
459 HRESULT WINAPI
StgStreamImpl_Seek(
461 LARGE_INTEGER dlibMove
, /* [in] */
462 DWORD dwOrigin
, /* [in] */
463 ULARGE_INTEGER
* plibNewPosition
) /* [out] */
465 ULARGE_INTEGER newPosition
;
468 * The caller is allowed to pass in NULL as the new position return value.
469 * If it happens, we assign it to a dynamic variable to avoid special cases
472 if (plibNewPosition
== 0)
474 plibNewPosition
= &newPosition
;
478 * The file pointer is moved depending on the given "function"
483 case STREAM_SEEK_SET
:
484 plibNewPosition
->HighPart
= 0;
485 plibNewPosition
->LowPart
= 0;
487 case STREAM_SEEK_CUR
:
488 *plibNewPosition
= This
->currentPosition
;
490 case STREAM_SEEK_END
:
491 *plibNewPosition
= This
->streamSize
;
494 return STG_E_INVALIDFUNCTION
;
498 * We don't support files with offsets of 64 bits.
500 assert(dlibMove
.HighPart
== 0);
503 * Check if we end-up before the beginning of the file. That should trigger an
506 if ( (dlibMove
.LowPart
<0) && (plibNewPosition
->LowPart
< (ULONG
)(-dlibMove
.LowPart
)) )
509 * I don't know what error to send there.
515 * Move the actual file pointer
516 * If the file pointer ends-up after the end of the stream, the next Write operation will
517 * make the file larger. This is how it is documented.
519 plibNewPosition
->LowPart
+= dlibMove
.LowPart
;
520 This
->currentPosition
= *plibNewPosition
;
526 * This method is part of the IStream interface.
528 * It will change the size of a stream.
530 * TODO: Switch from small blocks to big blocks and vice versa.
532 * See the documentation of IStream for more info.
534 HRESULT WINAPI
StgStreamImpl_SetSize(
536 ULARGE_INTEGER libNewSize
) /* [in] */
538 StgProperty curProperty
;
544 if (libNewSize
.HighPart
!= 0)
545 return STG_E_INVALIDFUNCTION
;
547 if (This
->streamSize
.LowPart
== libNewSize
.LowPart
)
551 * This will happen if we're creating a stream
553 if ((This
->smallBlockChain
== 0) && (This
->bigBlockChain
== 0))
555 if (libNewSize
.LowPart
< LIMIT_TO_USE_SMALL_BLOCK
)
557 This
->smallBlockChain
= SmallBlockChainStream_Construct(
558 This
->parentStorage
->ancestorStorage
,
559 This
->ownerProperty
);
563 This
->bigBlockChain
= BlockChainStream_Construct(
564 This
->parentStorage
->ancestorStorage
,
566 This
->ownerProperty
);
571 * Read this stream's property to see if it's small blocks or big blocks
573 Success
= Storage32Impl_ReadProperty(This
->parentStorage
->ancestorStorage
,
578 * determine if we have to switch from small to big blocks or vice versa
581 if (This
->smallBlockChain
!=0)
583 Success
= SmallBlockChainStream_SetSize(This
->smallBlockChain
, libNewSize
);
584 curProperty
.blockType
= SMALL_BLOCK_TYPE
;
588 Success
= BlockChainStream_SetSize(This
->bigBlockChain
, libNewSize
);
589 curProperty
.blockType
= BIG_BLOCK_TYPE
;
593 * Write to the property the new information about this stream
595 Success
= Storage32Impl_ReadProperty(This
->parentStorage
->ancestorStorage
,
599 curProperty
.size
.HighPart
= libNewSize
.HighPart
;
600 curProperty
.size
.LowPart
= libNewSize
.LowPart
;
604 Storage32Impl_WriteProperty(This
->parentStorage
->ancestorStorage
,
609 This
->streamSize
= libNewSize
;
614 HRESULT WINAPI
StgStreamImpl_CopyTo(
616 IStream32
* pstm
, /* [unique][in] */
617 ULARGE_INTEGER cb
, /* [in] */
618 ULARGE_INTEGER
* pcbRead
, /* [out] */
619 ULARGE_INTEGER
* pcbWritten
) /* [out] */
625 * This method is part of the IStream interface.
627 * For streams contained in structured storages, this method
628 * does nothing. This is what the documentation tells us.
630 * See the documentation of IStream for more info.
632 HRESULT WINAPI
StgStreamImpl_Commit(
634 DWORD grfCommitFlags
) /* [in] */
640 * This method is part of the IStream interface.
642 * For streams contained in structured storages, this method
643 * does nothing. This is what the documentation tells us.
645 * See the documentation of IStream for more info.
647 HRESULT WINAPI
StgStreamImpl_Revert(
653 HRESULT WINAPI
StgStreamImpl_LockRegion(
655 ULARGE_INTEGER libOffset
, /* [in] */
656 ULARGE_INTEGER cb
, /* [in] */
657 DWORD dwLockType
) /* [in] */
662 HRESULT WINAPI
StgStreamImpl_UnlockRegion(
664 ULARGE_INTEGER libOffset
, /* [in] */
665 ULARGE_INTEGER cb
, /* [in] */
666 DWORD dwLockType
) /* [in] */
672 * This method is part of the IStream interface.
674 * This method returns information about the current
677 * See the documentation of IStream for more info.
679 HRESULT WINAPI
StgStreamImpl_Stat(
681 STATSTG
* pstatstg
, /* [out] */
682 DWORD grfStatFlag
) /* [in] */
684 StgProperty curProperty
;
685 BOOL32 readSucessful
;
688 * Read the information from the property.
690 readSucessful
= Storage32Impl_ReadProperty(This
->parentStorage
->ancestorStorage
,
696 StorageUtl_CopyPropertyToSTATSTG(pstatstg
,
706 HRESULT WINAPI
StgStreamImpl_Clone(
708 IStream32
** ppstm
) /* [out] */