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