This is a library, for the C++98 language and its successive versions, to handle files as arrays of bytes, exploiting system calls provided by POSIX-compliant operating systems and by Microsoft Windows.
This package is made of one HTML documentation file, and two C++ files:
README.md
: This document.memory_mapped_file.hpp
: Header file, to be included by every source file that needs to read or to write a memory-mapped file.memory_mapped_file.cpp
: Implementation file, to be compiled separately and to be linked into the executable.
Only POSIX-compliant operating systems (like Unix, Linux, and Mac OS X) and Microsoft Windows are supported.
First of all, a complete example of use of the library is presented.
The following program just copies a file.
Put in an empty directory the two library files memory_mapped_file.hpp
and
memory_mapped_file.cpp
, and a new file named example.cpp
,
having the following contents:
#include "memory_mapped_file.hpp"
#include <iostream> // for std::cout and std::endl
#include <algorithm> // for std::copy
int CopyFile(char const* source, char const* dest, bool overwrite)
{
// Create a read-only memory-mapped-file for reading the source file.
memory_mapped_file::read_only_mmf source_mf(source);
// Check that the file has been opened.
if (! source_mf.is_open()) return 1;
// Check that the contents of the file has been mapped into memory.
if (! source_mf.data()) return 2;
// Create a writable memory-mapped-file for writing
// the destination file, with the option to overwrite it or not,
// if such file already exists.
memory_mapped_file::writable_mmf dest_mf(dest,
overwrite ? memory_mapped_file::if_exists_truncate :
memory_mapped_file::if_exists_fail,
memory_mapped_file::if_doesnt_exist_create);
// Check that the file has been opened.
if (! dest_mf.is_open()) return 3;
// Map into memory a (new) portion of the file,
// as large as the source file.
dest_mf.map(0, source_mf.file_size());
// Check that the contents of the file has been mapped into memory.
if (! dest_mf.data()) return 4;
// Check that the source buffer has the same size
// of the destination buffer. It cannot be otherwise.
if (source_mf.mapped_size() != dest_mf.mapped_size()) return 5;
// Check that the source file has the same size
// of the destination file. It cannot be otherwise.
if (source_mf.file_size() != dest_mf.file_size()) return 6;
// Copy the source buffer to the destination buffer.
std::copy(source_mf.data(), source_mf.data() + source_mf.mapped_size(),
dest_mf.data());
return 0;
}
int main()
{
using namespace std;
// Copy the first file, overwriting the second file,
// if it already exists.
// It should always print 0, meaning success.
cout << CopyFile("memory_mapped_file.hpp", "copy.tmp", true) << endl;
// Copy the first file to the second file,
// but only if the second file does not already exist.
// It should always print 3, meaning failure to open the second file,
// as here the second file already exists.
cout << CopyFile("memory_mapped_file.hpp", "copy.tmp", false) << endl;
}
To compile and run the example, in a POSIX environment with GCC installed, from a shell, type:
g++ example.cpp memory_mapped_file.cpp -o example
and then
./example
Instead, in a Windows environment with Visual C++ installed, from a command prompt, type:
cl /nologo /EHsc example.cpp memory_mapped_file.cpp /Feexample.exe
and then
example.exe
In both environments the program should print, even if it is run several times:
0
3
and should create a file named copy.tmp
, identical
to the file memory_mapped_file.hpp
.
The behavior of this example is explained in the comments embedded
in the file example.cpp
.
But now we'll do a step-by-step tutorial.
In this tutorial, we'll write several versions of a file named tutorial.cpp
.
The first version has the following contents:
#include "memory_mapped_file.hpp"
using namespace memory_mapped_file;
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
char const pathname[] = "a.txt";
void create_file()
{
ofstream f(pathname);
f << "Hello, world!";
f.close();
}
int main()
{
}
To compile it using using GCC, type the following command line:
g++ memory_mapped_file.cpp tutorial.cpp -o tutorial
To compile it using Visual C++, type the following command line:
cl /nologo /EHsc memory_mapped_file.cpp tutorial.cpp /Fetutorial.exe
The create_file
function creates in the current directory a file
named a.txt
, containing only the 13 bytes Hello, world!
.
Of course, this program does nothing, as it has an empty main
function.
The following versions will change only the body of the main
function.
Write the following contents for the main
function
of the tutorial.cpp
file:
cout << boolalpha;
create_file();
read_only_mmf mmf;
cout << mmf.is_open() << endl;
cout << mmf.file_handle() << endl;
cout << mmf.file_size() << " " << mmf.offset() << " "
<< mmf.mapped_size() << endl;
mmf.open(pathname, false);
cout << mmf.is_open() << endl;
cout << mmf.file_handle() << endl;
cout << mmf.file_size() << " " << mmf.offset() << " "
<< mmf.mapped_size() << endl;
mmf.close();
cout << mmf.is_open() << endl;
cout << mmf.file_handle() << endl;
cout << mmf.file_size() << " " << mmf.offset() << " "
<< mmf.mapped_size() << endl;
When it is run, it should print:
false
<OS dependent invalid value>
0 0 0
true
<OS dependent valid value>
13 0 0
false
<OS dependent invalid value>
0 0 0
The first statement ensures that bool
expressions are printed as
true
or false
.
The second statement ensures that there is a data file to use.
The third statement defines and initializes an object owning a memory-mapped-file for reading it, without specifying which file to use.
Therefore, no file is opened, and so the call to is_open
returns false
, the call to file_handle
returns an invalid file handle,
the call to file_size
, offset
, and mapped_size
return 0
.
Then the call to the open
member function tries to open
the specified file (searching it from the current directory),
without mapping its contents in the address space of the process.
Presumably it finds such file and opens it, and therefore
the next call to is_open
should return true
,
and the call to file_handle
should return an operating-system-dependent
value of a handle for the underlying file.
Such handle can be used, for example, to lock the file for exclusive use,
using operating system calls.
The call to file_size
should return the length of the opened file, i.e. 13
,
but, as the file is still not mapped to memory,
the calls to offset
and mapped_size
still return 0
.
Then the underlying file is explicitly closed, by calling close
,
restoring the file to the condition before the opening of the file.
Write the following contents for the main
function
of the tutorial.cpp
file:
cout << boolalpha;
read_only_mmf mmf;
mmf.open("x", false);
cout << mmf.is_open() << endl;
cout << mmf.file_handle() << endl;
cout << mmf.file_size() << endl;
cout << mmf.offset() << endl;
cout << mmf.mapped_size() << endl;
When it is run, assuming the current directory does not contain
a file named x
, it should print:
false
<OS dependent invalid value>
0
0
0
Now the open
call fails, as the specified file cannot be opened.
Now replace all the contents of the main
function with the following lines:
cout << boolalpha;
create_file();
read_only_mmf mmf;
mmf.open(pathname, false);
mmf.map(2, 6);
cout << mmf.is_open() << endl;
cout << mmf.file_size() << endl;
cout << mmf.offset() << endl;
cout << mmf.mapped_size() << endl;
mmf.unmap();
cout << mmf.is_open() << endl;
cout << mmf.file_size() << endl;
cout << mmf.offset() << endl;
cout << mmf.mapped_size() << endl;
When it is run, it should print:
true
13
2
6
true
13
0
0
The call to the map
member function creates a mapping
to memory of the file contents from the offset specified by the first argument,
for the length specified by the second argument.
This appears also by the ensuing calls to offset
and mapped_size
, that
return 2
and 6
, respectively.
Then, by calling the unmap
member function, the mapping is undone.
Now replace all the contents of the main
function with the following lines:
create_file();
read_only_mmf mmf;
mmf.open(pathname, false);
mmf.map(2);
cout << mmf.offset() << endl;
cout << mmf.mapped_size() << endl;
mmf.map();
cout << mmf.offset() << endl;
cout << mmf.mapped_size() << endl;
mmf.map(10, 10000);
cout << mmf.offset() << endl;
cout << mmf.mapped_size() << endl;
When it is run, it should print:
2
11
0
13
10
3
First, notice that map
is called three times and unmap
is never called.
Actually unmap
is implicitly called
by the map
function and by the destructor.
Then, notice that map
may have only one argument or no arguments,
as they have both the default value 0
.
Then, notice that the 0
value for the second argument of map
doesn't mean
that the mapping will have zero length (that is impossible),
but that it will extend up to the length of the file, if possible.
At last, notice that even if the specified offset plus the specified length exceeds the length of the file, the mapping extends anyway up to the length of the file, as shown by the last printed line.
Now replace all the contents of the main
function with the following lines:
cout << boolalpha;
create_file();
read_only_mmf mmf1(pathname, false);
cout << mmf1.is_open() << endl;
cout << mmf1.file_size() << endl;
cout << mmf1.offset() << endl;
cout << mmf1.mapped_size() << endl;
read_only_mmf mmf2;
mmf2.open(pathname);
cout << mmf2.is_open() << endl;
cout << mmf2.file_size() << endl;
cout << mmf2.offset() << endl;
cout << mmf2.mapped_size() << endl;
read_only_mmf mmf3(pathname);
cout << mmf3.is_open() << endl;
cout << mmf3.file_size() << endl;
cout << mmf3.offset() << endl;
cout << mmf3.mapped_size() << endl;
When it is run, it should print:
true
13
0
0
true
13
0
13
true
13
0
13
When the object mmf1
is constructed, it gets two arguments that
cause to open to specified file, but not to map its contents to memory.
This avoids to call separately the constructor and the open
function.
When the object mmf2
is opened, its contents is implicitly entirely
mapped to memory.
This avoids to call separately the open
and map
functions.
When the object mmf3
is constructed, it is implicitly opened,
and its contents is implicitly entirely mapped to memory.
This avoids to call separately the constructor, and the open
and map
functions.
Now replace all the contents of the main
function with the following lines:
create_file();
read_only_mmf mmf(pathname);
cout << string(mmf.data(), 8) << endl;
mmf.map(2);
cout << string(mmf.data(), 8) << endl;
mmf.map(3000);
cout << (size_t)mmf.data() << endl;
When it is run, it should print:
Hello, w
llo, wor
0
If the mapping succeeds, every ensuing calls to data
return
a pointer to the mapped buffer starting at the specified offset.
But the map
function may fail, for several reasons.
For example, no mapping is possible if the file is not opened,
or if the specified offset is equal to or greater than the file size,
or if there is not enough address space to map all the specified range.
If the map
call fails, every ensuing call to data
returns a null pointer;
therefore, every time you try to map a file, implicitly or by calling map
,
you should check the value returned by data
or by mapped_size
,
before dereferencing the pointer returned by data
.
Now replace all the contents of the main
function with the following lines:
create_file();
read_only_mmf mmf(pathname);
cout << mmf.data()[0];
mmf.data()[0] = 'a';
When it is compiled, a syntax error should occurs in the last statement,
as the call to data
returns a const
address.
However, if such const-ness is bypassed using a cast,
the program will compile, but that statement will generate a run-time error,
as the operating system has marked such address range as read-only.
Up to now, only the read_only_mmf
class has been used.
Such class disallows to change the file.
Memory mapped files may be used also for changing the contents of files, as the following examples show.
Now replace all the contents of the main
function with the following lines:
cout << boolalpha;
create_file();
writable_mmf mmf(pathname, if_exists_map_all, if_doesnt_exist_fail);
cout << mmf.is_open() << endl;
cout << mmf.file_size() << endl;
cout << mmf.offset() << endl;
cout << mmf.mapped_size() << endl;
cout << string(mmf.data(), 8) << endl;
When it is run, it should print:
true
13
0
13
Hello, w
It means that the file has been opened, its size is 13 bytes,
all the file has been mapped, and its first 8
characters are Hello, w
.
As it appears, this object can be used just like a read-only memory mapped file, except for the constructor.
We have already seen one usage instance of the writable_mmf
class.
There are several other ways to create such object,
described in the reference.
The four most typical ones are used in the program
whose main
function has the following contents:
cout << boolalpha;
// 1. Fail or create
{ // 1.1. File exists
create_file();
writable_mmf mmf(pathname,
if_exists_fail, if_doesnt_exist_create);
cout << mmf.is_open() << endl;
}
{ // 1.2. File doesn't exist
remove(pathname);
writable_mmf mmf(pathname,
if_exists_fail, if_doesnt_exist_create);
cout << mmf.is_open() << " " << mmf.file_size() << endl;
}
// 2. Truncate or create
{ // 2.1. File exists
create_file();
writable_mmf mmf(pathname,
if_exists_truncate, if_doesnt_exist_create);
cout << mmf.is_open() << " " << mmf.file_size() << endl;
}
{ // 2.2. File doesn't exist
remove(pathname);
writable_mmf mmf(pathname,
if_exists_truncate, if_doesnt_exist_create);
cout << mmf.is_open() << " " << mmf.file_size() << endl;
}
// 3. Just open or fail
{ // 3.1. File exists
create_file();
writable_mmf mmf(pathname,
if_exists_just_open, if_doesnt_exist_fail);
cout << mmf.is_open() << " " << mmf.file_size() << " "
<< mmf.mapped_size() << endl;
}
{ // 3.2. File doesn't exist
remove(pathname);
writable_mmf mmf(pathname,
if_exists_just_open, if_doesnt_exist_fail);
cout << mmf.is_open() << endl;
}
// 4. Map all or fail
{ // 4.1. File exists
create_file();
writable_mmf mmf(pathname,
if_exists_map_all, if_doesnt_exist_fail);
cout << mmf.is_open() << " " << mmf.file_size() << " "
<< mmf.mapped_size() << endl;
}
{ // 4.2. File doesn't exist
remove(pathname);
writable_mmf mmf(pathname,
if_exists_map_all, if_doesnt_exist_fail);
cout << mmf.is_open() << endl;
}
When it is run, it should print:
false
true 0
true 0
true 0
true 13 0
false
true 13 13
false
There are 4 cases, each one with two sub-cases: in the first one the file already exists, and in the second one the file doesn't exist yet.
In case 1.1., the file exists, and there is the option if_exists_fail
;
therefore such file is not opened, and is_open
returns false
.
In case 1.2., the file does not exist, and there is the option
if_doesnt_exist_create
;
therefore such file is created empty, and so is_open
returns true
,
and file_size
returns 0
.
Case 1 ("Fail or create") is useful when it is needed to create a file, without overwriting an existing file.
In case 2.1., the file exists, and there is the option if_exists_truncate
;
therefore such file is opened and truncated,
and so is_open
returns true
, and file_size
returns 0
.
In case 2.2., the file does not exist, and there is the option
if_doesnt_exist_create
;
therefore such file is created empty, and so is_open
returns true
,
and file_size
returns 0
.
Case 2 ("Truncate or create") is useful when it is needed to create a file, even overwriting an existing file.
In case 3.1., the file exists, and there is the option if_exists_just_open
;
therefore such file is opened and not truncated nor mapped,
and so is_open
returns true
, file_size
returns 13
,
and mapped_size
returns 0
.
In case 3.2., the file does not exist, and there is the option
if_doesnt_exist_fail
;
therefore such file is not opened, and so is_open
returns false
.
Case 3 ("Just open or fail") is useful when it is needed to change a very large existing file, to be mapped piece-wise.
In case 4.1., the file exists, and there is the option if_exists_map_all
;
therefore such file is opened and not truncated, and it is mapped all,
and so is_open
returns true
, file_size
returns 13
,
and mapped_size
returns 13
.
In case 4.2., the file does not exist, and there is the option
if_doesnt_exist_fail
;
therefore such file is not opened, and so is_open
returns false
.
Case 4 ("Map all or fail") is useful when it is needed to change a not-so-large existing file, to be handled as a single string.
Of course, when a file is to be created or changed, it is required to use
the writable_mmf
class.
Instead, when a file is only to be read, also the read_only_mmf
classes
could be used.
Nevertheless, using read_only_mmf
has the following advantages:
- Reading read-only files or files shared only for reading:
writable_mmf
objects require to open the specified file in read/write mode, and the operating system prevents such operation on files marked as read-only or shared only for reading. The only way to read a read-only file or a file shared only for reading is to use aread_only_mmf
object. - Simpler API: As
read_only_mmf
cannot change to specified file, it has fewer features, and therefore it is simpler to learn and use. - No risk of accidental change: As
writable_mmf
objects open the specified file in read/write mode, a logically erroneous operation or an undefined behavior operation could apply unwanted changes to the contents of such file. Asread_only_mmf
objects open the specified file in read-only mode, the operating system prevents any subsequent attempt to change it. - Possibly more efficient: Operating systems may use more efficient
buffering algorithms for read-only files, used by
read_only_mmf
objects, than for read/write files, used bywritable_mmf
objects.
Up to now, in our examples, no file has been changed by a memory-mapped-file.
Now replace all the contents of the main
function with the following lines:
create_file();
{
writable_mmf mmf(pathname,
if_exists_map_all, if_doesnt_exist_fail);
cout << string(mmf.data(), 6) << endl;
mmf.data()[1] = 'X';
cout << string(mmf.data(), 6) << endl;
}
{
read_only_mmf mmf(pathname);
cout << string(mmf.data(), 6) << endl;
}
It should print:
Hello,
HXllo,
HXllo,
This means that the data
function of the writable_mmf
class,
returns a non-const
address of a buffer,
and when some bytes of such buffer are changed,
such changes are immediately visible to the process, and are also
saved to the file sometime no later than when the current scope is closed.
Such effective write to the file is handled by the operating system,
and, for efficiency reasons, usually it does not happen immediately,
as it is shown by replacing all the contents of the main
function
with the following lines:
create_file();
writable_mmf mmf(pathname,
if_exists_map_all, if_doesnt_exist_fail);
mmf.data()[0] = 'X';
mmf.flush();
mmf.data()[1] = 'Y';
cin.get();
This program writes a letter "X" as the first byte of the file,
and calls the flush
function to ensure it is written to the file.
Then it writes a letter "Y" as the second byte of the file,
and waits for user input.
If you now press the reset button of your computer,
or remove any electric power supply, preventing the operating system to save
this second byte to safe storage, and then restart your computer,
and look into file "a.txt", you should find it has the following contents:
Xello, world!"
As you can see, the 'X' character has been written to the file,
thanks to the call to flush
, but the Y
character is not.
This brutal procedure is necessary to show the effect, because if you terminate the process in any other way, the operating system is still able to save the buffer to persistent storage.
The flush
call, albeit more efficient than closing and reopening the file,
is rather inefficient, though, because it writes data to physical storage,
and so it should be used only when data consistency is required
even in case of a power failure or an operating system crash.
To support several operating systems, the source files contain several code
portions under conditional compilation.
If the _WIN32
macro is defined, then the Microsoft Windows API is used;
otherwise the POSIX API is used, allowing compilation for Linux, Unix,
MAC OS X, and other POSIX-compliant operating systems.
The header file contains only the namespace memory_mapped_file
,
containing the definition of the following items:
- The
mmf_granularity
function: It allows to get operating system allocation granularity (for advanced uses). - The
base_mmf
abstract class: It contains what is common between the other classes. It cannot be instantiated. - The
read_only_mmf
class: It is used to access an already existing file only for reading it. - The
writable_mmf
class: It is used to access an already existing or a not yet existing file for both reading and writing it. - The
mmf_exists_mode
enumeration: Options for creating a writable memory-mapped-file based on an already existing file. - The
mmf_doesnt_exist_mode
enumeration: Options for creating a writable memory-mapped-file based on a not yet existing file.
Scope: namespace memory_mapped_file
.
Operating systems do not allow to map files to memory starting from every specified byte. They require that the offset be a multiple of a number, named granularity, that is dependent on the operating system, and typically may vary from 4 KiB to 64 KiB.
To avoid bothering users with such technicality, this library takes care
of mapping memory internally from the nearest boundary.
For example, if the granularity is 65536, and for a 10 MB long file a mapping
is requested from 500000 to 800000,
the memory actually mapped by the operating system is from 458752
to a number somewhat greater than 800000,
but the offset
function will return 500000,
and the mapped_size
function will return 300000.
To allow the user to take granularity into account,
there is a global function named mmf_granularity
,
that returns such granularity size.
It is called like in the following statement:
unsigned int granularity = memory_mapped_file::mmf_granularity();
Scope: namespace memory_mapped_file
.
Abstract class representing a memory-mapped-file.
It is the base class of read_only_mmf
and writable_mmf
,
and therefore it gathers the features common to both classes.
The possible states of the instances of this class are:
- File not opened.
- File opened but not mapped.
- File opened and mapped.
The constructor sets the object in one of the three possible states, as it can fail to open the underlying file or not even try to open it (state 1), successfully open the file but fail to map it or not even try to map it (state 2), or successfully open and map the file (state 3).
An object in state 1 (file not opened) can pass to another state,
by calling successfully the open
function. If the value
of the second argument of the call is false
, the mapping is not even
attempted.
If the value of the second argument of the call is true
or is missing,
the mapping is attempted, but it may fail.
An object in state 2 (file not mapped) can pass to state 1 (file not opened)
by calling the close
function, and it can pass to state 3 (file mapped)
by calling the map
function.
An object in state 3 (file mapped) can pass to state 1 (file not opened)
by calling the close
function, and it can pass to state 2 (file not mapped)
by calling the unmap
function.
Scope: class base_mmf
.
It is the only constructor of its class. As this class is abstract, it can be called only by the constructor of the derived classes.
Scope: class base_mmf
.
It is the destructor. It releases every resources previously allocated by the object.
Scope: class base_mmf
.
It returns the distance in bytes
of the beginning of the portion of the file currently mapped to memory
from the beginning of the file. Therefore, it returns 0
(zero)
when the mapping starts at the beginning of the file.
It returns 0
also when the file hasn't been opened successfully,
or when the file has been opened, but it hasn't been mapped successfully.
therefore it cannot be used to discern if the file is open or not,
nor to discern if the file is mapped or not.
Scope: class base_mmf
.
It returns the size in bytes of the portion of the file currently
mapped to memory.
It returns 0
when the file hasn't been opened successfully,
or when the file has been opened, but it hasn't been mapped successfully.
A mapping cannot have zero length, therefore this call can be used
to discern if the file is mapped or not.
Scope: class base_mmf
.
It returns the whole size of the underlying opened file.
Of course, it returns 0
when the opened file has zero-length,
but it returns 0
also when the file hasn't been opened successfully;
therefore it cannot be used to discern if the file is open or not.
Scope: class base_mmf
.
It cancels the current mapping.
It is called implicitly at the beginning of the map
function,
and by the destructor.
It is always assumed successful.
Scope: class base_mmf
.
It closes the currently open file.
It is called implicitly at the beginning of the open
function,
and by the destructor.
It is always assumed successful.
Scope: class base_mmf
.
It returns true
if and only if the underlying file has been opened
successfully.
Scope: class base_mmf
.
Such name represents the operating-system-dependent type
of the handle of a file.
For POSIX systems, it is int
; for Microsoft Windows, it is void *
.
Scope: class base_mmf
.
It returns the operating-system-dependent handle used internally to access the file. It may be used to perform operating-system-dependent operations, not defined by this library, like file locking. If the file is open it returns a valid handle, while if the file is not open it returns an invalid handle, therefore it can be used to discern if a file is open or not, but using an operating-system-dependent value.
Scope: namespace memory_mapped_file
.
The instances of this class encapsulate memory-mapped-files that access a file only for reading it. Internally it opens the underlying file only for reading it.
This class is derived from the class base_mmf
.
Therefore the documentation of such class should be read
to see the inherited features.
It has several advantages with respect to the writable_mmf
class,
whose instances are capable of changing a file. They are:
- May be the only way.
If the operating system prevents any change to the underlying file
by the current user, any attempt to open such file for reading/writing,
like
writable_mmf
objects always do, will fail. - It's simpler to use. There is no need to specify what to do if the file does not exist, as obviously it cannot be opened. It is simpler to specify what to do if the file exists, as it cannot be truncated, and it is senseless to fail.
- It's safer to use. There is no risk of modifying accidentally the file. Typically any attempt to change the file will cause a compilation error; but if the code can be compiled, it will cause a run-time error by the operating system.
- It's more efficient. Operating systems usually use more efficient buffering algorithms for read-only files, than for read/write files.
Scope: class read_only_mmf
.
It is the only constructor of its class.
The pathname
argument is the relative or absolute pathname
of the underlying file, specified according the operating system syntax.
The map_all
argument specifies if, in case the file could be successfully
opened, such file should also be entirely mapped to memory or not.
By default, it is mapped, as it is the most convenient thing to do.
Instead, if it is needed to map the file later, or if it is needed
to map the file a piece at a time, the value of second argument
should be false
.
Scope: class read_only_mmf
.
It tries to open a file to be used for a read-only mapping to memory, and optionally to map to memory the contents of that file.
The pathname
argument is the relative or absolute pathname
of the underlying file, specified according the operating system syntax.
The map_all
argument specifies if, in case the file could be successfully
opened, such file should also be entirely mapped to memory or not.
By default, it is mapped, as it is the most convenient thing to do.
Instead, if it is needed to map the file later, or if it is needed
to map the file a piece at a time, the value of second argument
should be false
.
If the open
function is called when the file is already open,
it is closed first.
Therefore, it useless to call close
just before calling open
.
Scope: class read_only_mmf
.
It returns the address of the beginning of the read-only memory buffer mapped to a portion of the file.
If and only if the file is not mapped,
it returns 0
(i.e. nullptr
).
Therefore this call can be used to discern if the file is mapped or not.
Of course, it is undefined behavior both dereferencing the null pointer, and accessing the referenced buffer before the beginning or after the end.
Scope: class read_only_mmf
.
It tries to create a mapping between a portion of the file and a memory buffer.
The offset
argument specifies the distance of the beginning of the mapped
portion from the beginning of the file. By default it is 0
(zero),
meaning that the mapping starts at the beginning of the file.
The size
argument specifies the length of the required mapped portion
of the file. If offset + size
is greater than the length of the file,
the mapping extends up to the end of the file.
For example, if a file is 500 bytes long, and object
is of type
read_only_mmf
, the following statement:
object.map(100, 130);
maps into memory the 130 bytes from position 100 included to position 230 excluded, counting from 0.
And the following statement:
object.map(100, 750);
maps the 400 bytes from position 100 included to position 500 excluded.
By default, the size
argument is 0
, meaning a request to map
the file up to its end.
The map
function fails in the following cases:
- the underlying file is not open (so that
is_open
returnsfalse
); - the specified offset is equal to or greater than the file size;
- there is is not enough address space to map all the specified range;
- the operating system refuses to map the file to memory for some other reason.
If the map
function is successful,
- the
offset
function returns the same value passed as theoffset
argument; - the
mapped_size
function returns the size of the mapped portion, that is not greater than the value of thesize
argument (except when it is zero); - the
data
function returns a non-null value, that is a valid memory address.
Instead, if the map
function fails, the offset
,
the mapped_size
, and the data
functions return 0
.
If the map
function is called when the file is already mapped,
it is unmapped first.
Therefore, it useless to call unmap
just before calling map
.
Scope: namespace memory_mapped_file
.
This enumeration specifies what to do when a writable_mmf
object
is created and the underlying file already exists.
There are four cases:
-
if_exists_fail
: the file is not opened, so that if lateris_open
is called, it will returnfalse
, and of course ifmapped_size
anddata
are called, they will return0
. -
if_exists_just_open
: the file is opened but not mapped, so that if the open is successful and lateris_open
is called, it will returntrue
, butmapped_size
anddata
will however return0
. -
if_exists_map_all
: the file is opened and mapped all to memory, so that if the open is successful and lateris_open
is called, it will returntrue
, and if the map is also successful and latermapped_size
anddata
are called they will return non-null values. -
if_exists_truncate
: the file is opened and truncated, so that if the open is successful and lateris_open
is called, it will returntrue
, butmapped_size
anddata
will however return0
.
Scope: namespace memory_mapped_file
.
This enumeration specifies what to do when a writable_mmf
object
is created and the underlying file does not exist yet.
There are only two cases:
if_doesnt_exist_fail
: the file is not created, so that if lateris_open
is called, it will returnfalse
, and of course ifmapped_size
anddata
are called, they will return0
.if_doesnt_exist_create
, the file is created empty, so that if the creation is successful and lateris_open
is called, it will returntrue
, butmapped_size
anddata
will however return0
.
Scope: namespace memory_mapped_file
.
The instances of this class encapsulate memory-mapped-files that access a file for reading or writing it. Internally it opens the underlying file for reading or writing it.
This class is derived from the base_mmf
class.
Therefore see the documentation of such class to see the inherited features.
In addition, this class is rather similar to the read_only_mmf
class,
therefore here only the difference from such class are specified.
explicit writable_mmf(char const* pathname, mmf_exists_mode exists_mode, mmf_doesnt_exist_mode doesnt_exist_mode)
Scope: class writable_mmf
.
It is the only constructor of this class.
For more information, look at the description of the constructors
of base_mmf
and of read_only_mmf
,
and at the description of the enumerations mmf_exists_mode
and mmf_doesnt_exist_mode
.
The exists_mode
argument has four possible values,
and the doesnt_exist_mode
has two possible values,
and therefore there are the following eight possible construction cases:
writable_mmf(pathname, if_exists_fail, if_doesnt_exist_fail)
: Fail always. Of course it is senseless.writable_mmf(pathname, if_exists_fail, if_doesnt_exist_create)
: Fail or create. To be used to copy a file without overwriting an existing file.writable_mmf(pathname, if_exists_just_open, if_doesnt_exist_fail)
: Open or fail. Similar toread_only_mmf(pathname, false)
.writable_mmf(pathname, if_exists_just_open, if_doesnt_exist_create)
: Open or create. To be used to modify a file piece-wise, by creating it if not yet existing.writable_mmf(pathname, if_exists_map_all, if_doesnt_exist_fail)
: Map or fail. Similar toread_only_mmf(pathname)
.writable_mmf(pathname, if_exists_map_all, if_doesnt_exist_create)
: Map or create. To be used to modify a file as a whole, by creating it if not yet existing.writable_mmf(pathname, if_exists_truncate, if_doesnt_exist_fail)
: Truncate or fail. To be used to overwrite an existing file. Rarely useful.writable_mmf(pathname, if_exists_truncate, if_doesnt_exist_create)
: Truncate or create. To be used to copy a file even overwriting an existing file.
Scope: class writable_mmf
.
void open(char const* pathname, mmf_exists_mode exists_mode = if_exists_fail, mmf_doesnt_exist_mode doesnt_exist_mode = if_doesnt_exist_create)
Scope: class writable_mmf
.
It tries to open a file to be used for a read-write mapping to memory, and optionally to map to memory the contents of that file.
It is similar to the function with the same name of the read_only_mmf
class,
and to the constructor of this class.
Scope: class writable_mmf
.
It is has the same syntax and semantics of the function with the same name
of the read_only_mmf
class.
Scope: class writable_mmf
.
It copies all the changes to the file system, ensuring that they are persistent.
Actually, when a byte is modified in a memory-mapped file, that change may be applied much later to the underlying file, possibly only when the memory-mapped file is closed.
To ensure that every previous change is actually applied to the storage device,
it is possible to close and reopen the memory-mapped file.
The flush
operation achieves the same effect much more efficiently.
If returns true
if the operation succeeds, otherwise false
.