2 * hed - Hexadecimal editor
3 * Copyright (C) 2011 Petr Tesarik <petr@tesarici.cz>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 /* Feature macros needed for:
29 #include <sys/types.h>
35 #include <util/lists.h>
39 #ifdef HED_CONFIG_SWAP
44 # define SDEBUG(x...) fprintf(stderr, x)
49 /* A swap file structure:
52 * SWAP_BLOCK_SIZE 1st mapping
53 * ... Allocated blocks
54 * SWAP_BLOCK_SIZE + SWP_MAPEXTENT 2nd mapping
59 struct list_head list
;
62 struct list_head alloclist
;
63 struct list_head freelist
;
67 struct list_head list
;
68 unsigned off
; /* Offset from block beginning */
69 unsigned size
; /* Size is max SWAP_BLOCK_SIZE */
72 #define SWP_MAPSIZE ((SWAP_BLOCK_SIZE) / sizeof(void*))
73 #define SWP_MAPMASK ((signed long)SWP_MAPSIZE - 1)
74 #define SWP_MAPEXTENT ((SWAP_BLOCK_SIZE) * (SWP_MAPSIZE))
75 #define SWP_MAPEXTSHIFT (2*SWAP_BLOCK_SHIFT - bitsize(sizeof(void*)))
76 #define SWP_ALLOC_ALIGN (8)
78 static struct swp_block
*swp_bappend(struct swp_file
*swp
);
83 swp_dump(struct swp_file
*swp
)
85 struct swp_block
*block
;
88 fputs("-- swap file dump --\n", stderr
);
90 fprintf(stderr
, "size: %ld\n", (long) swp
->size
);
92 fputs("free blocks:\n", stderr
);
93 list_for_each_entry(block
, &swp
->freeblocks
, list
) {
94 fprintf(stderr
, "%p: [%p] @freed %ld\n",
95 block
, block
->paddr
, (long) block
->pos
);
96 assert(list_empty(&block
->alloclist
));
97 assert(list_empty(&block
->freelist
));
100 fputs("used blocks:\n", stderr
);
101 list_for_each_entry(block
, &swp
->usedblocks
, list
) {
102 fprintf(stderr
, "%p: [%p] @%p %ld\n",
103 block
, block
->paddr
, *block
->paddr
,
106 if (!list_empty(&block
->alloclist
))
107 fputs(" allocations:\n", stderr
);
108 list_for_each_entry(a
, &block
->alloclist
, list
)
109 fprintf(stderr
, " %p: 0x%x + %u\n",
112 if (!list_empty(&block
->freelist
))
113 fputs(" free list: \n", stderr
);
114 list_for_each_entry (a
, &block
->freelist
, list
)
115 fprintf(stderr
, " %p: 0x%x + %u\n",
119 fputs("-- swap file dump end--\n", stderr
);
122 #else /* SWAP_DEBUG */
124 #define swp_dump(s) do{} while(0)
128 #ifdef HED_CONFIG_SWAP_MMAP
130 #include <sys/mman.h>
132 #define SWP_PAGE_INVALID MAP_FAILED
135 swp_init_flags(struct swp_file
*swp
, int flags
)
137 swp
->mmap_prot
= (flags
& O_ACCMODE
) == O_RDWR
138 ? PROT_READ
| PROT_WRITE
140 swp
->mmap_flags
= swp
->fd
< 0
141 ? MAP_PRIVATE
| MAP_ANONYMOUS
145 /* Set the file size. */
147 swp_setsize(struct swp_file
*swp
)
150 ? ftruncate(swp
->fd
, swp
->size
+ SWAP_BLOCK_SIZE
)
154 /* Allocate data for a file block */
156 swp_page_alloc(struct swp_file
*swp
, hed_off_t pos
)
158 return mmap(NULL
, SWAP_BLOCK_SIZE
,
159 swp
->mmap_prot
, swp
->mmap_flags
,
163 /* De-allocate data for a file block */
165 swp_page_free(struct swp_file
*swp
, void *ptr
)
167 munmap(ptr
, SWAP_BLOCK_SIZE
);
171 swp_page_read(struct swp_file
*swp
, void *ptr
, hed_off_t pos
)
177 swp_page_write(struct swp_file
*swp
, void *ptr
, hed_off_t pos
)
179 return msync(ptr
, SWAP_BLOCK_SIZE
, MS_ASYNC
);
182 #else /* HED_CONFIG_SWAP_MMAP */
184 #define SWP_PAGE_INVALID NULL
187 swp_init_flags(struct swp_file
*swp
, int flags
)
191 /* Set the file size. */
193 swp_setsize(struct swp_file
*swp
)
198 /* Allocate data for a file block */
200 swp_page_alloc(struct swp_file
*swp
, hed_off_t pos
)
202 return malloc(SWAP_BLOCK_SIZE
);
205 /* De-allocate data for a file block */
207 swp_page_free(struct swp_file
*swp
, void *ptr
)
213 swp_page_read(struct swp_file
*swp
, void *ptr
, hed_off_t pos
)
215 return SWAP_BLOCK_SIZE
- pread(swp
->fd
, ptr
, SWAP_BLOCK_SIZE
, pos
);
219 swp_page_write(struct swp_file
*swp
, void *ptr
, hed_off_t pos
)
221 return SWAP_BLOCK_SIZE
- pwrite(swp
->fd
, ptr
, SWAP_BLOCK_SIZE
, pos
);
224 #endif /* HED_CONFIG_SWAP_MMAP */
227 swp_close(struct swp_file
*swp
)
229 int ret
= swp
->fd
>= 0
236 static struct swp_file
*
237 swp_init(const char *name
, int openflags
)
239 struct swp_file
*swp
= calloc(1, sizeof(struct swp_file
));
241 INIT_LIST_HEAD(&swp
->freeblocks
);
242 INIT_LIST_HEAD(&swp
->usedblocks
);
243 swp
->fd
= open(name
, openflags
, 0664);
244 swp_init_flags(swp
, openflags
);
250 swp_init_write(const char *name
)
252 struct swp_file
*swp
;
253 struct swp_header
*hdr
;
255 if (! (swp
= swp_init(name
, O_RDWR
| O_CREAT
)) )
258 if (swp_setsize(swp
))
260 hdr
= swp_page_alloc(swp
, 0);
261 if (hdr
== SWP_PAGE_INVALID
)
265 memcpy(hdr
->signature
, SWAP_SIGNATURE
, SIG_LEN
);
266 hdr
->version
= SWAP_VERSION
;
267 hdr
->offsize
= sizeof(hed_off_t
);
277 swp_done(struct swp_file
*swp
)
279 struct swp_alloc
*cura
, *nexta
;
280 struct swp_block
*curb
, *nextb
;
283 /* Free all swap file pages */
284 list_for_each_entry_safe_reverse(curb
, nextb
, &swp
->usedblocks
, list
) {
285 list_for_each_entry_safe(cura
, nexta
, &curb
->alloclist
, list
)
287 list_for_each_entry_safe(cura
, nexta
, &curb
->freelist
, list
)
289 swp_page_free(swp
, *curb
->paddr
);
292 list_for_each_entry_safe(curb
, nextb
, &swp
->freeblocks
, list
)
297 swp_page_free(swp
, swp
->hdr
);
299 ret
= swp_close(swp
);
304 /* Get an existing mapping */
306 swp_getmapping(struct swp_file
*swp
, hed_off_t pos
)
308 int mapnum
= pos
>> SWP_MAPEXTSHIFT
;
309 unsigned long mapoff
= (pos
>> SWAP_BLOCK_SHIFT
) & SWP_MAPMASK
;
310 assert(mapnum
< swp
->nmaps
);
311 return swp
->maps
[mapnum
] + mapoff
;
314 /* Append a new mapping at EOF */
316 swp_appendmapping(struct swp_file
*swp
)
318 if (swp
->size
== (swp
->nmaps
<< SWP_MAPEXTSHIFT
)) {
321 struct swp_block
*block
;
323 mapvec
= realloc(swp
->maps
,
324 (swp
->nmaps
+ 1) * sizeof(void **));
329 newmap
= swp_page_alloc(swp
, swp
->size
+ SWAP_BLOCK_SIZE
);
330 if (newmap
== SWP_PAGE_INVALID
)
332 swp
->maps
[swp
->nmaps
++] = newmap
;
334 block
= swp_bappend(swp
);
337 swp_page_free(swp
, newmap
);
340 *block
->paddr
= newmap
;
342 return swp_getmapping(swp
, swp
->size
);
345 /* Mark a mapping as free */
347 swp_freemapping(struct swp_file
*swp
, void **paddr
)
349 *paddr
= SWP_PAGE_INVALID
;
351 if (swp
->size
== ((swp
->nmaps
-1) << SWP_MAPEXTSHIFT
) + SWAP_BLOCK_SIZE
) {
352 struct swp_block
*block
;
354 assert(!list_empty(&swp
->usedblocks
));
355 block
= list_entry(swp
->usedblocks
.prev
,
356 struct swp_block
, list
);
358 assert(*block
->paddr
== swp
->maps
[swp
->nmaps
-1]);
359 assert(block
->paddr
== *block
->paddr
);
361 list_del(&block
->list
);
362 swp_page_free(swp
, block
->paddr
);
363 swp
->size
-= SWAP_BLOCK_SIZE
;
369 /* Append a page at EOF */
370 static struct swp_block
*
371 swp_bappend(struct swp_file
*swp
)
373 struct swp_block
*ret
;
374 if (! (ret
= malloc(sizeof(struct swp_block
))) )
376 if (! (ret
->paddr
= swp_appendmapping(swp
)) )
379 INIT_LIST_HEAD(&ret
->list
);
380 ret
->pos
= swp
->size
;
381 INIT_LIST_HEAD(&ret
->alloclist
);
382 INIT_LIST_HEAD(&ret
->freelist
);
384 swp
->size
+= SWAP_BLOCK_SIZE
;
385 if (swp_setsize(swp
))
387 list_add_tail(&ret
->list
, &swp
->usedblocks
);
391 swp_freemapping(swp
, ret
->paddr
);
399 swp_bfree(struct swp_file
*swp
, struct swp_block
*block
)
401 struct swp_block
*cur
;
403 if (*block
->paddr
!= SWP_PAGE_INVALID
)
404 swp_page_free(swp
, *block
->paddr
);
406 list_for_each_entry_reverse(cur
, &swp
->freeblocks
, list
) {
407 if (cur
->pos
< block
->pos
)
410 list_move(&block
->list
, &cur
->list
);
412 while (swp
->freeblocks
.prev
!= &swp
->freeblocks
) {
413 struct swp_block
*cur
= list_entry(swp
->freeblocks
.prev
,
414 struct swp_block
, list
);
415 if (cur
->pos
!= swp
->size
- SWAP_BLOCK_SIZE
)
418 swp
->size
-= SWAP_BLOCK_SIZE
;
420 swp_freemapping(swp
, cur
->paddr
);
421 list_del(&cur
->list
);
426 static struct swp_block
*
427 swp_balloc(struct swp_file
*swp
)
429 struct swp_block
*ret
= list_entry(swp
->freeblocks
.next
,
430 struct swp_block
, list
);
432 if (&ret
->list
!= &swp
->freeblocks
) {
433 struct swp_block
*cur
;
434 list_for_each_entry_reverse(cur
, &swp
->usedblocks
, list
)
435 if (cur
->pos
< ret
->pos
)
437 list_move(&ret
->list
, &cur
->list
);
438 } else if (! (ret
= swp_bappend(swp
)) )
441 *ret
->paddr
= swp_page_alloc(swp
, ret
->pos
+ SWAP_BLOCK_SIZE
);
442 if (*ret
->paddr
== SWP_PAGE_INVALID
) {
450 static struct swp_alloc
*
451 swp_malloc_new(struct swp_file
*swp
, struct swp_block
**pblock
)
453 struct swp_block
*block
;
454 struct swp_alloc
*ret
;
456 if (! (ret
= malloc(sizeof(struct swp_alloc
))) )
458 if (! (block
= swp_balloc(swp
)) )
462 ret
->size
= SWAP_BLOCK_SIZE
;
473 static struct list_head
*
474 freelist_pos(struct swp_block
*block
, unsigned off
)
477 list_for_each_entry(a
, &block
->freelist
, list
)
484 swp_split(struct swp_block
*block
, struct swp_alloc
*a
, unsigned splitpoint
)
487 struct list_head
*pos
;
488 struct swp_alloc
*next
, *prev
;
490 splitpoint
+= SWP_ALLOC_ALIGN
- 1;
491 splitpoint
-= splitpoint
% SWP_ALLOC_ALIGN
;
492 remain
= a
->size
- splitpoint
;
497 /* Find the neighbours */
498 pos
= freelist_pos(block
, a
->off
);
499 if (pos
!= &block
->freelist
) {
500 next
= list_entry(pos
, struct swp_alloc
, list
);
501 if (next
->off
!= a
->off
+ a
->size
)
507 if (pos
!= &block
->freelist
) {
508 prev
= list_entry(pos
, struct swp_alloc
, list
);
509 if (prev
->off
+ prev
->size
!= a
->off
)
516 prev
->size
+= next
->size
;
517 list_del(&next
->list
);
519 } else if (prev
->size
< next
->size
)
524 prev
->size
+= remain
;
527 next
->size
+= remain
;
528 } else if (splitpoint
) {
529 if (! (next
= malloc(sizeof(struct swp_alloc
))) )
531 next
->off
= a
->off
+ splitpoint
;
533 list_add(&next
->list
, pos
);
535 list_move(&a
->list
, pos
);
539 a
->size
= splitpoint
;
546 static struct swp_alloc
*
547 find_free_alloc(struct swp_file
*swp
, size_t size
, struct swp_block
**pblock
,
548 struct list_head
*endnode
)
550 struct list_head
*blknode
;
552 assert(size
<= SWAP_BLOCK_SIZE
);
554 blknode
= swp
->usedblocks
.next
;
555 while (blknode
!= endnode
) {
556 struct swp_block
*block
=
557 list_entry(blknode
, struct swp_block
, list
);
559 list_for_each_entry(a
, &block
->freelist
, list
) {
560 if (a
->size
>= size
) {
566 blknode
= blknode
->next
;
573 swp_malloc(struct swp_file
*swp
, size_t size
)
575 struct swp_block
*block
;
578 a
= find_free_alloc(swp
, size
, &block
, &swp
->usedblocks
);
579 if (!a
&& ! (a
= swp_malloc_new(swp
, &block
)) )
582 list_add(&a
->list
, &block
->alloclist
);
584 swp_split(block
, a
, size
);
586 SDEBUG("Allocated %ld bytes at %p\n",
587 (long)size
, *block
->paddr
+ a
->off
);
590 return *block
->paddr
+ a
->off
;
594 swp_zalloc(struct swp_file
*swp
, size_t size
)
596 void *ret
= swp_malloc(swp
, size
);
598 memset(ret
, 0, size
);
603 do_free(struct swp_file
*swp
, struct swp_block
*block
, struct swp_alloc
*a
)
605 swp_split(block
, a
, 0);
607 if (list_empty(&block
->alloclist
)) {
608 struct swp_alloc
*cur
, *next
;
609 list_for_each_entry_safe(cur
, next
, &block
->freelist
, list
)
611 INIT_LIST_HEAD(&block
->freelist
);
612 swp_bfree(swp
, block
);
616 static struct swp_alloc
*
617 swp_find_alloc(struct swp_file
*swp
, void *ptr
, struct swp_block
**pblock
)
619 struct swp_block
*block
;
623 list_for_each_entry(block
, &swp
->usedblocks
, list
) {
624 off
= ptr
- *block
->paddr
;
625 if (off
< SWAP_BLOCK_SIZE
)
629 /* Fail if we cannot find the block */
630 assert(&block
->list
!= &swp
->usedblocks
);
632 list_for_each_entry(a
, &block
->alloclist
, list
)
636 /* Fail if we get an unallocated @ptr */
637 assert(&a
->list
!= &block
->alloclist
);
644 swp_shrink(struct swp_file
*swp
, void *ptr
, size_t newsize
)
646 struct swp_block
*block
, *newblock
;
647 struct swp_alloc
*a
, *newa
;
649 SDEBUG("Shrink %p to %ld\n", ptr
, (long)newsize
);
651 a
= swp_find_alloc(swp
, ptr
, &block
);
653 newa
= find_free_alloc(swp
, newsize
, &newblock
, &block
->list
);
655 list_add(&newa
->list
, &newblock
->alloclist
);
656 memcpy(*newblock
->paddr
+ newa
->off
, ptr
, newsize
);
657 do_free(swp
, block
, a
);
661 swp_split(block
, a
, newsize
);
664 return *block
->paddr
+ a
->off
;
668 swp_free(struct swp_file
*swp
, void *ptr
)
671 struct swp_block
*block
;
673 SDEBUG("Free %p\n", ptr
);
675 a
= swp_find_alloc(swp
, ptr
, &block
);
676 do_free(swp
, block
, a
);
682 swp_write(struct swp_file
*swp
)
684 struct swp_block
*block
;
687 SDEBUG("Write swap file\n");
690 if (swp_page_write(swp
, swp
->hdr
, 0))
693 list_for_each_entry(block
, &swp
->usedblocks
, list
)
694 if (swp_page_write(swp
, *block
->paddr
,
695 block
->pos
+ SWAP_BLOCK_SIZE
))
702 read_header(struct swp_file
*swp
)
704 struct swp_header
*hdr
;
709 /* Read the header block */
710 hdr
= swp_page_alloc(swp
, 0);
711 if (hdr
== SWP_PAGE_INVALID
)
715 if (swp_page_read(swp
, hdr
, 0))
719 if (memcmp(hdr
->signature
, SWAP_SIGNATURE
, SIG_LEN
))
721 if (hdr
->version
!= SWAP_VERSION
)
723 if (hdr
->offsize
!= sizeof(hed_off_t
))
726 /* Allocate the maps */
727 if ( (size
= lseek(swp
->fd
, 0, SEEK_END
)) < 0)
729 swp
->size
= size
- SWAP_BLOCK_SIZE
;
730 swp
->nmaps
= ((swp
->size
- SWAP_BLOCK_SIZE
) >> SWP_MAPEXTSHIFT
) + 1;
731 swp
->maps
= calloc(swp
->nmaps
, sizeof(void **));
735 /* Read in the maps */
736 mapoff
= SWAP_BLOCK_SIZE
;
737 for (i
= 0; i
< swp
->nmaps
; ++i
, mapoff
+= SWP_MAPEXTENT
) {
738 swp
->maps
[i
] = swp_page_alloc(swp
, mapoff
);
739 if (swp
->maps
[i
] == SWP_PAGE_INVALID
)
741 if (swp_page_read(swp
, swp
->maps
[i
], mapoff
))
749 swp_init_read(const char *name
)
751 struct swp_file
*swp
;
753 if (! (swp
= swp_init(name
, O_RDONLY
)) )
756 if (read_header(swp
)) {
765 addr2off(struct swp_file
*swp
, void *ptr
)
768 for (i
= 0; i
< swp
->nmaps
; ++i
)
769 for (j
= 0; j
< SWP_MAPSIZE
; ++j
) {
770 size_t off
= ptr
- swp
->maps
[i
][j
];
771 if (off
< SWAP_BLOCK_SIZE
)
772 return SWAP_BLOCK_SIZE
+ off
+
773 (i
<< SWP_MAPEXTSHIFT
) +
774 (j
<< SWAP_BLOCK_SHIFT
);
780 swp_cpin(struct swp_file
*swp
, void *buf
, void *ptr
, size_t len
)
782 hed_off_t off
= addr2off(swp
, ptr
);
785 return len
- pread(swp
->fd
, buf
, len
, off
);
788 #endif /* HED_CONFIG_SWAP */