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"
25 * Virtual function table for the StgStreamImpl class.
27 static ICOM_VTABLE(IStream) StgStreamImpl_Vtbl =
29 StgStreamImpl_QueryInterface,
31 StgStreamImpl_Release,
35 StgStreamImpl_SetSize,
39 StgStreamImpl_LockRegion,
40 StgStreamImpl_UnlockRegion,
45 /******************************************************************************
46 ** StgStreamImpl implementation
50 * This is the constructor for the StgStreamImpl class.
53 * parentStorage - Pointer to the storage that contains the stream to open
54 * ownerProperty - Index of the property that points to this stream.
56 StgStreamImpl* StgStreamImpl_Construct(
57 StorageBaseImpl* parentStorage,
60 StgStreamImpl* newStream;
62 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(StgStreamImpl));
67 * Set-up the virtual function table and reference count.
69 newStream->lpvtbl = &StgStreamImpl_Vtbl;
73 * We want to nail-down the reference to the storage in case the
74 * stream out-lives the storage in the client application.
76 newStream->parentStorage = parentStorage;
77 IStorage_AddRef((IStorage*)newStream->parentStorage);
79 newStream->ownerProperty = ownerProperty;
82 * Start the stream at the begining.
84 newStream->currentPosition.HighPart = 0;
85 newStream->currentPosition.LowPart = 0;
88 * Initialize the rest of the data.
90 newStream->streamSize.HighPart = 0;
91 newStream->streamSize.LowPart = 0;
92 newStream->bigBlockChain = 0;
93 newStream->smallBlockChain = 0;
96 * Read the size from the property and determine if the blocks forming
97 * this stream are large or small.
99 StgStreamImpl_OpenBlockChain(newStream);
106 * This is the destructor of the StgStreamImpl class.
108 * This method will clean-up all the resources used-up by the given StgStreamImpl
109 * class. The pointer passed-in to this function will be freed and will not
112 void StgStreamImpl_Destroy(StgStreamImpl* This)
115 * Release the reference we are holding on the parent storage.
117 IStorage_Release((IStorage*)This->parentStorage);
118 This->parentStorage = 0;
121 * Make sure we clean-up the block chain stream objects that we were using.
123 if (This->bigBlockChain != 0)
125 BlockChainStream_Destroy(This->bigBlockChain);
126 This->bigBlockChain = 0;
129 if (This->smallBlockChain != 0)
131 SmallBlockChainStream_Destroy(This->smallBlockChain);
132 This->smallBlockChain = 0;
136 * Finally, free the memory used-up by the class.
138 HeapFree(GetProcessHeap(), 0, This);
142 * This implements the IUnknown method QueryInterface for this
145 HRESULT WINAPI StgStreamImpl_QueryInterface(
147 REFIID riid, /* [in] */
148 void** ppvObject) /* [iid_is][out] */
150 StgStreamImpl* const This=(StgStreamImpl*)iface;
153 * Perform a sanity check on the parameters.
159 * Initialize the return parameter.
164 * Compare the riid with the interface IDs implemented by this object.
166 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
168 *ppvObject = (IStream*)This;
170 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IStream)) == 0)
172 *ppvObject = (IStream*)This;
176 * Check that we obtained an interface.
179 return E_NOINTERFACE;
182 * Query Interface always increases the reference count by one when it is
185 StgStreamImpl_AddRef(iface);
191 * This implements the IUnknown method AddRef for this
194 ULONG WINAPI StgStreamImpl_AddRef(
197 StgStreamImpl* const This=(StgStreamImpl*)iface;
205 * This implements the IUnknown method Release for this
208 ULONG WINAPI StgStreamImpl_Release(
211 StgStreamImpl* const This=(StgStreamImpl*)iface;
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;
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 = StorageImpl_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 StgStreamImpl* const This=(StgStreamImpl*)iface;
313 ULONG bytesReadBuffer;
314 ULONG bytesToReadFromBuffer;
317 * If the caller is not interested in the nubmer of bytes read,
318 * we use another buffer to avoid "if" statements in the code.
321 pcbRead = &bytesReadBuffer;
324 * Using the known size of the stream, calculate the number of bytes
325 * to read from the block chain
327 bytesToReadFromBuffer = MIN( This->streamSize.LowPart - This->currentPosition.LowPart, cb);
330 * Depending on the type of chain that was opened when the stream was constructed,
331 * we delegate the work to the method that read the block chains.
333 if (This->smallBlockChain!=0)
335 SmallBlockChainStream_ReadAt(This->smallBlockChain,
336 This->currentPosition,
337 bytesToReadFromBuffer,
342 else if (This->bigBlockChain!=0)
344 BlockChainStream_ReadAt(This->bigBlockChain,
345 This->currentPosition,
346 bytesToReadFromBuffer,
354 * We should always be able to read the proper amount of data from the
357 assert(bytesToReadFromBuffer == *pcbRead);
360 * Advance the pointer for the number of positions read.
362 This->currentPosition.LowPart += *pcbRead;
365 * The function returns S_OK if the buffer was filled completely
366 * it returns S_FALSE if the end of the stream is reached before the
376 * This method is part of the ISequentialStream interface.
378 * It writes a block of information to the stream at the current
379 * position. It then moves the current position at the end of the
380 * written block. If the stream is too small to fit the block,
381 * the stream is grown to fit.
383 * See the documentation of ISequentialStream for more info.
385 HRESULT WINAPI StgStreamImpl_Write(
387 const void* pv, /* [size_is][in] */
389 ULONG* pcbWritten) /* [out] */
391 StgStreamImpl* const This=(StgStreamImpl*)iface;
393 ULARGE_INTEGER newSize;
394 ULONG bytesWritten = 0;
397 * If the caller is not interested in the number of bytes written,
398 * we use another buffer to avoid "if" statements in the code.
401 pcbWritten = &bytesWritten;
409 newSize.HighPart = 0;
410 newSize.LowPart = This->currentPosition.LowPart + cb;
414 * Verify if we need to grow the stream
416 if (newSize.LowPart > This->streamSize.LowPart)
419 StgStreamImpl_SetSize(iface, newSize);
423 * Depending on the type of chain that was opened when the stream was constructed,
424 * we delegate the work to the method that readwrites to the block chains.
426 if (This->smallBlockChain!=0)
428 SmallBlockChainStream_WriteAt(This->smallBlockChain,
429 This->currentPosition,
435 else if (This->bigBlockChain!=0)
437 BlockChainStream_WriteAt(This->bigBlockChain,
438 This->currentPosition,
447 * Advance the position pointer for the number of positions written.
449 This->currentPosition.LowPart += *pcbWritten;
455 * This method is part of the IStream interface.
457 * It will move the current stream pointer according to the parameters
460 * See the documentation of IStream for more info.
462 HRESULT WINAPI StgStreamImpl_Seek(
464 LARGE_INTEGER dlibMove, /* [in] */
465 DWORD dwOrigin, /* [in] */
466 ULARGE_INTEGER* plibNewPosition) /* [out] */
468 StgStreamImpl* const This=(StgStreamImpl*)iface;
470 ULARGE_INTEGER newPosition;
473 * The caller is allowed to pass in NULL as the new position return value.
474 * If it happens, we assign it to a dynamic variable to avoid special cases
477 if (plibNewPosition == 0)
479 plibNewPosition = &newPosition;
483 * The file pointer is moved depending on the given "function"
488 case STREAM_SEEK_SET:
489 plibNewPosition->HighPart = 0;
490 plibNewPosition->LowPart = 0;
492 case STREAM_SEEK_CUR:
493 *plibNewPosition = This->currentPosition;
495 case STREAM_SEEK_END:
496 *plibNewPosition = This->streamSize;
499 return STG_E_INVALIDFUNCTION;
503 * We don't support files with offsets of 64 bits.
505 assert(dlibMove.HighPart == 0);
508 * Check if we end-up before the beginning of the file. That should trigger an
511 if ( (dlibMove.LowPart<0) && (plibNewPosition->LowPart < (ULONG)(-dlibMove.LowPart)) )
514 * I don't know what error to send there.
520 * Move the actual file pointer
521 * If the file pointer ends-up after the end of the stream, the next Write operation will
522 * make the file larger. This is how it is documented.
524 plibNewPosition->LowPart += dlibMove.LowPart;
525 This->currentPosition = *plibNewPosition;
531 * This method is part of the IStream interface.
533 * It will change the size of a stream.
535 * TODO: Switch from small blocks to big blocks and vice versa.
537 * See the documentation of IStream for more info.
539 HRESULT WINAPI StgStreamImpl_SetSize(
541 ULARGE_INTEGER libNewSize) /* [in] */
543 StgStreamImpl* const This=(StgStreamImpl*)iface;
545 StgProperty curProperty;
551 if (libNewSize.HighPart != 0)
552 return STG_E_INVALIDFUNCTION;
554 if (This->streamSize.LowPart == libNewSize.LowPart)
558 * This will happen if we're creating a stream
560 if ((This->smallBlockChain == 0) && (This->bigBlockChain == 0))
562 if (libNewSize.LowPart < LIMIT_TO_USE_SMALL_BLOCK)
564 This->smallBlockChain = SmallBlockChainStream_Construct(
565 This->parentStorage->ancestorStorage,
566 This->ownerProperty);
570 This->bigBlockChain = BlockChainStream_Construct(
571 This->parentStorage->ancestorStorage,
573 This->ownerProperty);
578 * Read this stream's property to see if it's small blocks or big blocks
580 Success = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
584 * Determine if we have to switch from small to big blocks or vice versa
587 if (curProperty.size.LowPart < LIMIT_TO_USE_SMALL_BLOCK)
589 if (libNewSize.LowPart >= LIMIT_TO_USE_SMALL_BLOCK)
592 * Transform the small block chain into a big block chain
594 This->bigBlockChain = Storage32Impl_SmallBlocksToBigBlocks(
595 This->parentStorage->ancestorStorage,
596 &This->smallBlockChain);
600 if (This->smallBlockChain!=0)
602 Success = SmallBlockChainStream_SetSize(This->smallBlockChain, libNewSize);
606 Success = BlockChainStream_SetSize(This->bigBlockChain, libNewSize);
610 * Write to the property the new information about this stream
612 Success = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
616 curProperty.size.HighPart = libNewSize.HighPart;
617 curProperty.size.LowPart = libNewSize.LowPart;
621 StorageImpl_WriteProperty(This->parentStorage->ancestorStorage,
626 This->streamSize = libNewSize;
632 * This method is part of the IStream interface.
634 * It will copy the 'cb' Bytes to 'pstm' IStream.
636 * See the documentation of IStream for more info.
638 HRESULT WINAPI StgStreamImpl_CopyTo(
640 IStream* pstm, /* [unique][in] */
641 ULARGE_INTEGER cb, /* [in] */
642 ULARGE_INTEGER* pcbRead, /* [out] */
643 ULARGE_INTEGER* pcbWritten) /* [out] */
645 StgStreamImpl* const This=(StgStreamImpl*)iface;
648 ULONG bytesRead, bytesWritten, copySize;
649 ULARGE_INTEGER totalBytesRead;
650 ULARGE_INTEGER totalBytesWritten;
656 return STG_E_INVALIDPOINTER;
658 totalBytesRead.LowPart = totalBytesRead.HighPart = 0;
659 totalBytesWritten.LowPart = totalBytesWritten.HighPart = 0;
662 * use stack to store data temporarly
663 * there is surely more performant way of doing it, for now this basic
664 * implementation will do the job
666 while ( cb.LowPart > 0 )
668 if ( cb.LowPart >= 128 )
671 copySize = cb.LowPart;
673 StgStreamImpl_Read(iface, tmpBuffer, 128, &bytesRead);
675 totalBytesRead.LowPart += bytesRead;
677 StgStreamImpl_Write(pstm, tmpBuffer, bytesRead, &bytesWritten);
679 totalBytesWritten.LowPart += bytesWritten;
682 * Check that read & write operations were succesfull
684 if ( (bytesRead != copySize) && (bytesWritten != copySize) )
686 hr = STG_E_MEDIUMFULL;
690 cb.LowPart = cb.LowPart - copySize;
694 * Update number of bytes read and written
698 pcbRead->LowPart = totalBytesRead.LowPart;
699 pcbRead->HighPart = totalBytesRead.HighPart;
704 pcbWritten->LowPart = totalBytesWritten.LowPart;
705 pcbWritten->HighPart = totalBytesWritten.HighPart;
711 * This method is part of the IStream interface.
713 * For streams contained in structured storages, this method
714 * does nothing. This is what the documentation tells us.
716 * See the documentation of IStream for more info.
718 HRESULT WINAPI StgStreamImpl_Commit(
720 DWORD grfCommitFlags) /* [in] */
726 * This method is part of the IStream interface.
728 * For streams contained in structured storages, this method
729 * does nothing. This is what the documentation tells us.
731 * See the documentation of IStream for more info.
733 HRESULT WINAPI StgStreamImpl_Revert(
739 HRESULT WINAPI StgStreamImpl_LockRegion(
741 ULARGE_INTEGER libOffset, /* [in] */
742 ULARGE_INTEGER cb, /* [in] */
743 DWORD dwLockType) /* [in] */
745 FIXME(ole, "not implemented!\n");
749 HRESULT WINAPI StgStreamImpl_UnlockRegion(
751 ULARGE_INTEGER libOffset, /* [in] */
752 ULARGE_INTEGER cb, /* [in] */
753 DWORD dwLockType) /* [in] */
755 FIXME(ole, "not implemented!\n");
760 * This method is part of the IStream interface.
762 * This method returns information about the current
765 * See the documentation of IStream for more info.
767 HRESULT WINAPI StgStreamImpl_Stat(
769 STATSTG* pstatstg, /* [out] */
770 DWORD grfStatFlag) /* [in] */
772 StgStreamImpl* const This=(StgStreamImpl*)iface;
774 StgProperty curProperty;
778 * Read the information from the property.
780 readSucessful = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
786 StorageUtl_CopyPropertyToSTATSTG(pstatstg,
796 HRESULT WINAPI StgStreamImpl_Clone(
798 IStream** ppstm) /* [out] */
800 FIXME(ole, "not implemented!\n");