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
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
33 #define NONAMELESSUNION
34 #define NONAMELESSSTRUCT
41 #include "wine/debug.h"
43 #include "storage32.h"
45 WINE_DEFAULT_DEBUG_CHANNEL(storage);
49 * This is the destructor of the StgStreamImpl class.
51 * This method will clean-up all the resources used-up by the given StgStreamImpl
52 * class. The pointer passed-in to this function will be freed and will not
55 static void StgStreamImpl_Destroy(StgStreamImpl* This)
57 TRACE("(%p)\n", This);
60 * Release the reference we are holding on the parent storage.
61 * IStorage_Release((IStorage*)This->parentStorage);
63 * No, don't do this. Some apps call IStorage_Release without
64 * calling IStream_Release first. If we grab a reference the
65 * file is not closed, and the app fails when it tries to
66 * reopen the file (Easy-PC, for example). Just inform the
67 * storage that we have closed the stream
70 if(This->parentStorage) {
72 StorageBaseImpl_RemoveStream(This->parentStorage, This);
76 This->parentStorage = 0;
79 * Make sure we clean-up the block chain stream objects that we were using.
81 if (This->bigBlockChain != 0)
83 BlockChainStream_Destroy(This->bigBlockChain);
84 This->bigBlockChain = 0;
87 if (This->smallBlockChain != 0)
89 SmallBlockChainStream_Destroy(This->smallBlockChain);
90 This->smallBlockChain = 0;
94 * Finally, free the memory used-up by the class.
96 HeapFree(GetProcessHeap(), 0, This);
100 * This implements the IUnknown method QueryInterface for this
103 static HRESULT WINAPI StgStreamImpl_QueryInterface(
105 REFIID riid, /* [in] */
106 void** ppvObject) /* [iid_is][out] */
108 StgStreamImpl* const This=(StgStreamImpl*)iface;
111 * Perform a sanity check on the parameters.
117 * Initialize the return parameter.
122 * Compare the riid with the interface IDs implemented by this object.
124 if (IsEqualGUID(&IID_IUnknown, riid)||
125 IsEqualGUID(&IID_IStream, riid))
127 *ppvObject = (IStream*)This;
131 * Check that we obtained an interface.
134 return E_NOINTERFACE;
137 * Query Interface always increases the reference count by one when it is
140 IStream_AddRef(iface);
146 * This implements the IUnknown method AddRef for this
149 static ULONG WINAPI StgStreamImpl_AddRef(
152 StgStreamImpl* const This=(StgStreamImpl*)iface;
153 return InterlockedIncrement(&This->ref);
157 * This implements the IUnknown method Release for this
160 static ULONG WINAPI StgStreamImpl_Release(
163 StgStreamImpl* const This=(StgStreamImpl*)iface;
167 ref = InterlockedDecrement(&This->ref);
170 * If the reference count goes down to 0, perform suicide.
174 StgStreamImpl_Destroy(This);
181 * This method will open the block chain pointed by the property
182 * that describes the stream.
183 * If the stream's size is null, no chain is opened.
185 static void StgStreamImpl_OpenBlockChain(
188 StgProperty curProperty;
192 * Make sure no old object is left over.
194 if (This->smallBlockChain != 0)
196 SmallBlockChainStream_Destroy(This->smallBlockChain);
197 This->smallBlockChain = 0;
200 if (This->bigBlockChain != 0)
202 BlockChainStream_Destroy(This->bigBlockChain);
203 This->bigBlockChain = 0;
207 * Read the information from the property.
209 readSucessful = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
215 This->streamSize = curProperty.size;
218 * This code supports only streams that are <32 bits in size.
220 assert(This->streamSize.u.HighPart == 0);
222 if(curProperty.startingBlock == BLOCK_END_OF_CHAIN)
224 assert( (This->streamSize.u.HighPart == 0) && (This->streamSize.u.LowPart == 0) );
228 if ( (This->streamSize.u.HighPart == 0) &&
229 (This->streamSize.u.LowPart < LIMIT_TO_USE_SMALL_BLOCK) )
231 This->smallBlockChain = SmallBlockChainStream_Construct(
232 This->parentStorage->ancestorStorage,
233 This->ownerProperty);
237 This->bigBlockChain = BlockChainStream_Construct(
238 This->parentStorage->ancestorStorage,
240 This->ownerProperty);
247 * This method is part of the ISequentialStream interface.
249 * It reads a block of information from the stream at the current
250 * position. It then moves the current position at the end of the
253 * See the documentation of ISequentialStream for more info.
255 static HRESULT WINAPI StgStreamImpl_Read(
257 void* pv, /* [length_is][size_is][out] */
259 ULONG* pcbRead) /* [out] */
261 StgStreamImpl* const This=(StgStreamImpl*)iface;
263 ULONG bytesReadBuffer;
264 ULONG bytesToReadFromBuffer;
267 TRACE("(%p, %p, %ld, %p)\n",
268 iface, pv, cb, pcbRead);
271 * If the caller is not interested in the number of bytes read,
272 * we use another buffer to avoid "if" statements in the code.
275 pcbRead = &bytesReadBuffer;
278 * Using the known size of the stream, calculate the number of bytes
279 * to read from the block chain
281 bytesToReadFromBuffer = min( This->streamSize.u.LowPart - This->currentPosition.u.LowPart, cb);
284 * Depending on the type of chain that was opened when the stream was constructed,
285 * we delegate the work to the method that reads the block chains.
287 if (This->smallBlockChain!=0)
289 res = SmallBlockChainStream_ReadAt(This->smallBlockChain,
290 This->currentPosition,
291 bytesToReadFromBuffer,
296 else if (This->bigBlockChain!=0)
298 BOOL success = BlockChainStream_ReadAt(This->bigBlockChain,
299 This->currentPosition,
300 bytesToReadFromBuffer,
306 res = STG_E_READFAULT;
311 * Small and big block chains are both NULL. This case will happen
312 * when a stream starts with BLOCK_END_OF_CHAIN and has size zero.
323 * We should always be able to read the proper amount of data from the
326 assert(bytesToReadFromBuffer == *pcbRead);
329 * Advance the pointer for the number of positions read.
331 This->currentPosition.u.LowPart += *pcbRead;
335 TRACE("<-- %08lx\n", res);
340 * This method is part of the ISequentialStream interface.
342 * It writes a block of information to the stream at the current
343 * position. It then moves the current position at the end of the
344 * written block. If the stream is too small to fit the block,
345 * the stream is grown to fit.
347 * See the documentation of ISequentialStream for more info.
349 static HRESULT WINAPI StgStreamImpl_Write(
351 const void* pv, /* [size_is][in] */
353 ULONG* pcbWritten) /* [out] */
355 StgStreamImpl* const This=(StgStreamImpl*)iface;
357 ULARGE_INTEGER newSize;
358 ULONG bytesWritten = 0;
360 TRACE("(%p, %p, %ld, %p)\n",
361 iface, pv, cb, pcbWritten);
364 * Do we have permission to write to this stream?
366 switch(STGM_ACCESS_MODE(This->grfMode))
372 return STG_E_ACCESSDENIED;
376 return STG_E_INVALIDPOINTER;
379 * If the caller is not interested in the number of bytes written,
380 * we use another buffer to avoid "if" statements in the code.
383 pcbWritten = &bytesWritten;
386 * Initialize the out parameter
396 newSize.u.HighPart = 0;
397 newSize.u.LowPart = This->currentPosition.u.LowPart + cb;
401 * Verify if we need to grow the stream
403 if (newSize.u.LowPart > This->streamSize.u.LowPart)
406 IStream_SetSize(iface, newSize);
410 * Depending on the type of chain that was opened when the stream was constructed,
411 * we delegate the work to the method that readwrites to the block chains.
413 if (This->smallBlockChain!=0)
415 SmallBlockChainStream_WriteAt(This->smallBlockChain,
416 This->currentPosition,
422 else if (This->bigBlockChain!=0)
424 BlockChainStream_WriteAt(This->bigBlockChain,
425 This->currentPosition,
434 * Advance the position pointer for the number of positions written.
436 This->currentPosition.u.LowPart += *pcbWritten;
442 * This method is part of the IStream interface.
444 * It will move the current stream pointer according to the parameters
447 * See the documentation of IStream for more info.
449 static HRESULT WINAPI StgStreamImpl_Seek(
451 LARGE_INTEGER dlibMove, /* [in] */
452 DWORD dwOrigin, /* [in] */
453 ULARGE_INTEGER* plibNewPosition) /* [out] */
455 StgStreamImpl* const This=(StgStreamImpl*)iface;
457 ULARGE_INTEGER newPosition;
459 TRACE("(%p, %ld, %ld, %p)\n",
460 iface, dlibMove.u.LowPart, dwOrigin, plibNewPosition);
463 * fail if the stream has no parent (as does windows)
466 if(!(This->parentStorage)) {
467 return STG_E_REVERTED;
471 * The caller is allowed to pass in NULL as the new position return value.
472 * If it happens, we assign it to a dynamic variable to avoid special cases
475 if (plibNewPosition == 0)
477 plibNewPosition = &newPosition;
481 * The file pointer is moved depending on the given "function"
486 case STREAM_SEEK_SET:
487 plibNewPosition->u.HighPart = 0;
488 plibNewPosition->u.LowPart = 0;
490 case STREAM_SEEK_CUR:
491 *plibNewPosition = This->currentPosition;
493 case STREAM_SEEK_END:
494 *plibNewPosition = This->streamSize;
497 return STG_E_INVALIDFUNCTION;
500 plibNewPosition->QuadPart = RtlLargeIntegerAdd( plibNewPosition->QuadPart, dlibMove.QuadPart );
503 * tell the caller what we calculated
505 This->currentPosition = *plibNewPosition;
511 * This method is part of the IStream interface.
513 * It will change the size of a stream.
515 * TODO: Switch from small blocks to big blocks and vice versa.
517 * See the documentation of IStream for more info.
519 static HRESULT WINAPI StgStreamImpl_SetSize(
521 ULARGE_INTEGER libNewSize) /* [in] */
523 StgStreamImpl* const This=(StgStreamImpl*)iface;
525 StgProperty curProperty;
528 TRACE("(%p, %ld)\n", iface, libNewSize.u.LowPart);
530 if(!This->parentStorage) {
531 return STG_E_REVERTED;
537 if (libNewSize.u.HighPart != 0)
538 return STG_E_INVALIDFUNCTION;
541 * Do we have permission?
543 if (!(This->grfMode & (STGM_WRITE | STGM_READWRITE)))
544 return STG_E_ACCESSDENIED;
546 if (This->streamSize.u.LowPart == libNewSize.u.LowPart)
550 * This will happen if we're creating a stream
552 if ((This->smallBlockChain == 0) && (This->bigBlockChain == 0))
554 if (libNewSize.u.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 = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
576 * Determine if we have to switch from small to big blocks or vice versa
578 if ( (This->smallBlockChain!=0) &&
579 (curProperty.size.u.LowPart < LIMIT_TO_USE_SMALL_BLOCK) )
581 if (libNewSize.u.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 the new information about this stream to the property
604 Success = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
608 curProperty.size.u.HighPart = libNewSize.u.HighPart;
609 curProperty.size.u.LowPart = libNewSize.u.LowPart;
613 StorageImpl_WriteProperty(This->parentStorage->ancestorStorage,
618 This->streamSize = libNewSize;
624 * This method is part of the IStream interface.
626 * It will copy the 'cb' Bytes to 'pstm' IStream.
628 * See the documentation of IStream for more info.
630 static HRESULT WINAPI StgStreamImpl_CopyTo(
632 IStream* pstm, /* [unique][in] */
633 ULARGE_INTEGER cb, /* [in] */
634 ULARGE_INTEGER* pcbRead, /* [out] */
635 ULARGE_INTEGER* pcbWritten) /* [out] */
637 StgStreamImpl* const This=(StgStreamImpl*)iface;
640 ULONG bytesRead, bytesWritten, copySize;
641 ULARGE_INTEGER totalBytesRead;
642 ULARGE_INTEGER totalBytesWritten;
644 TRACE("(%p, %p, %ld, %p, %p)\n",
645 iface, pstm, cb.u.LowPart, pcbRead, pcbWritten);
651 if(!This->parentStorage) {
652 return STG_E_REVERTED;
656 return STG_E_INVALIDPOINTER;
658 totalBytesRead.u.LowPart = totalBytesRead.u.HighPart = 0;
659 totalBytesWritten.u.LowPart = totalBytesWritten.u.HighPart = 0;
662 * use stack to store data temporarily
663 * there is surely a more performant way of doing it, for now this basic
664 * implementation will do the job
666 while ( cb.u.LowPart > 0 )
668 if ( cb.u.LowPart >= 128 )
671 copySize = cb.u.LowPart;
673 IStream_Read(iface, tmpBuffer, copySize, &bytesRead);
675 totalBytesRead.u.LowPart += bytesRead;
677 IStream_Write(pstm, tmpBuffer, bytesRead, &bytesWritten);
679 totalBytesWritten.u.LowPart += bytesWritten;
682 * Check that read & write operations were successful
684 if (bytesRead != bytesWritten)
686 hr = STG_E_MEDIUMFULL;
690 if (bytesRead!=copySize)
693 cb.u.LowPart -= bytesRead;
697 * Update number of bytes read and written
701 pcbRead->u.LowPart = totalBytesRead.u.LowPart;
702 pcbRead->u.HighPart = totalBytesRead.u.HighPart;
707 pcbWritten->u.LowPart = totalBytesWritten.u.LowPart;
708 pcbWritten->u.HighPart = totalBytesWritten.u.HighPart;
714 * This method is part of the IStream interface.
716 * For streams contained in structured storages, this method
717 * does nothing. This is what the documentation tells us.
719 * See the documentation of IStream for more info.
721 static HRESULT WINAPI StgStreamImpl_Commit(
723 DWORD grfCommitFlags) /* [in] */
725 StgStreamImpl* const This=(StgStreamImpl*)iface;
727 if(!This->parentStorage) {
728 return STG_E_REVERTED;
734 * This method is part of the IStream interface.
736 * For streams contained in structured storages, this method
737 * does nothing. This is what the documentation tells us.
739 * See the documentation of IStream for more info.
741 static HRESULT WINAPI StgStreamImpl_Revert(
747 static HRESULT WINAPI StgStreamImpl_LockRegion(
749 ULARGE_INTEGER libOffset, /* [in] */
750 ULARGE_INTEGER cb, /* [in] */
751 DWORD dwLockType) /* [in] */
753 StgStreamImpl* const This=(StgStreamImpl*)iface;
755 if(!This->parentStorage) {
756 return STG_E_REVERTED;
759 FIXME("not implemented!\n");
763 static HRESULT WINAPI StgStreamImpl_UnlockRegion(
765 ULARGE_INTEGER libOffset, /* [in] */
766 ULARGE_INTEGER cb, /* [in] */
767 DWORD dwLockType) /* [in] */
769 StgStreamImpl* const This=(StgStreamImpl*)iface;
771 if(!This->parentStorage) {
772 return STG_E_REVERTED;
775 FIXME("not implemented!\n");
780 * This method is part of the IStream interface.
782 * This method returns information about the current
785 * See the documentation of IStream for more info.
787 static HRESULT WINAPI StgStreamImpl_Stat(
789 STATSTG* pstatstg, /* [out] */
790 DWORD grfStatFlag) /* [in] */
792 StgStreamImpl* const This=(StgStreamImpl*)iface;
794 StgProperty curProperty;
798 * if stream has no parent, return STG_E_REVERTED
801 if(!This->parentStorage) {
802 return STG_E_REVERTED;
806 * Read the information from the property.
808 readSucessful = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
814 StorageUtl_CopyPropertyToSTATSTG(pstatstg,
818 pstatstg->grfMode = This->grfMode;
827 * This method is part of the IStream interface.
829 * This method returns a clone of the interface that allows for
830 * another seek pointer
832 * See the documentation of IStream for more info.
834 * I am not totally sure what I am doing here but I presume that this
835 * should be basically as simple as creating a new stream with the same
836 * parent etc and positioning its seek cursor.
838 static HRESULT WINAPI StgStreamImpl_Clone(
840 IStream** ppstm) /* [out] */
842 StgStreamImpl* const This=(StgStreamImpl*)iface;
844 StgStreamImpl* new_stream;
845 LARGE_INTEGER seek_pos;
851 if(!This->parentStorage) {
852 return STG_E_REVERTED;
856 return STG_E_INVALIDPOINTER;
858 new_stream = StgStreamImpl_Construct (This->parentStorage, This->grfMode, This->ownerProperty);
861 return STG_E_INSUFFICIENTMEMORY; /* Currently the only reason for new_stream=0 */
863 *ppstm = (IStream*) new_stream;
864 seek_pos.QuadPart = This->currentPosition.QuadPart;
866 hres=StgStreamImpl_Seek (*ppstm, seek_pos, STREAM_SEEK_SET, NULL);
868 assert (SUCCEEDED(hres));
874 * Virtual function table for the StgStreamImpl class.
876 static const IStreamVtbl StgStreamImpl_Vtbl =
878 StgStreamImpl_QueryInterface,
879 StgStreamImpl_AddRef,
880 StgStreamImpl_Release,
884 StgStreamImpl_SetSize,
885 StgStreamImpl_CopyTo,
886 StgStreamImpl_Commit,
887 StgStreamImpl_Revert,
888 StgStreamImpl_LockRegion,
889 StgStreamImpl_UnlockRegion,
894 /******************************************************************************
895 ** StgStreamImpl implementation
899 * This is the constructor for the StgStreamImpl class.
902 * parentStorage - Pointer to the storage that contains the stream to open
903 * ownerProperty - Index of the property that points to this stream.
905 StgStreamImpl* StgStreamImpl_Construct(
906 StorageBaseImpl* parentStorage,
910 StgStreamImpl* newStream;
912 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(StgStreamImpl));
917 * Set-up the virtual function table and reference count.
919 newStream->lpVtbl = &StgStreamImpl_Vtbl;
922 newStream->parentStorage = parentStorage;
925 * We want to nail-down the reference to the storage in case the
926 * stream out-lives the storage in the client application.
928 * -- IStorage_AddRef((IStorage*)newStream->parentStorage);
930 * No, don't do this. Some apps call IStorage_Release without
931 * calling IStream_Release first. If we grab a reference the
932 * file is not closed, and the app fails when it tries to
933 * reopen the file (Easy-PC, for example)
936 newStream->grfMode = grfMode;
937 newStream->ownerProperty = ownerProperty;
940 * Start the stream at the beginning.
942 newStream->currentPosition.u.HighPart = 0;
943 newStream->currentPosition.u.LowPart = 0;
946 * Initialize the rest of the data.
948 newStream->streamSize.u.HighPart = 0;
949 newStream->streamSize.u.LowPart = 0;
950 newStream->bigBlockChain = 0;
951 newStream->smallBlockChain = 0;
954 * Read the size from the property and determine if the blocks forming
955 * this stream are large or small.
957 StgStreamImpl_OpenBlockChain(newStream);