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
18 #include "wine/obj_storage.h"
20 #include "storage32.h"
24 * Virtual function table for the StgStreamImpl class.
26 static ICOM_VTABLE(IStream
) StgStreamImpl_Vtbl
=
28 StgStreamImpl_QueryInterface
,
30 StgStreamImpl_Release
,
34 StgStreamImpl_SetSize
,
38 StgStreamImpl_LockRegion
,
39 StgStreamImpl_UnlockRegion
,
44 /******************************************************************************
45 ** StgStreamImpl implementation
49 * This is the constructor for the StgStreamImpl class.
52 * parentStorage - Pointer to the storage that contains the stream to open
53 * ownerProperty - Index of the property that points to this stream.
55 StgStreamImpl
* StgStreamImpl_Construct(
56 StorageBaseImpl
* parentStorage
,
59 StgStreamImpl
* newStream
;
61 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(StgStreamImpl
));
66 * Set-up the virtual function table and reference count.
68 newStream
->lpvtbl
= &StgStreamImpl_Vtbl
;
72 * We want to nail-down the reference to the storage in case the
73 * stream out-lives the storage in the client application.
75 newStream
->parentStorage
= parentStorage
;
76 IStorage_AddRef((IStorage
*)newStream
->parentStorage
);
78 newStream
->ownerProperty
= ownerProperty
;
81 * Start the stream at the begining.
83 newStream
->currentPosition
.HighPart
= 0;
84 newStream
->currentPosition
.LowPart
= 0;
87 * Initialize the rest of the data.
89 newStream
->streamSize
.HighPart
= 0;
90 newStream
->streamSize
.LowPart
= 0;
91 newStream
->bigBlockChain
= 0;
92 newStream
->smallBlockChain
= 0;
95 * Read the size from the property and determine if the blocks forming
96 * this stream are large or small.
98 StgStreamImpl_OpenBlockChain(newStream
);
105 * This is the destructor of the StgStreamImpl class.
107 * This method will clean-up all the resources used-up by the given StgStreamImpl
108 * class. The pointer passed-in to this function will be freed and will not
111 void StgStreamImpl_Destroy(StgStreamImpl
* This
)
114 * Release the reference we are holding on the parent storage.
116 IStorage_Release((IStorage
*)This
->parentStorage
);
117 This
->parentStorage
= 0;
120 * Make sure we clean-up the block chain stream objects that we were using.
122 if (This
->bigBlockChain
!= 0)
124 BlockChainStream_Destroy(This
->bigBlockChain
);
125 This
->bigBlockChain
= 0;
128 if (This
->smallBlockChain
!= 0)
130 SmallBlockChainStream_Destroy(This
->smallBlockChain
);
131 This
->smallBlockChain
= 0;
135 * Finally, free the memory used-up by the class.
137 HeapFree(GetProcessHeap(), 0, This
);
141 * This implements the IUnknown method QueryInterface for this
144 HRESULT WINAPI
StgStreamImpl_QueryInterface(
146 REFIID riid
, /* [in] */
147 void** ppvObject
) /* [iid_is][out] */
149 StgStreamImpl
* const This
=(StgStreamImpl
*)iface
;
152 * Perform a sanity check on the parameters.
158 * Initialize the return parameter.
163 * Compare the riid with the interface IDs implemented by this object.
165 if (memcmp(&IID_IUnknown
, riid
, sizeof(IID_IUnknown
)) == 0)
167 *ppvObject
= (IStream
*)This
;
169 else if (memcmp(&IID_IStorage
, riid
, sizeof(IID_IStream
)) == 0)
171 *ppvObject
= (IStream
*)This
;
175 * Check that we obtained an interface.
178 return E_NOINTERFACE
;
181 * Query Interface always increases the reference count by one when it is
184 StgStreamImpl_AddRef(iface
);
190 * This implements the IUnknown method AddRef for this
193 ULONG WINAPI
StgStreamImpl_AddRef(
196 StgStreamImpl
* const This
=(StgStreamImpl
*)iface
;
204 * This implements the IUnknown method Release for this
207 ULONG WINAPI
StgStreamImpl_Release(
210 StgStreamImpl
* const This
=(StgStreamImpl
*)iface
;
219 * If the reference count goes down to 0, perform suicide.
223 StgStreamImpl_Destroy(This
);
230 * This method will open the block chain pointed by the property
231 * that describes the stream.
232 * If the stream's size is null, no chain is opened.
234 void StgStreamImpl_OpenBlockChain(
237 StgProperty curProperty
;
241 * Make sure no old object is staying behind.
243 if (This
->smallBlockChain
!= 0)
245 SmallBlockChainStream_Destroy(This
->smallBlockChain
);
246 This
->smallBlockChain
= 0;
249 if (This
->bigBlockChain
!= 0)
251 BlockChainStream_Destroy(This
->bigBlockChain
);
252 This
->bigBlockChain
= 0;
256 * Read the information from the property.
258 readSucessful
= StorageImpl_ReadProperty(This
->parentStorage
->ancestorStorage
,
264 This
->streamSize
= curProperty
.size
;
267 * This code supports only streams that are <32 bits in size.
269 assert(This
->streamSize
.HighPart
== 0);
271 if(curProperty
.startingBlock
== BLOCK_END_OF_CHAIN
)
273 assert( (This
->streamSize
.HighPart
== 0) && (This
->streamSize
.LowPart
== 0) );
277 if ( (This
->streamSize
.HighPart
== 0) &&
278 (This
->streamSize
.LowPart
< LIMIT_TO_USE_SMALL_BLOCK
) )
280 This
->smallBlockChain
= SmallBlockChainStream_Construct(
281 This
->parentStorage
->ancestorStorage
,
282 This
->ownerProperty
);
286 This
->bigBlockChain
= BlockChainStream_Construct(
287 This
->parentStorage
->ancestorStorage
,
289 This
->ownerProperty
);
296 * This method is part of the ISequentialStream interface.
298 * If reads a block of information from the stream at the current
299 * position. It then moves the current position at the end of the
302 * See the documentation of ISequentialStream for more info.
304 HRESULT WINAPI
StgStreamImpl_Read(
306 void* pv
, /* [length_is][size_is][out] */
308 ULONG
* pcbRead
) /* [out] */
310 StgStreamImpl
* const This
=(StgStreamImpl
*)iface
;
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 StgStreamImpl
* const This
=(StgStreamImpl
*)iface
;
392 ULARGE_INTEGER newSize
;
393 ULONG bytesWritten
= 0;
396 * If the caller is not interested in the number of bytes written,
397 * we use another buffer to avoid "if" statements in the code.
400 pcbWritten
= &bytesWritten
;
408 newSize
.HighPart
= 0;
409 newSize
.LowPart
= This
->currentPosition
.LowPart
+ cb
;
413 * Verify if we need to grow the stream
415 if (newSize
.LowPart
> This
->streamSize
.LowPart
)
418 StgStreamImpl_SetSize(iface
, newSize
);
422 * Depending on the type of chain that was opened when the stream was constructed,
423 * we delegate the work to the method that readwrites to the block chains.
425 if (This
->smallBlockChain
!=0)
427 SmallBlockChainStream_WriteAt(This
->smallBlockChain
,
428 This
->currentPosition
,
434 else if (This
->bigBlockChain
!=0)
436 BlockChainStream_WriteAt(This
->bigBlockChain
,
437 This
->currentPosition
,
446 * Advance the position pointer for the number of positions written.
448 This
->currentPosition
.LowPart
+= *pcbWritten
;
454 * This method is part of the IStream interface.
456 * It will move the current stream pointer according to the parameters
459 * See the documentation of IStream for more info.
461 HRESULT WINAPI
StgStreamImpl_Seek(
463 LARGE_INTEGER dlibMove
, /* [in] */
464 DWORD dwOrigin
, /* [in] */
465 ULARGE_INTEGER
* plibNewPosition
) /* [out] */
467 StgStreamImpl
* const This
=(StgStreamImpl
*)iface
;
469 ULARGE_INTEGER newPosition
;
472 * The caller is allowed to pass in NULL as the new position return value.
473 * If it happens, we assign it to a dynamic variable to avoid special cases
476 if (plibNewPosition
== 0)
478 plibNewPosition
= &newPosition
;
482 * The file pointer is moved depending on the given "function"
487 case STREAM_SEEK_SET
:
488 plibNewPosition
->HighPart
= 0;
489 plibNewPosition
->LowPart
= 0;
491 case STREAM_SEEK_CUR
:
492 *plibNewPosition
= This
->currentPosition
;
494 case STREAM_SEEK_END
:
495 *plibNewPosition
= This
->streamSize
;
498 return STG_E_INVALIDFUNCTION
;
502 * We don't support files with offsets of 64 bits.
504 assert(dlibMove
.HighPart
== 0);
507 * Check if we end-up before the beginning of the file. That should trigger an
510 if ( (dlibMove
.LowPart
<0) && (plibNewPosition
->LowPart
< (ULONG
)(-dlibMove
.LowPart
)) )
513 * I don't know what error to send there.
519 * Move the actual file pointer
520 * If the file pointer ends-up after the end of the stream, the next Write operation will
521 * make the file larger. This is how it is documented.
523 plibNewPosition
->LowPart
+= dlibMove
.LowPart
;
524 This
->currentPosition
= *plibNewPosition
;
530 * This method is part of the IStream interface.
532 * It will change the size of a stream.
534 * TODO: Switch from small blocks to big blocks and vice versa.
536 * See the documentation of IStream for more info.
538 HRESULT WINAPI
StgStreamImpl_SetSize(
540 ULARGE_INTEGER libNewSize
) /* [in] */
542 StgStreamImpl
* const This
=(StgStreamImpl
*)iface
;
544 StgProperty curProperty
;
550 if (libNewSize
.HighPart
!= 0)
551 return STG_E_INVALIDFUNCTION
;
553 if (This
->streamSize
.LowPart
== libNewSize
.LowPart
)
557 * This will happen if we're creating a stream
559 if ((This
->smallBlockChain
== 0) && (This
->bigBlockChain
== 0))
561 if (libNewSize
.LowPart
< LIMIT_TO_USE_SMALL_BLOCK
)
563 This
->smallBlockChain
= SmallBlockChainStream_Construct(
564 This
->parentStorage
->ancestorStorage
,
565 This
->ownerProperty
);
569 This
->bigBlockChain
= BlockChainStream_Construct(
570 This
->parentStorage
->ancestorStorage
,
572 This
->ownerProperty
);
577 * Read this stream's property to see if it's small blocks or big blocks
579 Success
= StorageImpl_ReadProperty(This
->parentStorage
->ancestorStorage
,
583 * Determine if we have to switch from small to big blocks or vice versa
586 if (curProperty
.size
.LowPart
< LIMIT_TO_USE_SMALL_BLOCK
)
588 if (libNewSize
.LowPart
>= LIMIT_TO_USE_SMALL_BLOCK
)
591 * Transform the small block chain into a big block chain
593 This
->bigBlockChain
= Storage32Impl_SmallBlocksToBigBlocks(
594 This
->parentStorage
->ancestorStorage
,
595 &This
->smallBlockChain
);
599 if (This
->smallBlockChain
!=0)
601 Success
= SmallBlockChainStream_SetSize(This
->smallBlockChain
, libNewSize
);
605 Success
= BlockChainStream_SetSize(This
->bigBlockChain
, libNewSize
);
609 * Write to the property the new information about this stream
611 Success
= StorageImpl_ReadProperty(This
->parentStorage
->ancestorStorage
,
615 curProperty
.size
.HighPart
= libNewSize
.HighPart
;
616 curProperty
.size
.LowPart
= libNewSize
.LowPart
;
620 StorageImpl_WriteProperty(This
->parentStorage
->ancestorStorage
,
625 This
->streamSize
= libNewSize
;
630 HRESULT WINAPI
StgStreamImpl_CopyTo(
632 IStream
* pstm
, /* [unique][in] */
633 ULARGE_INTEGER cb
, /* [in] */
634 ULARGE_INTEGER
* pcbRead
, /* [out] */
635 ULARGE_INTEGER
* pcbWritten
) /* [out] */
641 * This method is part of the IStream interface.
643 * For streams contained in structured storages, this method
644 * does nothing. This is what the documentation tells us.
646 * See the documentation of IStream for more info.
648 HRESULT WINAPI
StgStreamImpl_Commit(
650 DWORD grfCommitFlags
) /* [in] */
656 * This method is part of the IStream interface.
658 * For streams contained in structured storages, this method
659 * does nothing. This is what the documentation tells us.
661 * See the documentation of IStream for more info.
663 HRESULT WINAPI
StgStreamImpl_Revert(
669 HRESULT WINAPI
StgStreamImpl_LockRegion(
671 ULARGE_INTEGER libOffset
, /* [in] */
672 ULARGE_INTEGER cb
, /* [in] */
673 DWORD dwLockType
) /* [in] */
678 HRESULT WINAPI
StgStreamImpl_UnlockRegion(
680 ULARGE_INTEGER libOffset
, /* [in] */
681 ULARGE_INTEGER cb
, /* [in] */
682 DWORD dwLockType
) /* [in] */
688 * This method is part of the IStream interface.
690 * This method returns information about the current
693 * See the documentation of IStream for more info.
695 HRESULT WINAPI
StgStreamImpl_Stat(
697 STATSTG
* pstatstg
, /* [out] */
698 DWORD grfStatFlag
) /* [in] */
700 StgStreamImpl
* const This
=(StgStreamImpl
*)iface
;
702 StgProperty curProperty
;
706 * Read the information from the property.
708 readSucessful
= StorageImpl_ReadProperty(This
->parentStorage
->ancestorStorage
,
714 StorageUtl_CopyPropertyToSTATSTG(pstatstg
,
724 HRESULT WINAPI
StgStreamImpl_Clone(
726 IStream
** ppstm
) /* [out] */