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
19 #include "wine/obj_storage.h"
21 #include "storage32.h"
23 DEFAULT_DEBUG_CHANNEL(storage
)
27 * Virtual function table for the StgStreamImpl class.
29 static ICOM_VTABLE(IStream
) StgStreamImpl_Vtbl
=
31 StgStreamImpl_QueryInterface
,
33 StgStreamImpl_Release
,
37 StgStreamImpl_SetSize
,
41 StgStreamImpl_LockRegion
,
42 StgStreamImpl_UnlockRegion
,
47 /******************************************************************************
48 ** StgStreamImpl implementation
52 * This is the constructor for the StgStreamImpl class.
55 * parentStorage - Pointer to the storage that contains the stream to open
56 * ownerProperty - Index of the property that points to this stream.
58 StgStreamImpl
* StgStreamImpl_Construct(
59 StorageBaseImpl
* parentStorage
,
62 StgStreamImpl
* newStream
;
64 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(StgStreamImpl
));
69 * Set-up the virtual function table and reference count.
71 newStream
->lpvtbl
= &StgStreamImpl_Vtbl
;
75 * We want to nail-down the reference to the storage in case the
76 * stream out-lives the storage in the client application.
78 newStream
->parentStorage
= parentStorage
;
79 IStorage_AddRef((IStorage
*)newStream
->parentStorage
);
81 newStream
->ownerProperty
= ownerProperty
;
84 * Start the stream at the begining.
86 newStream
->currentPosition
.HighPart
= 0;
87 newStream
->currentPosition
.LowPart
= 0;
90 * Initialize the rest of the data.
92 newStream
->streamSize
.HighPart
= 0;
93 newStream
->streamSize
.LowPart
= 0;
94 newStream
->bigBlockChain
= 0;
95 newStream
->smallBlockChain
= 0;
98 * Read the size from the property and determine if the blocks forming
99 * this stream are large or small.
101 StgStreamImpl_OpenBlockChain(newStream
);
108 * This is the destructor of the StgStreamImpl class.
110 * This method will clean-up all the resources used-up by the given StgStreamImpl
111 * class. The pointer passed-in to this function will be freed and will not
114 void StgStreamImpl_Destroy(StgStreamImpl
* This
)
116 TRACE(storage
, "(%p)\n", This
);
119 * Release the reference we are holding on the parent storage.
121 IStorage_Release((IStorage
*)This
->parentStorage
);
122 This
->parentStorage
= 0;
125 * Make sure we clean-up the block chain stream objects that we were using.
127 if (This
->bigBlockChain
!= 0)
129 BlockChainStream_Destroy(This
->bigBlockChain
);
130 This
->bigBlockChain
= 0;
133 if (This
->smallBlockChain
!= 0)
135 SmallBlockChainStream_Destroy(This
->smallBlockChain
);
136 This
->smallBlockChain
= 0;
140 * Finally, free the memory used-up by the class.
142 HeapFree(GetProcessHeap(), 0, This
);
146 * This implements the IUnknown method QueryInterface for this
149 HRESULT WINAPI
StgStreamImpl_QueryInterface(
151 REFIID riid
, /* [in] */
152 void** ppvObject
) /* [iid_is][out] */
154 StgStreamImpl
* const This
=(StgStreamImpl
*)iface
;
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
= (IStream
*)This
;
174 else if (memcmp(&IID_IStream
, riid
, sizeof(IID_IStream
)) == 0)
176 *ppvObject
= (IStream
*)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(iface
);
195 * This implements the IUnknown method AddRef for this
198 ULONG WINAPI
StgStreamImpl_AddRef(
201 StgStreamImpl
* const This
=(StgStreamImpl
*)iface
;
209 * This implements the IUnknown method Release for this
212 ULONG WINAPI
StgStreamImpl_Release(
215 StgStreamImpl
* const This
=(StgStreamImpl
*)iface
;
224 * If the reference count goes down to 0, perform suicide.
228 StgStreamImpl_Destroy(This
);
235 * This method will open the block chain pointed by the property
236 * that describes the stream.
237 * If the stream's size is null, no chain is opened.
239 void StgStreamImpl_OpenBlockChain(
242 StgProperty curProperty
;
246 * Make sure no old object is staying behind.
248 if (This
->smallBlockChain
!= 0)
250 SmallBlockChainStream_Destroy(This
->smallBlockChain
);
251 This
->smallBlockChain
= 0;
254 if (This
->bigBlockChain
!= 0)
256 BlockChainStream_Destroy(This
->bigBlockChain
);
257 This
->bigBlockChain
= 0;
261 * Read the information from the property.
263 readSucessful
= StorageImpl_ReadProperty(This
->parentStorage
->ancestorStorage
,
269 This
->streamSize
= curProperty
.size
;
272 * This code supports only streams that are <32 bits in size.
274 assert(This
->streamSize
.HighPart
== 0);
276 if(curProperty
.startingBlock
== BLOCK_END_OF_CHAIN
)
278 assert( (This
->streamSize
.HighPart
== 0) && (This
->streamSize
.LowPart
== 0) );
282 if ( (This
->streamSize
.HighPart
== 0) &&
283 (This
->streamSize
.LowPart
< LIMIT_TO_USE_SMALL_BLOCK
) )
285 This
->smallBlockChain
= SmallBlockChainStream_Construct(
286 This
->parentStorage
->ancestorStorage
,
287 This
->ownerProperty
);
291 This
->bigBlockChain
= BlockChainStream_Construct(
292 This
->parentStorage
->ancestorStorage
,
294 This
->ownerProperty
);
301 * This method is part of the ISequentialStream interface.
303 * If reads a block of information from the stream at the current
304 * position. It then moves the current position at the end of the
307 * See the documentation of ISequentialStream for more info.
309 HRESULT WINAPI
StgStreamImpl_Read(
311 void* pv
, /* [length_is][size_is][out] */
313 ULONG
* pcbRead
) /* [out] */
315 StgStreamImpl
* const This
=(StgStreamImpl
*)iface
;
317 ULONG bytesReadBuffer
;
318 ULONG bytesToReadFromBuffer
;
320 TRACE(storage
, "(%p, %p, %ld, %p)\n",
321 iface
, pv
, cb
, pcbRead
);
324 * If the caller is not interested in the nubmer of bytes read,
325 * we use another buffer to avoid "if" statements in the code.
328 pcbRead
= &bytesReadBuffer
;
331 * Using the known size of the stream, calculate the number of bytes
332 * to read from the block chain
334 bytesToReadFromBuffer
= MIN( This
->streamSize
.LowPart
- This
->currentPosition
.LowPart
, cb
);
337 * Depending on the type of chain that was opened when the stream was constructed,
338 * we delegate the work to the method that read the block chains.
340 if (This
->smallBlockChain
!=0)
342 SmallBlockChainStream_ReadAt(This
->smallBlockChain
,
343 This
->currentPosition
,
344 bytesToReadFromBuffer
,
349 else if (This
->bigBlockChain
!=0)
351 BlockChainStream_ReadAt(This
->bigBlockChain
,
352 This
->currentPosition
,
353 bytesToReadFromBuffer
,
361 * We should always be able to read the proper amount of data from the
364 assert(bytesToReadFromBuffer
== *pcbRead
);
367 * Advance the pointer for the number of positions read.
369 This
->currentPosition
.LowPart
+= *pcbRead
;
372 * The function returns S_OK if the buffer was filled completely
373 * it returns S_FALSE if the end of the stream is reached before the
383 * This method is part of the ISequentialStream interface.
385 * It writes a block of information to the stream at the current
386 * position. It then moves the current position at the end of the
387 * written block. If the stream is too small to fit the block,
388 * the stream is grown to fit.
390 * See the documentation of ISequentialStream for more info.
392 HRESULT WINAPI
StgStreamImpl_Write(
394 const void* pv
, /* [size_is][in] */
396 ULONG
* pcbWritten
) /* [out] */
398 StgStreamImpl
* const This
=(StgStreamImpl
*)iface
;
400 ULARGE_INTEGER newSize
;
401 ULONG bytesWritten
= 0;
403 TRACE(storage
, "(%p, %p, %ld, %p)\n",
404 iface
, pv
, cb
, pcbWritten
);
407 * If the caller is not interested in the number of bytes written,
408 * we use another buffer to avoid "if" statements in the code.
411 pcbWritten
= &bytesWritten
;
414 * Initialize the out parameter
424 newSize
.HighPart
= 0;
425 newSize
.LowPart
= This
->currentPosition
.LowPart
+ cb
;
429 * Verify if we need to grow the stream
431 if (newSize
.LowPart
> This
->streamSize
.LowPart
)
434 IStream_SetSize(iface
, newSize
);
438 * Depending on the type of chain that was opened when the stream was constructed,
439 * we delegate the work to the method that readwrites to the block chains.
441 if (This
->smallBlockChain
!=0)
443 SmallBlockChainStream_WriteAt(This
->smallBlockChain
,
444 This
->currentPosition
,
450 else if (This
->bigBlockChain
!=0)
452 BlockChainStream_WriteAt(This
->bigBlockChain
,
453 This
->currentPosition
,
462 * Advance the position pointer for the number of positions written.
464 This
->currentPosition
.LowPart
+= *pcbWritten
;
470 * This method is part of the IStream interface.
472 * It will move the current stream pointer according to the parameters
475 * See the documentation of IStream for more info.
477 HRESULT WINAPI
StgStreamImpl_Seek(
479 LARGE_INTEGER dlibMove
, /* [in] */
480 DWORD dwOrigin
, /* [in] */
481 ULARGE_INTEGER
* plibNewPosition
) /* [out] */
483 StgStreamImpl
* const This
=(StgStreamImpl
*)iface
;
485 ULARGE_INTEGER newPosition
;
487 TRACE(storage
, "(%p, %ld, %ld, %p)\n",
488 iface
, dlibMove
.LowPart
, dwOrigin
, plibNewPosition
);
491 * The caller is allowed to pass in NULL as the new position return value.
492 * If it happens, we assign it to a dynamic variable to avoid special cases
495 if (plibNewPosition
== 0)
497 plibNewPosition
= &newPosition
;
501 * The file pointer is moved depending on the given "function"
506 case STREAM_SEEK_SET
:
507 plibNewPosition
->HighPart
= 0;
508 plibNewPosition
->LowPart
= 0;
510 case STREAM_SEEK_CUR
:
511 *plibNewPosition
= This
->currentPosition
;
513 case STREAM_SEEK_END
:
514 *plibNewPosition
= This
->streamSize
;
517 return STG_E_INVALIDFUNCTION
;
521 * We don't support files with offsets of 64 bits.
523 assert(dlibMove
.HighPart
== 0);
526 * Check if we end-up before the beginning of the file. That should trigger an
529 if ( (dlibMove
.LowPart
<0) && (plibNewPosition
->LowPart
< (ULONG
)(-dlibMove
.LowPart
)) )
532 * I don't know what error to send there.
538 * Move the actual file pointer
539 * If the file pointer ends-up after the end of the stream, the next Write operation will
540 * make the file larger. This is how it is documented.
542 plibNewPosition
->LowPart
+= dlibMove
.LowPart
;
543 This
->currentPosition
= *plibNewPosition
;
549 * This method is part of the IStream interface.
551 * It will change the size of a stream.
553 * TODO: Switch from small blocks to big blocks and vice versa.
555 * See the documentation of IStream for more info.
557 HRESULT WINAPI
StgStreamImpl_SetSize(
559 ULARGE_INTEGER libNewSize
) /* [in] */
561 StgStreamImpl
* const This
=(StgStreamImpl
*)iface
;
563 StgProperty curProperty
;
566 TRACE(storage
, "(%p, %ld)\n", iface
, libNewSize
.LowPart
);
571 if (libNewSize
.HighPart
!= 0)
572 return STG_E_INVALIDFUNCTION
;
574 if (This
->streamSize
.LowPart
== libNewSize
.LowPart
)
578 * This will happen if we're creating a stream
580 if ((This
->smallBlockChain
== 0) && (This
->bigBlockChain
== 0))
582 if (libNewSize
.LowPart
< LIMIT_TO_USE_SMALL_BLOCK
)
584 This
->smallBlockChain
= SmallBlockChainStream_Construct(
585 This
->parentStorage
->ancestorStorage
,
586 This
->ownerProperty
);
590 This
->bigBlockChain
= BlockChainStream_Construct(
591 This
->parentStorage
->ancestorStorage
,
593 This
->ownerProperty
);
598 * Read this stream's property to see if it's small blocks or big blocks
600 Success
= StorageImpl_ReadProperty(This
->parentStorage
->ancestorStorage
,
604 * Determine if we have to switch from small to big blocks or vice versa
606 if ( (This
->smallBlockChain
!=0) &&
607 (curProperty
.size
.LowPart
< LIMIT_TO_USE_SMALL_BLOCK
) )
609 if (libNewSize
.LowPart
>= LIMIT_TO_USE_SMALL_BLOCK
)
612 * Transform the small block chain into a big block chain
614 This
->bigBlockChain
= Storage32Impl_SmallBlocksToBigBlocks(
615 This
->parentStorage
->ancestorStorage
,
616 &This
->smallBlockChain
);
620 if (This
->smallBlockChain
!=0)
622 Success
= SmallBlockChainStream_SetSize(This
->smallBlockChain
, libNewSize
);
626 Success
= BlockChainStream_SetSize(This
->bigBlockChain
, libNewSize
);
630 * Write to the property the new information about this stream
632 Success
= StorageImpl_ReadProperty(This
->parentStorage
->ancestorStorage
,
636 curProperty
.size
.HighPart
= libNewSize
.HighPart
;
637 curProperty
.size
.LowPart
= libNewSize
.LowPart
;
641 StorageImpl_WriteProperty(This
->parentStorage
->ancestorStorage
,
646 This
->streamSize
= libNewSize
;
652 * This method is part of the IStream interface.
654 * It will copy the 'cb' Bytes to 'pstm' IStream.
656 * See the documentation of IStream for more info.
658 HRESULT WINAPI
StgStreamImpl_CopyTo(
660 IStream
* pstm
, /* [unique][in] */
661 ULARGE_INTEGER cb
, /* [in] */
662 ULARGE_INTEGER
* pcbRead
, /* [out] */
663 ULARGE_INTEGER
* pcbWritten
) /* [out] */
667 ULONG bytesRead
, bytesWritten
, copySize
;
668 ULARGE_INTEGER totalBytesRead
;
669 ULARGE_INTEGER totalBytesWritten
;
671 TRACE(storage
, "(%p, %p, %ld, %p, %p)\n",
672 iface
, pstm
, cb
.LowPart
, pcbRead
, pcbWritten
);
678 return STG_E_INVALIDPOINTER
;
680 totalBytesRead
.LowPart
= totalBytesRead
.HighPart
= 0;
681 totalBytesWritten
.LowPart
= totalBytesWritten
.HighPart
= 0;
684 * use stack to store data temporarly
685 * there is surely more performant way of doing it, for now this basic
686 * implementation will do the job
688 while ( cb
.LowPart
> 0 )
690 if ( cb
.LowPart
>= 128 )
693 copySize
= cb
.LowPart
;
695 IStream_Read(iface
, tmpBuffer
, copySize
, &bytesRead
);
697 totalBytesRead
.LowPart
+= bytesRead
;
699 IStream_Write(pstm
, tmpBuffer
, bytesRead
, &bytesWritten
);
701 totalBytesWritten
.LowPart
+= bytesWritten
;
704 * Check that read & write operations were succesfull
706 if (bytesRead
!= bytesWritten
)
708 hr
= STG_E_MEDIUMFULL
;
712 if (bytesRead
!=copySize
)
715 cb
.LowPart
-= bytesRead
;
719 * Update number of bytes read and written
723 pcbRead
->LowPart
= totalBytesRead
.LowPart
;
724 pcbRead
->HighPart
= totalBytesRead
.HighPart
;
729 pcbWritten
->LowPart
= totalBytesWritten
.LowPart
;
730 pcbWritten
->HighPart
= totalBytesWritten
.HighPart
;
736 * This method is part of the IStream interface.
738 * For streams contained in structured storages, this method
739 * does nothing. This is what the documentation tells us.
741 * See the documentation of IStream for more info.
743 HRESULT WINAPI
StgStreamImpl_Commit(
745 DWORD grfCommitFlags
) /* [in] */
751 * This method is part of the IStream interface.
753 * For streams contained in structured storages, this method
754 * does nothing. This is what the documentation tells us.
756 * See the documentation of IStream for more info.
758 HRESULT WINAPI
StgStreamImpl_Revert(
764 HRESULT WINAPI
StgStreamImpl_LockRegion(
766 ULARGE_INTEGER libOffset
, /* [in] */
767 ULARGE_INTEGER cb
, /* [in] */
768 DWORD dwLockType
) /* [in] */
770 FIXME(storage
, "not implemented!\n");
774 HRESULT WINAPI
StgStreamImpl_UnlockRegion(
776 ULARGE_INTEGER libOffset
, /* [in] */
777 ULARGE_INTEGER cb
, /* [in] */
778 DWORD dwLockType
) /* [in] */
780 FIXME(storage
, "not implemented!\n");
785 * This method is part of the IStream interface.
787 * This method returns information about the current
790 * See the documentation of IStream for more info.
792 HRESULT WINAPI
StgStreamImpl_Stat(
794 STATSTG
* pstatstg
, /* [out] */
795 DWORD grfStatFlag
) /* [in] */
797 StgStreamImpl
* const This
=(StgStreamImpl
*)iface
;
799 StgProperty curProperty
;
803 * Read the information from the property.
805 readSucessful
= StorageImpl_ReadProperty(This
->parentStorage
->ancestorStorage
,
811 StorageUtl_CopyPropertyToSTATSTG(pstatstg
,
821 HRESULT WINAPI
StgStreamImpl_Clone(
823 IStream
** ppstm
) /* [out] */
825 FIXME(storage
, "not implemented!\n");