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
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
41 * Itdentifies a single big block and the related
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.
65 #define BLOCKS_PER_PAGE 128
66 #define PAGE_SIZE 65536
68 /***********************************************************
69 * Prototypes for private methods
71 static void* BIGBLOCKFILE_GetMappedView(LPBIGBLOCKFILE This
,
73 DWORD desired_access
);
74 static void BIGBLOCKFILE_ReleaseMappedPage(LPBIGBLOCKFILE This
,
77 static void BIGBLOCKFILE_FreeAllMappedPages(LPBIGBLOCKFILE This
);
78 static void* BIGBLOCKFILE_GetBigBlockPointer(LPBIGBLOCKFILE This
,
80 DWORD desired_access
);
81 static BigBlock
* BIGBLOCKFILE_GetBigBlockFromPointer(LPBIGBLOCKFILE This
,
83 static void BIGBLOCKFILE_RemoveBlock(LPBIGBLOCKFILE This
,
85 static BigBlock
* BIGBLOCKFILE_AddBigBlock(LPBIGBLOCKFILE This
,
87 static BigBlock
* BIGBLOCKFILE_CreateBlock(ULONG index
);
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(
102 This
= (LPBIGBLOCKFILE
)HeapAlloc(GetProcessHeap(), 0, sizeof(BigBlockFile
));
109 if (This
->hfile
== INVALID_HANDLE_VALUE32
)
111 HeapFree(GetProcessHeap(), 0, This
);
115 /* create the file mapping object
117 This
->hfilemap
= CreateFileMapping32A(This
->hfile
,
123 if (This
->hfilemap
== NULL
)
125 CloseHandle(This
->hfile
);
126 HeapFree(GetProcessHeap(), 0, This
);
132 This
->filesize
.LowPart
= GetFileSize(This
->hfile
, NULL
);
133 This
->blocksize
= blocksize
;
135 /* create the read only mapped pages list
137 This
->headmap_ro
= HeapAlloc(GetProcessHeap(), 0, sizeof(MappedPage
));
139 if (This
->headmap_ro
== NULL
)
141 CloseHandle(This
->hfilemap
);
142 CloseHandle(This
->hfile
);
143 HeapFree(GetProcessHeap(), 0, This
);
147 This
->headmap_ro
->next
= NULL
;
149 /* create the writeable mapped pages list
151 This
->headmap_w
= HeapAlloc(GetProcessHeap(), 0, sizeof(MappedPage
));
153 if (This
->headmap_w
== NULL
)
155 CloseHandle(This
->hfilemap
);
156 CloseHandle(This
->hfile
);
157 HeapFree(GetProcessHeap(), 0, This
->headmap_ro
);
158 HeapFree(GetProcessHeap(), 0, This
);
162 This
->headmap_w
->next
= NULL
;
164 /* initialize the block list
166 This
->headblock
= NULL
;
171 /******************************************************************************
172 * BIGBLOCKFILE_Destructor
174 * Destructor. Clean up, free memory.
176 void BIGBLOCKFILE_Destructor(
179 /* unmap all views and destroy the mapped page lists
181 BIGBLOCKFILE_FreeAllMappedPages(This
);
182 HeapFree(GetProcessHeap(), 0, This
->headmap_ro
);
183 HeapFree(GetProcessHeap(), 0, This
->headmap_w
);
185 /* close all open handles
187 CloseHandle(This
->hfilemap
);
188 CloseHandle(This
->hfile
);
192 HeapFree(GetProcessHeap(), 0, This
);
195 /******************************************************************************
196 * BIGBLOCKFILE_GetROBigBlock
198 * Returns the specified block in read only mode.
199 * Will return NULL if the block doesn't exists.
201 void* BIGBLOCKFILE_GetROBigBlock(
206 * block index starts at -1
207 * translate to zero based index
209 if (index
== 0xffffffff)
215 * validate the block index
218 if ((This
->blocksize
* (index
+ 1)) >
219 (This
->filesize
.LowPart
+
220 (This
->blocksize
- (This
->filesize
.LowPart
% This
->blocksize
))))
223 return BIGBLOCKFILE_GetBigBlockPointer(This
, index
, FILE_MAP_READ
);
226 /******************************************************************************
227 * BIGBLOCKFILE_GetBigBlock
229 * Returns the specified block.
230 * Will grow the file if necessary.
232 void* BIGBLOCKFILE_GetBigBlock(LPBIGBLOCKFILE This
, ULONG index
)
235 * block index starts at -1
236 * translate to zero based index
238 if (index
== 0xffffffff)
244 * make sure that the block physically exists
246 if ((This
->blocksize
* (index
+ 1)) > This
->filesize
.LowPart
)
248 ULARGE_INTEGER newSize
;
250 newSize
.HighPart
= 0;
251 newSize
.LowPart
= This
->blocksize
* (index
+ 1);
253 BIGBLOCKFILE_SetSize(This
, newSize
);
256 return BIGBLOCKFILE_GetBigBlockPointer(This
, index
, FILE_MAP_WRITE
);
259 /******************************************************************************
260 * BIGBLOCKFILE_ReleaseBigBlock
262 * Releases the specified block.
265 void BIGBLOCKFILE_ReleaseBigBlock(LPBIGBLOCKFILE This
, void *pBlock
)
268 BigBlock
* theBigBlock
;
274 * get the block from the block list
276 theBigBlock
= BIGBLOCKFILE_GetBigBlockFromPointer(This
, pBlock
);
278 if (theBigBlock
== NULL
)
282 * find out which page this block is in
284 page_num
= theBigBlock
->index
/ BLOCKS_PER_PAGE
;
289 BIGBLOCKFILE_ReleaseMappedPage(This
, page_num
, theBigBlock
->access_mode
);
292 * remove block from list
294 BIGBLOCKFILE_RemoveBlock(This
, theBigBlock
->index
);
297 /******************************************************************************
298 * BIGBLOCKFILE_SetSize
300 * Sets the size of the file.
303 void BIGBLOCKFILE_SetSize(LPBIGBLOCKFILE This
, ULARGE_INTEGER newSize
)
305 if (This
->filesize
.LowPart
== newSize
.LowPart
)
309 * unmap all views, must be done before call to SetEndFile
311 BIGBLOCKFILE_FreeAllMappedPages(This
);
314 * close file-mapping object, must be done before call to SetEndFile
316 CloseHandle(This
->hfilemap
);
317 This
->hfilemap
= NULL
;
320 * set the new end of file
322 SetFilePointer(This
->hfile
, newSize
.LowPart
, NULL
, FILE_BEGIN
);
323 SetEndOfFile(This
->hfile
);
326 * re-create the file mapping object
328 This
->hfilemap
= CreateFileMapping32A(This
->hfile
,
334 This
->filesize
.LowPart
= newSize
.LowPart
;
335 This
->filesize
.HighPart
= newSize
.HighPart
;
338 /******************************************************************************
339 * BIGBLOCKFILE_GetSize
341 * Returns the size of the file.
344 ULARGE_INTEGER
BIGBLOCKFILE_GetSize(LPBIGBLOCKFILE This
)
346 return This
->filesize
;
349 /******************************************************************************
350 * BIGBLOCKFILE_GetBigBlockPointer [PRIVATE]
352 * Returns a pointer to the specified block.
354 static void* BIGBLOCKFILE_GetBigBlockPointer(
357 DWORD desired_access
)
359 DWORD page_num
, block_num
;
363 /* get the big block from the list or add it to the list
365 aBigBlock
= BIGBLOCKFILE_AddBigBlock(This
, index
);
367 if (aBigBlock
== NULL
)
370 /* we already have an address for this block
372 if (aBigBlock
->lpBlock
!= NULL
)
374 /* make sure the desired access matches what we already have
376 if (aBigBlock
->access_mode
== desired_access
)
377 return aBigBlock
->lpBlock
;
383 * else aBigBlock->lpBigBlock == NULL, it's a new block
386 /* find out which page this block is in
388 page_num
= index
/ BLOCKS_PER_PAGE
;
390 /* offset of the block in the page
392 block_num
= index
% BLOCKS_PER_PAGE
;
394 /* get a pointer to the first byte in the page
396 pBytes
= BIGBLOCKFILE_GetMappedView(This
, page_num
, desired_access
);
403 aBigBlock
->lpBlock
= ((BYTE
*)pBytes
+ (block_num
*This
->blocksize
));
404 aBigBlock
->access_mode
= desired_access
;
406 return aBigBlock
->lpBlock
;
409 /******************************************************************************
410 * BIGBLOCKFILE_CreateBlock [PRIVATE]
412 * Creates a node of the blocks list.
414 static BigBlock
* BIGBLOCKFILE_CreateBlock(
417 BigBlock
*newBigBlock
;
419 /* create new list node
421 newBigBlock
= HeapAlloc(GetProcessHeap(), 0, sizeof(BigBlock
));
423 if (newBigBlock
== NULL
)
428 newBigBlock
->index
= index
;
429 newBigBlock
->lpBlock
= NULL
;
434 /******************************************************************************
435 * BIGBLOCKFILE_AddBigBlock [PRIVATE]
437 * Returns the specified block from the blocks list.
438 * If the block is not found in the list, we will create it and add it to the
441 static BigBlock
* BIGBLOCKFILE_AddBigBlock(
445 BigBlock
*current
= This
->headblock
;
446 BigBlock
*newBigBlock
;
448 if (current
== NULL
) /* empty list */
450 newBigBlock
= BIGBLOCKFILE_CreateBlock(index
);
452 if (newBigBlock
!= NULL
)
454 newBigBlock
->next
= NULL
;
455 This
->headblock
= newBigBlock
;
463 * special handling for head of the list
466 if (current
->index
== index
) /* it's already here */
468 else if (current
->index
> index
) /* insertion at head of the list */
470 newBigBlock
= BIGBLOCKFILE_CreateBlock(index
);
472 if (newBigBlock
!= NULL
)
474 newBigBlock
->next
= current
;
475 This
->headblock
= newBigBlock
;
482 /* iterate through rest the list
484 while (current
->next
!= NULL
)
486 if (current
->next
->index
== index
) /* found it */
488 return current
->next
;
490 else if (current
->next
->index
> index
) /* it's not in the list */
492 newBigBlock
= BIGBLOCKFILE_CreateBlock(index
);
494 if (newBigBlock
!= NULL
)
496 newBigBlock
->next
= current
->next
;
497 current
->next
= newBigBlock
;
503 current
= current
->next
;
507 * insertion at end of the list
509 if (current
->next
== NULL
)
511 newBigBlock
= BIGBLOCKFILE_CreateBlock(index
);
513 if (newBigBlock
!= NULL
)
515 newBigBlock
->next
= NULL
;
516 current
->next
= newBigBlock
;
525 /******************************************************************************
526 * BIGBLOCKFILE_RemoveBlock [PRIVATE]
528 * Removes the specified block from the blocks list.
530 static void BIGBLOCKFILE_RemoveBlock(
534 BigBlock
*current
= This
->headblock
;
543 *special case: removing head of list
545 if (current
->index
== index
)
548 * set new head free the old one
550 This
->headblock
= current
->next
;
551 HeapFree(GetProcessHeap(), 0, current
);
557 * iterate through rest of the list
559 while (current
->next
!= NULL
)
561 if (current
->next
->index
== index
) /* found it */
564 * unlink the block and free the block
566 current
->next
= current
->next
->next
;
567 HeapFree(GetProcessHeap(), 0, current
->next
);
575 current
= current
->next
;
580 /******************************************************************************
581 * BIGBLOCKFILE_GetBigBlockFromPointer [PRIVATE]
583 * Given a block pointer, this will return the corresponding block
584 * from the blocks list.
586 static BigBlock
* BIGBLOCKFILE_GetBigBlockFromPointer(
590 BigBlock
*current
= This
->headblock
;
592 while (current
!= NULL
)
594 if (current
->lpBlock
== pBlock
)
599 current
= current
->next
;
605 /******************************************************************************
606 * BIGBLOCKFILE_GetMappedView [PRIVATE]
608 * Gets the page requested if it is already mapped.
609 * If it's not already mapped, this method will map it
611 static void * BIGBLOCKFILE_GetMappedView(
614 DWORD desired_access
)
616 MappedPage
* current
;
617 MappedPage
* newMappedPage
;
618 DWORD hioffset
, lowoffset
;
622 if (desired_access
== FILE_MAP_READ
)
623 current
= This
->headmap_ro
;
624 else if (desired_access
== FILE_MAP_WRITE
)
625 current
= This
->headmap_w
;
630 lowoffset
= PAGE_SIZE
* pagenum
;
632 while (current
->next
!= NULL
)
634 if (current
->next
->number
== pagenum
) /* page already mapped */
636 current
->next
->ref
++;
637 return current
->next
->lpBytes
;
639 else if (current
->next
->number
> pagenum
) /* this page is not mapped yet */
641 /* allocate new MappedPage
643 newMappedPage
= HeapAlloc(GetProcessHeap(), 0, sizeof(MappedPage
));
645 if (newMappedPage
== NULL
)
648 /* initialize the new MappedPage
650 newMappedPage
->number
= pagenum
;
651 newMappedPage
->ref
= 1;
653 newMappedPage
->next
= current
->next
;
654 current
->next
= newMappedPage
;
656 /* actually map the page
658 if (This
->filesize
.LowPart
< PAGE_SIZE
)
660 newMappedPage
->lpBytes
= MapViewOfFile(This
->hfilemap
,
663 This
->filesize
.LowPart
);
667 newMappedPage
->lpBytes
= MapViewOfFile(This
->hfilemap
,
674 return newMappedPage
->lpBytes
;
677 current
= current
->next
;
680 /* reached end of the list, this view is not mapped yet
682 if (current
->next
== NULL
)
684 /* allocate new MappedPage
686 newMappedPage
= HeapAlloc(GetProcessHeap(), 0, sizeof(MappedPage
));
688 if (newMappedPage
== NULL
)
691 /* initialize the new MappedPage
693 newMappedPage
->number
= pagenum
;
694 newMappedPage
->ref
= 1;
696 newMappedPage
->next
= NULL
;
697 current
->next
= newMappedPage
;
699 /* actually map the page
701 if (This
->filesize
.LowPart
< PAGE_SIZE
)
703 newMappedPage
->lpBytes
= MapViewOfFile(This
->hfilemap
,
707 This
->filesize
.LowPart
);
711 newMappedPage
->lpBytes
= MapViewOfFile(This
->hfilemap
,
718 return newMappedPage
->lpBytes
;
724 /******************************************************************************
725 * BIGBLOCKFILE_ReleaseMappedPage [PRIVATE]
727 * Decrements the reference count of the mapped page.
728 * If the page is not used anymore it will be unmapped.
730 static void BIGBLOCKFILE_ReleaseMappedPage(
735 MappedPage
* previous
;
736 MappedPage
* current
;
738 /* use the list corresponding to the desired access mode
740 if (access
== FILE_MAP_READ
)
741 previous
= This
->headmap_ro
;
742 else if (access
== FILE_MAP_WRITE
)
743 previous
= This
->headmap_w
;
747 current
= previous
->next
;
749 /* search for the page in the list
751 while (current
!= NULL
)
753 if (current
->number
== pagenum
)
755 /* decrement the reference count
759 if (current
->ref
== 0)
761 /* this page is not used anymore, we can unmap it
763 UnmapViewOfFile(current
->lpBytes
);
765 previous
->next
= current
->next
;
766 HeapFree(GetProcessHeap(), 0, current
);
774 current
= current
->next
;
779 /******************************************************************************
780 * BIGBLOCKFILE_FreeAllMappedPages [PRIVATE]
782 * Unmap all currently mapped pages.
783 * Empty both mapped pages lists.
785 static void BIGBLOCKFILE_FreeAllMappedPages(
789 * start with the read only list
791 MappedPage
* current
= This
->headmap_ro
->next
;
793 while (current
!= NULL
)
797 UnmapViewOfFile(current
->lpBytes
);
801 This
->headmap_ro
->next
= current
->next
;
802 HeapFree(GetProcessHeap(), 0, current
);
804 current
= This
->headmap_ro
->next
;
808 * then do the write list
810 current
= This
->headmap_w
->next
;
812 while (current
!= NULL
)
816 UnmapViewOfFile(current
->lpBytes
);
820 This
->headmap_w
->next
= current
->next
;
821 HeapFree(GetProcessHeap(), 0, current
);
823 current
= This
->headmap_w
->next
;