├── LICENSE.md ├── README.md ├── memory_mapped_file.cpp └── memory_mapped_file.hpp /LICENSE.md: -------------------------------------------------------------------------------- 1 | Mozilla Public License, version 2.0 2 | 3 | 1. Definitions 4 | 5 | 1.1. "Contributor" 6 | 7 | means each individual or legal entity that creates, contributes to the 8 | creation of, or owns Covered Software. 9 | 10 | 1.2. "Contributor Version" 11 | 12 | means the combination of the Contributions of others (if any) used by a 13 | Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | 17 | means Covered Software of a particular Contributor. 18 | 19 | 1.4. "Covered Software" 20 | 21 | means Source Code Form to which the initial Contributor has attached the 22 | notice in Exhibit A, the Executable Form of such Source Code Form, and 23 | Modifications of such Source Code Form, in each case including portions 24 | thereof. 25 | 26 | 1.5. "Incompatible With Secondary Licenses" 27 | means 28 | 29 | a. that the initial Contributor has attached the notice described in 30 | Exhibit B to the Covered Software; or 31 | 32 | b. that the Covered Software was made available under the terms of 33 | version 1.1 or earlier of the License, but not also under the terms of 34 | a Secondary License. 35 | 36 | 1.6. "Executable Form" 37 | 38 | means any form of the work other than Source Code Form. 39 | 40 | 1.7. "Larger Work" 41 | 42 | means a work that combines Covered Software with other material, in a 43 | separate file or files, that is not Covered Software. 44 | 45 | 1.8. "License" 46 | 47 | means this document. 48 | 49 | 1.9. "Licensable" 50 | 51 | means having the right to grant, to the maximum extent possible, whether 52 | at the time of the initial grant or subsequently, any and all of the 53 | rights conveyed by this License. 54 | 55 | 1.10. "Modifications" 56 | 57 | means any of the following: 58 | 59 | a. any file in Source Code Form that results from an addition to, 60 | deletion from, or modification of the contents of Covered Software; or 61 | 62 | b. any new file in Source Code Form that contains any Covered Software. 63 | 64 | 1.11. "Patent Claims" of a Contributor 65 | 66 | means any patent claim(s), including without limitation, method, 67 | process, and apparatus claims, in any patent Licensable by such 68 | Contributor that would be infringed, but for the grant of the License, 69 | by the making, using, selling, offering for sale, having made, import, 70 | or transfer of either its Contributions or its Contributor Version. 71 | 72 | 1.12. "Secondary License" 73 | 74 | means either the GNU General Public License, Version 2.0, the GNU Lesser 75 | General Public License, Version 2.1, the GNU Affero General Public 76 | License, Version 3.0, or any later versions of those licenses. 77 | 78 | 1.13. "Source Code Form" 79 | 80 | means the form of the work preferred for making modifications. 81 | 82 | 1.14. "You" (or "Your") 83 | 84 | means an individual or a legal entity exercising rights under this 85 | License. For legal entities, "You" includes any entity that controls, is 86 | controlled by, or is under common control with You. For purposes of this 87 | definition, "control" means (a) the power, direct or indirect, to cause 88 | the direction or management of such entity, whether by contract or 89 | otherwise, or (b) ownership of more than fifty percent (50%) of the 90 | outstanding shares or beneficial ownership of such entity. 91 | 92 | 93 | 2. License Grants and Conditions 94 | 95 | 2.1. Grants 96 | 97 | Each Contributor hereby grants You a world-wide, royalty-free, 98 | non-exclusive license: 99 | 100 | a. under intellectual property rights (other than patent or trademark) 101 | Licensable by such Contributor to use, reproduce, make available, 102 | modify, display, perform, distribute, and otherwise exploit its 103 | Contributions, either on an unmodified basis, with Modifications, or 104 | as part of a Larger Work; and 105 | 106 | b. under Patent Claims of such Contributor to make, use, sell, offer for 107 | sale, have made, import, and otherwise transfer either its 108 | Contributions or its Contributor Version. 109 | 110 | 2.2. Effective Date 111 | 112 | The licenses granted in Section 2.1 with respect to any Contribution 113 | become effective for each Contribution on the date the Contributor first 114 | distributes such Contribution. 115 | 116 | 2.3. Limitations on Grant Scope 117 | 118 | The licenses granted in this Section 2 are the only rights granted under 119 | this License. No additional rights or licenses will be implied from the 120 | distribution or licensing of Covered Software under this License. 121 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 122 | Contributor: 123 | 124 | a. for any code that a Contributor has removed from Covered Software; or 125 | 126 | b. for infringements caused by: (i) Your and any other third party's 127 | modifications of Covered Software, or (ii) the combination of its 128 | Contributions with other software (except as part of its Contributor 129 | Version); or 130 | 131 | c. under Patent Claims infringed by Covered Software in the absence of 132 | its Contributions. 133 | 134 | This License does not grant any rights in the trademarks, service marks, 135 | or logos of any Contributor (except as may be necessary to comply with 136 | the notice requirements in Section 3.4). 137 | 138 | 2.4. Subsequent Licenses 139 | 140 | No Contributor makes additional grants as a result of Your choice to 141 | distribute the Covered Software under a subsequent version of this 142 | License (see Section 10.2) or under the terms of a Secondary License (if 143 | permitted under the terms of Section 3.3). 144 | 145 | 2.5. Representation 146 | 147 | Each Contributor represents that the Contributor believes its 148 | Contributions are its original creation(s) or it has sufficient rights to 149 | grant the rights to its Contributions conveyed by this License. 150 | 151 | 2.6. Fair Use 152 | 153 | This License is not intended to limit any rights You have under 154 | applicable copyright doctrines of fair use, fair dealing, or other 155 | equivalents. 156 | 157 | 2.7. Conditions 158 | 159 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in 160 | Section 2.1. 161 | 162 | 163 | 3. Responsibilities 164 | 165 | 3.1. Distribution of Source Form 166 | 167 | All distribution of Covered Software in Source Code Form, including any 168 | Modifications that You create or to which You contribute, must be under 169 | the terms of this License. You must inform recipients that the Source 170 | Code Form of the Covered Software is governed by the terms of this 171 | License, and how they can obtain a copy of this License. You may not 172 | attempt to alter or restrict the recipients' rights in the Source Code 173 | Form. 174 | 175 | 3.2. Distribution of Executable Form 176 | 177 | If You distribute Covered Software in Executable Form then: 178 | 179 | a. such Covered Software must also be made available in Source Code Form, 180 | as described in Section 3.1, and You must inform recipients of the 181 | Executable Form how they can obtain a copy of such Source Code Form by 182 | reasonable means in a timely manner, at a charge no more than the cost 183 | of distribution to the recipient; and 184 | 185 | b. You may distribute such Executable Form under the terms of this 186 | License, or sublicense it under different terms, provided that the 187 | license for the Executable Form does not attempt to limit or alter the 188 | recipients' rights in the Source Code Form under this License. 189 | 190 | 3.3. Distribution of a Larger Work 191 | 192 | You may create and distribute a Larger Work under terms of Your choice, 193 | provided that You also comply with the requirements of this License for 194 | the Covered Software. If the Larger Work is a combination of Covered 195 | Software with a work governed by one or more Secondary Licenses, and the 196 | Covered Software is not Incompatible With Secondary Licenses, this 197 | License permits You to additionally distribute such Covered Software 198 | under the terms of such Secondary License(s), so that the recipient of 199 | the Larger Work may, at their option, further distribute the Covered 200 | Software under the terms of either this License or such Secondary 201 | License(s). 202 | 203 | 3.4. Notices 204 | 205 | You may not remove or alter the substance of any license notices 206 | (including copyright notices, patent notices, disclaimers of warranty, or 207 | limitations of liability) contained within the Source Code Form of the 208 | Covered Software, except that You may alter any license notices to the 209 | extent required to remedy known factual inaccuracies. 210 | 211 | 3.5. Application of Additional Terms 212 | 213 | You may choose to offer, and to charge a fee for, warranty, support, 214 | indemnity or liability obligations to one or more recipients of Covered 215 | Software. However, You may do so only on Your own behalf, and not on 216 | behalf of any Contributor. You must make it absolutely clear that any 217 | such warranty, support, indemnity, or liability obligation is offered by 218 | You alone, and You hereby agree to indemnify every Contributor for any 219 | liability incurred by such Contributor as a result of warranty, support, 220 | indemnity or liability terms You offer. You may include additional 221 | disclaimers of warranty and limitations of liability specific to any 222 | jurisdiction. 223 | 224 | 4. Inability to Comply Due to Statute or Regulation 225 | 226 | If it is impossible for You to comply with any of the terms of this License 227 | with respect to some or all of the Covered Software due to statute, 228 | judicial order, or regulation then You must: (a) comply with the terms of 229 | this License to the maximum extent possible; and (b) describe the 230 | limitations and the code they affect. Such description must be placed in a 231 | text file included with all distributions of the Covered Software under 232 | this License. Except to the extent prohibited by statute or regulation, 233 | such description must be sufficiently detailed for a recipient of ordinary 234 | skill to be able to understand it. 235 | 236 | 5. Termination 237 | 238 | 5.1. The rights granted under this License will terminate automatically if You 239 | fail to comply with any of its terms. However, if You become compliant, 240 | then the rights granted under this License from a particular Contributor 241 | are reinstated (a) provisionally, unless and until such Contributor 242 | explicitly and finally terminates Your grants, and (b) on an ongoing 243 | basis, if such Contributor fails to notify You of the non-compliance by 244 | some reasonable means prior to 60 days after You have come back into 245 | compliance. Moreover, Your grants from a particular Contributor are 246 | reinstated on an ongoing basis if such Contributor notifies You of the 247 | non-compliance by some reasonable means, this is the first time You have 248 | received notice of non-compliance with this License from such 249 | Contributor, and You become compliant prior to 30 days after Your receipt 250 | of the notice. 251 | 252 | 5.2. If You initiate litigation against any entity by asserting a patent 253 | infringement claim (excluding declaratory judgment actions, 254 | counter-claims, and cross-claims) alleging that a Contributor Version 255 | directly or indirectly infringes any patent, then the rights granted to 256 | You by any and all Contributors for the Covered Software under Section 257 | 2.1 of this License shall terminate. 258 | 259 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user 260 | license agreements (excluding distributors and resellers) which have been 261 | validly granted by You or Your distributors under this License prior to 262 | termination shall survive termination. 263 | 264 | 6. Disclaimer of Warranty 265 | 266 | Covered Software is provided under this License on an "as is" basis, 267 | without warranty of any kind, either expressed, implied, or statutory, 268 | including, without limitation, warranties that the Covered Software is free 269 | of defects, merchantable, fit for a particular purpose or non-infringing. 270 | The entire risk as to the quality and performance of the Covered Software 271 | is with You. Should any Covered Software prove defective in any respect, 272 | You (not any Contributor) assume the cost of any necessary servicing, 273 | repair, or correction. This disclaimer of warranty constitutes an essential 274 | part of this License. No use of any Covered Software is authorized under 275 | this License except under this disclaimer. 276 | 277 | 7. Limitation of Liability 278 | 279 | Under no circumstances and under no legal theory, whether tort (including 280 | negligence), contract, or otherwise, shall any Contributor, or anyone who 281 | distributes Covered Software as permitted above, be liable to You for any 282 | direct, indirect, special, incidental, or consequential damages of any 283 | character including, without limitation, damages for lost profits, loss of 284 | goodwill, work stoppage, computer failure or malfunction, or any and all 285 | other commercial damages or losses, even if such party shall have been 286 | informed of the possibility of such damages. This limitation of liability 287 | shall not apply to liability for death or personal injury resulting from 288 | such party's negligence to the extent applicable law prohibits such 289 | limitation. Some jurisdictions do not allow the exclusion or limitation of 290 | incidental or consequential damages, so this exclusion and limitation may 291 | not apply to You. 292 | 293 | 8. Litigation 294 | 295 | Any litigation relating to this License may be brought only in the courts 296 | of a jurisdiction where the defendant maintains its principal place of 297 | business and such litigation shall be governed by laws of that 298 | jurisdiction, without reference to its conflict-of-law provisions. Nothing 299 | in this Section shall prevent a party's ability to bring cross-claims or 300 | counter-claims. 301 | 302 | 9. Miscellaneous 303 | 304 | This License represents the complete agreement concerning the subject 305 | matter hereof. If any provision of this License is held to be 306 | unenforceable, such provision shall be reformed only to the extent 307 | necessary to make it enforceable. Any law or regulation which provides that 308 | the language of a contract shall be construed against the drafter shall not 309 | be used to construe this License against a Contributor. 310 | 311 | 312 | 10. Versions of the License 313 | 314 | 10.1. New Versions 315 | 316 | Mozilla Foundation is the license steward. Except as provided in Section 317 | 10.3, no one other than the license steward has the right to modify or 318 | publish new versions of this License. Each version will be given a 319 | distinguishing version number. 320 | 321 | 10.2. Effect of New Versions 322 | 323 | You may distribute the Covered Software under the terms of the version 324 | of the License under which You originally received the Covered Software, 325 | or under the terms of any subsequent version published by the license 326 | steward. 327 | 328 | 10.3. Modified Versions 329 | 330 | If you create software not governed by this License, and you want to 331 | create a new license for such software, you may create and use a 332 | modified version of this License if you rename the license and remove 333 | any references to the name of the license steward (except to note that 334 | such modified license differs from this License). 335 | 336 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 337 | Licenses If You choose to distribute Source Code Form that is 338 | Incompatible With Secondary Licenses under the terms of this version of 339 | the License, the notice described in Exhibit B of this License must be 340 | attached. 341 | 342 | Exhibit A - Source Code Form License Notice 343 | 344 | This Source Code Form is subject to the 345 | terms of the Mozilla Public License, v. 346 | 2.0. If a copy of the MPL was not 347 | distributed with this file, You can 348 | obtain one at 349 | http://mozilla.org/MPL/2.0/. 350 | 351 | If it is not possible or desirable to put the notice in a particular file, 352 | then You may include the notice in a location (such as a LICENSE file in a 353 | relevant directory) where a recipient would be likely to look for such a 354 | notice. 355 | 356 | You may add additional accurate notices of copyright ownership. 357 | 358 | Exhibit B - "Incompatible With Secondary Licenses" Notice 359 | 360 | This Source Code Form is "Incompatible 361 | With Secondary Licenses", as defined by 362 | the Mozilla Public License, v. 2.0. 363 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Memory-Mapped File C++ Library Tutorial and Reference 2 | 3 | 4 | ## Purpose 5 | 6 | This is a library, for the C++98 language and its successive versions, 7 | to handle files as arrays of bytes, exploiting system calls provided 8 | by POSIX-compliant operating systems and by Microsoft Windows. 9 | 10 | 11 | ## Contents 12 | 13 | This package is made of one HTML documentation file, 14 | and two C++ files: 15 | 16 | * `README.md`: This document. 17 | * `memory_mapped_file.hpp`: Header file, to be included by every source file 18 | that needs to read or to write a memory-mapped file. 19 | * `memory_mapped_file.cpp`: Implementation file, to be compiled separately 20 | and to be linked into the executable. 21 | 22 | Only POSIX-compliant operating systems (like Unix, Linux, and Mac OS X) 23 | and Microsoft Windows are supported. 24 | 25 | 26 | ## Tutorial 27 | 28 | 29 | ### Example 30 | 31 | First of all, a complete example of use of the library is presented. 32 | The following program just copies a file. 33 | Put in an empty directory the two library files `memory_mapped_file.hpp` and 34 | `memory_mapped_file.cpp`, and a new file named `example.cpp`, 35 | having the following contents: 36 | 37 | #include "memory_mapped_file.hpp" 38 | #include // for std::cout and std::endl 39 | #include // for std::copy 40 | 41 | int CopyFile(char const* source, char const* dest, bool overwrite) 42 | { 43 | // Create a read-only memory-mapped-file for reading the source file. 44 | memory_mapped_file::read_only_mmf source_mf(source); 45 | 46 | // Check that the file has been opened. 47 | if (! source_mf.is_open()) return 1; 48 | 49 | // Check that the contents of the file has been mapped into memory. 50 | if (! source_mf.data()) return 2; 51 | 52 | // Create a writable memory-mapped-file for writing 53 | // the destination file, with the option to overwrite it or not, 54 | // if such file already exists. 55 | memory_mapped_file::writable_mmf dest_mf(dest, 56 | overwrite ? memory_mapped_file::if_exists_truncate : 57 | memory_mapped_file::if_exists_fail, 58 | memory_mapped_file::if_doesnt_exist_create); 59 | 60 | // Check that the file has been opened. 61 | if (! dest_mf.is_open()) return 3; 62 | 63 | // Map into memory a (new) portion of the file, 64 | // as large as the source file. 65 | dest_mf.map(0, source_mf.file_size()); 66 | 67 | // Check that the contents of the file has been mapped into memory. 68 | if (! dest_mf.data()) return 4; 69 | 70 | // Check that the source buffer has the same size 71 | // of the destination buffer. It cannot be otherwise. 72 | if (source_mf.mapped_size() != dest_mf.mapped_size()) return 5; 73 | 74 | // Check that the source file has the same size 75 | // of the destination file. It cannot be otherwise. 76 | if (source_mf.file_size() != dest_mf.file_size()) return 6; 77 | 78 | // Copy the source buffer to the destination buffer. 79 | std::copy(source_mf.data(), source_mf.data() + source_mf.mapped_size(), 80 | dest_mf.data()); 81 | return 0; 82 | } 83 | 84 | int main() 85 | { 86 | using namespace std; 87 | 88 | // Copy the first file, overwriting the second file, 89 | // if it already exists. 90 | // It should always print 0, meaning success. 91 | cout << CopyFile("memory_mapped_file.hpp", "copy.tmp", true) << endl; 92 | 93 | // Copy the first file to the second file, 94 | // but only if the second file does not already exist. 95 | // It should always print 3, meaning failure to open the second file, 96 | // as here the second file already exists. 97 | cout << CopyFile("memory_mapped_file.hpp", "copy.tmp", false) << endl; 98 | } 99 | 100 | To compile and run the example, in a POSIX environment with GCC installed, 101 | from a shell, type: 102 | 103 | g++ example.cpp memory_mapped_file.cpp -o example 104 | 105 | and then 106 | 107 | ./example 108 | 109 | Instead, in a Windows environment with Visual C++ installed, 110 | from a command prompt, type: 111 | 112 | cl /nologo /EHsc example.cpp memory_mapped_file.cpp /Feexample.exe 113 | 114 | and then 115 | 116 | example.exe 117 | 118 | In both environments the program should print, 119 | even if it is run several times: 120 | 121 | 0 122 | 3 123 | 124 | and should create a file named `copy.tmp`, identical 125 | to the file `memory_mapped_file.hpp`. 126 | 127 | The behavior of this example is explained in the comments embedded 128 | in the file `example.cpp`. 129 | 130 | But now we'll do a step-by-step tutorial. 131 | 132 | 133 | ### Set-up 134 | 135 | In this tutorial, we'll write several versions of a file named `tutorial.cpp`. 136 | The first version has the following contents: 137 | 138 | #include "memory_mapped_file.hpp" 139 | using namespace memory_mapped_file; 140 | #include 141 | #include 142 | #include 143 | using namespace std; 144 | 145 | char const pathname[] = "a.txt"; 146 | 147 | void create_file() 148 | { 149 | ofstream f(pathname); 150 | f << "Hello, world!"; 151 | f.close(); 152 | } 153 | 154 | int main() 155 | { 156 | } 157 | 158 | To compile it using using GCC, type the following command line: 159 | 160 | g++ memory_mapped_file.cpp tutorial.cpp -o tutorial 161 | 162 | To compile it using Visual C++, type the following command line: 163 | 164 | cl /nologo /EHsc memory_mapped_file.cpp tutorial.cpp /Fetutorial.exe 165 | 166 | The `create_file` function creates in the current directory a file 167 | named `a.txt`, containing only the 13 bytes `Hello, world!`. 168 | 169 | Of course, this program does nothing, as it has an empty `main` function. 170 | 171 | The following versions will change only the body of the `main` function. 172 | 173 | 174 | ### Opening and closing a file 175 | 176 | Write the following contents for the `main` function 177 | of the `tutorial.cpp` file: 178 | 179 | cout << boolalpha; 180 | create_file(); 181 | read_only_mmf mmf; 182 | cout << mmf.is_open() << endl; 183 | cout << mmf.file_handle() << endl; 184 | cout << mmf.file_size() << " " << mmf.offset() << " " 185 | << mmf.mapped_size() << endl; 186 | mmf.open(pathname, false); 187 | cout << mmf.is_open() << endl; 188 | cout << mmf.file_handle() << endl; 189 | cout << mmf.file_size() << " " << mmf.offset() << " " 190 | << mmf.mapped_size() << endl; 191 | mmf.close(); 192 | cout << mmf.is_open() << endl; 193 | cout << mmf.file_handle() << endl; 194 | cout << mmf.file_size() << " " << mmf.offset() << " " 195 | << mmf.mapped_size() << endl; 196 | 197 | When it is run, it should print: 198 | 199 | false 200 | 201 | 0 0 0 202 | true 203 | 204 | 13 0 0 205 | false 206 | 207 | 0 0 0 208 | 209 | The first statement ensures that `bool` expressions are printed as 210 | `true` or `false`. 211 | 212 | The second statement ensures that there is a data file to use. 213 | 214 | The third statement defines and initializes an object owning 215 | a memory-mapped-file for reading it, without specifying which file to use. 216 | 217 | Therefore, no file is opened, and so the call to `is_open` 218 | returns `false`, the call to `file_handle` returns an invalid file handle, 219 | the call to `file_size`, `offset`, and `mapped_size` return `0`. 220 | 221 | Then the call to the `open` member function tries to open 222 | the specified file (searching it from the current directory), 223 | without mapping its contents in the address space of the process. 224 | 225 | Presumably it finds such file and opens it, and therefore 226 | the next call to `is_open` should return `true`, 227 | and the call to `file_handle` should return an operating-system-dependent 228 | value of a handle for the underlying file. 229 | Such handle can be used, for example, to lock the file for exclusive use, 230 | using operating system calls. 231 | 232 | The call to `file_size` should return the length of the opened file, i.e. `13`, 233 | but, as the file is still not mapped to memory, 234 | the calls to `offset` and `mapped_size` still return `0`. 235 | 236 | Then the underlying file is explicitly closed, by calling `close`, 237 | restoring the file to the condition before the opening of the file. 238 | 239 | 240 | ### Failing to open a file 241 | 242 | Write the following contents for the `main` function 243 | of the `tutorial.cpp` file: 244 | 245 | cout << boolalpha; 246 | read_only_mmf mmf; 247 | mmf.open("x", false); 248 | cout << mmf.is_open() << endl; 249 | cout << mmf.file_handle() << endl; 250 | cout << mmf.file_size() << endl; 251 | cout << mmf.offset() << endl; 252 | cout << mmf.mapped_size() << endl; 253 | 254 | When it is run, assuming the current directory does not contain 255 | a file named `x`, it should print: 256 | 257 | false 258 | 259 | 0 260 | 0 261 | 0 262 | 263 | Now the `open` call fails, as the specified file cannot be opened. 264 | 265 | 266 | ### Mapping and un-mapping 267 | 268 | Now replace all the contents of the `main` function with the following lines: 269 | 270 | cout << boolalpha; 271 | create_file(); 272 | read_only_mmf mmf; 273 | mmf.open(pathname, false); 274 | mmf.map(2, 6); 275 | cout << mmf.is_open() << endl; 276 | cout << mmf.file_size() << endl; 277 | cout << mmf.offset() << endl; 278 | cout << mmf.mapped_size() << endl; 279 | mmf.unmap(); 280 | cout << mmf.is_open() << endl; 281 | cout << mmf.file_size() << endl; 282 | cout << mmf.offset() << endl; 283 | cout << mmf.mapped_size() << endl; 284 | 285 | When it is run, it should print: 286 | 287 | true 288 | 13 289 | 2 290 | 6 291 | true 292 | 13 293 | 0 294 | 0 295 | 296 | The call to the `map` member function creates a mapping 297 | to memory of the file contents from the offset specified by the first argument, 298 | for the length specified by the second argument. 299 | This appears also by the ensuing calls to `offset` and `mapped_size`, that 300 | return `2` and `6`, respectively. 301 | 302 | Then, by calling the `unmap` member function, the mapping is undone. 303 | 304 | 305 | ### More on mapping 306 | 307 | Now replace all the contents of the `main` function with the following lines: 308 | 309 | create_file(); 310 | read_only_mmf mmf; 311 | mmf.open(pathname, false); 312 | mmf.map(2); 313 | cout << mmf.offset() << endl; 314 | cout << mmf.mapped_size() << endl; 315 | mmf.map(); 316 | cout << mmf.offset() << endl; 317 | cout << mmf.mapped_size() << endl; 318 | mmf.map(10, 10000); 319 | cout << mmf.offset() << endl; 320 | cout << mmf.mapped_size() << endl; 321 | 322 | When it is run, it should print: 323 | 324 | 2 325 | 11 326 | 0 327 | 13 328 | 10 329 | 3 330 | 331 | First, notice that `map` is called three times and `unmap` is never called. 332 | Actually `unmap` is implicitly called 333 | by the `map` function and by the destructor. 334 | 335 | Then, notice that `map` may have only one argument or no arguments, 336 | as they have both the default value `0`. 337 | 338 | Then, notice that the `0` value for the second argument of `map` doesn't mean 339 | that the mapping will have zero length (that is impossible), 340 | but that it will extend up to the length of the file, if possible. 341 | 342 | At last, notice that even if the specified offset plus the specified length 343 | exceeds the length of the file, the mapping extends anyway up to the length 344 | of the file, as shown by the last printed line. 345 | 346 | 347 | ### Implicit opening and mapping 348 | 349 | Now replace all the contents of the `main` function with the following lines: 350 | 351 | cout << boolalpha; 352 | create_file(); 353 | read_only_mmf mmf1(pathname, false); 354 | cout << mmf1.is_open() << endl; 355 | cout << mmf1.file_size() << endl; 356 | cout << mmf1.offset() << endl; 357 | cout << mmf1.mapped_size() << endl; 358 | 359 | read_only_mmf mmf2; 360 | mmf2.open(pathname); 361 | cout << mmf2.is_open() << endl; 362 | cout << mmf2.file_size() << endl; 363 | cout << mmf2.offset() << endl; 364 | cout << mmf2.mapped_size() << endl; 365 | 366 | read_only_mmf mmf3(pathname); 367 | cout << mmf3.is_open() << endl; 368 | cout << mmf3.file_size() << endl; 369 | cout << mmf3.offset() << endl; 370 | cout << mmf3.mapped_size() << endl; 371 | 372 | When it is run, it should print: 373 | 374 | true 375 | 13 376 | 0 377 | 0 378 | true 379 | 13 380 | 0 381 | 13 382 | true 383 | 13 384 | 0 385 | 13 386 | 387 | When the object `mmf1` is constructed, it gets two arguments that 388 | cause to open to specified file, but not to map its contents to memory. 389 | This avoids to call separately the constructor and the `open` function. 390 | 391 | When the object `mmf2` is opened, its contents is implicitly entirely 392 | mapped to memory. 393 | This avoids to call separately the `open` and `map` functions. 394 | 395 | When the object `mmf3` is constructed, it is implicitly opened, 396 | and its contents is implicitly entirely mapped to memory. 397 | This avoids to call separately the constructor, and the `open` 398 | and `map` functions. 399 | 400 | 401 | ### Reading the file contents 402 | 403 | Now replace all the contents of the `main` function with the following lines: 404 | 405 | create_file(); 406 | read_only_mmf mmf(pathname); 407 | cout << string(mmf.data(), 8) << endl; 408 | mmf.map(2); 409 | cout << string(mmf.data(), 8) << endl; 410 | mmf.map(3000); 411 | cout << (size_t)mmf.data() << endl; 412 | 413 | When it is run, it should print: 414 | 415 | Hello, w 416 | llo, wor 417 | 0 418 | 419 | If the mapping succeeds, every ensuing calls to `data` return 420 | a pointer to the mapped buffer starting at the specified offset. 421 | 422 | But the `map` function may fail, for several reasons. 423 | For example, no mapping is possible if the file is not opened, 424 | or if the specified offset is equal to or greater than the file size, 425 | or if there is not enough address space to map all the specified range. 426 | 427 | If the `map` call fails, every ensuing call to `data` returns a null pointer; 428 | therefore, every time you try to map a file, implicitly or by calling `map`, 429 | you should check the value returned by `data` or by `mapped_size`, 430 | before dereferencing the pointer returned by `data`. 431 | 432 | 433 | ### Read-only access 434 | 435 | Now replace all the contents of the `main` function with the following lines: 436 | 437 | create_file(); 438 | read_only_mmf mmf(pathname); 439 | cout << mmf.data()[0]; 440 | mmf.data()[0] = 'a'; 441 | 442 | When it is compiled, a syntax error should occurs in the last statement, 443 | as the call to `data` returns a `const` address. 444 | However, if such const-ness is bypassed using a cast, 445 | the program will compile, but that statement will generate a run-time error, 446 | as the operating system has marked such address range as _read-only_. 447 | 448 | 449 | ### Opening for writing 450 | 451 | Up to now, only the `read_only_mmf` class has been used. 452 | Such class disallows to change the file. 453 | 454 | Memory mapped files may be used also for changing the contents of files, 455 | as the following examples show. 456 | 457 | Now replace all the contents of the `main` function with the following lines: 458 | 459 | cout << boolalpha; 460 | create_file(); 461 | writable_mmf mmf(pathname, if_exists_map_all, if_doesnt_exist_fail); 462 | cout << mmf.is_open() << endl; 463 | cout << mmf.file_size() << endl; 464 | cout << mmf.offset() << endl; 465 | cout << mmf.mapped_size() << endl; 466 | cout << string(mmf.data(), 8) << endl; 467 | 468 | When it is run, it should print: 469 | 470 | true 471 | 13 472 | 0 473 | 13 474 | Hello, w 475 | 476 | It means that the file has been opened, its size is 13 bytes, 477 | all the file has been mapped, and its first `8` characters are `Hello, w`. 478 | 479 | As it appears, this object can be used just like a read-only memory mapped 480 | file, except for the constructor. 481 | 482 | 483 | ### Read/write mapping mode 484 | 485 | We have already seen one usage instance of the `writable_mmf` class. 486 | There are several other ways to create such object, 487 | described in the reference. 488 | The four most typical ones are used in the program 489 | whose `main` function has the following contents: 490 | 491 | cout << boolalpha; 492 | 493 | // 1. Fail or create 494 | { // 1.1. File exists 495 | create_file(); 496 | writable_mmf mmf(pathname, 497 | if_exists_fail, if_doesnt_exist_create); 498 | cout << mmf.is_open() << endl; 499 | } 500 | { // 1.2. File doesn't exist 501 | remove(pathname); 502 | writable_mmf mmf(pathname, 503 | if_exists_fail, if_doesnt_exist_create); 504 | cout << mmf.is_open() << " " << mmf.file_size() << endl; 505 | } 506 | 507 | // 2. Truncate or create 508 | { // 2.1. File exists 509 | create_file(); 510 | writable_mmf mmf(pathname, 511 | if_exists_truncate, if_doesnt_exist_create); 512 | cout << mmf.is_open() << " " << mmf.file_size() << endl; 513 | } 514 | { // 2.2. File doesn't exist 515 | remove(pathname); 516 | writable_mmf mmf(pathname, 517 | if_exists_truncate, if_doesnt_exist_create); 518 | cout << mmf.is_open() << " " << mmf.file_size() << endl; 519 | } 520 | 521 | // 3. Just open or fail 522 | { // 3.1. File exists 523 | create_file(); 524 | writable_mmf mmf(pathname, 525 | if_exists_just_open, if_doesnt_exist_fail); 526 | cout << mmf.is_open() << " " << mmf.file_size() << " " 527 | << mmf.mapped_size() << endl; 528 | } 529 | { // 3.2. File doesn't exist 530 | remove(pathname); 531 | writable_mmf mmf(pathname, 532 | if_exists_just_open, if_doesnt_exist_fail); 533 | cout << mmf.is_open() << endl; 534 | } 535 | 536 | // 4. Map all or fail 537 | { // 4.1. File exists 538 | create_file(); 539 | writable_mmf mmf(pathname, 540 | if_exists_map_all, if_doesnt_exist_fail); 541 | cout << mmf.is_open() << " " << mmf.file_size() << " " 542 | << mmf.mapped_size() << endl; 543 | } 544 | { // 4.2. File doesn't exist 545 | remove(pathname); 546 | writable_mmf mmf(pathname, 547 | if_exists_map_all, if_doesnt_exist_fail); 548 | cout << mmf.is_open() << endl; 549 | } 550 | 551 | When it is run, it should print: 552 | 553 | false 554 | true 0 555 | true 0 556 | true 0 557 | true 13 0 558 | false 559 | true 13 13 560 | false 561 | 562 | There are 4 cases, each one with two sub-cases: 563 | in the first one the file already exists, 564 | and in the second one the file doesn't exist yet. 565 | 566 | In case 1.1., the file exists, and there is the option `if_exists_fail`; 567 | therefore such file is not opened, and `is_open` returns `false`. 568 | 569 | In case 1.2., the file does not exist, and there is the option 570 | `if_doesnt_exist_create`; 571 | therefore such file is created empty, and so `is_open` returns `true`, 572 | and `file_size` returns `0`. 573 | 574 | Case 1 ("Fail or create") is useful when it is needed to create a file, 575 | without overwriting an existing file. 576 | 577 | In case 2.1., the file exists, and there is the option `if_exists_truncate`; 578 | therefore such file is opened and truncated, 579 | and so `is_open` returns `true`, and `file_size` returns `0`. 580 | 581 | In case 2.2., the file does not exist, and there is the option 582 | `if_doesnt_exist_create`; 583 | therefore such file is created empty, and so `is_open` returns `true`, 584 | and `file_size` returns `0`. 585 | 586 | Case 2 ("Truncate or create") is useful when it is needed to create a file, 587 | even overwriting an existing file. 588 | 589 | In case 3.1., the file exists, and there is the option `if_exists_just_open`; 590 | therefore such file is opened and not truncated nor mapped, 591 | and so `is_open` returns `true`, `file_size` returns `13`, 592 | and `mapped_size` returns `0`. 593 | 594 | In case 3.2., the file does not exist, and there is the option 595 | `if_doesnt_exist_fail`; 596 | therefore such file is not opened, and so `is_open` returns `false`. 597 | 598 | Case 3 ("Just open or fail") is useful when it is needed 599 | to change a very large existing file, to be mapped piece-wise. 600 | 601 | In case 4.1., the file exists, and there is the option `if_exists_map_all`; 602 | therefore such file is opened and not truncated, and it is mapped all, 603 | and so `is_open` returns `true`, `file_size` returns `13`, 604 | and `mapped_size` returns `13`. 605 | 606 | In case 4.2., the file does not exist, and there is the option 607 | `if_doesnt_exist_fail`; 608 | therefore such file is not opened, and so `is_open` returns `false`. 609 | 610 | Case 4 ("Map all or fail") is useful when it is needed 611 | to change a not-so-large existing file, to be handled as a single string. 612 | 613 | Of course, when a file is to be created or changed, it is required to use 614 | the `writable_mmf` class. 615 | Instead, when a file is only to be read, also the `read_only_mmf` classes 616 | could be used. 617 | Nevertheless, using `read_only_mmf` has the following advantages: 618 | 619 | * **Reading read-only files or files shared only for reading**: 620 | `writable_mmf` objects require to open the specified file 621 | in read/write mode, and the operating system prevents such operation 622 | on files marked as *read-only* or shared only for reading. 623 | The only way to read a read-only file or a file shared only for reading 624 | is to use a `read_only_mmf` object. 625 | * **Simpler API**: As `read_only_mmf` cannot change to specified file, 626 | it has fewer features, and therefore it is simpler to learn and use. 627 | * **No risk of accidental change**: As `writable_mmf` objects 628 | open the specified file in read/write mode, a logically erroneous operation 629 | or an undefined behavior operation could apply unwanted changes 630 | to the contents of such file. 631 | As `read_only_mmf` objects open the specified file in read-only mode, 632 | the operating system prevents any subsequent attempt to change it. 633 | * **Possibly more efficient**: Operating systems may use more efficient 634 | buffering algorithms for read-only files, used by `read_only_mmf` objects, 635 | than for read/write files, used by `writable_mmf` objects. 636 | 637 | 638 | ### Read/write access 639 | 640 | Up to now, in our examples, no file has been changed by a memory-mapped-file. 641 | 642 | Now replace all the contents of the `main` function with the following lines: 643 | 644 | create_file(); 645 | { 646 | writable_mmf mmf(pathname, 647 | if_exists_map_all, if_doesnt_exist_fail); 648 | 649 | cout << string(mmf.data(), 6) << endl; 650 | mmf.data()[1] = 'X'; 651 | cout << string(mmf.data(), 6) << endl; 652 | } 653 | { 654 | read_only_mmf mmf(pathname); 655 | cout << string(mmf.data(), 6) << endl; 656 | } 657 | 658 | It should print: 659 | 660 | Hello, 661 | HXllo, 662 | HXllo, 663 | 664 | This means that the `data` function of the `writable_mmf` class, 665 | returns a non-`const` address of a buffer, 666 | and when some bytes of such buffer are changed, 667 | such changes are immediately visible to the process, and are also 668 | saved to the file sometime no later than when the current scope is closed. 669 | 670 | 671 | ### Flushing writes 672 | 673 | Such effective write to the file is handled by the operating system, 674 | and, for efficiency reasons, usually it does not happen immediately, 675 | as it is shown by replacing all the contents of the `main` function 676 | with the following lines: 677 | 678 | create_file(); 679 | writable_mmf mmf(pathname, 680 | if_exists_map_all, if_doesnt_exist_fail); 681 | mmf.data()[0] = 'X'; 682 | mmf.flush(); 683 | mmf.data()[1] = 'Y'; 684 | cin.get(); 685 | 686 | This program writes a letter "X" as the first byte of the file, 687 | and calls the `flush` function to ensure it is written to the file. 688 | Then it writes a letter "Y" as the second byte of the file, 689 | and waits for user input. 690 | If you now press the reset button of your computer, 691 | or remove any electric power supply, preventing the operating system to save 692 | this second byte to safe storage, and then restart your computer, 693 | and look into file "a.txt", you should find it has the following contents: 694 | 695 | Xello, world!" 696 | 697 | As you can see, the 'X' character has been written to the file, 698 | thanks to the call to `flush`, but the `Y` character is not. 699 | 700 | This brutal procedure is necessary to show the effect, 701 | because if you terminate the process in any other way, 702 | the operating system is still able to save the buffer to persistent storage. 703 | 704 | The `flush` call, albeit more efficient than closing and reopening the file, 705 | is rather inefficient, though, because it writes data to physical storage, 706 | and so it should be used only when data consistency is required 707 | even in case of a power failure or an operating system crash. 708 | 709 | 710 | # Reference 711 | 712 | 713 | ## Introduction 714 | 715 | To support several operating systems, the source files contain several code 716 | portions under conditional compilation. 717 | If the `_WIN32` macro is defined, then the Microsoft Windows API is used; 718 | otherwise the POSIX API is used, allowing compilation for Linux, Unix, 719 | MAC OS X, and other POSIX-compliant operating systems. 720 | 721 | The header file contains only the namespace `memory_mapped_file`, 722 | containing the definition of the following items: 723 | 724 | * The `mmf_granularity` function: It allows to get operating system allocation 725 | granularity (for advanced uses). 726 | * The `base_mmf` abstract class: It contains what is common 727 | between the other classes. It cannot be instantiated. 728 | * The `read_only_mmf` class: It is used to access an already existing 729 | file only for reading it. 730 | * The `writable_mmf` class: It is used to access an already existing 731 | or a not yet existing file for both reading and writing it. 732 | * The `mmf_exists_mode` enumeration: Options for creating a writable 733 | memory-mapped-file based on an already existing file. 734 | * The `mmf_doesnt_exist_mode` enumeration: Options for creating a writable 735 | memory-mapped-file based on a not yet existing file. 736 | 737 | 738 | ## The `mmf_granularity` function 739 | 740 | Scope: namespace `memory_mapped_file`. 741 | 742 | Operating systems do not allow to map files to memory starting from every 743 | specified byte. They require that the offset be a multiple of a number, 744 | named _granularity_, that is dependent on the operating system, 745 | and typically may vary from 4 KiB to 64 KiB. 746 | 747 | To avoid bothering users with such technicality, this library takes care 748 | of mapping memory internally from the nearest boundary. 749 | For example, if the granularity is 65536, and for a 10 MB long file a mapping 750 | is requested from 500000 to 800000, 751 | the memory actually mapped by the operating system is from 458752 752 | to a number somewhat greater than 800000, 753 | but the `offset` function will return 500000, 754 | and the `mapped_size` function will return 300000. 755 | 756 | To allow the user to take granularity into account, 757 | there is a global function named `mmf_granularity`, 758 | that returns such granularity size. 759 | 760 | It is called like in the following statement: 761 | 762 | unsigned int granularity = memory_mapped_file::mmf_granularity(); 763 | 764 | 765 | ## The `base_mmf` class 766 | 767 | Scope: namespace `memory_mapped_file`. 768 | 769 | Abstract class representing a memory-mapped-file. 770 | It is the base class of `read_only_mmf` and `writable_mmf`, 771 | and therefore it gathers the features common to both classes. 772 | 773 | The possible states of the instances of this class are: 774 | 775 | 1. File not opened. 776 | 1. File opened but not mapped. 777 | 1. File opened and mapped. 778 | 779 | The constructor sets the object in one of the three possible states, 780 | as it can fail to open the underlying file or not even try to open it 781 | (state 1), successfully open the file but fail to map it or not even try 782 | to map it (state 2), or successfully open and map the file (state 3). 783 | 784 | An object in state 1 (file not opened) can pass to another state, 785 | by calling successfully the `open` function. If the value 786 | of the second argument of the call is `false`, the mapping is not even 787 | attempted. 788 | If the value of the second argument of the call is `true` or is missing, 789 | the mapping is attempted, but it may fail. 790 | 791 | An object in state 2 (file not mapped) can pass to state 1 (file not opened) 792 | by calling the `close` function, and it can pass to state 3 (file mapped) 793 | by calling the `map` function. 794 | 795 | An object in state 3 (file mapped) can pass to state 1 (file not opened) 796 | by calling the `close` function, and it can pass to state 2 (file not mapped) 797 | by calling the `unmap` function. 798 | 799 | ### The function `base_mmf()` 800 | 801 | Scope: class `base_mmf`. 802 | 803 | It is the only constructor of its class. 804 | As this class is abstract, it can be called 805 | only by the constructor of the derived classes. 806 | 807 | ### The function `~base_mmf()` 808 | 809 | Scope: class `base_mmf`. 810 | 811 | It is the destructor. 812 | It releases every resources previously allocated by the object. 813 | 814 | ### The function `size_t offset() const` 815 | 816 | Scope: class `base_mmf`. 817 | 818 | It returns the distance in bytes 819 | of the beginning of the portion of the file currently mapped to memory 820 | from the beginning of the file. Therefore, it returns `0` (zero) 821 | when the mapping starts at the beginning of the file. 822 | It returns `0` also when the file hasn't been opened successfully, 823 | or when the file has been opened, but it hasn't been mapped successfully. 824 | therefore it cannot be used to discern if the file is open or not, 825 | nor to discern if the file is mapped or not. 826 | 827 | ### The function `size_t mapped_size() const` 828 | 829 | Scope: class `base_mmf`. 830 | 831 | It returns the size in bytes of the portion of the file currently 832 | mapped to memory. 833 | It returns `0` when the file hasn't been opened successfully, 834 | or when the file has been opened, but it hasn't been mapped successfully. 835 | A mapping cannot have zero length, therefore this call can be used 836 | to discern if the file is mapped or not. 837 | 838 | ### The function `size_t file_size() const` 839 | 840 | Scope: class `base_mmf`. 841 | 842 | It returns the whole size of the underlying opened file. 843 | Of course, it returns `0` when the opened file has zero-length, 844 | but it returns `0` also when the file hasn't been opened successfully; 845 | therefore it cannot be used to discern if the file is open or not. 846 | 847 | ### The function `void unmap()` 848 | 849 | Scope: class `base_mmf`. 850 | 851 | It cancels the current mapping. 852 | It is called implicitly at the beginning of the `map` function, 853 | and by the destructor. 854 | It is always assumed successful. 855 | 856 | ### The function `void close()` 857 | 858 | Scope: class `base_mmf`. 859 | 860 | It closes the currently open file. 861 | It is called implicitly at the beginning of the `open` function, 862 | and by the destructor. 863 | It is always assumed successful. 864 | 865 | ### The function `bool is_open() const` 866 | 867 | Scope: class `base_mmf`. 868 | 869 | It returns `true` if and only if the underlying file has been opened 870 | successfully. 871 | 872 | ### The type name `HANDLE` 873 | 874 | Scope: class `base_mmf`. 875 | 876 | Such name represents the operating-system-dependent type 877 | of the handle of a file. 878 | For POSIX systems, it is `int`; for Microsoft Windows, it is `void *`. 879 | 880 | ### The function `HANDLE file_handle() const` 881 | 882 | Scope: class `base_mmf`. 883 | 884 | It returns the operating-system-dependent handle used internally to access 885 | the file. 886 | It may be used to perform operating-system-dependent operations, 887 | not defined by this library, like file locking. 888 | If the file is open it returns a valid handle, while if the file 889 | is not open it returns an invalid handle, therefore it can be used 890 | to discern if a file is open or not, but using 891 | an operating-system-dependent value. 892 | 893 | ## The `read_only_mmf` class 894 | 895 | Scope: namespace `memory_mapped_file`. 896 | 897 | The instances of this class encapsulate memory-mapped-files 898 | that access a file only for reading it. 899 | Internally it opens the underlying file only for reading it. 900 | 901 | This class is derived from the class `base_mmf`. 902 | Therefore the documentation of such class should be read 903 | to see the inherited features. 904 | 905 | It has several advantages with respect to the `writable_mmf` class, 906 | whose instances are capable of changing a file. They are: 907 | 908 | * **May be the only way**. 909 | If the operating system prevents any change to the underlying file 910 | by the current user, any attempt to open such file for reading/writing, 911 | like `writable_mmf` objects always do, will fail. 912 | * **It's simpler to use**. 913 | There is no need to specify what to do if the file does not exist, 914 | as obviously it cannot be opened. 915 | It is simpler to specify what to do if the file exists, 916 | as it cannot be truncated, and it is senseless to fail. 917 | * **It's safer to use**. 918 | There is no risk of modifying accidentally the file. 919 | Typically any attempt to change the file will cause a compilation error; 920 | but if the code can be compiled, it will cause a run-time error 921 | by the operating system. 922 | * **It's more efficient**. 923 | Operating systems usually use more efficient buffering algorithms 924 | for read-only files, than for read/write files. 925 | 926 | ### `explicit read_only_mmf(char const* pathname, bool map_all = true)` 927 | 928 | Scope: class `read_only_mmf`. 929 | 930 | It is the only constructor of its class. 931 | 932 | The `pathname` argument is the relative or absolute pathname 933 | of the underlying file, specified according the operating system syntax. 934 | 935 | The `map_all` argument specifies if, in case the file could be successfully 936 | opened, such file should also be entirely mapped to memory or not. 937 | 938 | By default, it is mapped, as it is the most convenient thing to do. 939 | Instead, if it is needed to map the file later, or if it is needed 940 | to map the file a piece at a time, the value of second argument 941 | should be `false`. 942 | 943 | ### `void open(char const* pathname, bool map_all = true)` 944 | 945 | Scope: class `read_only_mmf`. 946 | 947 | It tries to open a file to be used for a read-only mapping to memory, 948 | and optionally to map to memory the contents of that file. 949 | 950 | The `pathname` argument is the relative or absolute pathname 951 | of the underlying file, specified according the operating system syntax. 952 | 953 | The `map_all` argument specifies if, in case the file could be successfully 954 | opened, such file should also be entirely mapped to memory or not. 955 | 956 | By default, it is mapped, as it is the most convenient thing to do. 957 | Instead, if it is needed to map the file later, or if it is needed 958 | to map the file a piece at a time, the value of second argument 959 | should be `false`. 960 | 961 | If the `open` function is called when the file is already open, 962 | it is closed first. 963 | Therefore, it useless to call `close` just before calling `open`. 964 | 965 | 966 | ### `char const* data() const` 967 | 968 | Scope: class `read_only_mmf`. 969 | 970 | It returns the address of the beginning of the read-only memory buffer mapped 971 | to a portion of the file. 972 | 973 | If and only if the file is not mapped, 974 | it returns `0` (i.e. `nullptr`). 975 | Therefore this call can be used to discern if the file is mapped or not. 976 | 977 | Of course, it is undefined behavior both dereferencing the null pointer, 978 | and accessing the referenced buffer before the beginning or after the end. 979 | 980 | ### `void map(size_t offset = 0, size_t size = 0)` 981 | 982 | Scope: class `read_only_mmf`. 983 | 984 | It tries to create a mapping between a portion of the file and a memory buffer. 985 | 986 | The `offset` argument specifies the distance of the beginning of the mapped 987 | portion from the beginning of the file. By default it is `0` (zero), 988 | meaning that the mapping starts at the beginning of the file. 989 | 990 | The `size` argument specifies the length of the required mapped portion 991 | of the file. If `offset + size` is greater than the length of the file, 992 | the mapping extends up to the end of the file. 993 | For example, if a file is 500 bytes long, and `object` is of type 994 | `read_only_mmf`, the following statement: 995 | 996 | object.map(100, 130); 997 | 998 | maps into memory the 130 bytes from position 100 included 999 | to position 230 excluded, counting from 0. 1000 | 1001 | And the following statement: 1002 | 1003 | object.map(100, 750); 1004 | 1005 | maps the 400 bytes from position 100 included to position 500 excluded. 1006 | 1007 | By default, the `size` argument is `0`, meaning a request to map 1008 | the file up to its end. 1009 | 1010 | The `map` function fails in the following cases: 1011 | 1012 | * the underlying file is not open (so that `is_open` returns `false`); 1013 | * the specified offset is equal to or greater than the file size; 1014 | * there is is not enough address space to map all the specified range; 1015 | * the operating system refuses to map the file to memory for some other reason. 1016 | 1017 | If the `map` function is successful, 1018 | 1019 | * the `offset` function returns the same value passed as the `offset` 1020 | argument; 1021 | * the `mapped_size` function returns the size of the mapped portion, 1022 | that is not greater than the value of the `size` argument 1023 | (except when it is zero); 1024 | * the `data` function returns a non-null value, that is a valid memory address. 1025 | 1026 | Instead, if the `map` function fails, the `offset`, 1027 | the `mapped_size`, and the `data` functions return `0`. 1028 | 1029 | If the `map` function is called when the file is already mapped, 1030 | it is unmapped first. 1031 | Therefore, it useless to call `unmap` just before calling `map`. 1032 | 1033 | 1034 | ## The `mmf_exists_mode` enumeration 1035 | 1036 | Scope: namespace `memory_mapped_file`. 1037 | 1038 | This enumeration specifies what to do when a `writable_mmf` object 1039 | is created and the underlying file already exists. 1040 | 1041 | There are four cases: 1042 | 1043 | * `if_exists_fail`: the file is not opened, 1044 | so that if later `is_open` is called, it will return `false`, 1045 | and of course if `mapped_size` and `data` are called, they will return `0`. 1046 | 1047 | * `if_exists_just_open`: the file is opened but not mapped, 1048 | so that if the open is successful and later `is_open` is called, 1049 | it will return `true`, but `mapped_size` and `data` will however return `0`. 1050 | 1051 | * `if_exists_map_all`: the file is opened and mapped all to memory, 1052 | so that if the open is successful and later `is_open` is called, 1053 | it will return `true`, and if the map is also successful and later 1054 | `mapped_size` and `data` are called they will return non-null values. 1055 | 1056 | * `if_exists_truncate`: the file is opened and truncated, 1057 | so that if the open is successful and later `is_open` is called, 1058 | it will return `true`, but `mapped_size` and `data` will however return `0`. 1059 | 1060 | 1061 | ## The `mmf_doesnt_exist_mode` enumeration 1062 | 1063 | Scope: namespace `memory_mapped_file`. 1064 | 1065 | This enumeration specifies what to do when a `writable_mmf` object 1066 | is created and the underlying file does not exist yet. 1067 | 1068 | There are only two cases: 1069 | 1070 | * `if_doesnt_exist_fail`: the file is not created, 1071 | so that if later `is_open` is called, it will return `false`, 1072 | and of course if `mapped_size` and `data` are called, they will return `0`. 1073 | * `if_doesnt_exist_create`, the file is created empty, 1074 | so that if the creation is successful and later `is_open` is called, 1075 | it will return `true`, but `mapped_size` and `data` will however return `0`. 1076 | 1077 | 1078 | ## The `writable_mmf` class 1079 | 1080 | Scope: namespace `memory_mapped_file`. 1081 | 1082 | The instances of this class encapsulate memory-mapped-files 1083 | that access a file for reading or writing it. 1084 | Internally it opens the underlying file for reading or writing it. 1085 | 1086 | This class is derived from the `base_mmf` class. 1087 | Therefore see the documentation of such class to see the inherited features. 1088 | 1089 | In addition, this class is rather similar to the `read_only_mmf` class, 1090 | therefore here only the difference from such class are specified. 1091 | 1092 | 1093 | ### `explicit writable_mmf(char const* pathname, mmf_exists_mode exists_mode, mmf_doesnt_exist_mode doesnt_exist_mode)` 1094 | 1095 | Scope: class `writable_mmf`. 1096 | 1097 | It is the only constructor of this class. 1098 | 1099 | For more information, look at the description of the constructors 1100 | of `base_mmf` and of `read_only_mmf`, 1101 | and at the description of the enumerations `mmf_exists_mode` 1102 | and `mmf_doesnt_exist_mode`. 1103 | 1104 | The `exists_mode` argument has four possible values, 1105 | and the `doesnt_exist_mode` has two possible values, 1106 | and therefore there are the following eight possible construction cases: 1107 | 1108 | * `writable_mmf(pathname, if_exists_fail, if_doesnt_exist_fail)`: 1109 | Fail always. Of course it is senseless. 1110 | * `writable_mmf(pathname, if_exists_fail, if_doesnt_exist_create)`: 1111 | Fail or create. To be used to copy a file without overwriting 1112 | an existing file. 1113 | * `writable_mmf(pathname, if_exists_just_open, if_doesnt_exist_fail)`: 1114 | Open or fail. Similar to `read_only_mmf(pathname, false)`. 1115 | * `writable_mmf(pathname, if_exists_just_open, if_doesnt_exist_create)`: 1116 | Open or create. To be used to modify a file piece-wise, 1117 | by creating it if not yet existing. 1118 | * `writable_mmf(pathname, if_exists_map_all, if_doesnt_exist_fail)`: 1119 | Map or fail. Similar to `read_only_mmf(pathname)`. 1120 | * `writable_mmf(pathname, if_exists_map_all, if_doesnt_exist_create)`: 1121 | Map or create. To be used to modify a file as a whole, 1122 | by creating it if not yet existing. 1123 | * `writable_mmf(pathname, if_exists_truncate, if_doesnt_exist_fail)`: 1124 | Truncate or fail. To be used to overwrite an existing file. Rarely useful. 1125 | * `writable_mmf(pathname, if_exists_truncate, if_doesnt_exist_create)`: 1126 | Truncate or create. To be used to copy a file even overwriting 1127 | an existing file. 1128 | 1129 | ### `char* data()` 1130 | 1131 | Scope: class `writable_mmf`. 1132 | 1133 | ### `void open(char const* pathname, mmf_exists_mode exists_mode = if_exists_fail, mmf_doesnt_exist_mode doesnt_exist_mode = if_doesnt_exist_create)` 1134 | 1135 | Scope: class `writable_mmf`. 1136 | 1137 | It tries to open a file to be used for a read-write mapping to memory, 1138 | and optionally to map to memory the contents of that file. 1139 | 1140 | It is similar to the function with the same name of the `read_only_mmf` class, 1141 | and to the constructor of this class. 1142 | 1143 | ### `void map(size_t offset = 0, size_t size = 0)` 1144 | 1145 | Scope: class `writable_mmf`. 1146 | 1147 | It is has the same syntax and semantics of the function with the same name 1148 | of the `read_only_mmf` class. 1149 | 1150 | ### `bool flush()` 1151 | 1152 | Scope: class `writable_mmf`. 1153 | 1154 | It copies all the changes to the file system, ensuring that 1155 | they are persistent. 1156 | 1157 | Actually, when a byte is modified in a memory-mapped file, that change 1158 | may be applied much later to the underlying file, possibly only when 1159 | the memory-mapped file is closed. 1160 | 1161 | To ensure that every previous change is actually applied to the storage device, 1162 | it is possible to close and reopen the memory-mapped file. 1163 | The `flush` operation achieves the same effect much more efficiently. 1164 | 1165 | If returns `true` if the operation succeeds, otherwise `false`. 1166 | -------------------------------------------------------------------------------- /memory_mapped_file.cpp: -------------------------------------------------------------------------------- 1 | #include "memory_mapped_file.hpp" 2 | #if defined(_WIN32) 3 | #include 4 | #else 5 | #include 6 | #include 7 | #include 8 | #include 9 | #endif 10 | 11 | namespace memory_mapped_file 12 | { 13 | unsigned int mmf_granularity() 14 | { 15 | #if defined(_WIN32) 16 | SYSTEM_INFO SystemInfo; 17 | GetSystemInfo(&SystemInfo); 18 | return SystemInfo.dwAllocationGranularity; 19 | #else 20 | return sysconf(_SC_PAGE_SIZE); 21 | #endif 22 | } 23 | 24 | base_mmf::base_mmf(): 25 | data_(0), 26 | offset_(0), 27 | mapped_size_(0), 28 | file_size_(0), 29 | granularity_(mmf_granularity()), 30 | #if defined(_WIN32) 31 | file_handle_(INVALID_HANDLE_VALUE), 32 | file_mapping_handle_(INVALID_HANDLE_VALUE) 33 | #else 34 | file_handle_(-1) 35 | #endif 36 | { 37 | } 38 | 39 | base_mmf::~base_mmf() 40 | { 41 | close(); 42 | } 43 | 44 | void base_mmf::close() 45 | { 46 | unmap(); 47 | #if defined(_WIN32) 48 | ::CloseHandle(file_handle_); 49 | file_handle_ = (void*)-1; 50 | #else 51 | ::close(file_handle_); 52 | file_handle_ = -1; 53 | #endif 54 | file_size_ = 0; 55 | } 56 | 57 | void base_mmf::unmap() 58 | { 59 | if (data_) 60 | { 61 | char* real_data = data_ 62 | - (offset_ - offset_ / granularity_ * granularity_); 63 | #if defined(_WIN32) 64 | ::UnmapViewOfFile(real_data); 65 | ::CloseHandle(file_mapping_handle_); 66 | file_mapping_handle_ = INVALID_HANDLE_VALUE; 67 | #else 68 | size_t real_mapped_size = mapped_size_ + (data_ - real_data); 69 | ::munmap(const_cast(real_data), real_mapped_size); 70 | #endif 71 | } 72 | data_ = 0; 73 | offset_ = 0; 74 | mapped_size_ = 0; 75 | } 76 | 77 | size_t base_mmf::query_file_size_() 78 | { 79 | #if defined(_WIN32) 80 | DWORD high_size; 81 | DWORD low_size = GetFileSize(file_handle_, &high_size); 82 | return (size_t(high_size) << 32) | low_size; 83 | #else 84 | struct stat sbuf; 85 | if (::fstat(file_handle_, &sbuf) == -1) return 0; 86 | return sbuf.st_size; 87 | #endif 88 | } 89 | 90 | read_only_mmf::read_only_mmf(char const* pathname, bool map_all) 91 | { 92 | open(pathname, map_all); 93 | } 94 | 95 | void read_only_mmf::open(char const* pathname, bool map_all) 96 | { 97 | if (! pathname) return; 98 | if (is_open()) close(); 99 | #if defined(_WIN32) 100 | file_handle_ = ::CreateFile(pathname, GENERIC_READ, 101 | FILE_SHARE_READ | FILE_SHARE_WRITE, 0, 102 | OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); 103 | if (file_handle_ == INVALID_HANDLE_VALUE) return; 104 | #else 105 | file_handle_ = ::open(pathname, O_RDONLY); 106 | if (file_handle_ == -1) return; 107 | #endif 108 | file_size_ = query_file_size_(); 109 | if (map_all) map(); 110 | } 111 | 112 | void read_only_mmf::map( 113 | size_t offset, size_t requested_size) 114 | { 115 | unmap(); 116 | if (offset >= file_size_) return; 117 | size_t mapping_size = requested_size && offset + requested_size 118 | < file_size_ ? requested_size : file_size_ - offset; 119 | if (mapping_size <= 0) return; 120 | size_t real_offset = offset / granularity_ * granularity_; 121 | #if defined(_WIN32) 122 | file_mapping_handle_ = ::CreateFileMapping( 123 | file_handle_, 0, PAGE_READONLY, (offset + mapping_size) >> 32, 124 | (offset + mapping_size) & 0xFFFFFFFF, 0); 125 | if (file_mapping_handle_ == INVALID_HANDLE_VALUE) return; 126 | char* real_data = static_cast(::MapViewOfFile( 127 | file_mapping_handle_, FILE_MAP_READ, real_offset >> 32, 128 | real_offset & 0xFFFFFFFF, offset - real_offset + mapping_size)); 129 | if (! real_data) return; 130 | #else 131 | char* real_data = static_cast(::mmap( 132 | 0, offset - real_offset + mapping_size, PROT_READ, MAP_SHARED, 133 | file_handle_, real_offset)); 134 | if (real_data == MAP_FAILED) return; 135 | #endif 136 | data_ = real_data + (offset - real_offset); 137 | mapped_size_ = mapping_size; 138 | offset_ = offset; 139 | } 140 | 141 | writable_mmf::writable_mmf(char const* pathname, 142 | memory_mapped_file::mmf_exists_mode exists_mode, 143 | memory_mapped_file::mmf_doesnt_exist_mode doesnt_exist_mode) 144 | { 145 | open(pathname, exists_mode, doesnt_exist_mode); 146 | } 147 | 148 | void writable_mmf::open(char const* pathname, 149 | memory_mapped_file::mmf_exists_mode exists_mode, 150 | memory_mapped_file::mmf_doesnt_exist_mode doesnt_exist_mode) 151 | { 152 | if (! pathname) return; 153 | if (is_open()) close(); 154 | #if defined(_WIN32) 155 | int win_open_mode; 156 | 157 | switch (exists_mode) 158 | { 159 | case if_exists_just_open: 160 | case if_exists_map_all: 161 | win_open_mode = doesnt_exist_mode == if_doesnt_exist_create ? 162 | OPEN_ALWAYS : OPEN_EXISTING; 163 | break; 164 | case if_exists_truncate: 165 | win_open_mode = doesnt_exist_mode == if_doesnt_exist_create ? 166 | CREATE_ALWAYS : TRUNCATE_EXISTING; 167 | break; 168 | default: 169 | if (doesnt_exist_mode == if_doesnt_exist_create) 170 | { 171 | win_open_mode = CREATE_NEW; 172 | } 173 | else return; 174 | } 175 | 176 | file_handle_ = ::CreateFile(pathname, GENERIC_READ | GENERIC_WRITE, 177 | FILE_SHARE_READ | FILE_SHARE_WRITE, 0, 178 | win_open_mode, FILE_ATTRIBUTE_NORMAL, 0); 179 | if (file_handle_ == INVALID_HANDLE_VALUE) return; 180 | #else 181 | int posix_open_mode = O_RDWR; 182 | switch (exists_mode) 183 | { 184 | case if_exists_just_open: 185 | case if_exists_map_all: 186 | posix_open_mode |= doesnt_exist_mode == if_doesnt_exist_create ? 187 | O_CREAT : 0; 188 | break; 189 | case if_exists_truncate: 190 | posix_open_mode |= doesnt_exist_mode == if_doesnt_exist_create ? 191 | O_TRUNC | O_CREAT : O_TRUNC; 192 | break; 193 | default: 194 | if (doesnt_exist_mode == if_doesnt_exist_create) 195 | posix_open_mode |= O_EXCL | O_CREAT; 196 | else return; 197 | } 198 | file_handle_ = ::open(pathname, posix_open_mode, 199 | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 200 | if (file_handle_ == -1) return; 201 | #endif 202 | file_size_ = query_file_size_(); 203 | if (exists_mode == if_exists_map_all && file_size_ > 0) map(); 204 | } 205 | 206 | void writable_mmf::map(size_t offset, size_t requested_size) 207 | { 208 | unmap(); 209 | if (offset > file_size_) return; 210 | size_t mapping_size = requested_size ? 211 | requested_size : file_size_ - offset; 212 | size_t real_offset = offset / granularity_ * granularity_; 213 | #if defined(_WIN32) 214 | file_mapping_handle_ = ::CreateFileMapping( 215 | file_handle_, 0, PAGE_READWRITE, (offset + mapping_size) >> 32, 216 | (offset + mapping_size) & 0xFFFFFFFF, 0); 217 | if (file_mapping_handle_ == INVALID_HANDLE_VALUE) return; 218 | char* real_data = static_cast(::MapViewOfFile( 219 | file_mapping_handle_, FILE_MAP_WRITE, real_offset >> 32, 220 | real_offset & 0xFFFFFFFF, offset - real_offset + mapping_size)); 221 | if (! real_data) return; 222 | #else 223 | if (offset + mapping_size > file_size_) 224 | { 225 | if (-1 == ftruncate(file_handle_, offset + mapping_size)) return; 226 | file_size_ = offset + mapping_size; 227 | } 228 | char* real_data = static_cast(::mmap( 229 | 0, offset - real_offset + mapping_size, PROT_READ | PROT_WRITE, MAP_SHARED, 230 | file_handle_, real_offset)); 231 | if (data_ == MAP_FAILED) return; 232 | #endif 233 | if (offset + mapping_size > file_size_) 234 | { 235 | file_size_ = offset + mapping_size; 236 | } 237 | data_ = real_data + (offset - real_offset); 238 | mapped_size_ = mapping_size; 239 | offset_ = offset; 240 | } 241 | 242 | bool writable_mmf::flush() 243 | { 244 | if (data_) 245 | { 246 | char* real_data = data_ 247 | - (offset_ - offset_ / granularity_ * granularity_); 248 | size_t real_mapped_size = mapped_size_ + (data_ - real_data); 249 | #if defined(_WIN32) 250 | return ::FlushViewOfFile(real_data, real_mapped_size) != 0 251 | && FlushFileBuffers(file_handle_) != 0; 252 | if (::FlushViewOfFile(real_data, real_mapped_size) == 0) return false; 253 | #else 254 | if (::msync(real_data, real_mapped_size, MS_SYNC) != 0) return false; 255 | #endif 256 | } 257 | #if defined(_WIN32) 258 | return FlushFileBuffers(file_handle_) != 0; 259 | #else 260 | return true; 261 | #endif 262 | } 263 | } 264 | -------------------------------------------------------------------------------- /memory_mapped_file.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MEMORY_MAPPED_FILE_HPP 2 | #define MEMORY_MAPPED_FILE_HPP 3 | #include // for size_t 4 | 5 | namespace memory_mapped_file 6 | { 7 | unsigned int mmf_granularity(); 8 | 9 | class base_mmf 10 | { 11 | public: 12 | explicit base_mmf(); 13 | ~base_mmf(); 14 | size_t offset() const { return offset_; } 15 | size_t mapped_size() const { return mapped_size_; } 16 | size_t file_size() const { return file_size_; } 17 | void unmap(); 18 | void close(); 19 | bool is_open() const 20 | { 21 | return file_handle_ != 22 | #if defined(_WIN32) 23 | (void*) 24 | #endif 25 | -1; 26 | } 27 | #if defined(_WIN32) 28 | typedef void* HANDLE; 29 | #else 30 | typedef int HANDLE; 31 | #endif 32 | HANDLE file_handle() const 33 | { 34 | return file_handle_; 35 | } 36 | protected: 37 | size_t query_file_size_(); 38 | char* data_; 39 | size_t offset_; 40 | size_t mapped_size_; 41 | size_t file_size_; 42 | int granularity_; 43 | HANDLE file_handle_; 44 | #if defined(_WIN32) 45 | HANDLE file_mapping_handle_; 46 | #endif 47 | }; 48 | 49 | class read_only_mmf: public base_mmf 50 | { 51 | public: 52 | explicit read_only_mmf(char const* pathname = 0, bool map_all = true); 53 | void open(char const* pathname, bool map_all = true); 54 | char const* data() const { return data_; } 55 | void map(size_t offset = 0, size_t size = 0); 56 | }; 57 | 58 | enum mmf_exists_mode 59 | { 60 | if_exists_fail, 61 | if_exists_just_open, 62 | if_exists_map_all, 63 | if_exists_truncate, 64 | }; 65 | 66 | enum mmf_doesnt_exist_mode 67 | { 68 | if_doesnt_exist_fail, 69 | if_doesnt_exist_create, 70 | }; 71 | 72 | class writable_mmf: public base_mmf 73 | { 74 | public: 75 | explicit writable_mmf(char const* pathname = 0, 76 | mmf_exists_mode exists_mode = if_exists_fail, 77 | mmf_doesnt_exist_mode doesnt_exist_mode = if_doesnt_exist_create); 78 | void open(char const* pathname, 79 | mmf_exists_mode exists_mode = if_exists_fail, 80 | mmf_doesnt_exist_mode doesnt_exist_mode = if_doesnt_exist_create); 81 | char* data() { return data_; } 82 | void map(size_t offset = 0, size_t size = 0); 83 | bool flush(); 84 | }; 85 | } 86 | #endif // MEMORY_MAPPED_FILE_HPP 87 | --------------------------------------------------------------------------------