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
20 #include "wine/obj_base.h"
21 #include "wine/obj_storage.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
);
287 This
->bigBlockChain
= BlockChainStream_Construct(
288 This
->parentStorage
->ancestorStorage
,
290 This
->ownerProperty
);
297 * This method is part of the ISequentialStream interface.
299 * If reads a block of information from the stream at the current
300 * position. It then moves the current position at the end of the
303 * See the documentation of ISequentialStream for more info.
305 HRESULT WINAPI
StgStreamImpl_Read(
307 void* pv
, /* [length_is][size_is][out] */
309 ULONG
* pcbRead
) /* [out] */
311 ULONG bytesReadBuffer
;
312 ULONG bytesToReadFromBuffer
;
315 * If the caller is not interested in the nubmer of bytes read,
316 * we use another buffer to avoid "if" statements in the code.
319 pcbRead
= &bytesReadBuffer
;
322 * Using the known size of the stream, calculate the number of bytes
323 * to read from the block chain
325 bytesToReadFromBuffer
= MIN( This
->streamSize
.LowPart
- This
->currentPosition
.LowPart
, cb
);
328 * Depending on the type of chain that was opened when the stream was constructed,
329 * we delegate the work to the method that read the block chains.
331 if (This
->smallBlockChain
!=0)
333 SmallBlockChainStream_ReadAt(This
->smallBlockChain
,
334 This
->currentPosition
,
335 bytesToReadFromBuffer
,
340 else if (This
->bigBlockChain
!=0)
342 BlockChainStream_ReadAt(This
->bigBlockChain
,
343 This
->currentPosition
,
344 bytesToReadFromBuffer
,
352 * We should always be able to read the proper amount of data from the
355 assert(bytesToReadFromBuffer
== *pcbRead
);
358 * Advance the pointer for the number of positions read.
360 This
->currentPosition
.LowPart
+= *pcbRead
;
363 * The function returns S_OK if the buffer was filled completely
364 * it returns S_FALSE if the end of the stream is reached before the
374 * This method is part of the ISequentialStream interface.
376 * It writes a block of information to the stream at the current
377 * position. It then moves the current position at the end of the
378 * written block. If the stream is too small to fit the block,
379 * the stream is grown to fit.
381 * See the documentation of ISequentialStream for more info.
383 HRESULT WINAPI
StgStreamImpl_Write(
385 const void* pv
, /* [size_is][in] */
387 ULONG
* pcbWritten
) /* [out] */
389 ULARGE_INTEGER newSize
;
390 ULONG bytesWritten
= 0;
393 * If the caller is not interested in the number of bytes written,
394 * we use another buffer to avoid "if" statements in the code.
397 pcbWritten
= &bytesWritten
;
405 newSize
.HighPart
= 0;
406 newSize
.LowPart
= This
->currentPosition
.LowPart
+ cb
;
410 * Verify if we need to grow the stream
412 if (newSize
.LowPart
> This
->streamSize
.LowPart
)
415 StgStreamImpl_SetSize(This
, newSize
);
419 * Depending on the type of chain that was opened when the stream was constructed,
420 * we delegate the work to the method that readwrites to the block chains.
422 if (This
->smallBlockChain
!=0)
424 SmallBlockChainStream_WriteAt(This
->smallBlockChain
,
425 This
->currentPosition
,
431 else if (This
->bigBlockChain
!=0)
433 BlockChainStream_WriteAt(This
->bigBlockChain
,
434 This
->currentPosition
,
443 * Advance the position pointer for the number of positions written.
445 This
->currentPosition
.LowPart
+= *pcbWritten
;
451 * This method is part of the IStream interface.
453 * It will move the current stream pointer according to the parameters
456 * See the documentation of IStream for more info.
458 HRESULT WINAPI
StgStreamImpl_Seek(
460 LARGE_INTEGER dlibMove
, /* [in] */
461 DWORD dwOrigin
, /* [in] */
462 ULARGE_INTEGER
* plibNewPosition
) /* [out] */
464 ULARGE_INTEGER newPosition
;
467 * The caller is allowed to pass in NULL as the new position return value.
468 * If it happens, we assign it to a dynamic variable to avoid special cases
471 if (plibNewPosition
== 0)
473 plibNewPosition
= &newPosition
;
477 * The file pointer is moved depending on the given "function"
482 case STREAM_SEEK_SET
:
483 plibNewPosition
->HighPart
= 0;
484 plibNewPosition
->LowPart
= 0;
486 case STREAM_SEEK_CUR
:
487 *plibNewPosition
= This
->currentPosition
;
489 case STREAM_SEEK_END
:
490 *plibNewPosition
= This
->streamSize
;
493 return STG_E_INVALIDFUNCTION
;
497 * We don't support files with offsets of 64 bits.
499 assert(dlibMove
.HighPart
== 0);
502 * Check if we end-up before the beginning of the file. That should trigger an
505 if ( (dlibMove
.LowPart
<0) && (plibNewPosition
->LowPart
< (ULONG
)(-dlibMove
.LowPart
)) )
508 * I don't know what error to send there.
514 * Move the actual file pointer
515 * If the file pointer ends-up after the end of the stream, the next Write operation will
516 * make the file larger. This is how it is documented.
518 plibNewPosition
->LowPart
+= dlibMove
.LowPart
;
519 This
->currentPosition
= *plibNewPosition
;
525 * This method is part of the IStream interface.
527 * It will change the size of a stream.
529 * TODO: Switch from small blocks to big blocks and vice versa.
531 * See the documentation of IStream for more info.
533 HRESULT WINAPI
StgStreamImpl_SetSize(
535 ULARGE_INTEGER libNewSize
) /* [in] */
537 StgProperty curProperty
;
543 if (libNewSize
.HighPart
!= 0)
544 return STG_E_INVALIDFUNCTION
;
546 if (This
->streamSize
.LowPart
== libNewSize
.LowPart
)
550 * This will happen if we're creating a stream
552 if ((This
->smallBlockChain
== 0) && (This
->bigBlockChain
== 0))
554 if (libNewSize
.LowPart
< LIMIT_TO_USE_SMALL_BLOCK
)
556 This
->smallBlockChain
= SmallBlockChainStream_Construct(
557 This
->parentStorage
->ancestorStorage
,
558 This
->ownerProperty
);
562 This
->bigBlockChain
= BlockChainStream_Construct(
563 This
->parentStorage
->ancestorStorage
,
565 This
->ownerProperty
);
570 * Read this stream's property to see if it's small blocks or big blocks
572 Success
= Storage32Impl_ReadProperty(This
->parentStorage
->ancestorStorage
,
576 * Determine if we have to switch from small to big blocks or vice versa
579 if (curProperty
.size
.LowPart
< LIMIT_TO_USE_SMALL_BLOCK
)
581 if (libNewSize
.LowPart
>= LIMIT_TO_USE_SMALL_BLOCK
)
584 * Transform the small block chain into a big block chain
586 This
->bigBlockChain
= Storage32Impl_SmallBlocksToBigBlocks(
587 This
->parentStorage
->ancestorStorage
,
588 &This
->smallBlockChain
);
592 if (This
->smallBlockChain
!=0)
594 Success
= SmallBlockChainStream_SetSize(This
->smallBlockChain
, libNewSize
);
598 Success
= BlockChainStream_SetSize(This
->bigBlockChain
, libNewSize
);
602 * Write to the property the new information about this stream
604 Success
= Storage32Impl_ReadProperty(This
->parentStorage
->ancestorStorage
,
608 curProperty
.size
.HighPart
= libNewSize
.HighPart
;
609 curProperty
.size
.LowPart
= libNewSize
.LowPart
;
613 Storage32Impl_WriteProperty(This
->parentStorage
->ancestorStorage
,
618 This
->streamSize
= libNewSize
;
623 HRESULT WINAPI
StgStreamImpl_CopyTo(
625 IStream32
* pstm
, /* [unique][in] */
626 ULARGE_INTEGER cb
, /* [in] */
627 ULARGE_INTEGER
* pcbRead
, /* [out] */
628 ULARGE_INTEGER
* pcbWritten
) /* [out] */
634 * This method is part of the IStream interface.
636 * For streams contained in structured storages, this method
637 * does nothing. This is what the documentation tells us.
639 * See the documentation of IStream for more info.
641 HRESULT WINAPI
StgStreamImpl_Commit(
643 DWORD grfCommitFlags
) /* [in] */
649 * This method is part of the IStream interface.
651 * For streams contained in structured storages, this method
652 * does nothing. This is what the documentation tells us.
654 * See the documentation of IStream for more info.
656 HRESULT WINAPI
StgStreamImpl_Revert(
662 HRESULT WINAPI
StgStreamImpl_LockRegion(
664 ULARGE_INTEGER libOffset
, /* [in] */
665 ULARGE_INTEGER cb
, /* [in] */
666 DWORD dwLockType
) /* [in] */
671 HRESULT WINAPI
StgStreamImpl_UnlockRegion(
673 ULARGE_INTEGER libOffset
, /* [in] */
674 ULARGE_INTEGER cb
, /* [in] */
675 DWORD dwLockType
) /* [in] */
681 * This method is part of the IStream interface.
683 * This method returns information about the current
686 * See the documentation of IStream for more info.
688 HRESULT WINAPI
StgStreamImpl_Stat(
690 STATSTG
* pstatstg
, /* [out] */
691 DWORD grfStatFlag
) /* [in] */
693 StgProperty curProperty
;
694 BOOL32 readSucessful
;
697 * Read the information from the property.
699 readSucessful
= Storage32Impl_ReadProperty(This
->parentStorage
->ancestorStorage
,
705 StorageUtl_CopyPropertyToSTATSTG(pstatstg
,
715 HRESULT WINAPI
StgStreamImpl_Clone(
717 IStream32
** ppstm
) /* [out] */