paul@342 | 1 | /* |
paul@342 | 2 | * A page mapper providing memory pages to satisfy file accesses, masking the |
paul@342 | 3 | * limits of a visible region of the file's contents. |
paul@342 | 4 | * |
paul@342 | 5 | * Copyright (C) 2021, 2022 Paul Boddie <paul@boddie.org.uk> |
paul@342 | 6 | * |
paul@342 | 7 | * This program is free software; you can redistribute it and/or |
paul@342 | 8 | * modify it under the terms of the GNU General Public License as |
paul@342 | 9 | * published by the Free Software Foundation; either version 2 of |
paul@342 | 10 | * the License, or (at your option) any later version. |
paul@342 | 11 | * |
paul@342 | 12 | * This program is distributed in the hope that it will be useful, |
paul@342 | 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
paul@342 | 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
paul@342 | 15 | * GNU General Public License for more details. |
paul@342 | 16 | * |
paul@342 | 17 | * You should have received a copy of the GNU General Public License |
paul@342 | 18 | * along with this program; if not, write to the Free Software |
paul@342 | 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, |
paul@342 | 20 | * Boston, MA 02110-1301, USA |
paul@342 | 21 | */ |
paul@342 | 22 | |
paul@342 | 23 | #include <string.h> |
paul@342 | 24 | |
paul@342 | 25 | #include "masked_page_mapper.h" |
paul@342 | 26 | |
paul@342 | 27 | |
paul@342 | 28 | |
paul@342 | 29 | /* Provide mapped pages populated using the given 'mapper', with a visible |
paul@342 | 30 | region defined that causes the limits of this region to be masked. */ |
paul@342 | 31 | |
paul@342 | 32 | MaskedPageMapper::MaskedPageMapper(GenericPageMapper *mapper, |
paul@342 | 33 | offset_t start_visible, offset_t end_visible) |
paul@342 | 34 | : _mapper(mapper), _start_visible(start_visible), _end_visible(end_visible) |
paul@342 | 35 | { |
paul@342 | 36 | _size = _mapper->get_data_size(); |
paul@342 | 37 | } |
paul@342 | 38 | |
paul@342 | 39 | MaskedPageMapper::~MaskedPageMapper() |
paul@342 | 40 | { |
paul@342 | 41 | if (_start_flexpage.region != NULL) |
paul@342 | 42 | _memory.release(_start_flexpage.region); |
paul@342 | 43 | |
paul@342 | 44 | if (_end_flexpage.region != NULL) |
paul@342 | 45 | _memory.release(_end_flexpage.region); |
paul@342 | 46 | |
paul@342 | 47 | if (_zero_flexpage.region != NULL) |
paul@342 | 48 | _memory.release(_zero_flexpage.region); |
paul@342 | 49 | } |
paul@342 | 50 | |
paul@342 | 51 | /* Interface for the pager. */ |
paul@342 | 52 | |
paul@342 | 53 | /* Return a flexpage providing access to the indicated file 'offset'. |
paul@342 | 54 | |
paul@342 | 55 | The returned flexpage will either provide direct access to the underlying |
paul@342 | 56 | file content or be a completely new flexpage containing masked content. */ |
paul@342 | 57 | |
paul@342 | 58 | Flexpage *MaskedPageMapper::get(offset_t offset, map_flags_t flags) |
paul@342 | 59 | { |
paul@342 | 60 | Flexpage *f = _mapper->get(offset, flags); |
paul@342 | 61 | Flexpage *mf = get_masked_flexpage(f); |
paul@342 | 62 | |
paul@342 | 63 | if (f != mf) |
paul@342 | 64 | _mapper->queue(f); |
paul@342 | 65 | |
paul@342 | 66 | return mf; |
paul@342 | 67 | } |
paul@342 | 68 | |
paul@342 | 69 | /* Queue the given 'flexpage'. */ |
paul@342 | 70 | |
paul@342 | 71 | void MaskedPageMapper::queue(Flexpage *flexpage) |
paul@342 | 72 | { |
paul@342 | 73 | if (dynamic_cast<MaskedFlexpage *>(flexpage) == NULL) |
paul@342 | 74 | _mapper->queue(flexpage); |
paul@342 | 75 | } |
paul@342 | 76 | |
paul@342 | 77 | /* Flush pages in the given range from 'start' with 'size'. */ |
paul@342 | 78 | |
paul@342 | 79 | void MaskedPageMapper::flush_all(offset_t start, offset_t size) |
paul@342 | 80 | { |
paul@342 | 81 | /* NOTE: This might be superfluous since masked regions probably should be |
paul@342 | 82 | read-only or copy-on-write. */ |
paul@342 | 83 | |
paul@342 | 84 | _mapper->flush_all(start, size); |
paul@342 | 85 | } |
paul@342 | 86 | |
paul@342 | 87 | /* Return the maximum extent of the mapped resource. */ |
paul@342 | 88 | |
paul@342 | 89 | offset_t MaskedPageMapper::get_data_size() |
paul@342 | 90 | { |
paul@342 | 91 | return _size; |
paul@342 | 92 | } |
paul@342 | 93 | |
paul@342 | 94 | /* Set the maximum extent of the mapped resource. */ |
paul@342 | 95 | |
paul@342 | 96 | void MaskedPageMapper::set_data_size(offset_t size) |
paul@342 | 97 | { |
paul@342 | 98 | _size = size; |
paul@342 | 99 | } |
paul@342 | 100 | |
paul@342 | 101 | /* Internal flexpage retrieval methods. */ |
paul@342 | 102 | |
paul@342 | 103 | /* Detect flexpages with masked content, introducing separate flexpages |
paul@342 | 104 | offering masked content from the same region. */ |
paul@342 | 105 | |
paul@342 | 106 | Flexpage *MaskedPageMapper::get_masked_flexpage(Flexpage *flexpage) |
paul@342 | 107 | { |
paul@342 | 108 | /* Determine whether the flexpage involves the limits of the visible |
paul@342 | 109 | region or is beyond such limits. */ |
paul@342 | 110 | |
paul@342 | 111 | bool has_start = flexpage->supports_position(_start_visible) && |
paul@342 | 112 | flexpage->base_offset != _start_visible; |
paul@342 | 113 | bool has_end = flexpage->supports_position(_end_visible); |
paul@342 | 114 | bool has_zero = (flexpage->base_offset >= _end_visible) || |
paul@342 | 115 | (flexpage->base_offset + flexpage->size < _start_visible); |
paul@342 | 116 | |
paul@342 | 117 | /* Return the original flexpage within the visible limits. */ |
paul@342 | 118 | |
paul@342 | 119 | if (!has_start && !has_end && !has_zero) |
paul@342 | 120 | return flexpage; |
paul@342 | 121 | |
paul@342 | 122 | /* Allocate and populate a region in one of the preallocated flexpages for |
paul@342 | 123 | masked content. */ |
paul@342 | 124 | |
paul@342 | 125 | Flexpage &masked = has_start ? _start_flexpage : |
paul@342 | 126 | has_end ? _end_flexpage : |
paul@342 | 127 | _zero_flexpage; |
paul@342 | 128 | |
paul@342 | 129 | allocate_region(flexpage, masked); |
paul@342 | 130 | populate_region(flexpage, masked, has_start, has_end); |
paul@342 | 131 | |
paul@342 | 132 | /* Associate the preallocated flexpage with the original flexpage. */ |
paul@342 | 133 | |
paul@342 | 134 | flexpage->associate(&masked); |
paul@342 | 135 | |
paul@342 | 136 | return &masked; |
paul@342 | 137 | } |
paul@342 | 138 | |
paul@342 | 139 | void MaskedPageMapper::allocate_region(Flexpage *flexpage, Flexpage &masked) |
paul@342 | 140 | { |
paul@342 | 141 | offset_t needed = flexpage->region->size(); |
paul@342 | 142 | |
paul@342 | 143 | /* Attempt to re-use an existing region. */ |
paul@342 | 144 | |
paul@342 | 145 | if (masked.region != NULL) |
paul@342 | 146 | { |
paul@342 | 147 | if (masked.region->size() == needed) |
paul@342 | 148 | return; |
paul@342 | 149 | else |
paul@342 | 150 | _memory.release(masked.region); |
paul@342 | 151 | } |
paul@342 | 152 | |
paul@342 | 153 | /* Set the region in the flexpage. */ |
paul@342 | 154 | |
paul@342 | 155 | masked.set_region(_memory.region(needed)); |
paul@342 | 156 | masked.reset(flexpage->base_offset); |
paul@342 | 157 | } |
paul@342 | 158 | |
paul@342 | 159 | void MaskedPageMapper::populate_region(Flexpage *flexpage, Flexpage &masked, |
paul@342 | 160 | bool has_start, bool has_end) |
paul@342 | 161 | { |
paul@342 | 162 | /* Without any limits involved, provide a zero flexpage. */ |
paul@342 | 163 | |
paul@342 | 164 | if (!has_start && !has_end) |
paul@342 | 165 | { |
paul@342 | 166 | memset((void *) masked.region->start, 0, masked.size); |
paul@342 | 167 | return; |
paul@342 | 168 | } |
paul@342 | 169 | |
paul@342 | 170 | /* Determine the content limits given start and/or end offsets. */ |
paul@342 | 171 | |
paul@342 | 172 | offset_t start_offset = has_start ? _start_visible - masked.base_offset : 0; |
paul@342 | 173 | offset_t end_offset = has_end ? _end_visible - masked.base_offset : masked.size; |
paul@342 | 174 | |
paul@342 | 175 | if (has_start) |
paul@342 | 176 | memset((void *) masked.region->start, 0, start_offset); |
paul@342 | 177 | |
paul@342 | 178 | memcpy((void *) (masked.region->start + start_offset), |
paul@342 | 179 | (const void *) (flexpage->region->start + start_offset), |
paul@342 | 180 | end_offset - start_offset); |
paul@342 | 181 | |
paul@342 | 182 | if (has_end) |
paul@342 | 183 | memset((void *) (masked.region->start + end_offset), 0, masked.size - end_offset); |
paul@342 | 184 | } |
paul@342 | 185 | |
paul@342 | 186 | |
paul@342 | 187 | |
paul@342 | 188 | /* Masked flexpage constructor. */ |
paul@342 | 189 | |
paul@342 | 190 | MaskedFlexpage::MaskedFlexpage(Region *region) |
paul@342 | 191 | : Flexpage(region) |
paul@342 | 192 | { |
paul@342 | 193 | } |
paul@342 | 194 | |
paul@342 | 195 | // vim: tabstop=4 expandtab shiftwidth=4 |