1 /******************************************************************************
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
15 * - Support for a transacted mode
17 * Copyright 1999 Thuy Nguyen
28 #include "wine/obj_storage.h"
30 #include "storage32.h"
32 /***********************************************************
33 * Data structures used internally by the BigBlockFile
38 * Itdentifies a single big block and the related
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.
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
,
72 DWORD desired_access
);
73 static void BIGBLOCKFILE_ReleaseMappedPage(LPBIGBLOCKFILE This
,
76 static void BIGBLOCKFILE_FreeAllMappedPages(LPBIGBLOCKFILE This
);
77 static void* BIGBLOCKFILE_GetBigBlockPointer(LPBIGBLOCKFILE This
,
79 DWORD desired_access
);
80 static BigBlock
* BIGBLOCKFILE_GetBigBlockFromPointer(LPBIGBLOCKFILE This
,
82 static void BIGBLOCKFILE_RemoveBlock(LPBIGBLOCKFILE This
,
84 static BigBlock
* BIGBLOCKFILE_AddBigBlock(LPBIGBLOCKFILE This
,
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(
103 This
= (LPBIGBLOCKFILE
)HeapAlloc(GetProcessHeap(), 0, sizeof(BigBlockFile
));
110 if (This
->hfile
== INVALID_HANDLE_VALUE
)
112 HeapFree(GetProcessHeap(), 0, This
);
116 This
->flProtect
= BIGBLOCKFILE_GetProtectMode(openFlags
);
118 /* create the file mapping object
120 This
->hfilemap
= CreateFileMappingA(This
->hfile
,
126 if (This
->hfilemap
== NULL
)
128 CloseHandle(This
->hfile
);
129 HeapFree(GetProcessHeap(), 0, 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
);
150 This
->maplisthead
->next
= NULL
;
152 /* initialize the block list
154 This
->headblock
= NULL
;
159 /******************************************************************************
160 * BIGBLOCKFILE_Destructor
162 * Destructor. Clean up, free memory.
164 void BIGBLOCKFILE_Destructor(
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
);
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(
193 * block index starts at -1
194 * translate to zero based index
196 if (index
== 0xffffffff)
202 * validate the block index
205 if ((This
->blocksize
* (index
+ 1)) >
206 (This
->filesize
.LowPart
+
207 (This
->blocksize
- (This
->filesize
.LowPart
% This
->blocksize
))))
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)
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
)
254 BigBlock
* theBigBlock
;
260 * get the block from the block list
262 theBigBlock
= BIGBLOCKFILE_GetBigBlockFromPointer(This
, pBlock
);
264 if (theBigBlock
== NULL
)
268 * find out which page this block is in
270 page_num
= theBigBlock
->index
/ BLOCKS_PER_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
)
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
,
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(
343 DWORD desired_access
)
345 DWORD page_num
, block_num
;
349 /* get the big block from the list or add it to the list
351 aBigBlock
= BIGBLOCKFILE_AddBigBlock(This
, index
);
353 if (aBigBlock
== 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
;
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
);
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(
403 BigBlock
*newBigBlock
;
405 /* create new list node
407 newBigBlock
= HeapAlloc(GetProcessHeap(), 0, sizeof(BigBlock
));
409 if (newBigBlock
== NULL
)
414 newBigBlock
->index
= index
;
415 newBigBlock
->lpBlock
= NULL
;
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
427 static BigBlock
* BIGBLOCKFILE_AddBigBlock(
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
;
449 * special handling for head of the list
452 if (current
->index
== index
) /* it's already here */
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
;
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
;
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
;
511 /******************************************************************************
512 * BIGBLOCKFILE_RemoveBlock [PRIVATE]
514 * Removes the specified block from the blocks list.
516 static void BIGBLOCKFILE_RemoveBlock(
520 BigBlock
*current
= This
->headblock
;
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
);
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
);
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(
576 BigBlock
*current
= This
->headblock
;
578 while (current
!= NULL
)
580 if (current
->lpBlock
== pBlock
)
585 current
= current
->next
;
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(
600 DWORD desired_access
)
602 MappedPage
* current
= This
->maplisthead
;
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
)
618 * If it's not already at the head of the list
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
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
;
654 * Add the page at the head of the list.
658 MappedPage
* newMappedPage
;
661 DWORD lowoffset
= PAGE_SIZE
* pagenum
;
663 newMappedPage
= HeapAlloc(GetProcessHeap(), 0, sizeof(MappedPage
));
665 if (newMappedPage
== 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
);
677 numBytesToMap
= PAGE_SIZE
;
679 if (This
->flProtect
== PAGE_READONLY
)
680 desired_access
= FILE_MAP_READ
;
682 desired_access
= FILE_MAP_WRITE
;
684 newMappedPage
->lpBytes
= MapViewOfFile(This
->hfilemap
,
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
;
699 return current
->lpBytes
;
702 /******************************************************************************
703 * BIGBLOCKFILE_ReleaseMappedPage [PRIVATE]
705 * Decrements the reference count of the mapped page.
707 static void BIGBLOCKFILE_ReleaseMappedPage(
712 MappedPage
* previous
= This
->maplisthead
;
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
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(
747 MappedPage
* current
= This
->maplisthead
->next
;
749 while (current
!= NULL
)
753 UnmapViewOfFile(current
->lpBytes
);
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
);
778 flProtect
= PAGE_READONLY
;
780 if ((bSTGM_WRITE
) || (bSTGM_READWRITE
))
781 flProtect
= PAGE_READWRITE
;