source: trunk/src/ole32/stg_stream.cpp@ 1036

Last change on this file since 1036 was 1033, checked in by davidr, 26 years ago

Ported remaining files pertaining to OLE32 from WINE

File size: 20.0 KB
Line 
1/* $Id: stg_stream.cpp,v 1.1 1999-09-24 21:49:44 davidr Exp $ */
2/*
3 * Compound Storage (32 bit version)
4 * Stream implementation
5 *
6 * 20/9/99
7 *
8 * Copyright 1999 David J. Raison
9 *
10 * Direct port of Wine Implementation
11 *
12 * This file contains the implementation of the stream interface
13 * for streams contained in a compound storage.
14 *
15 * TODO:
16 * - Support for a transacted mode
17 *
18 * Copyright 1999 Francis Beaudet
19 * Copyright 1999 Thuy Nguyen
20 */
21
22#include "ole32.h"
23#include "heapstring.h"
24#include "debugtools.h"
25#include "storage.h"
26
27#include <assert.h>
28
29DEFAULT_DEBUG_CHANNEL(storage)
30
31
32/*
33 * Virtual function table for the StgStreamImpl class.
34 */
35static ICOM_VTABLE(IStream) StgStreamImpl_Vtbl =
36{
37 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
38 StgStreamImpl_QueryInterface,
39 StgStreamImpl_AddRef,
40 StgStreamImpl_Release,
41 StgStreamImpl_Read,
42 StgStreamImpl_Write,
43 StgStreamImpl_Seek,
44 StgStreamImpl_SetSize,
45 StgStreamImpl_CopyTo,
46 StgStreamImpl_Commit,
47 StgStreamImpl_Revert,
48 StgStreamImpl_LockRegion,
49 StgStreamImpl_UnlockRegion,
50 StgStreamImpl_Stat,
51 StgStreamImpl_Clone
52};
53
54/******************************************************************************
55** StgStreamImpl implementation
56*/
57
58/***
59 * This is the constructor for the StgStreamImpl class.
60 *
61 * Params:
62 * parentStorage - Pointer to the storage that contains the stream to open
63 * ownerProperty - Index of the property that points to this stream.
64 */
65StgStreamImpl* StgStreamImpl_Construct(
66 StorageBaseImpl* parentStorage,
67 ULONG ownerProperty)
68{
69 StgStreamImpl* newStream;
70
71 newStream = (StgStreamImpl *)HeapAlloc(GetProcessHeap(), 0, sizeof(StgStreamImpl));
72
73 if (newStream!=0)
74 {
75 /*
76 * Set-up the virtual function table and reference count.
77 */
78 newStream->lpvtbl = &StgStreamImpl_Vtbl;
79 newStream->ref = 0;
80
81 /*
82 * We want to nail-down the reference to the storage in case the
83 * stream out-lives the storage in the client application.
84 */
85 newStream->parentStorage = parentStorage;
86 IStorage_AddRef((IStorage*)newStream->parentStorage);
87
88 newStream->ownerProperty = ownerProperty;
89
90 /*
91 * Start the stream at the begining.
92 */
93 newStream->currentPosition.HighPart = 0;
94 newStream->currentPosition.LowPart = 0;
95
96 /*
97 * Initialize the rest of the data.
98 */
99 newStream->streamSize.HighPart = 0;
100 newStream->streamSize.LowPart = 0;
101 newStream->bigBlockChain = 0;
102 newStream->smallBlockChain = 0;
103
104 /*
105 * Read the size from the property and determine if the blocks forming
106 * this stream are large or small.
107 */
108 StgStreamImpl_OpenBlockChain(newStream);
109 }
110
111 return newStream;
112}
113
114/***
115 * This is the destructor of the StgStreamImpl class.
116 *
117 * This method will clean-up all the resources used-up by the given StgStreamImpl
118 * class. The pointer passed-in to this function will be freed and will not
119 * be valid anymore.
120 */
121void StgStreamImpl_Destroy(StgStreamImpl* This)
122{
123 TRACE("(%p)\n", This);
124
125 /*
126 * Release the reference we are holding on the parent storage.
127 */
128 IStorage_Release((IStorage*)This->parentStorage);
129 This->parentStorage = 0;
130
131 /*
132 * Make sure we clean-up the block chain stream objects that we were using.
133 */
134 if (This->bigBlockChain != 0)
135 {
136 BlockChainStream_Destroy(This->bigBlockChain);
137 This->bigBlockChain = 0;
138 }
139
140 if (This->smallBlockChain != 0)
141 {
142 SmallBlockChainStream_Destroy(This->smallBlockChain);
143 This->smallBlockChain = 0;
144 }
145
146 /*
147 * Finally, free the memory used-up by the class.
148 */
149 HeapFree(GetProcessHeap(), 0, This);
150}
151
152/***
153 * This implements the IUnknown method QueryInterface for this
154 * class
155 */
156HRESULT WINAPI StgStreamImpl_QueryInterface(
157 IStream* iface,
158 REFIID riid, /* [in] */
159 void** ppvObject) /* [iid_is][out] */
160{
161 StgStreamImpl* const This=(StgStreamImpl*)iface;
162
163 /*
164 * Perform a sanity check on the parameters.
165 */
166 if (ppvObject==0)
167 return E_INVALIDARG;
168
169 /*
170 * Initialize the return parameter.
171 */
172 *ppvObject = 0;
173
174 /*
175 * Compare the riid with the interface IDs implemented by this object.
176 */
177 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
178 {
179 *ppvObject = (IStream*)This;
180 }
181 else if (memcmp(&IID_IStream, riid, sizeof(IID_IStream)) == 0)
182 {
183 *ppvObject = (IStream*)This;
184 }
185
186 /*
187 * Check that we obtained an interface.
188 */
189 if ((*ppvObject)==0)
190 return E_NOINTERFACE;
191
192 /*
193 * Query Interface always increases the reference count by one when it is
194 * successful
195 */
196 StgStreamImpl_AddRef(iface);
197
198 return S_OK;;
199}
200
201/***
202 * This implements the IUnknown method AddRef for this
203 * class
204 */
205ULONG WINAPI StgStreamImpl_AddRef(
206 IStream* iface)
207{
208 StgStreamImpl* const This=(StgStreamImpl*)iface;
209
210 This->ref++;
211
212 return This->ref;
213}
214
215/***
216 * This implements the IUnknown method Release for this
217 * class
218 */
219ULONG WINAPI StgStreamImpl_Release(
220 IStream* iface)
221{
222 StgStreamImpl* const This=(StgStreamImpl*)iface;
223
224 ULONG newRef;
225
226 This->ref--;
227
228 newRef = This->ref;
229
230 /*
231 * If the reference count goes down to 0, perform suicide.
232 */
233 if (newRef==0)
234 {
235 StgStreamImpl_Destroy(This);
236 }
237
238 return newRef;
239}
240
241/***
242 * This method will open the block chain pointed by the property
243 * that describes the stream.
244 * If the stream's size is null, no chain is opened.
245 */
246void StgStreamImpl_OpenBlockChain(
247 StgStreamImpl* This)
248{
249 StgProperty curProperty;
250 BOOL readSucessful;
251
252 /*
253 * Make sure no old object is staying behind.
254 */
255 if (This->smallBlockChain != 0)
256 {
257 SmallBlockChainStream_Destroy(This->smallBlockChain);
258 This->smallBlockChain = 0;
259 }
260
261 if (This->bigBlockChain != 0)
262 {
263 BlockChainStream_Destroy(This->bigBlockChain);
264 This->bigBlockChain = 0;
265 }
266
267 /*
268 * Read the information from the property.
269 */
270 readSucessful = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
271 This->ownerProperty,
272 &curProperty);
273
274 if (readSucessful)
275 {
276 This->streamSize = curProperty.size;
277
278 /*
279 * This code supports only streams that are <32 bits in size.
280 */
281 assert(This->streamSize.HighPart == 0);
282
283 if(curProperty.startingBlock == BLOCK_END_OF_CHAIN)
284 {
285 assert( (This->streamSize.HighPart == 0) && (This->streamSize.LowPart == 0) );
286 }
287 else
288 {
289 if ( (This->streamSize.HighPart == 0) &&
290 (This->streamSize.LowPart < LIMIT_TO_USE_SMALL_BLOCK) )
291 {
292 This->smallBlockChain = SmallBlockChainStream_Construct(
293 This->parentStorage->ancestorStorage,
294 This->ownerProperty);
295 }
296 else
297 {
298 This->bigBlockChain = BlockChainStream_Construct(
299 This->parentStorage->ancestorStorage,
300 NULL,
301 This->ownerProperty);
302 }
303 }
304 }
305}
306
307/***
308 * This method is part of the ISequentialStream interface.
309 *
310 * If reads a block of information from the stream at the current
311 * position. It then moves the current position at the end of the
312 * read block
313 *
314 * See the documentation of ISequentialStream for more info.
315 */
316HRESULT WINAPI StgStreamImpl_Read(
317 IStream* iface,
318 void* pv, /* [length_is][size_is][out] */
319 ULONG cb, /* [in] */
320 ULONG* pcbRead) /* [out] */
321{
322 StgStreamImpl* const This=(StgStreamImpl*)iface;
323
324 ULONG bytesReadBuffer;
325 ULONG bytesToReadFromBuffer;
326
327 TRACE("(%p, %p, %ld, %p)\n",
328 iface, pv, cb, pcbRead);
329
330 /*
331 * If the caller is not interested in the nubmer of bytes read,
332 * we use another buffer to avoid "if" statements in the code.
333 */
334 if (pcbRead==0)
335 pcbRead = &bytesReadBuffer;
336
337 /*
338 * Using the known size of the stream, calculate the number of bytes
339 * to read from the block chain
340 */
341 bytesToReadFromBuffer = MIN( This->streamSize.LowPart - This->currentPosition.LowPart, cb);
342
343 /*
344 * Depending on the type of chain that was opened when the stream was constructed,
345 * we delegate the work to the method that read the block chains.
346 */
347 if (This->smallBlockChain!=0)
348 {
349 SmallBlockChainStream_ReadAt(This->smallBlockChain,
350 This->currentPosition,
351 bytesToReadFromBuffer,
352 pv,
353 pcbRead);
354
355 }
356 else if (This->bigBlockChain!=0)
357 {
358 BlockChainStream_ReadAt(This->bigBlockChain,
359 This->currentPosition,
360 bytesToReadFromBuffer,
361 pv,
362 pcbRead);
363 }
364 else
365 {
366 /*
367 * Small and big block chains are both NULL. This case will happen
368 * when a stream starts with BLOCK_END_OF_CHAIN and has size zero.
369 */
370
371 *pcbRead = 0;
372 return S_OK;
373 }
374
375 /*
376 * We should always be able to read the proper amount of data from the
377 * chain.
378 */
379 assert(bytesToReadFromBuffer == *pcbRead);
380
381 /*
382 * Advance the pointer for the number of positions read.
383 */
384 This->currentPosition.LowPart += *pcbRead;
385
386 /*
387 * The function returns S_OK if the buffer was filled completely
388 * it returns S_FALSE if the end of the stream is reached before the
389 * buffer is filled
390 */
391 if(*pcbRead == cb)
392 return S_OK;
393
394 return S_FALSE;
395}
396
397/***
398 * This method is part of the ISequentialStream interface.
399 *
400 * It writes a block of information to the stream at the current
401 * position. It then moves the current position at the end of the
402 * written block. If the stream is too small to fit the block,
403 * the stream is grown to fit.
404 *
405 * See the documentation of ISequentialStream for more info.
406 */
407HRESULT WINAPI StgStreamImpl_Write(
408 IStream* iface,
409 const void* pv, /* [size_is][in] */
410 ULONG cb, /* [in] */
411 ULONG* pcbWritten) /* [out] */
412{
413 StgStreamImpl* const This=(StgStreamImpl*)iface;
414
415 ULARGE_INTEGER newSize;
416 ULONG bytesWritten = 0;
417
418 TRACE("(%p, %p, %ld, %p)\n",
419 iface, pv, cb, pcbWritten);
420
421 /*
422 * If the caller is not interested in the number of bytes written,
423 * we use another buffer to avoid "if" statements in the code.
424 */
425 if (pcbWritten == 0)
426 pcbWritten = &bytesWritten;
427
428 /*
429 * Initialize the out parameter
430 */
431 *pcbWritten = 0;
432
433 if (cb == 0)
434 {
435 return S_OK;
436 }
437 else
438 {
439 newSize.HighPart = 0;
440 newSize.LowPart = This->currentPosition.LowPart + cb;
441 }
442
443 /*
444 * Verify if we need to grow the stream
445 */
446 if (newSize.LowPart > This->streamSize.LowPart)
447 {
448 /* grow stream */
449 IStream_SetSize(iface, newSize);
450 }
451
452 /*
453 * Depending on the type of chain that was opened when the stream was constructed,
454 * we delegate the work to the method that readwrites to the block chains.
455 */
456 if (This->smallBlockChain!=0)
457 {
458 SmallBlockChainStream_WriteAt(This->smallBlockChain,
459 This->currentPosition,
460 cb,
461 pv,
462 pcbWritten);
463
464 }
465 else if (This->bigBlockChain!=0)
466 {
467 BlockChainStream_WriteAt(This->bigBlockChain,
468 This->currentPosition,
469 cb,
470 pv,
471 pcbWritten);
472 }
473 else
474 assert(FALSE);
475
476 /*
477 * Advance the position pointer for the number of positions written.
478 */
479 This->currentPosition.LowPart += *pcbWritten;
480
481 return S_OK;
482}
483
484/***
485 * This method is part of the IStream interface.
486 *
487 * It will move the current stream pointer according to the parameters
488 * given.
489 *
490 * See the documentation of IStream for more info.
491 */
492HRESULT WINAPI StgStreamImpl_Seek(
493 IStream* iface,
494 LARGE_INTEGER dlibMove, /* [in] */
495 DWORD dwOrigin, /* [in] */
496 ULARGE_INTEGER* plibNewPosition) /* [out] */
497{
498 StgStreamImpl* const This=(StgStreamImpl*)iface;
499
500 ULARGE_INTEGER newPosition;
501
502 TRACE("(%p, %ld, %ld, %p)\n",
503 iface, dlibMove.LowPart, dwOrigin, plibNewPosition);
504
505 /*
506 * The caller is allowed to pass in NULL as the new position return value.
507 * If it happens, we assign it to a dynamic variable to avoid special cases
508 * in the code below.
509 */
510 if (plibNewPosition == 0)
511 {
512 plibNewPosition = &newPosition;
513 }
514
515 /*
516 * The file pointer is moved depending on the given "function"
517 * parameter.
518 */
519 switch (dwOrigin)
520 {
521 case STREAM_SEEK_SET:
522 plibNewPosition->HighPart = 0;
523 plibNewPosition->LowPart = 0;
524 break;
525 case STREAM_SEEK_CUR:
526 *plibNewPosition = This->currentPosition;
527 break;
528 case STREAM_SEEK_END:
529 *plibNewPosition = This->streamSize;
530 break;
531 default:
532 return STG_E_INVALIDFUNCTION;
533 }
534
535 /*
536 * We don't support files with offsets of 64 bits.
537 */
538 assert(dlibMove.HighPart == 0);
539
540 /*
541 * Check if we end-up before the beginning of the file. That should trigger an
542 * error.
543 */
544 if ( (dlibMove.LowPart<0) && (plibNewPosition->LowPart < (ULONG)(-dlibMove.LowPart)) )
545 {
546 /*
547 * I don't know what error to send there.
548 */
549 return E_FAIL;
550 }
551
552 /*
553 * Move the actual file pointer
554 * If the file pointer ends-up after the end of the stream, the next Write operation will
555 * make the file larger. This is how it is documented.
556 */
557 plibNewPosition->LowPart += dlibMove.LowPart;
558 This->currentPosition = *plibNewPosition;
559
560 return S_OK;
561}
562
563/***
564 * This method is part of the IStream interface.
565 *
566 * It will change the size of a stream.
567 *
568 * TODO: Switch from small blocks to big blocks and vice versa.
569 *
570 * See the documentation of IStream for more info.
571 */
572HRESULT WINAPI StgStreamImpl_SetSize(
573 IStream* iface,
574 ULARGE_INTEGER libNewSize) /* [in] */
575{
576 StgStreamImpl* const This=(StgStreamImpl*)iface;
577
578 StgProperty curProperty;
579 BOOL Success;
580
581 TRACE("(%p, %ld)\n", iface, libNewSize.LowPart);
582
583 /*
584 * As documented.
585 */
586 if (libNewSize.HighPart != 0)
587 return STG_E_INVALIDFUNCTION;
588
589 if (This->streamSize.LowPart == libNewSize.LowPart)
590 return S_OK;
591
592 /*
593 * This will happen if we're creating a stream
594 */
595 if ((This->smallBlockChain == 0) && (This->bigBlockChain == 0))
596 {
597 if (libNewSize.LowPart < LIMIT_TO_USE_SMALL_BLOCK)
598 {
599 This->smallBlockChain = SmallBlockChainStream_Construct(
600 This->parentStorage->ancestorStorage,
601 This->ownerProperty);
602 }
603 else
604 {
605 This->bigBlockChain = BlockChainStream_Construct(
606 This->parentStorage->ancestorStorage,
607 NULL,
608 This->ownerProperty);
609 }
610 }
611
612 /*
613 * Read this stream's property to see if it's small blocks or big blocks
614 */
615 Success = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
616 This->ownerProperty,
617 &curProperty);
618 /*
619 * Determine if we have to switch from small to big blocks or vice versa
620 */
621 if ( (This->smallBlockChain!=0) &&
622 (curProperty.size.LowPart < LIMIT_TO_USE_SMALL_BLOCK) )
623 {
624 if (libNewSize.LowPart >= LIMIT_TO_USE_SMALL_BLOCK)
625 {
626 /*
627 * Transform the small block chain into a big block chain
628 */
629 This->bigBlockChain = Storage32Impl_SmallBlocksToBigBlocks(
630 This->parentStorage->ancestorStorage,
631 &This->smallBlockChain);
632 }
633 }
634
635 if (This->smallBlockChain!=0)
636 {
637 Success = SmallBlockChainStream_SetSize(This->smallBlockChain, libNewSize);
638 }
639 else
640 {
641 Success = BlockChainStream_SetSize(This->bigBlockChain, libNewSize);
642 }
643
644 /*
645 * Write to the property the new information about this stream
646 */
647 Success = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
648 This->ownerProperty,
649 &curProperty);
650
651 curProperty.size.HighPart = libNewSize.HighPart;
652 curProperty.size.LowPart = libNewSize.LowPart;
653
654 if (Success)
655 {
656 StorageImpl_WriteProperty(This->parentStorage->ancestorStorage,
657 This->ownerProperty,
658 &curProperty);
659 }
660
661 This->streamSize = libNewSize;
662
663 return S_OK;
664}
665
666/***
667 * This method is part of the IStream interface.
668 *
669 * It will copy the 'cb' Bytes to 'pstm' IStream.
670 *
671 * See the documentation of IStream for more info.
672 */
673HRESULT WINAPI StgStreamImpl_CopyTo(
674 IStream* iface,
675 IStream* pstm, /* [unique][in] */
676 ULARGE_INTEGER cb, /* [in] */
677 ULARGE_INTEGER* pcbRead, /* [out] */
678 ULARGE_INTEGER* pcbWritten) /* [out] */
679{
680 HRESULT hr = S_OK;
681 BYTE tmpBuffer[128];
682 ULONG bytesRead, bytesWritten, copySize;
683 ULARGE_INTEGER totalBytesRead;
684 ULARGE_INTEGER totalBytesWritten;
685
686 TRACE("(%p, %p, %ld, %p, %p)\n",
687 iface, pstm, cb.LowPart, pcbRead, pcbWritten);
688
689 /*
690 * Sanity check
691 */
692 if ( pstm == 0 )
693 return STG_E_INVALIDPOINTER;
694
695 totalBytesRead.LowPart = totalBytesRead.HighPart = 0;
696 totalBytesWritten.LowPart = totalBytesWritten.HighPart = 0;
697
698 /*
699 * use stack to store data temporarly
700 * there is surely more performant way of doing it, for now this basic
701 * implementation will do the job
702 */
703 while ( cb.LowPart > 0 )
704 {
705 if ( cb.LowPart >= 128 )
706 copySize = 128;
707 else
708 copySize = cb.LowPart;
709
710 IStream_Read(iface, tmpBuffer, copySize, &bytesRead);
711
712 totalBytesRead.LowPart += bytesRead;
713
714 IStream_Write(pstm, tmpBuffer, bytesRead, &bytesWritten);
715
716 totalBytesWritten.LowPart += bytesWritten;
717
718 /*
719 * Check that read & write operations were succesfull
720 */
721 if (bytesRead != bytesWritten)
722 {
723 hr = STG_E_MEDIUMFULL;
724 break;
725 }
726
727 if (bytesRead!=copySize)
728 cb.LowPart = 0;
729 else
730 cb.LowPart -= bytesRead;
731 }
732
733 /*
734 * Update number of bytes read and written
735 */
736 if (pcbRead)
737 {
738 pcbRead->LowPart = totalBytesRead.LowPart;
739 pcbRead->HighPart = totalBytesRead.HighPart;
740 }
741
742 if (pcbWritten)
743 {
744 pcbWritten->LowPart = totalBytesWritten.LowPart;
745 pcbWritten->HighPart = totalBytesWritten.HighPart;
746 }
747 return hr;
748}
749
750/***
751 * This method is part of the IStream interface.
752 *
753 * For streams contained in structured storages, this method
754 * does nothing. This is what the documentation tells us.
755 *
756 * See the documentation of IStream for more info.
757 */
758HRESULT WINAPI StgStreamImpl_Commit(
759 IStream* iface,
760 DWORD grfCommitFlags) /* [in] */
761{
762 return S_OK;
763}
764
765/***
766 * This method is part of the IStream interface.
767 *
768 * For streams contained in structured storages, this method
769 * does nothing. This is what the documentation tells us.
770 *
771 * See the documentation of IStream for more info.
772 */
773HRESULT WINAPI StgStreamImpl_Revert(
774 IStream* iface)
775{
776 return S_OK;
777}
778
779HRESULT WINAPI StgStreamImpl_LockRegion(
780 IStream* iface,
781 ULARGE_INTEGER libOffset, /* [in] */
782 ULARGE_INTEGER cb, /* [in] */
783 DWORD dwLockType) /* [in] */
784{
785 FIXME("not implemented!\n");
786 return E_NOTIMPL;
787}
788
789HRESULT WINAPI StgStreamImpl_UnlockRegion(
790 IStream* iface,
791 ULARGE_INTEGER libOffset, /* [in] */
792 ULARGE_INTEGER cb, /* [in] */
793 DWORD dwLockType) /* [in] */
794{
795 FIXME("not implemented!\n");
796 return E_NOTIMPL;
797}
798
799/***
800 * This method is part of the IStream interface.
801 *
802 * This method returns information about the current
803 * stream.
804 *
805 * See the documentation of IStream for more info.
806 */
807HRESULT WINAPI StgStreamImpl_Stat(
808 IStream* iface,
809 STATSTG* pstatstg, /* [out] */
810 DWORD grfStatFlag) /* [in] */
811{
812 StgStreamImpl* const This=(StgStreamImpl*)iface;
813
814 StgProperty curProperty;
815 BOOL readSucessful;
816
817 /*
818 * Read the information from the property.
819 */
820 readSucessful = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
821 This->ownerProperty,
822 &curProperty);
823
824 if (readSucessful)
825 {
826 StorageUtl_CopyPropertyToSTATSTG(pstatstg,
827 &curProperty,
828 grfStatFlag);
829
830 return S_OK;
831 }
832
833 return E_FAIL;
834}
835
836HRESULT WINAPI StgStreamImpl_Clone(
837 IStream* iface,
838 IStream** ppstm) /* [out] */
839{
840 FIXME("not implemented!\n");
841 return E_NOTIMPL;
842}
Note: See TracBrowser for help on using the repository browser.