Une bibliothèque de mappage de mémoire C++11 multiplateforme multiplateforme facile à utiliser avec une licence MIT.
mio a été créé dans le but d'être facilement incluable (c'est-à-dire sans dépendances) dans tout projet C++ nécessitant des E/S de fichiers mappés en mémoire sans avoir besoin d'extraire Boost.
N'hésitez pas à ouvrir un problème, j'essaierai de répondre à toutes vos préoccupations du mieux que je peux.
Parce que la cartographie mémoire est la meilleure chose depuis le pain tranché !
Plus sérieusement, la principale motivation pour écrire cette bibliothèque au lieu d'utiliser Boost.Iostreams était le manque de prise en charge pour établir un mappage mémoire avec un descripteur/descripteur de fichier déjà ouvert. C'est possible avec mio.
De plus, la solution de Boost.Iostreams nécessite que l'utilisateur sélectionne les décalages exactement aux limites de la page, ce qui est fastidieux et sujet aux erreurs. mio, d'autre part, gère cela en interne, acceptant tout décalage et trouvant la limite de page la plus proche.
Bien qu'il s'agisse d'un petit problème, Boost.Iostreams implémente les E/S de fichiers mappés en mémoire avec un std::shared_ptr
pour fournir une sémantique partagée, même si elle n'est pas nécessaire, et la surcharge de l'allocation de tas peut être inutile et/ou indésirable. Dans mio, il existe deux classes pour couvrir les deux cas d'utilisation : l'une qui se déplace uniquement (essentiellement une abstraction sans coût sur les fonctions de mmappage spécifiques au système), et l'autre qui agit exactement comme son homologue Boost.Iostreams, avec sémantique partagée.
REMARQUE : le fichier doit exister avant de créer un mappage.
Il existe trois façons de mapper un fichier en mémoire :
std::system_error
en cas d'échec : mio::mmap_source mmap (path, offset, size_to_map);
ou vous pouvez omettre les arguments offset
et size_to_map
, auquel cas le fichier entier est mappé :
mio::mmap_source mmap (path);
std::error_code error;
mio::mmap_source mmap = mio::make_mmap_source(path, offset, size_to_map, error);
ou:
mio::mmap_source mmap = mio::make_mmap_source(path, error);
map
: std::error_code error;
mio::mmap_source mmap;
mmap.map(path, offset, size_to_map, error);
ou:
mmap.map(path, error);
REMARQUE : Les constructeurs nécessitent que les exceptions soient activées. Si vous préférez construire vos projets avec -fno-exceptions
, vous pouvez toujours utiliser les autres méthodes.
De plus, dans chaque cas, vous pouvez fournir soit un type de chaîne pour le chemin du fichier, soit utiliser un descripteur de fichier existant et valide.
# include < sys/types.h >
# include < sys/stat.h >
# include < fcntl.h >
# include < mio/mmap.hpp >
// #include <mio/mio.hpp> if using single header
# include < algorithm >
int main ()
{
// NOTE: error handling omitted for brevity.
const int fd = open ( " file.txt " , O_RDONLY);
mio::mmap_source mmap (fd, 0 , mio::map_entire_file);
// ...
}
Cependant, mio ne vérifie pas si le descripteur de fichier fourni dispose des mêmes autorisations d'accès que le mappage souhaité, le mappage peut donc échouer. De telles erreurs sont signalées via le paramètre std::error_code
out qui est transmis à la fonction de mappage.
UTILISATEURS WINDOWS : Cette bibliothèque prend en charge l'utilisation de types de caractères larges pour les fonctions où des chaînes de caractères sont attendues (par exemple les paramètres de chemin).
# include < mio/mmap.hpp >
// #include <mio/mio.hpp> if using single header
# include < system_error > // for std::error_code
# include < cstdio > // for std::printf
# include < cassert >
# include < algorithm >
# include < fstream >
int handle_error ( const std::error_code& error);
void allocate_file ( const std::string& path, const int size);
int main ()
{
const auto path = " file.txt " ;
// NOTE: mio does *not* create the file for you if it doesn't exist! You
// must ensure that the file exists before establishing a mapping. It
// must also be non-empty. So for illustrative purposes the file is
// created now.
allocate_file (path, 155 );
// Read-write memory map the whole file by using `map_entire_file` where the
// length of the mapping is otherwise expected, with the factory method.
std::error_code error;
mio::mmap_sink rw_mmap = mio::make_mmap_sink (
path, 0 , mio::map_entire_file, error);
if (error) { return handle_error (error); }
// You can use any iterator based function.
std::fill (rw_mmap. begin (), rw_mmap. end (), ' a ' );
// Or manually iterate through the mapped region just as if it were any other
// container, and change each byte's value (since this is a read-write mapping).
for ( auto & b : rw_mmap) {
b += 10 ;
}
// Or just change one value with the subscript operator.
const int answer_index = rw_mmap. size () / 2 ;
rw_mmap[answer_index] = 42 ;
// Don't forget to flush changes to disk before unmapping. However, if
// `rw_mmap` were to go out of scope at this point, the destructor would also
// automatically invoke `sync` before `unmap`.
rw_mmap. sync (error);
if (error) { return handle_error (error); }
// We can then remove the mapping, after which rw_mmap will be in a default
// constructed state, i.e. this and the above call to `sync` have the same
// effect as if the destructor had been invoked.
rw_mmap. unmap ();
// Now create the same mapping, but in read-only mode. Note that calling the
// overload without the offset and file length parameters maps the entire
// file.
mio::mmap_source ro_mmap;
ro_mmap. map (path, error);
if (error) { return handle_error (error); }
const int the_answer_to_everything = ro_mmap[answer_index];
assert (the_answer_to_everything == 42 );
}
int handle_error ( const std::error_code& error)
{
const auto & errmsg = error. message ();
std::printf ( " error mapping file: %s, exiting... n " , errmsg. c_str ());
return error. value ();
}
void allocate_file ( const std::string& path, const int size)
{
std::ofstream file (path);
std::string s (size, ' 0 ' );
file << s;
}
mio::basic_mmap
est uniquement en déplacement, mais si plusieurs copies vers le même mappage sont nécessaires, utilisez mio::basic_shared_mmap
qui a la sémantique std::shared_ptr
et a la même interface que mio::basic_mmap
.
# include < mio/shared_mmap.hpp >
mio::shared_mmap_source shared_mmap1 ( " path " , offset, size_to_map);
mio::shared_mmap_source shared_mmap2 (std::move(mmap1)); // or use operator=
mio::shared_mmap_source shared_mmap3 (std::make_shared<mio::mmap_source>(mmap1)); // or use operator=
mio::shared_mmap_source shared_mmap4;
shared_mmap4.map( " path " , offset, size_to_map, error);
Il est possible de définir le type d'un octet (qui doit avoir la même largeur que char
), bien que les alias pour les plus courants soient fournis par défaut :
using mmap_source = basic_mmap_source< char >;
using ummap_source = basic_mmap_source< unsigned char >;
using mmap_sink = basic_mmap_sink< char >;
using ummap_sink = basic_mmap_sink< unsigned char >;
Mais il peut être utile de définir vos propres types, par exemple lorsque vous utilisez le nouveau std::byte
en C++17 :
using mmap_source = mio::basic_mmap_source<std::byte>;
using mmap_sink = mio::basic_mmap_sink<std::byte>;
Bien que cela ne soit généralement pas nécessaire, puisque mio mappe les décalages demandés par les utilisateurs aux limites de page, vous pouvez interroger la granularité d'allocation de page du système sous-jacent en appelant mio::page_size()
, qui se trouve dans mio/page.hpp
.
Mio peut être ajouté à votre projet en tant que fichier d'en-tête unique en incluant simplement single_includemiomio.hpp
. Les fichiers d'en-tête unique peuvent être régénérés à tout moment en exécutant le script amalgamate.py
dans third_party
.
python amalgamate.py -c config.json -s ../include
En tant que bibliothèque d'en-tête uniquement, mio n'a aucun composant compilé. Néanmoins, un système de build CMake est fourni pour permettre des tests, une installation et une composition de sous-projets faciles sur de nombreuses plates-formes et systèmes d'exploitation.
Mio est distribué avec une petite suite de tests et d'exemples. Lorsque mio est configuré comme projet CMake de plus haut niveau, cette suite d'exécutables est construite par défaut. Les exécutables de test de Mio sont intégrés au programme de pilote de test CMake, CTest.
CMake prend en charge un certain nombre de backends pour la compilation et la liaison.
Pour utiliser un outil de création de configuration statique, tel que GNU Make ou Ninja :
cd < mio source directory >
mkdir build
cd build
# Configure the build
cmake -D CMAKE_BUILD_TYPE= < Debug | Release >
-G < " Unix Makefiles " | " Ninja " > ..
# build the tests
< make | ninja | cmake --build . >
# run the tests
< make test | ninja test | cmake --build . --target test | ctest >
Pour utiliser un outil de création de configuration dynamique, tel que Visual Studio ou Xcode :
cd < mio source directory >
mkdir build
cd build
# Configure the build
cmake -G < " Visual Studio 14 2015 Win64 " | " Xcode " > ..
# build the tests
cmake --build . --config < Debug | Release >
# run the tests via ctest...
ctest --build-config < Debug | Release >
# ... or via CMake build tool mode...
cmake --build . --config < Debug | Release > --target test
Bien entendu, les étapes de construction et de test peuvent également être exécutées via les cibles all et test , respectivement, depuis l'EDI après avoir ouvert le fichier de projet généré lors de l'étape de configuration.
Les tests de Mio sont également configurés pour fonctionner en tant que client de l'application de tableau de bord de qualité du logiciel CDash. Veuillez consulter la documentation de Kitware pour plus d'informations sur ce mode de fonctionnement.
Le système de build de Mio fournit une cible d'installation et une prise en charge de la consommation en aval via la fonction intrinsèque find_package
de CMake. CMake permet l'installation à un emplacement arbitraire, qui peut être spécifié en définissant CMAKE_INSTALL_PREFIX
au moment de la configuration. En l'absence de spécification de l'utilisateur, CMake installera mio à un emplacement conventionnel en fonction du système d'exploitation de la plate-forme.
Pour utiliser un outil de création de configuration statique, tel que GNU Make ou Ninja :
cd < mio source directory >
mkdir build
cd build
# Configure the build
cmake [-D CMAKE_INSTALL_PREFIX = " path/to/installation " ]
[-D BUILD_TESTING = False]
-D CMAKE_BUILD_TYPE=Release
-G < " Unix Makefiles " | " Ninja " > ..
# install mio
< make install | ninja install | cmake --build . --target install >
Pour utiliser un outil de création de configuration dynamique, tel que Visual Studio ou Xcode :
cd < mio source directory >
mkdir build
cd build
# Configure the project
cmake [-D CMAKE_INSTALL_PREFIX = " path/to/installation " ]
[-D BUILD_TESTING = False]
-G < " Visual Studio 14 2015 Win64 " | " Xcode " > ..
# install mio
cmake --build . --config Release --target install
Notez que la dernière commande de la séquence d'installation peut nécessiter des privilèges d'administrateur (par exemple sudo
) si le répertoire racine d'installation se trouve en dehors de votre répertoire personnel.
Cette installation
include/mio
de la racine d'installationshare/cmake/mio
de la racine d'installation Cette dernière étape permet aux projets CMake en aval de consommer des millions via find_package
, par exemple
find_package ( mio REQUIRED )
target_link_libraries ( MyTarget PUBLIC mio::mio )
UTILISATEURS WINDOWS : La cible mio::mio
#define
s WIN32_LEAN_AND_MEAN
et NOMINMAX
. Le premier garantit que la surface importée de l'API Win est minimale, et le second désactive les macros min
et max
de Windows afin qu'elles n'interfèrent pas avec std::min
et std::max
. Étant donné que mio est une bibliothèque d'en-tête uniquement, ces définitions seront divulguées dans les versions CMake en aval. Si leur présence pose des problèmes avec votre build, vous pouvez utiliser la cible alternative mio::mio_full_winapi
, qui n'ajoute aucune de ces définitions.
Si mio a été installé dans un emplacement non conventionnel, il peut être nécessaire pour les projets en aval de spécifier le répertoire racine d'installation de mio via l'un ou l'autre
CMAKE_PREFIX_PATH
,CMAKE_PREFIX_PATH
, oumio_DIR
.Veuillez consulter la documentation de Kitware pour plus d'informations.
De plus, mio prend en charge les installations relocalisables packagées via CPack. Après la configuration, à partir du répertoire build, appelez cpack comme suit pour générer une installation packagée :
cpack -G < generator name > -C Release
La liste des générateurs pris en charge varie d'une plateforme à l'autre. Consultez le résultat de cpack --help
pour une liste complète des générateurs pris en charge sur votre plateforme.
Pour utiliser mio comme sous-projet, copiez le référentiel mio dans le dossier dependencies/externals de votre projet. Si votre projet est contrôlé en version à l'aide de git, un sous-module git ou un sous-arbre git peut être utilisé pour la synchronisation avec le référentiel updstream. L'utilisation et les avantages relatifs de ces fonctionnalités git dépassent le cadre de ce document, mais en bref, chacun peut être établi comme suit :
# via git submodule
cd < my project ' s dependencies directory>
git submodule add -b master https://github.com/mandreyel/mio.git
# via git subtree
cd <my project ' s root directory >
git subtree add --prefix < path/to/dependencies > /mio
https://github.com/mandreyel/mio.git master --squash
Étant donné un sous-répertoire mio dans un projet, ajoutez simplement les lignes suivantes à celui de votre projet pour ajouter les répertoires d'inclusion mio au chemin d'inclusion de votre cible.
add_subdirectory ( path /to/mio/ )
target_link_libraries ( MyTarget PUBLIC <mio::mio | mio> )
Notez qu'en tant que sous-projet, les tests et exemples de mio ne seront pas construits et l'intégration du CPack est reportée au projet hôte.