No longer necessary.
[wine/multimedia.git] / ole / stg_bigblockfile.c
blob0e1c2b9ae84fbf54cd0282c44b627dad8746f203
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 "winbase.h"
27 #include "winerror.h"
28 #include "wine/obj_storage.h"
30 #include "storage32.h"
32 /***********************************************************
33 * Data structures used internally by the BigBlockFile
34 * class.
37 /***
38 * Itdentifies a single big block and the related
39 * information
41 struct BigBlock
43 BigBlock * next;
44 DWORD index;
45 DWORD access_mode;
46 LPVOID lpBlock;
49 /***
50 * This structure identifies the paged that are mapped
51 * from the file and their position in memory. It is
52 * also used to hold a reference count to those pages.
54 struct MappedPage
56 MappedPage * next;
57 DWORD number;
58 int ref;
59 LPVOID lpBytes;
62 #define PAGE_SIZE 131072
63 #define BLOCKS_PER_PAGE 256
65 #define NUMBER_OF_MAPPED_PAGES 100
67 /***********************************************************
68 * Prototypes for private methods
70 static void* BIGBLOCKFILE_GetMappedView(LPBIGBLOCKFILE This,
71 DWORD pagenum,
72 DWORD desired_access);
73 static void BIGBLOCKFILE_ReleaseMappedPage(LPBIGBLOCKFILE This,
74 DWORD pagenum,
75 DWORD access);
76 static void BIGBLOCKFILE_FreeAllMappedPages(LPBIGBLOCKFILE This);
77 static void* BIGBLOCKFILE_GetBigBlockPointer(LPBIGBLOCKFILE This,
78 ULONG index,
79 DWORD desired_access);
80 static BigBlock* BIGBLOCKFILE_GetBigBlockFromPointer(LPBIGBLOCKFILE This,
81 void* pBlock);
82 static void BIGBLOCKFILE_RemoveBlock(LPBIGBLOCKFILE This,
83 ULONG index);
84 static BigBlock* BIGBLOCKFILE_AddBigBlock(LPBIGBLOCKFILE This,
85 ULONG index);
86 static BigBlock* BIGBLOCKFILE_CreateBlock(ULONG index);
87 static DWORD BIGBLOCKFILE_GetProtectMode(DWORD openFlags);
89 /******************************************************************************
90 * BIGBLOCKFILE_Construct
92 * Construct a big block file. Create the file mapping object.
93 * Create the read only mapped pages list, the writeable mapped page list
94 * and the blocks in use list.
96 BigBlockFile * BIGBLOCKFILE_Construct(
97 HANDLE hFile,
98 DWORD openFlags,
99 ULONG blocksize)
101 LPBIGBLOCKFILE This;
103 This = (LPBIGBLOCKFILE)HeapAlloc(GetProcessHeap(), 0, sizeof(BigBlockFile));
105 if (This == NULL)
106 return NULL;
108 This->hfile = hFile;
110 if (This->hfile == INVALID_HANDLE_VALUE)
112 HeapFree(GetProcessHeap(), 0, This);
113 return NULL;
116 This->flProtect = BIGBLOCKFILE_GetProtectMode(openFlags);
118 /* create the file mapping object
120 This->hfilemap = CreateFileMappingA(This->hfile,
121 NULL,
122 This->flProtect,
123 0, 0,
124 NULL);
126 if (This->hfilemap == NULL)
128 CloseHandle(This->hfile);
129 HeapFree(GetProcessHeap(), 0, This);
130 return NULL;
133 /* initialize this
135 This->filesize.LowPart = GetFileSize(This->hfile, NULL);
136 This->blocksize = blocksize;
138 /* create the mapped pages list
140 This->maplisthead = HeapAlloc(GetProcessHeap(), 0, sizeof(MappedPage));
142 if (This->maplisthead == NULL)
144 CloseHandle(This->hfilemap);
145 CloseHandle(This->hfile);
146 HeapFree(GetProcessHeap(), 0, This);
147 return NULL;
150 This->maplisthead->next = NULL;
152 /* initialize the block list
154 This->headblock = NULL;
156 return This;
159 /******************************************************************************
160 * BIGBLOCKFILE_Destructor
162 * Destructor. Clean up, free memory.
164 void BIGBLOCKFILE_Destructor(
165 LPBIGBLOCKFILE This)
167 /* unmap all views and destroy the mapped page list
169 BIGBLOCKFILE_FreeAllMappedPages(This);
170 HeapFree(GetProcessHeap(), 0, This->maplisthead);
172 /* close all open handles
174 CloseHandle(This->hfilemap);
175 CloseHandle(This->hfile);
177 /* destroy this
179 HeapFree(GetProcessHeap(), 0, This);
182 /******************************************************************************
183 * BIGBLOCKFILE_GetROBigBlock
185 * Returns the specified block in read only mode.
186 * Will return NULL if the block doesn't exists.
188 void* BIGBLOCKFILE_GetROBigBlock(
189 LPBIGBLOCKFILE This,
190 ULONG index)
193 * block index starts at -1
194 * translate to zero based index
196 if (index == 0xffffffff)
197 index = 0;
198 else
199 index++;
202 * validate the block index
205 if ((This->blocksize * (index + 1)) >
206 (This->filesize.LowPart +
207 (This->blocksize - (This->filesize.LowPart % This->blocksize))))
208 return 0;
210 return BIGBLOCKFILE_GetBigBlockPointer(This, index, FILE_MAP_READ);
213 /******************************************************************************
214 * BIGBLOCKFILE_GetBigBlock
216 * Returns the specified block.
217 * Will grow the file if necessary.
219 void* BIGBLOCKFILE_GetBigBlock(LPBIGBLOCKFILE This, ULONG index)
222 * block index starts at -1
223 * translate to zero based index
225 if (index == 0xffffffff)
226 index = 0;
227 else
228 index++;
231 * make sure that the block physically exists
233 if ((This->blocksize * (index + 1)) > This->filesize.LowPart)
235 ULARGE_INTEGER newSize;
237 newSize.HighPart = 0;
238 newSize.LowPart = This->blocksize * (index + 1);
240 BIGBLOCKFILE_SetSize(This, newSize);
243 return BIGBLOCKFILE_GetBigBlockPointer(This, index, FILE_MAP_WRITE);
246 /******************************************************************************
247 * BIGBLOCKFILE_ReleaseBigBlock
249 * Releases the specified block.
251 void BIGBLOCKFILE_ReleaseBigBlock(LPBIGBLOCKFILE This, void *pBlock)
253 DWORD page_num;
254 BigBlock* theBigBlock;
256 if (pBlock == NULL)
257 return;
260 * get the block from the block list
262 theBigBlock = BIGBLOCKFILE_GetBigBlockFromPointer(This, pBlock);
264 if (theBigBlock == NULL)
265 return;
268 * find out which page this block is in
270 page_num = theBigBlock->index / BLOCKS_PER_PAGE;
273 * release this page
275 BIGBLOCKFILE_ReleaseMappedPage(This, page_num, theBigBlock->access_mode);
278 * remove block from list
280 BIGBLOCKFILE_RemoveBlock(This, theBigBlock->index);
283 /******************************************************************************
284 * BIGBLOCKFILE_SetSize
286 * Sets the size of the file.
289 void BIGBLOCKFILE_SetSize(LPBIGBLOCKFILE This, ULARGE_INTEGER newSize)
291 if (This->filesize.LowPart == newSize.LowPart)
292 return;
295 * unmap all views, must be done before call to SetEndFile
297 BIGBLOCKFILE_FreeAllMappedPages(This);
300 * close file-mapping object, must be done before call to SetEndFile
302 CloseHandle(This->hfilemap);
303 This->hfilemap = NULL;
306 * set the new end of file
308 SetFilePointer(This->hfile, newSize.LowPart, NULL, FILE_BEGIN);
309 SetEndOfFile(This->hfile);
312 * re-create the file mapping object
314 This->hfilemap = CreateFileMappingA(This->hfile,
315 NULL,
316 This->flProtect,
317 0, 0,
318 NULL);
320 This->filesize.LowPart = newSize.LowPart;
321 This->filesize.HighPart = newSize.HighPart;
324 /******************************************************************************
325 * BIGBLOCKFILE_GetSize
327 * Returns the size of the file.
330 ULARGE_INTEGER BIGBLOCKFILE_GetSize(LPBIGBLOCKFILE This)
332 return This->filesize;
335 /******************************************************************************
336 * BIGBLOCKFILE_GetBigBlockPointer [PRIVATE]
338 * Returns a pointer to the specified block.
340 static void* BIGBLOCKFILE_GetBigBlockPointer(
341 LPBIGBLOCKFILE This,
342 ULONG index,
343 DWORD desired_access)
345 DWORD page_num, block_num;
346 void * pBytes;
347 BigBlock *aBigBlock;
349 /* get the big block from the list or add it to the list
351 aBigBlock = BIGBLOCKFILE_AddBigBlock(This, index);
353 if (aBigBlock == NULL)
354 return NULL;
356 /* we already have an address for this block
358 if (aBigBlock->lpBlock != NULL)
360 /* make sure the desired access matches what we already have
362 if (aBigBlock->access_mode == desired_access)
363 return aBigBlock->lpBlock;
364 else
365 return NULL;
369 * else aBigBlock->lpBigBlock == NULL, it's a new block
372 /* find out which page this block is in
374 page_num = index / BLOCKS_PER_PAGE;
376 /* offset of the block in the page
378 block_num = index % BLOCKS_PER_PAGE;
380 /* get a pointer to the first byte in the page
382 pBytes = BIGBLOCKFILE_GetMappedView(This, page_num, desired_access);
384 if (pBytes == NULL)
385 return NULL;
387 /* initialize block
389 aBigBlock->lpBlock = ((BYTE*)pBytes + (block_num*This->blocksize));
390 aBigBlock->access_mode = desired_access;
392 return aBigBlock->lpBlock;
395 /******************************************************************************
396 * BIGBLOCKFILE_CreateBlock [PRIVATE]
398 * Creates a node of the blocks list.
400 static BigBlock* BIGBLOCKFILE_CreateBlock(
401 ULONG index)
403 BigBlock *newBigBlock;
405 /* create new list node
407 newBigBlock = HeapAlloc(GetProcessHeap(), 0, sizeof(BigBlock));
409 if (newBigBlock == NULL)
410 return NULL;
412 /* initialize node
414 newBigBlock->index = index;
415 newBigBlock->lpBlock = NULL;
417 return newBigBlock;
420 /******************************************************************************
421 * BIGBLOCKFILE_AddBigBlock [PRIVATE]
423 * Returns the specified block from the blocks list.
424 * If the block is not found in the list, we will create it and add it to the
425 * list.
427 static BigBlock* BIGBLOCKFILE_AddBigBlock(
428 LPBIGBLOCKFILE This,
429 ULONG index)
431 BigBlock *current = This->headblock;
432 BigBlock *newBigBlock;
434 if (current == NULL) /* empty list */
436 newBigBlock = BIGBLOCKFILE_CreateBlock(index);
438 if (newBigBlock != NULL)
440 newBigBlock->next = NULL;
441 This->headblock = newBigBlock;
444 return newBigBlock;
446 else
449 * special handling for head of the list
452 if (current->index == index) /* it's already here */
453 return current;
454 else if (current->index > index) /* insertion at head of the list */
456 newBigBlock = BIGBLOCKFILE_CreateBlock(index);
458 if (newBigBlock != NULL)
460 newBigBlock->next = current;
461 This->headblock = newBigBlock;
464 return newBigBlock;
468 /* iterate through rest the list
470 while (current->next != NULL)
472 if (current->next->index == index) /* found it */
474 return current->next;
476 else if (current->next->index > index) /* it's not in the list */
478 newBigBlock = BIGBLOCKFILE_CreateBlock(index);
480 if (newBigBlock != NULL)
482 newBigBlock->next = current->next;
483 current->next = newBigBlock;
486 return newBigBlock;
488 else
489 current = current->next;
493 * insertion at end of the list
495 if (current->next == NULL)
497 newBigBlock = BIGBLOCKFILE_CreateBlock(index);
499 if (newBigBlock != NULL)
501 newBigBlock->next = NULL;
502 current->next = newBigBlock;
505 return newBigBlock;
508 return NULL;
511 /******************************************************************************
512 * BIGBLOCKFILE_RemoveBlock [PRIVATE]
514 * Removes the specified block from the blocks list.
516 static void BIGBLOCKFILE_RemoveBlock(
517 LPBIGBLOCKFILE This,
518 ULONG index)
520 BigBlock *current = This->headblock;
523 * empty list
525 if (current == NULL)
526 return;
529 *special case: removing head of list
531 if (current->index == index)
534 * set new head free the old one
536 This->headblock = current->next;
537 HeapFree(GetProcessHeap(), 0, current);
539 return;
543 * iterate through rest of the list
545 while (current->next != NULL)
547 if (current->next->index == index) /* found it */
550 * unlink the block and free the block
552 current->next = current->next->next;
553 HeapFree(GetProcessHeap(), 0, current->next);
555 return;
557 else
559 /* next node
561 current = current->next;
566 /******************************************************************************
567 * BIGBLOCKFILE_GetBigBlockFromPointer [PRIVATE]
569 * Given a block pointer, this will return the corresponding block
570 * from the blocks list.
572 static BigBlock* BIGBLOCKFILE_GetBigBlockFromPointer(
573 LPBIGBLOCKFILE This,
574 void* pBlock)
576 BigBlock *current = This->headblock;
578 while (current != NULL)
580 if (current->lpBlock == pBlock)
582 break;
584 else
585 current = current->next;
588 return current;
591 /******************************************************************************
592 * BIGBLOCKFILE_GetMappedView [PRIVATE]
594 * Gets the page requested if it is already mapped.
595 * If it's not already mapped, this method will map it
597 static void * BIGBLOCKFILE_GetMappedView(
598 LPBIGBLOCKFILE This,
599 DWORD pagenum,
600 DWORD desired_access)
602 MappedPage* current = This->maplisthead;
603 ULONG count = 1;
604 BOOL found = FALSE;
606 assert(This->maplisthead != NULL);
609 * Search for the page in the list.
611 while ((found == FALSE) && (current->next != NULL))
613 if (current->next->number == pagenum)
615 found = TRUE;
618 * If it's not already at the head of the list
619 * move it there.
621 if (current != This->maplisthead)
623 MappedPage* temp = current->next;
625 current->next = current->next->next;
627 temp->next = This->maplisthead->next;
628 This->maplisthead->next = temp;
633 * The list is full and we haven't found it.
634 * Free the last element of the list because we'll add a new
635 * one at the head.
637 if ((found == FALSE) &&
638 (count >= NUMBER_OF_MAPPED_PAGES) &&
639 (current->next != NULL))
641 UnmapViewOfFile(current->next->lpBytes);
643 HeapFree(GetProcessHeap(), 0, current->next);
644 current->next = NULL;
647 if (current->next != NULL)
648 current = current->next;
650 count++;
654 * Add the page at the head of the list.
656 if (found == FALSE)
658 MappedPage* newMappedPage;
659 DWORD numBytesToMap;
660 DWORD hioffset = 0;
661 DWORD lowoffset = PAGE_SIZE * pagenum;
663 newMappedPage = HeapAlloc(GetProcessHeap(), 0, sizeof(MappedPage));
665 if (newMappedPage == NULL)
666 return NULL;
668 newMappedPage->number = pagenum;
669 newMappedPage->ref = 0;
671 newMappedPage->next = This->maplisthead->next;
672 This->maplisthead->next = newMappedPage;
674 if (((pagenum + 1) * PAGE_SIZE) > This->filesize.LowPart)
675 numBytesToMap = This->filesize.LowPart - (pagenum * PAGE_SIZE);
676 else
677 numBytesToMap = PAGE_SIZE;
679 if (This->flProtect == PAGE_READONLY)
680 desired_access = FILE_MAP_READ;
681 else
682 desired_access = FILE_MAP_WRITE;
684 newMappedPage->lpBytes = MapViewOfFile(This->hfilemap,
685 desired_access,
686 hioffset,
687 lowoffset,
688 numBytesToMap);
692 * The page we want should now be at the head of the list.
694 assert(This->maplisthead->next != NULL);
696 current = This->maplisthead->next;
697 current->ref++;
699 return current->lpBytes;
702 /******************************************************************************
703 * BIGBLOCKFILE_ReleaseMappedPage [PRIVATE]
705 * Decrements the reference count of the mapped page.
707 static void BIGBLOCKFILE_ReleaseMappedPage(
708 LPBIGBLOCKFILE This,
709 DWORD pagenum,
710 DWORD access)
712 MappedPage* previous = This->maplisthead;
713 MappedPage* current;
715 assert(This->maplisthead->next != NULL);
717 current = previous->next;
719 /* search for the page in the list
721 while (current != NULL)
723 if (current->number == pagenum)
725 /* decrement the reference count
727 current->ref--;
728 return;
730 else
732 previous = current;
733 current = current->next;
738 /******************************************************************************
739 * BIGBLOCKFILE_FreeAllMappedPages [PRIVATE]
741 * Unmap all currently mapped pages.
742 * Empty mapped pages list.
744 static void BIGBLOCKFILE_FreeAllMappedPages(
745 LPBIGBLOCKFILE This)
747 MappedPage * current = This->maplisthead->next;
749 while (current != NULL)
751 /* Unmap views.
753 UnmapViewOfFile(current->lpBytes);
755 /* Free the nodes.
757 This->maplisthead->next = current->next;
758 HeapFree(GetProcessHeap(), 0, current);
760 current = This->maplisthead->next;
764 /****************************************************************************
765 * BIGBLOCKFILE_GetProtectMode
767 * This function will return a protection mode flag for a file-mapping object
768 * from the open flags of a file.
770 static DWORD BIGBLOCKFILE_GetProtectMode(DWORD openFlags)
772 DWORD flProtect = PAGE_READONLY;
773 BOOL bSTGM_WRITE = ((openFlags & STGM_WRITE) == STGM_WRITE);
774 BOOL bSTGM_READWRITE = ((openFlags & STGM_READWRITE) == STGM_READWRITE);
775 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
777 if (bSTGM_READ)
778 flProtect = PAGE_READONLY;
780 if ((bSTGM_WRITE) || (bSTGM_READWRITE))
781 flProtect = PAGE_READWRITE;
783 return flProtect;