Large-scale renaming of all Win32 functions and types to use the
[wine/multimedia.git] / ole / stg_bigblockfile.c
blobe523a576f25b9eccfdc2f9129a45e8f8772fb1ee
1 /******************************************************************************
3 * BigBlockFile
5 * This is the implementation of a file that consists of blocks of
6 * a predetermined size.
7 * This class is used in the Compound File implementation of the
8 * IStorage and IStream interfaces. It provides the functionality
9 * to read and write any blocks in the file as well as setting and
10 * obtaining the size of the file.
11 * The blocks are indexed sequentially from the start of the file
12 * starting with -1.
14 * TODO:
15 * - Support for a transacted mode
17 * Copyright 1999 Thuy Nguyen
21 #include <assert.h>
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
26 #include "windows.h"
27 #include "winerror.h"
28 #include "ole.h"
29 #include "ole2.h"
30 #include "wine/obj_base.h"
31 #include "wine/obj_storage.h"
33 #include "storage32.h"
35 /***********************************************************
36 * Data structures used internally by the BigBlockFile
37 * class.
40 /***
41 * Itdentifies a single big block and the related
42 * information
44 struct BigBlock
46 BigBlock * next;
47 DWORD index;
48 DWORD access_mode;
49 LPVOID lpBlock;
52 /***
53 * This structure identifies the paged that are mapped
54 * from the file and their position in memory. It is
55 * also used to hold a reference count to those pages.
57 struct MappedPage
59 MappedPage * next;
60 DWORD number;
61 int ref;
62 LPVOID lpBytes;
65 #define BLOCKS_PER_PAGE 128
66 #define PAGE_SIZE 65536
68 /***********************************************************
69 * Prototypes for private methods
71 static void* BIGBLOCKFILE_GetMappedView(LPBIGBLOCKFILE This,
72 DWORD pagenum,
73 DWORD desired_access);
74 static void BIGBLOCKFILE_ReleaseMappedPage(LPBIGBLOCKFILE This,
75 DWORD pagenum,
76 DWORD access);
77 static void BIGBLOCKFILE_FreeAllMappedPages(LPBIGBLOCKFILE This);
78 static void* BIGBLOCKFILE_GetBigBlockPointer(LPBIGBLOCKFILE This,
79 ULONG index,
80 DWORD desired_access);
81 static BigBlock* BIGBLOCKFILE_GetBigBlockFromPointer(LPBIGBLOCKFILE This,
82 void* pBlock);
83 static void BIGBLOCKFILE_RemoveBlock(LPBIGBLOCKFILE This,
84 ULONG index);
85 static BigBlock* BIGBLOCKFILE_AddBigBlock(LPBIGBLOCKFILE This,
86 ULONG index);
87 static BigBlock* BIGBLOCKFILE_CreateBlock(ULONG index);
89 static DWORD BIGBLOCKFILE_GetProtectMode(DWORD openFlags);
91 /******************************************************************************
92 * BIGBLOCKFILE_Construct
94 * Construct a big block file. Create the file mapping object.
95 * Create the read only mapped pages list, the writeable mapped page list
96 * and the blocks in use list.
98 BigBlockFile * BIGBLOCKFILE_Construct(
99 HANDLE hFile,
100 DWORD openFlags,
101 ULONG blocksize)
103 LPBIGBLOCKFILE This;
105 This = (LPBIGBLOCKFILE)HeapAlloc(GetProcessHeap(), 0, sizeof(BigBlockFile));
107 if (This == NULL)
108 return NULL;
110 This->hfile = hFile;
112 if (This->hfile == INVALID_HANDLE_VALUE)
114 HeapFree(GetProcessHeap(), 0, This);
115 return NULL;
118 This->flProtect = BIGBLOCKFILE_GetProtectMode(openFlags);
120 /* create the file mapping object
122 This->hfilemap = CreateFileMappingA(This->hfile,
123 NULL,
124 This->flProtect,
125 0, 0,
126 NULL);
128 if (This->hfilemap == NULL)
130 CloseHandle(This->hfile);
131 HeapFree(GetProcessHeap(), 0, This);
132 return NULL;
135 /* initialize this
137 This->filesize.LowPart = GetFileSize(This->hfile, NULL);
138 This->blocksize = blocksize;
140 /* create the read only mapped pages list
142 This->headmap_ro = HeapAlloc(GetProcessHeap(), 0, sizeof(MappedPage));
144 if (This->headmap_ro == NULL)
146 CloseHandle(This->hfilemap);
147 CloseHandle(This->hfile);
148 HeapFree(GetProcessHeap(), 0, This);
149 return NULL;
152 This->headmap_ro->next = NULL;
154 /* create the writeable mapped pages list
156 This->headmap_w = HeapAlloc(GetProcessHeap(), 0, sizeof(MappedPage));
158 if (This->headmap_w == NULL)
160 CloseHandle(This->hfilemap);
161 CloseHandle(This->hfile);
162 HeapFree(GetProcessHeap(), 0, This->headmap_ro);
163 HeapFree(GetProcessHeap(), 0, This);
164 return NULL;
167 This->headmap_w->next = NULL;
169 /* initialize the block list
171 This->headblock = NULL;
173 return This;
176 /******************************************************************************
177 * BIGBLOCKFILE_Destructor
179 * Destructor. Clean up, free memory.
181 void BIGBLOCKFILE_Destructor(
182 LPBIGBLOCKFILE This)
184 /* unmap all views and destroy the mapped page lists
186 BIGBLOCKFILE_FreeAllMappedPages(This);
187 HeapFree(GetProcessHeap(), 0, This->headmap_ro);
188 HeapFree(GetProcessHeap(), 0, This->headmap_w);
190 /* close all open handles
192 CloseHandle(This->hfilemap);
193 CloseHandle(This->hfile);
195 /* destroy this
197 HeapFree(GetProcessHeap(), 0, This);
200 /******************************************************************************
201 * BIGBLOCKFILE_GetROBigBlock
203 * Returns the specified block in read only mode.
204 * Will return NULL if the block doesn't exists.
206 void* BIGBLOCKFILE_GetROBigBlock(
207 LPBIGBLOCKFILE This,
208 ULONG index)
211 * block index starts at -1
212 * translate to zero based index
214 if (index == 0xffffffff)
215 index = 0;
216 else
217 index++;
220 * validate the block index
223 if ((This->blocksize * (index + 1)) >
224 (This->filesize.LowPart +
225 (This->blocksize - (This->filesize.LowPart % This->blocksize))))
226 return 0;
228 return BIGBLOCKFILE_GetBigBlockPointer(This, index, FILE_MAP_READ);
231 /******************************************************************************
232 * BIGBLOCKFILE_GetBigBlock
234 * Returns the specified block.
235 * Will grow the file if necessary.
237 void* BIGBLOCKFILE_GetBigBlock(LPBIGBLOCKFILE This, ULONG index)
240 * block index starts at -1
241 * translate to zero based index
243 if (index == 0xffffffff)
244 index = 0;
245 else
246 index++;
249 * make sure that the block physically exists
251 if ((This->blocksize * (index + 1)) > This->filesize.LowPart)
253 ULARGE_INTEGER newSize;
255 newSize.HighPart = 0;
256 newSize.LowPart = This->blocksize * (index + 1);
258 BIGBLOCKFILE_SetSize(This, newSize);
261 return BIGBLOCKFILE_GetBigBlockPointer(This, index, FILE_MAP_WRITE);
264 /******************************************************************************
265 * BIGBLOCKFILE_ReleaseBigBlock
267 * Releases the specified block.
270 void BIGBLOCKFILE_ReleaseBigBlock(LPBIGBLOCKFILE This, void *pBlock)
272 DWORD page_num;
273 BigBlock* theBigBlock;
275 if (pBlock == NULL)
276 return;
279 * get the block from the block list
281 theBigBlock = BIGBLOCKFILE_GetBigBlockFromPointer(This, pBlock);
283 if (theBigBlock == NULL)
284 return;
287 * find out which page this block is in
289 page_num = theBigBlock->index / BLOCKS_PER_PAGE;
292 * release this page
294 BIGBLOCKFILE_ReleaseMappedPage(This, page_num, theBigBlock->access_mode);
297 * remove block from list
299 BIGBLOCKFILE_RemoveBlock(This, theBigBlock->index);
302 /******************************************************************************
303 * BIGBLOCKFILE_SetSize
305 * Sets the size of the file.
308 void BIGBLOCKFILE_SetSize(LPBIGBLOCKFILE This, ULARGE_INTEGER newSize)
310 if (This->filesize.LowPart == newSize.LowPart)
311 return;
314 * unmap all views, must be done before call to SetEndFile
316 BIGBLOCKFILE_FreeAllMappedPages(This);
319 * close file-mapping object, must be done before call to SetEndFile
321 CloseHandle(This->hfilemap);
322 This->hfilemap = NULL;
325 * set the new end of file
327 SetFilePointer(This->hfile, newSize.LowPart, NULL, FILE_BEGIN);
328 SetEndOfFile(This->hfile);
331 * re-create the file mapping object
333 This->hfilemap = CreateFileMappingA(This->hfile,
334 NULL,
335 This->flProtect,
336 0, 0,
337 NULL);
339 This->filesize.LowPart = newSize.LowPart;
340 This->filesize.HighPart = newSize.HighPart;
343 /******************************************************************************
344 * BIGBLOCKFILE_GetSize
346 * Returns the size of the file.
349 ULARGE_INTEGER BIGBLOCKFILE_GetSize(LPBIGBLOCKFILE This)
351 return This->filesize;
354 /******************************************************************************
355 * BIGBLOCKFILE_GetBigBlockPointer [PRIVATE]
357 * Returns a pointer to the specified block.
359 static void* BIGBLOCKFILE_GetBigBlockPointer(
360 LPBIGBLOCKFILE This,
361 ULONG index,
362 DWORD desired_access)
364 DWORD page_num, block_num;
365 void * pBytes;
366 BigBlock *aBigBlock;
368 /* get the big block from the list or add it to the list
370 aBigBlock = BIGBLOCKFILE_AddBigBlock(This, index);
372 if (aBigBlock == NULL)
373 return NULL;
375 /* we already have an address for this block
377 if (aBigBlock->lpBlock != NULL)
379 /* make sure the desired access matches what we already have
381 if (aBigBlock->access_mode == desired_access)
382 return aBigBlock->lpBlock;
383 else
384 return NULL;
388 * else aBigBlock->lpBigBlock == NULL, it's a new block
391 /* find out which page this block is in
393 page_num = index / BLOCKS_PER_PAGE;
395 /* offset of the block in the page
397 block_num = index % BLOCKS_PER_PAGE;
399 /* get a pointer to the first byte in the page
401 pBytes = BIGBLOCKFILE_GetMappedView(This, page_num, desired_access);
403 if (pBytes == NULL)
404 return NULL;
406 /* initialize block
408 aBigBlock->lpBlock = ((BYTE*)pBytes + (block_num*This->blocksize));
409 aBigBlock->access_mode = desired_access;
411 return aBigBlock->lpBlock;
414 /******************************************************************************
415 * BIGBLOCKFILE_CreateBlock [PRIVATE]
417 * Creates a node of the blocks list.
419 static BigBlock* BIGBLOCKFILE_CreateBlock(
420 ULONG index)
422 BigBlock *newBigBlock;
424 /* create new list node
426 newBigBlock = HeapAlloc(GetProcessHeap(), 0, sizeof(BigBlock));
428 if (newBigBlock == NULL)
429 return NULL;
431 /* initialize node
433 newBigBlock->index = index;
434 newBigBlock->lpBlock = NULL;
436 return newBigBlock;
439 /******************************************************************************
440 * BIGBLOCKFILE_AddBigBlock [PRIVATE]
442 * Returns the specified block from the blocks list.
443 * If the block is not found in the list, we will create it and add it to the
444 * list.
446 static BigBlock* BIGBLOCKFILE_AddBigBlock(
447 LPBIGBLOCKFILE This,
448 ULONG index)
450 BigBlock *current = This->headblock;
451 BigBlock *newBigBlock;
453 if (current == NULL) /* empty list */
455 newBigBlock = BIGBLOCKFILE_CreateBlock(index);
457 if (newBigBlock != NULL)
459 newBigBlock->next = NULL;
460 This->headblock = newBigBlock;
463 return newBigBlock;
465 else
468 * special handling for head of the list
471 if (current->index == index) /* it's already here */
472 return current;
473 else if (current->index > index) /* insertion at head of the list */
475 newBigBlock = BIGBLOCKFILE_CreateBlock(index);
477 if (newBigBlock != NULL)
479 newBigBlock->next = current;
480 This->headblock = newBigBlock;
483 return newBigBlock;
487 /* iterate through rest the list
489 while (current->next != NULL)
491 if (current->next->index == index) /* found it */
493 return current->next;
495 else if (current->next->index > index) /* it's not in the list */
497 newBigBlock = BIGBLOCKFILE_CreateBlock(index);
499 if (newBigBlock != NULL)
501 newBigBlock->next = current->next;
502 current->next = newBigBlock;
505 return newBigBlock;
507 else
508 current = current->next;
512 * insertion at end of the list
514 if (current->next == NULL)
516 newBigBlock = BIGBLOCKFILE_CreateBlock(index);
518 if (newBigBlock != NULL)
520 newBigBlock->next = NULL;
521 current->next = newBigBlock;
524 return newBigBlock;
527 return NULL;
530 /******************************************************************************
531 * BIGBLOCKFILE_RemoveBlock [PRIVATE]
533 * Removes the specified block from the blocks list.
535 static void BIGBLOCKFILE_RemoveBlock(
536 LPBIGBLOCKFILE This,
537 ULONG index)
539 BigBlock *current = This->headblock;
542 * empty list
544 if (current == NULL)
545 return;
548 *special case: removing head of list
550 if (current->index == index)
553 * set new head free the old one
555 This->headblock = current->next;
556 HeapFree(GetProcessHeap(), 0, current);
558 return;
562 * iterate through rest of the list
564 while (current->next != NULL)
566 if (current->next->index == index) /* found it */
569 * unlink the block and free the block
571 current->next = current->next->next;
572 HeapFree(GetProcessHeap(), 0, current->next);
574 return;
576 else
578 /* next node
580 current = current->next;
585 /******************************************************************************
586 * BIGBLOCKFILE_GetBigBlockFromPointer [PRIVATE]
588 * Given a block pointer, this will return the corresponding block
589 * from the blocks list.
591 static BigBlock* BIGBLOCKFILE_GetBigBlockFromPointer(
592 LPBIGBLOCKFILE This,
593 void* pBlock)
595 BigBlock *current = This->headblock;
597 while (current != NULL)
599 if (current->lpBlock == pBlock)
601 break;
603 else
604 current = current->next;
607 return current;
610 /******************************************************************************
611 * BIGBLOCKFILE_GetMappedView [PRIVATE]
613 * Gets the page requested if it is already mapped.
614 * If it's not already mapped, this method will map it
616 static void * BIGBLOCKFILE_GetMappedView(
617 LPBIGBLOCKFILE This,
618 DWORD pagenum,
619 DWORD desired_access)
621 MappedPage * current;
622 MappedPage * newMappedPage;
623 DWORD hioffset, lowoffset;
624 DWORD numBytesToMap;
626 /* use correct list
628 if (desired_access == FILE_MAP_READ)
629 current = This->headmap_ro;
630 else if (desired_access == FILE_MAP_WRITE)
631 current = This->headmap_w;
632 else
633 return NULL;
635 hioffset = 0;
636 lowoffset = PAGE_SIZE * pagenum;
638 while (current->next != NULL)
640 if (current->next->number == pagenum) /* page already mapped */
642 current->next->ref++;
643 return current->next->lpBytes;
645 else if (current->next->number > pagenum) /* this page is not mapped yet */
647 /* allocate new MappedPage
649 newMappedPage = HeapAlloc(GetProcessHeap(), 0, sizeof(MappedPage));
651 if (newMappedPage == NULL)
652 return NULL;
654 /* initialize the new MappedPage
656 newMappedPage->number = pagenum;
657 newMappedPage->ref = 1;
659 newMappedPage->next = current->next;
660 current->next = newMappedPage;
662 /* actually map the page
664 if (((pagenum + 1) * PAGE_SIZE) > This->filesize.LowPart)
665 numBytesToMap = This->filesize.LowPart - (pagenum * PAGE_SIZE);
666 else
667 numBytesToMap = PAGE_SIZE;
669 newMappedPage->lpBytes = MapViewOfFile(This->hfilemap,
670 desired_access,
671 hioffset,
672 lowoffset,
673 numBytesToMap);
675 return newMappedPage->lpBytes;
677 else
678 current = current->next;
681 /* reached end of the list, this view is not mapped yet
683 if (current->next == NULL)
685 /* allocate new MappedPage
687 newMappedPage = HeapAlloc(GetProcessHeap(), 0, sizeof(MappedPage));
689 if (newMappedPage == NULL)
690 return NULL;
692 /* initialize the new MappedPage
694 newMappedPage->number = pagenum;
695 newMappedPage->ref = 1;
697 newMappedPage->next = NULL;
698 current->next = newMappedPage;
700 /* actually map the page
702 if (((pagenum + 1) * PAGE_SIZE) > This->filesize.LowPart)
703 numBytesToMap = This->filesize.LowPart - (pagenum * PAGE_SIZE);
704 else
705 numBytesToMap = PAGE_SIZE;
707 newMappedPage->lpBytes = MapViewOfFile(This->hfilemap,
708 desired_access,
709 hioffset,
710 lowoffset,
711 numBytesToMap);
713 return newMappedPage->lpBytes;
716 return NULL;
719 /******************************************************************************
720 * BIGBLOCKFILE_ReleaseMappedPage [PRIVATE]
722 * Decrements the reference count of the mapped page.
723 * If the page is not used anymore it will be unmapped.
725 static void BIGBLOCKFILE_ReleaseMappedPage(
726 LPBIGBLOCKFILE This,
727 DWORD pagenum,
728 DWORD access)
730 MappedPage * previous;
731 MappedPage * current;
733 /* use the list corresponding to the desired access mode
735 if (access == FILE_MAP_READ)
736 previous = This->headmap_ro;
737 else if (access == FILE_MAP_WRITE)
738 previous = This->headmap_w;
739 else
740 return;
742 current = previous->next;
744 /* search for the page in the list
746 while (current != NULL)
748 if (current->number == pagenum)
750 /* decrement the reference count
752 current->ref--;
754 if (current->ref == 0)
756 /* this page is not used anymore, we can unmap it
758 UnmapViewOfFile(current->lpBytes);
760 previous->next = current->next;
761 HeapFree(GetProcessHeap(), 0, current);
764 return;
766 else
768 previous = current;
769 current = current->next;
774 /******************************************************************************
775 * BIGBLOCKFILE_FreeAllMappedPages [PRIVATE]
777 * Unmap all currently mapped pages.
778 * Empty both mapped pages lists.
780 static void BIGBLOCKFILE_FreeAllMappedPages(
781 LPBIGBLOCKFILE This)
784 * start with the read only list
786 MappedPage * current = This->headmap_ro->next;
788 while (current != NULL)
790 /* unmap views
792 UnmapViewOfFile(current->lpBytes);
794 /* free the nodes
796 This->headmap_ro->next = current->next;
797 HeapFree(GetProcessHeap(), 0, current);
799 current = This->headmap_ro->next;
803 * then do the write list
805 current = This->headmap_w->next;
807 while (current != NULL)
809 /* unmap views
811 UnmapViewOfFile(current->lpBytes);
813 /* free the nodes
815 This->headmap_w->next = current->next;
816 HeapFree(GetProcessHeap(), 0, current);
818 current = This->headmap_w->next;
822 /****************************************************************************
823 * BIGBLOCKFILE_GetProtectMode
825 * This function will return a protection mode flag for a file-mapping object
826 * from the open flags of a file.
828 static DWORD BIGBLOCKFILE_GetProtectMode(DWORD openFlags)
830 DWORD flProtect = PAGE_READONLY;
831 BOOL bSTGM_WRITE = ((openFlags & STGM_WRITE) == STGM_WRITE);
832 BOOL bSTGM_READWRITE = ((openFlags & STGM_READWRITE) == STGM_READWRITE);
833 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
835 if (bSTGM_READ)
836 flProtect = PAGE_READONLY;
838 if ((bSTGM_WRITE) || (bSTGM_READWRITE))
839 flProtect = PAGE_READWRITE;
841 return flProtect;