tfrun

An easy-to-use C++ wrapper over the stable C API of TensorFlow
git clone https://0xff.ir/g/tfrun.git
Log | Files | Refs | README | LICENSE

mio.hpp (59393B)


      1 /* Copyright 2017 https://github.com/mandreyel
      2  *
      3  * Permission is hereby granted, free of charge, to any person obtaining a copy of this
      4  * software and associated documentation files (the "Software"), to deal in the Software
      5  * without restriction, including without limitation the rights to use, copy, modify,
      6  * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
      7  * permit persons to whom the Software is furnished to do so, subject to the following
      8  * conditions:
      9  *
     10  * The above copyright notice and this permission notice shall be included in all copies
     11  * or substantial portions of the Software.
     12  *
     13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
     14  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
     15  * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
     16  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
     17  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
     18  * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     19  */
     20 
     21 #ifndef MIO_MMAP_HEADER
     22 #define MIO_MMAP_HEADER
     23 
     24 // #include "mio/page.hpp"
     25 /* Copyright 2017 https://github.com/mandreyel
     26  *
     27  * Permission is hereby granted, free of charge, to any person obtaining a copy of this
     28  * software and associated documentation files (the "Software"), to deal in the Software
     29  * without restriction, including without limitation the rights to use, copy, modify,
     30  * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
     31  * permit persons to whom the Software is furnished to do so, subject to the following
     32  * conditions:
     33  *
     34  * The above copyright notice and this permission notice shall be included in all copies
     35  * or substantial portions of the Software.
     36  *
     37  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
     38  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
     39  * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
     40  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
     41  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
     42  * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     43  */
     44 
     45 #ifndef MIO_PAGE_HEADER
     46 #define MIO_PAGE_HEADER
     47 
     48 #ifdef _WIN32
     49 # include <windows.h>
     50 #else
     51 # include <unistd.h>
     52 #endif
     53 
     54 namespace mio {
     55 
     56 /**
     57  * This is used by `basic_mmap` to determine whether to create a read-only or
     58  * a read-write memory mapping.
     59  */
     60 enum class access_mode
     61 {
     62     read,
     63     write
     64 };
     65 
     66 /**
     67  * Determines the operating system's page allocation granularity.
     68  *
     69  * On the first call to this function, it invokes the operating system specific syscall
     70  * to determine the page size, caches the value, and returns it. Any subsequent call to
     71  * this function serves the cached value, so no further syscalls are made.
     72  */
     73 inline size_t page_size()
     74 {
     75     static const size_t page_size = []
     76     {
     77 #ifdef _WIN32
     78         SYSTEM_INFO SystemInfo;
     79         GetSystemInfo(&SystemInfo);
     80         return SystemInfo.dwAllocationGranularity;
     81 #else
     82         return sysconf(_SC_PAGE_SIZE);
     83 #endif
     84     }();
     85     return page_size;
     86 }
     87 
     88 /**
     89  * Alligns `offset` to the operating's system page size such that it subtracts the
     90  * difference until the nearest page boundary before `offset`, or does nothing if
     91  * `offset` is already page aligned.
     92  */
     93 inline size_t make_offset_page_aligned(size_t offset) noexcept
     94 {
     95     const size_t page_size_ = page_size();
     96     // Use integer division to round down to the nearest page alignment.
     97     return offset / page_size_ * page_size_;
     98 }
     99 
    100 } // namespace mio
    101 
    102 #endif // MIO_PAGE_HEADER
    103 
    104 
    105 #include <iterator>
    106 #include <string>
    107 #include <system_error>
    108 #include <cstdint>
    109 
    110 #ifdef _WIN32
    111 # ifndef WIN32_LEAN_AND_MEAN
    112 #  define WIN32_LEAN_AND_MEAN
    113 # endif // WIN32_LEAN_AND_MEAN
    114 # include <windows.h>
    115 #else // ifdef _WIN32
    116 # define INVALID_HANDLE_VALUE -1
    117 #endif // ifdef _WIN32
    118 
    119 namespace mio {
    120 
    121 // This value may be provided as the `length` parameter to the constructor or
    122 // `map`, in which case a memory mapping of the entire file is created.
    123 enum { map_entire_file = 0 };
    124 
    125 #ifdef _WIN32
    126 using file_handle_type = HANDLE;
    127 #else
    128 using file_handle_type = int;
    129 #endif
    130 
    131 // This value represents an invalid file handle type. This can be used to
    132 // determine whether `basic_mmap::file_handle` is valid, for example.
    133 const static file_handle_type invalid_handle = INVALID_HANDLE_VALUE;
    134 
    135 template<access_mode AccessMode, typename ByteT>
    136 struct basic_mmap
    137 {
    138     using value_type = ByteT;
    139     using size_type = size_t;
    140     using reference = value_type&;
    141     using const_reference = const value_type&;
    142     using pointer = value_type*;
    143     using const_pointer = const value_type*;
    144     using difference_type = std::ptrdiff_t;
    145     using iterator = pointer;
    146     using const_iterator = const_pointer;
    147     using reverse_iterator = std::reverse_iterator<iterator>;
    148     using const_reverse_iterator = std::reverse_iterator<const_iterator>;
    149     using iterator_category = std::random_access_iterator_tag;
    150     using handle_type = file_handle_type;
    151 
    152     static_assert(sizeof(ByteT) == sizeof(char), "ByteT must be the same size as char.");
    153 
    154 private:
    155     // Points to the first requested byte, and not to the actual start of the mapping.
    156     pointer data_ = nullptr;
    157 
    158     // Length--in bytes--requested by user (which may not be the length of the
    159     // full mapping) and the length of the full mapping.
    160     size_type length_ = 0;
    161     size_type mapped_length_ = 0;
    162 
    163     // Letting user map a file using both an existing file handle and a path
    164     // introcudes some complexity (see `is_handle_internal_`).
    165     // On POSIX, we only need a file handle to create a mapping, while on
    166     // Windows systems the file handle is necessary to retrieve a file mapping
    167     // handle, but any subsequent operations on the mapped region must be done
    168     // through the latter.
    169     handle_type file_handle_ = INVALID_HANDLE_VALUE;
    170 #ifdef _WIN32
    171     handle_type file_mapping_handle_ = INVALID_HANDLE_VALUE;
    172 #endif
    173 
    174     // Letting user map a file using both an existing file handle and a path
    175     // introcudes some complexity in that we must not close the file handle if
    176     // user provided it, but we must close it if we obtained it using the
    177     // provided path. For this reason, this flag is used to determine when to
    178     // close `file_handle_`.
    179     bool is_handle_internal_;
    180 
    181 public:
    182     /**
    183      * The default constructed mmap object is in a non-mapped state, that is,
    184      * any operation that attempts to access nonexistent underlying data will
    185      * result in undefined behaviour/segmentation faults.
    186      */
    187     basic_mmap() = default;
    188 
    189 #ifdef __cpp_exceptions
    190     /**
    191      * The same as invoking the `map` function, except any error that may occur
    192      * while establishing the mapping is wrapped in a `std::system_error` and is
    193      * thrown.
    194      */
    195     template<typename String>
    196     basic_mmap(const String& path, const size_type offset = 0, const size_type length = map_entire_file)
    197     {
    198         std::error_code error;
    199         map(path, offset, length, error);
    200         if(error) { throw std::system_error(error); }
    201     }
    202 
    203     /**
    204      * The same as invoking the `map` function, except any error that may occur
    205      * while establishing the mapping is wrapped in a `std::system_error` and is
    206      * thrown.
    207      */
    208     basic_mmap(const handle_type handle, const size_type offset = 0, const size_type length = map_entire_file)
    209     {
    210         std::error_code error;
    211         map(handle, offset, length, error);
    212         if(error) { throw std::system_error(error); }
    213     }
    214 #endif // __cpp_exceptions
    215 
    216     /**
    217      * `basic_mmap` has single-ownership semantics, so transferring ownership
    218      * may only be accomplished by moving the object.
    219      */
    220     basic_mmap(const basic_mmap&) = delete;
    221     basic_mmap(basic_mmap&&);
    222     basic_mmap& operator=(const basic_mmap&) = delete;
    223     basic_mmap& operator=(basic_mmap&&);
    224 
    225     /**
    226      * If this is a read-write mapping, the destructor invokes sync. Regardless
    227      * of the access mode, unmap is invoked as a final step.
    228      */
    229     ~basic_mmap();
    230 
    231     /**
    232      * On UNIX systems 'file_handle' and 'mapping_handle' are the same. On Windows,
    233      * however, a mapped region of a file gets its own handle, which is returned by
    234      * 'mapping_handle'.
    235      */
    236     handle_type file_handle() const noexcept { return file_handle_; }
    237     handle_type mapping_handle() const noexcept;
    238 
    239     /** Returns whether a valid memory mapping has been created. */
    240     bool is_open() const noexcept { return file_handle_ != invalid_handle; }
    241 
    242     /**
    243      * Returns true if no mapping was established, that is, conceptually the
    244      * same as though the length that was mapped was 0. This function is
    245      * provided so that this class has Container semantics.
    246      */
    247     bool empty() const noexcept { return length() == 0; }
    248 
    249     /** Returns true if a mapping was established. */
    250     bool is_mapped() const noexcept;
    251 
    252     /**
    253      * `size` and `length` both return the logical length, i.e. the number of bytes
    254      * user requested to be mapped, while `mapped_length` returns the actual number of
    255      * bytes that were mapped which is a multiple of the underlying operating system's
    256      * page allocation granularity.
    257      */
    258     size_type size() const noexcept { return length(); }
    259     size_type length() const noexcept { return length_; }
    260     size_type mapped_length() const noexcept { return mapped_length_; }
    261 
    262     /** Returns the offset relative to the start of the mapping. */
    263     size_type mapping_offset() const noexcept
    264     {
    265         return mapped_length_ - length_;
    266     }
    267 
    268     /**
    269      * Returns a pointer to the first requested byte, or `nullptr` if no memory mapping
    270      * exists.
    271      */
    272     template<
    273         access_mode A = AccessMode,
    274         typename = typename std::enable_if<A == access_mode::write>::type
    275     > pointer data() noexcept { return data_; }
    276     const_pointer data() const noexcept { return data_; }
    277 
    278     /**
    279      * Returns an iterator to the first requested byte, if a valid memory mapping
    280      * exists, otherwise this function call is undefined behaviour.
    281      */
    282     template<
    283         access_mode A = AccessMode,
    284         typename = typename std::enable_if<A == access_mode::write>::type
    285     > iterator begin() noexcept { return data(); }
    286     const_iterator begin() const noexcept { return data(); }
    287     const_iterator cbegin() const noexcept { return data(); }
    288 
    289     /**
    290      * Returns an iterator one past the last requested byte, if a valid memory mapping
    291      * exists, otherwise this function call is undefined behaviour.
    292      */
    293     template<
    294         access_mode A = AccessMode,
    295         typename = typename std::enable_if<A == access_mode::write>::type
    296     > iterator end() noexcept { return data() + length(); }
    297     const_iterator end() const noexcept { return data() + length(); }
    298     const_iterator cend() const noexcept { return data() + length(); }
    299 
    300     /**
    301      * Returns a reverse iterator to the last memory mapped byte, if a valid
    302      * memory mapping exists, otherwise this function call is undefined
    303      * behaviour.
    304      */
    305     template<
    306         access_mode A = AccessMode,
    307         typename = typename std::enable_if<A == access_mode::write>::type
    308     > reverse_iterator rbegin() noexcept { return reverse_iterator(end()); }
    309     const_reverse_iterator rbegin() const noexcept
    310     { return const_reverse_iterator(end()); }
    311     const_reverse_iterator crbegin() const noexcept
    312     { return const_reverse_iterator(end()); }
    313 
    314     /**
    315      * Returns a reverse iterator past the first mapped byte, if a valid memory
    316      * mapping exists, otherwise this function call is undefined behaviour.
    317      */
    318     template<
    319         access_mode A = AccessMode,
    320         typename = typename std::enable_if<A == access_mode::write>::type
    321     > reverse_iterator rend() noexcept { return reverse_iterator(begin()); }
    322     const_reverse_iterator rend() const noexcept
    323     { return const_reverse_iterator(begin()); }
    324     const_reverse_iterator crend() const noexcept
    325     { return const_reverse_iterator(begin()); }
    326 
    327     /**
    328      * Returns a reference to the `i`th byte from the first requested byte (as returned
    329      * by `data`). If this is invoked when no valid memory mapping has been created
    330      * prior to this call, undefined behaviour ensues.
    331      */
    332     reference operator[](const size_type i) noexcept { return data_[i]; }
    333     const_reference operator[](const size_type i) const noexcept { return data_[i]; }
    334 
    335     /**
    336      * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the
    337      * reason is reported via `error` and the object remains in a state as if this
    338      * function hadn't been called.
    339      *
    340      * `path`, which must be a path to an existing file, is used to retrieve a file
    341      * handle (which is closed when the object destructs or `unmap` is called), which is
    342      * then used to memory map the requested region. Upon failure, `error` is set to
    343      * indicate the reason and the object remains in an unmapped state.
    344      *
    345      * `offset` is the number of bytes, relative to the start of the file, where the
    346      * mapping should begin. When specifying it, there is no need to worry about
    347      * providing a value that is aligned with the operating system's page allocation
    348      * granularity. This is adjusted by the implementation such that the first requested
    349      * byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at
    350      * `offset` from the start of the file.
    351      *
    352      * `length` is the number of bytes to map. It may be `map_entire_file`, in which
    353      * case a mapping of the entire file is created.
    354      */
    355     template<typename String>
    356     void map(const String& path, const size_type offset,
    357             const size_type length, std::error_code& error);
    358 
    359     /**
    360      * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the
    361      * reason is reported via `error` and the object remains in a state as if this
    362      * function hadn't been called.
    363      *
    364      * `path`, which must be a path to an existing file, is used to retrieve a file
    365      * handle (which is closed when the object destructs or `unmap` is called), which is
    366      * then used to memory map the requested region. Upon failure, `error` is set to
    367      * indicate the reason and the object remains in an unmapped state.
    368      *
    369      * The entire file is mapped.
    370      */
    371     template<typename String>
    372     void map(const String& path, std::error_code& error)
    373     {
    374         map(path, 0, map_entire_file, error);
    375     }
    376 
    377     /**
    378      * Establishes a memory mapping with AccessMode. If the mapping is
    379      * unsuccesful, the reason is reported via `error` and the object remains in
    380      * a state as if this function hadn't been called.
    381      *
    382      * `handle`, which must be a valid file handle, which is used to memory map the
    383      * requested region. Upon failure, `error` is set to indicate the reason and the
    384      * object remains in an unmapped state.
    385      *
    386      * `offset` is the number of bytes, relative to the start of the file, where the
    387      * mapping should begin. When specifying it, there is no need to worry about
    388      * providing a value that is aligned with the operating system's page allocation
    389      * granularity. This is adjusted by the implementation such that the first requested
    390      * byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at
    391      * `offset` from the start of the file.
    392      *
    393      * `length` is the number of bytes to map. It may be `map_entire_file`, in which
    394      * case a mapping of the entire file is created.
    395      */
    396     void map(const handle_type handle, const size_type offset,
    397             const size_type length, std::error_code& error);
    398 
    399     /**
    400      * Establishes a memory mapping with AccessMode. If the mapping is
    401      * unsuccesful, the reason is reported via `error` and the object remains in
    402      * a state as if this function hadn't been called.
    403      *
    404      * `handle`, which must be a valid file handle, which is used to memory map the
    405      * requested region. Upon failure, `error` is set to indicate the reason and the
    406      * object remains in an unmapped state.
    407      *
    408      * The entire file is mapped.
    409      */
    410     void map(const handle_type handle, std::error_code& error)
    411     {
    412         map(handle, 0, map_entire_file, error);
    413     }
    414 
    415     /**
    416      * If a valid memory mapping has been created prior to this call, this call
    417      * instructs the kernel to unmap the memory region and disassociate this object
    418      * from the file.
    419      *
    420      * The file handle associated with the file that is mapped is only closed if the
    421      * mapping was created using a file path. If, on the other hand, an existing
    422      * file handle was used to create the mapping, the file handle is not closed.
    423      */
    424     void unmap();
    425 
    426     void swap(basic_mmap& other);
    427 
    428     /** Flushes the memory mapped page to disk. Errors are reported via `error`. */
    429     template<access_mode A = AccessMode>
    430     typename std::enable_if<A == access_mode::write, void>::type
    431     sync(std::error_code& error);
    432 
    433     /**
    434      * All operators compare the address of the first byte and size of the two mapped
    435      * regions.
    436      */
    437 
    438 private:
    439     template<
    440         access_mode A = AccessMode,
    441         typename = typename std::enable_if<A == access_mode::write>::type
    442     > pointer get_mapping_start() noexcept
    443     {
    444         return !data() ? nullptr : data() - mapping_offset();
    445     }
    446 
    447     const_pointer get_mapping_start() const noexcept
    448     {
    449         return !data() ? nullptr : data() - mapping_offset();
    450     }
    451 
    452     /**
    453      * The destructor syncs changes to disk if `AccessMode` is `write`, but not
    454      * if it's `read`, but since the destructor cannot be templated, we need to
    455      * do SFINAE in a dedicated function, where one syncs and the other is a noop.
    456      */
    457     template<access_mode A = AccessMode>
    458     typename std::enable_if<A == access_mode::write, void>::type
    459     conditional_sync();
    460     template<access_mode A = AccessMode>
    461     typename std::enable_if<A == access_mode::read, void>::type conditional_sync();
    462 };
    463 
    464 template<access_mode AccessMode, typename ByteT>
    465 bool operator==(const basic_mmap<AccessMode, ByteT>& a,
    466         const basic_mmap<AccessMode, ByteT>& b);
    467 
    468 template<access_mode AccessMode, typename ByteT>
    469 bool operator!=(const basic_mmap<AccessMode, ByteT>& a,
    470         const basic_mmap<AccessMode, ByteT>& b);
    471 
    472 template<access_mode AccessMode, typename ByteT>
    473 bool operator<(const basic_mmap<AccessMode, ByteT>& a,
    474         const basic_mmap<AccessMode, ByteT>& b);
    475 
    476 template<access_mode AccessMode, typename ByteT>
    477 bool operator<=(const basic_mmap<AccessMode, ByteT>& a,
    478         const basic_mmap<AccessMode, ByteT>& b);
    479 
    480 template<access_mode AccessMode, typename ByteT>
    481 bool operator>(const basic_mmap<AccessMode, ByteT>& a,
    482         const basic_mmap<AccessMode, ByteT>& b);
    483 
    484 template<access_mode AccessMode, typename ByteT>
    485 bool operator>=(const basic_mmap<AccessMode, ByteT>& a,
    486         const basic_mmap<AccessMode, ByteT>& b);
    487 
    488 /**
    489  * This is the basis for all read-only mmap objects and should be preferred over
    490  * directly using `basic_mmap`.
    491  */
    492 template<typename ByteT>
    493 using basic_mmap_source = basic_mmap<access_mode::read, ByteT>;
    494 
    495 /**
    496  * This is the basis for all read-write mmap objects and should be preferred over
    497  * directly using `basic_mmap`.
    498  */
    499 template<typename ByteT>
    500 using basic_mmap_sink = basic_mmap<access_mode::write, ByteT>;
    501 
    502 /**
    503  * These aliases cover the most common use cases, both representing a raw byte stream
    504  * (either with a char or an unsigned char/uint8_t).
    505  */
    506 using mmap_source = basic_mmap_source<char>;
    507 using ummap_source = basic_mmap_source<unsigned char>;
    508 
    509 using mmap_sink = basic_mmap_sink<char>;
    510 using ummap_sink = basic_mmap_sink<unsigned char>;
    511 
    512 /**
    513  * Convenience factory method that constructs a mapping for any `basic_mmap` or
    514  * `basic_mmap` type.
    515  */
    516 template<
    517     typename MMap,
    518     typename MappingToken
    519 > MMap make_mmap(const MappingToken& token,
    520         int64_t offset, int64_t length, std::error_code& error)
    521 {
    522     MMap mmap;
    523     mmap.map(token, offset, length, error);
    524     return mmap;
    525 }
    526 
    527 /**
    528  * Convenience factory method.
    529  *
    530  * MappingToken may be a String (`std::string`, `std::string_view`, `const char*`,
    531  * `std::filesystem::path`, `std::vector<char>`, or similar), or a
    532  * `mmap_source::handle_type`.
    533  */
    534 template<typename MappingToken>
    535 mmap_source make_mmap_source(const MappingToken& token, mmap_source::size_type offset,
    536         mmap_source::size_type length, std::error_code& error)
    537 {
    538     return make_mmap<mmap_source>(token, offset, length, error);
    539 }
    540 
    541 template<typename MappingToken>
    542 mmap_source make_mmap_source(const MappingToken& token, std::error_code& error)
    543 {
    544     return make_mmap_source(token, 0, map_entire_file, error);
    545 }
    546 
    547 /**
    548  * Convenience factory method.
    549  *
    550  * MappingToken may be a String (`std::string`, `std::string_view`, `const char*`,
    551  * `std::filesystem::path`, `std::vector<char>`, or similar), or a
    552  * `mmap_sink::handle_type`.
    553  */
    554 template<typename MappingToken>
    555 mmap_sink make_mmap_sink(const MappingToken& token, mmap_sink::size_type offset,
    556         mmap_sink::size_type length, std::error_code& error)
    557 {
    558     return make_mmap<mmap_sink>(token, offset, length, error);
    559 }
    560 
    561 template<typename MappingToken>
    562 mmap_sink make_mmap_sink(const MappingToken& token, std::error_code& error)
    563 {
    564     return make_mmap_sink(token, 0, map_entire_file, error);
    565 }
    566 
    567 } // namespace mio
    568 
    569 // #include "detail/mmap.ipp"
    570 /* Copyright 2017 https://github.com/mandreyel
    571  *
    572  * Permission is hereby granted, free of charge, to any person obtaining a copy of this
    573  * software and associated documentation files (the "Software"), to deal in the Software
    574  * without restriction, including without limitation the rights to use, copy, modify,
    575  * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
    576  * permit persons to whom the Software is furnished to do so, subject to the following
    577  * conditions:
    578  *
    579  * The above copyright notice and this permission notice shall be included in all copies
    580  * or substantial portions of the Software.
    581  *
    582  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    583  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    584  * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    585  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
    586  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
    587  * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    588  */
    589 
    590 #ifndef MIO_BASIC_MMAP_IMPL
    591 #define MIO_BASIC_MMAP_IMPL
    592 
    593 // #include "mio/mmap.hpp"
    594 
    595 // #include "mio/page.hpp"
    596 
    597 // #include "mio/detail/string_util.hpp"
    598 /* Copyright 2017 https://github.com/mandreyel
    599  *
    600  * Permission is hereby granted, free of charge, to any person obtaining a copy of this
    601  * software and associated documentation files (the "Software"), to deal in the Software
    602  * without restriction, including without limitation the rights to use, copy, modify,
    603  * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
    604  * permit persons to whom the Software is furnished to do so, subject to the following
    605  * conditions:
    606  *
    607  * The above copyright notice and this permission notice shall be included in all copies
    608  * or substantial portions of the Software.
    609  *
    610  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    611  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    612  * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    613  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
    614  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
    615  * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    616  */
    617 
    618 #ifndef MIO_STRING_UTIL_HEADER
    619 #define MIO_STRING_UTIL_HEADER
    620 
    621 #include <type_traits>
    622 
    623 namespace mio {
    624 namespace detail {
    625 
    626 template<
    627     typename S,
    628     typename C = typename std::decay<S>::type,
    629     typename = decltype(std::declval<C>().data()),
    630     typename = typename std::enable_if<
    631         std::is_same<typename C::value_type, char>::value
    632 #ifdef _WIN32
    633         || std::is_same<typename C::value_type, wchar_t>::value
    634 #endif
    635     >::type
    636 > struct char_type_helper {
    637     using type = typename C::value_type;
    638 };
    639 
    640 template<class T>
    641 struct char_type {
    642     using type = typename char_type_helper<T>::type;
    643 };
    644 
    645 // TODO: can we avoid this brute force approach?
    646 template<>
    647 struct char_type<char*> {
    648     using type = char;
    649 };
    650 
    651 template<>
    652 struct char_type<const char*> {
    653     using type = char;
    654 };
    655 
    656 template<size_t N>
    657 struct char_type<char[N]> {
    658     using type = char;
    659 };
    660 
    661 template<size_t N>
    662 struct char_type<const char[N]> {
    663     using type = char;
    664 };
    665 
    666 #ifdef _WIN32
    667 template<>
    668 struct char_type<wchar_t*> {
    669     using type = wchar_t;
    670 };
    671 
    672 template<>
    673 struct char_type<const wchar_t*> {
    674     using type = wchar_t;
    675 };
    676 
    677 template<size_t N>
    678 struct char_type<wchar_t[N]> {
    679     using type = wchar_t;
    680 };
    681 
    682 template<size_t N>
    683 struct char_type<const wchar_t[N]> {
    684     using type = wchar_t;
    685 };
    686 #endif // _WIN32
    687 
    688 template<typename CharT, typename S>
    689 struct is_c_str_helper
    690 {
    691     static constexpr bool value = std::is_same<
    692         CharT*,
    693         // TODO: I'm so sorry for this... Can this be made cleaner?
    694         typename std::add_pointer<
    695             typename std::remove_cv<
    696                 typename std::remove_pointer<
    697                     typename std::decay<
    698                         S
    699                     >::type
    700                 >::type
    701             >::type
    702         >::type
    703     >::value;
    704 };
    705 
    706 template<typename S>
    707 struct is_c_str
    708 {
    709     static constexpr bool value = is_c_str_helper<char, S>::value;
    710 };
    711 
    712 #ifdef _WIN32
    713 template<typename S>
    714 struct is_c_wstr
    715 {
    716     static constexpr bool value = is_c_str_helper<wchar_t, S>::value;
    717 };
    718 #endif // _WIN32
    719 
    720 template<typename S>
    721 struct is_c_str_or_c_wstr
    722 {
    723     static constexpr bool value = is_c_str<S>::value
    724 #ifdef _WIN32
    725         || is_c_wstr<S>::value
    726 #endif
    727         ;
    728 };
    729 
    730 template<
    731     typename String,
    732     typename = decltype(std::declval<String>().data()),
    733     typename = typename std::enable_if<!is_c_str_or_c_wstr<String>::value>::type
    734 > const typename char_type<String>::type* c_str(const String& path)
    735 {
    736     return path.data();
    737 }
    738 
    739 template<
    740     typename String,
    741     typename = decltype(std::declval<String>().empty()),
    742     typename = typename std::enable_if<!is_c_str_or_c_wstr<String>::value>::type
    743 > bool empty(const String& path)
    744 {
    745     return path.empty();
    746 }
    747 
    748 template<
    749     typename String,
    750     typename = typename std::enable_if<is_c_str_or_c_wstr<String>::value>::type
    751 > const typename char_type<String>::type* c_str(String path)
    752 {
    753     return path;
    754 }
    755 
    756 template<
    757     typename String,
    758     typename = typename std::enable_if<is_c_str_or_c_wstr<String>::value>::type
    759 > bool empty(String path)
    760 {
    761     return !path || (*path == 0);
    762 }
    763 
    764 } // namespace detail
    765 } // namespace mio
    766 
    767 #endif // MIO_STRING_UTIL_HEADER
    768 
    769 
    770 #include <algorithm>
    771 
    772 #ifndef _WIN32
    773 # include <unistd.h>
    774 # include <fcntl.h>
    775 # include <sys/mman.h>
    776 # include <sys/stat.h>
    777 #endif
    778 
    779 namespace mio {
    780 namespace detail {
    781 
    782 #ifdef _WIN32
    783 namespace win {
    784 
    785 /** Returns the 4 upper bytes of an 8-byte integer. */
    786 inline DWORD int64_high(int64_t n) noexcept
    787 {
    788     return n >> 32;
    789 }
    790 
    791 /** Returns the 4 lower bytes of an 8-byte integer. */
    792 inline DWORD int64_low(int64_t n) noexcept
    793 {
    794     return n & 0xffffffff;
    795 }
    796 
    797 template<
    798     typename String,
    799     typename = typename std::enable_if<
    800         std::is_same<typename char_type<String>::type, char>::value
    801     >::type
    802 > file_handle_type open_file_helper(const String& path, const access_mode mode)
    803 {
    804     return ::CreateFileA(c_str(path),
    805             mode == access_mode::read ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE,
    806             FILE_SHARE_READ | FILE_SHARE_WRITE,
    807             0,
    808             OPEN_EXISTING,
    809             FILE_ATTRIBUTE_NORMAL,
    810             0);
    811 }
    812 
    813 template<typename String>
    814 typename std::enable_if<
    815     std::is_same<typename char_type<String>::type, wchar_t>::value,
    816     file_handle_type
    817 >::type open_file_helper(const String& path, const access_mode mode)
    818 {
    819     return ::CreateFileW(c_str(path),
    820             mode == access_mode::read ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE,
    821             FILE_SHARE_READ | FILE_SHARE_WRITE,
    822             0,
    823             OPEN_EXISTING,
    824             FILE_ATTRIBUTE_NORMAL,
    825             0);
    826 }
    827 
    828 } // win
    829 #endif // _WIN32
    830 
    831 /**
    832  * Returns the last platform specific system error (errno on POSIX and
    833  * GetLastError on Win) as a `std::error_code`.
    834  */
    835 inline std::error_code last_error() noexcept
    836 {
    837     std::error_code error;
    838 #ifdef _WIN32
    839     error.assign(GetLastError(), std::system_category());
    840 #else
    841     error.assign(errno, std::system_category());
    842 #endif
    843     return error;
    844 }
    845 
    846 template<typename String>
    847 file_handle_type open_file(const String& path, const access_mode mode,
    848         std::error_code& error)
    849 {
    850     error.clear();
    851     if(detail::empty(path))
    852     {
    853         error = std::make_error_code(std::errc::invalid_argument);
    854         return invalid_handle;
    855     }
    856 #ifdef _WIN32
    857     const auto handle = win::open_file_helper(path, mode);
    858 #else // POSIX
    859     const auto handle = ::open(c_str(path),
    860             mode == access_mode::read ? O_RDONLY : O_RDWR);
    861 #endif
    862     if(handle == invalid_handle)
    863     {
    864         error = detail::last_error();
    865     }
    866     return handle;
    867 }
    868 
    869 inline size_t query_file_size(file_handle_type handle, std::error_code& error)
    870 {
    871     error.clear();
    872 #ifdef _WIN32
    873     LARGE_INTEGER file_size;
    874     if(::GetFileSizeEx(handle, &file_size) == 0)
    875     {
    876         error = detail::last_error();
    877         return 0;
    878     }
    879 	return static_cast<int64_t>(file_size.QuadPart);
    880 #else // POSIX
    881     struct stat sbuf;
    882     if(::fstat(handle, &sbuf) == -1)
    883     {
    884         error = detail::last_error();
    885         return 0;
    886     }
    887     return sbuf.st_size;
    888 #endif
    889 }
    890 
    891 struct mmap_context
    892 {
    893     char* data;
    894     int64_t length;
    895     int64_t mapped_length;
    896 #ifdef _WIN32
    897     file_handle_type file_mapping_handle;
    898 #endif
    899 };
    900 
    901 inline mmap_context memory_map(const file_handle_type file_handle, const int64_t offset,
    902     const int64_t length, const access_mode mode, std::error_code& error)
    903 {
    904     const int64_t aligned_offset = make_offset_page_aligned(offset);
    905     const int64_t length_to_map = offset - aligned_offset + length;
    906 #ifdef _WIN32
    907     const int64_t max_file_size = offset + length;
    908     const auto file_mapping_handle = ::CreateFileMapping(
    909             file_handle,
    910             0,
    911             mode == access_mode::read ? PAGE_READONLY : PAGE_READWRITE,
    912             win::int64_high(max_file_size),
    913             win::int64_low(max_file_size),
    914             0);
    915     if(file_mapping_handle == invalid_handle)
    916     {
    917         error = detail::last_error();
    918         return {};
    919     }
    920     char* mapping_start = static_cast<char*>(::MapViewOfFile(
    921             file_mapping_handle,
    922             mode == access_mode::read ? FILE_MAP_READ : FILE_MAP_WRITE,
    923             win::int64_high(aligned_offset),
    924             win::int64_low(aligned_offset),
    925             length_to_map));
    926     if(mapping_start == nullptr)
    927     {
    928         error = detail::last_error();
    929         return {};
    930     }
    931 #else // POSIX
    932     char* mapping_start = static_cast<char*>(::mmap(
    933             0, // Don't give hint as to where to map.
    934             length_to_map,
    935             mode == access_mode::read ? PROT_READ : PROT_WRITE,
    936             MAP_SHARED,
    937             file_handle,
    938             aligned_offset));
    939     if(mapping_start == MAP_FAILED)
    940     {
    941         error = detail::last_error();
    942         return {};
    943     }
    944 #endif
    945     mmap_context ctx;
    946     ctx.data = mapping_start + offset - aligned_offset;
    947     ctx.length = length;
    948     ctx.mapped_length = length_to_map;
    949 #ifdef _WIN32
    950     ctx.file_mapping_handle = file_mapping_handle;
    951 #endif
    952     return ctx;
    953 }
    954 
    955 } // namespace detail
    956 
    957 // -- basic_mmap --
    958 
    959 template<access_mode AccessMode, typename ByteT>
    960 basic_mmap<AccessMode, ByteT>::~basic_mmap()
    961 {
    962     conditional_sync();
    963     unmap();
    964 }
    965 
    966 template<access_mode AccessMode, typename ByteT>
    967 basic_mmap<AccessMode, ByteT>::basic_mmap(basic_mmap&& other)
    968     : data_(std::move(other.data_))
    969     , length_(std::move(other.length_))
    970     , mapped_length_(std::move(other.mapped_length_))
    971     , file_handle_(std::move(other.file_handle_))
    972 #ifdef _WIN32
    973     , file_mapping_handle_(std::move(other.file_mapping_handle_))
    974 #endif
    975     , is_handle_internal_(std::move(other.is_handle_internal_))
    976 {
    977     other.data_ = nullptr;
    978     other.length_ = other.mapped_length_ = 0;
    979     other.file_handle_ = invalid_handle;
    980 #ifdef _WIN32
    981     other.file_mapping_handle_ = invalid_handle;
    982 #endif
    983 }
    984 
    985 template<access_mode AccessMode, typename ByteT>
    986 basic_mmap<AccessMode, ByteT>&
    987 basic_mmap<AccessMode, ByteT>::operator=(basic_mmap&& other)
    988 {
    989     if(this != &other)
    990     {
    991         // First the existing mapping needs to be removed.
    992         unmap();
    993         data_ = std::move(other.data_);
    994         length_ = std::move(other.length_);
    995         mapped_length_ = std::move(other.mapped_length_);
    996         file_handle_ = std::move(other.file_handle_);
    997 #ifdef _WIN32
    998         file_mapping_handle_ = std::move(other.file_mapping_handle_);
    999 #endif
   1000         is_handle_internal_ = std::move(other.is_handle_internal_);
   1001 
   1002         // The moved from basic_mmap's fields need to be reset, because
   1003         // otherwise other's destructor will unmap the same mapping that was
   1004         // just moved into this.
   1005         other.data_ = nullptr;
   1006         other.length_ = other.mapped_length_ = 0;
   1007         other.file_handle_ = invalid_handle;
   1008 #ifdef _WIN32
   1009         other.file_mapping_handle_ = invalid_handle;
   1010 #endif
   1011         other.is_handle_internal_ = false;
   1012     }
   1013     return *this;
   1014 }
   1015 
   1016 template<access_mode AccessMode, typename ByteT>
   1017 typename basic_mmap<AccessMode, ByteT>::handle_type
   1018 basic_mmap<AccessMode, ByteT>::mapping_handle() const noexcept
   1019 {
   1020 #ifdef _WIN32
   1021     return file_mapping_handle_;
   1022 #else
   1023     return file_handle_;
   1024 #endif
   1025 }
   1026 
   1027 template<access_mode AccessMode, typename ByteT>
   1028 template<typename String>
   1029 void basic_mmap<AccessMode, ByteT>::map(const String& path, const size_type offset,
   1030         const size_type length, std::error_code& error)
   1031 {
   1032     error.clear();
   1033     if(detail::empty(path))
   1034     {
   1035         error = std::make_error_code(std::errc::invalid_argument);
   1036         return;
   1037     }
   1038     const auto handle = detail::open_file(path, AccessMode, error);
   1039     if(error)
   1040     {
   1041         return;
   1042     }
   1043 
   1044     map(handle, offset, length, error);
   1045     // This MUST be after the call to map, as that sets this to true.
   1046     if(!error)
   1047     {
   1048         is_handle_internal_ = true;
   1049     }
   1050 }
   1051 
   1052 template<access_mode AccessMode, typename ByteT>
   1053 void basic_mmap<AccessMode, ByteT>::map(const handle_type handle,
   1054         const size_type offset, const size_type length, std::error_code& error)
   1055 {
   1056     error.clear();
   1057     if(handle == invalid_handle)
   1058     {
   1059         error = std::make_error_code(std::errc::bad_file_descriptor);
   1060         return;
   1061     }
   1062 
   1063     const auto file_size = detail::query_file_size(handle, error);
   1064     if(error)
   1065     {
   1066         return;
   1067     }
   1068 
   1069     if(offset + length > file_size)
   1070     {
   1071         error = std::make_error_code(std::errc::invalid_argument);
   1072         return;
   1073     }
   1074 
   1075     const auto ctx = detail::memory_map(handle, offset,
   1076             length == map_entire_file ? (file_size - offset) : length,
   1077             AccessMode, error);
   1078     if(!error)
   1079     {
   1080         // We must unmap the previous mapping that may have existed prior to this call.
   1081         // Note that this must only be invoked after a new mapping has been created in
   1082         // order to provide the strong guarantee that, should the new mapping fail, the
   1083         // `map` function leaves this instance in a state as though the function had
   1084         // never been invoked.
   1085         unmap();
   1086         file_handle_ = handle;
   1087         is_handle_internal_ = false;
   1088         data_ = reinterpret_cast<pointer>(ctx.data);
   1089         length_ = ctx.length;
   1090         mapped_length_ = ctx.mapped_length;
   1091 #ifdef _WIN32
   1092         file_mapping_handle_ = ctx.file_mapping_handle;
   1093 #endif
   1094     }
   1095 }
   1096 
   1097 template<access_mode AccessMode, typename ByteT>
   1098 template<access_mode A>
   1099 typename std::enable_if<A == access_mode::write, void>::type
   1100 basic_mmap<AccessMode, ByteT>::sync(std::error_code& error)
   1101 {
   1102     error.clear();
   1103     if(!is_open())
   1104     {
   1105         error = std::make_error_code(std::errc::bad_file_descriptor);
   1106         return;
   1107     }
   1108 
   1109     if(data())
   1110     {
   1111 #ifdef _WIN32
   1112         if(::FlushViewOfFile(get_mapping_start(), mapped_length_) == 0
   1113            || ::FlushFileBuffers(file_handle_) == 0)
   1114 #else // POSIX
   1115         if(::msync(get_mapping_start(), mapped_length_, MS_SYNC) != 0)
   1116 #endif
   1117         {
   1118             error = detail::last_error();
   1119             return;
   1120         }
   1121     }
   1122 #ifdef _WIN32
   1123     if(::FlushFileBuffers(file_handle_) == 0)
   1124     {
   1125         error = detail::last_error();
   1126     }
   1127 #endif
   1128 }
   1129 
   1130 template<access_mode AccessMode, typename ByteT>
   1131 void basic_mmap<AccessMode, ByteT>::unmap()
   1132 {
   1133     if(!is_open()) { return; }
   1134     // TODO do we care about errors here?
   1135 #ifdef _WIN32
   1136     if(is_mapped())
   1137     {
   1138         ::UnmapViewOfFile(get_mapping_start());
   1139         ::CloseHandle(file_mapping_handle_);
   1140     }
   1141 #else // POSIX
   1142     if(data_) { ::munmap(const_cast<pointer>(get_mapping_start()), mapped_length_); }
   1143 #endif
   1144 
   1145     // If `file_handle_` was obtained by our opening it (when map is called with
   1146     // a path, rather than an existing file handle), we need to close it,
   1147     // otherwise it must not be closed as it may still be used outside this
   1148     // instance.
   1149     if(is_handle_internal_)
   1150     {
   1151 #ifdef _WIN32
   1152         ::CloseHandle(file_handle_);
   1153 #else // POSIX
   1154         ::close(file_handle_);
   1155 #endif
   1156     }
   1157 
   1158     // Reset fields to their default values.
   1159     data_ = nullptr;
   1160     length_ = mapped_length_ = 0;
   1161     file_handle_ = invalid_handle;
   1162 #ifdef _WIN32
   1163     file_mapping_handle_ = invalid_handle;
   1164 #endif
   1165 }
   1166 
   1167 template<access_mode AccessMode, typename ByteT>
   1168 bool basic_mmap<AccessMode, ByteT>::is_mapped() const noexcept
   1169 {
   1170 #ifdef _WIN32
   1171     return file_mapping_handle_ != invalid_handle;
   1172 #else // POSIX
   1173     return is_open();
   1174 #endif
   1175 }
   1176 
   1177 template<access_mode AccessMode, typename ByteT>
   1178 void basic_mmap<AccessMode, ByteT>::swap(basic_mmap& other)
   1179 {
   1180     if(this != &other)
   1181     {
   1182         using std::swap;
   1183         swap(data_, other.data_);
   1184         swap(file_handle_, other.file_handle_);
   1185 #ifdef _WIN32
   1186         swap(file_mapping_handle_, other.file_mapping_handle_);
   1187 #endif
   1188         swap(length_, other.length_);
   1189         swap(mapped_length_, other.mapped_length_);
   1190         swap(is_handle_internal_, other.is_handle_internal_);
   1191     }
   1192 }
   1193 
   1194 template<access_mode AccessMode, typename ByteT>
   1195 template<access_mode A>
   1196 typename std::enable_if<A == access_mode::write, void>::type
   1197 basic_mmap<AccessMode, ByteT>::conditional_sync()
   1198 {
   1199     // This is invoked from the destructor, so not much we can do about
   1200     // failures here.
   1201     std::error_code ec;
   1202     sync(ec);
   1203 }
   1204 
   1205 template<access_mode AccessMode, typename ByteT>
   1206 template<access_mode A>
   1207 typename std::enable_if<A == access_mode::read, void>::type
   1208 basic_mmap<AccessMode, ByteT>::conditional_sync()
   1209 {
   1210     // noop
   1211 }
   1212 
   1213 template<access_mode AccessMode, typename ByteT>
   1214 bool operator==(const basic_mmap<AccessMode, ByteT>& a,
   1215         const basic_mmap<AccessMode, ByteT>& b)
   1216 {
   1217     return a.data() == b.data()
   1218         && a.size() == b.size();
   1219 }
   1220 
   1221 template<access_mode AccessMode, typename ByteT>
   1222 bool operator!=(const basic_mmap<AccessMode, ByteT>& a,
   1223         const basic_mmap<AccessMode, ByteT>& b)
   1224 {
   1225     return !(a == b);
   1226 }
   1227 
   1228 template<access_mode AccessMode, typename ByteT>
   1229 bool operator<(const basic_mmap<AccessMode, ByteT>& a,
   1230         const basic_mmap<AccessMode, ByteT>& b)
   1231 {
   1232     if(a.data() == b.data()) { return a.size() < b.size(); }
   1233     return a.data() < b.data();
   1234 }
   1235 
   1236 template<access_mode AccessMode, typename ByteT>
   1237 bool operator<=(const basic_mmap<AccessMode, ByteT>& a,
   1238         const basic_mmap<AccessMode, ByteT>& b)
   1239 {
   1240     return !(a > b);
   1241 }
   1242 
   1243 template<access_mode AccessMode, typename ByteT>
   1244 bool operator>(const basic_mmap<AccessMode, ByteT>& a,
   1245         const basic_mmap<AccessMode, ByteT>& b)
   1246 {
   1247     if(a.data() == b.data()) { return a.size() > b.size(); }
   1248     return a.data() > b.data();
   1249 }
   1250 
   1251 template<access_mode AccessMode, typename ByteT>
   1252 bool operator>=(const basic_mmap<AccessMode, ByteT>& a,
   1253         const basic_mmap<AccessMode, ByteT>& b)
   1254 {
   1255     return !(a < b);
   1256 }
   1257 
   1258 } // namespace mio
   1259 
   1260 #endif // MIO_BASIC_MMAP_IMPL
   1261 
   1262 
   1263 #endif // MIO_MMAP_HEADER
   1264 /* Copyright 2017 https://github.com/mandreyel
   1265  *
   1266  * Permission is hereby granted, free of charge, to any person obtaining a copy of this
   1267  * software and associated documentation files (the "Software"), to deal in the Software
   1268  * without restriction, including without limitation the rights to use, copy, modify,
   1269  * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
   1270  * permit persons to whom the Software is furnished to do so, subject to the following
   1271  * conditions:
   1272  *
   1273  * The above copyright notice and this permission notice shall be included in all copies
   1274  * or substantial portions of the Software.
   1275  *
   1276  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
   1277  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
   1278  * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
   1279  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
   1280  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
   1281  * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   1282  */
   1283 
   1284 #ifndef MIO_PAGE_HEADER
   1285 #define MIO_PAGE_HEADER
   1286 
   1287 #ifdef _WIN32
   1288 # include <windows.h>
   1289 #else
   1290 # include <unistd.h>
   1291 #endif
   1292 
   1293 namespace mio {
   1294 
   1295 /**
   1296  * This is used by `basic_mmap` to determine whether to create a read-only or
   1297  * a read-write memory mapping.
   1298  */
   1299 enum class access_mode
   1300 {
   1301     read,
   1302     write
   1303 };
   1304 
   1305 /**
   1306  * Determines the operating system's page allocation granularity.
   1307  *
   1308  * On the first call to this function, it invokes the operating system specific syscall
   1309  * to determine the page size, caches the value, and returns it. Any subsequent call to
   1310  * this function serves the cached value, so no further syscalls are made.
   1311  */
   1312 inline size_t page_size()
   1313 {
   1314     static const size_t page_size = []
   1315     {
   1316 #ifdef _WIN32
   1317         SYSTEM_INFO SystemInfo;
   1318         GetSystemInfo(&SystemInfo);
   1319         return SystemInfo.dwAllocationGranularity;
   1320 #else
   1321         return sysconf(_SC_PAGE_SIZE);
   1322 #endif
   1323     }();
   1324     return page_size;
   1325 }
   1326 
   1327 /**
   1328  * Alligns `offset` to the operating's system page size such that it subtracts the
   1329  * difference until the nearest page boundary before `offset`, or does nothing if
   1330  * `offset` is already page aligned.
   1331  */
   1332 inline size_t make_offset_page_aligned(size_t offset) noexcept
   1333 {
   1334     const size_t page_size_ = page_size();
   1335     // Use integer division to round down to the nearest page alignment.
   1336     return offset / page_size_ * page_size_;
   1337 }
   1338 
   1339 } // namespace mio
   1340 
   1341 #endif // MIO_PAGE_HEADER
   1342 /* Copyright 2017 https://github.com/mandreyel
   1343  *
   1344  * Permission is hereby granted, free of charge, to any person obtaining a copy of this
   1345  * software and associated documentation files (the "Software"), to deal in the Software
   1346  * without restriction, including without limitation the rights to use, copy, modify,
   1347  * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
   1348  * permit persons to whom the Software is furnished to do so, subject to the following
   1349  * conditions:
   1350  *
   1351  * The above copyright notice and this permission notice shall be included in all copies
   1352  * or substantial portions of the Software.
   1353  *
   1354  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
   1355  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
   1356  * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
   1357  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
   1358  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
   1359  * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   1360  */
   1361 
   1362 #ifndef MIO_SHARED_MMAP_HEADER
   1363 #define MIO_SHARED_MMAP_HEADER
   1364 
   1365 // #include "mio/mmap.hpp"
   1366 
   1367 
   1368 #include <system_error> // std::error_code
   1369 #include <memory> // std::shared_ptr
   1370 
   1371 namespace mio {
   1372 
   1373 /**
   1374  * Exposes (nearly) the same interface as `basic_mmap`, but endowes it with
   1375  * `std::shared_ptr` semantics.
   1376  *
   1377  * This is not the default behaviour of `basic_mmap` to avoid allocating on the heap if
   1378  * shared semantics are not required.
   1379  */
   1380 template<
   1381     access_mode AccessMode,
   1382     typename ByteT
   1383 > class basic_shared_mmap
   1384 {
   1385     using impl_type = basic_mmap<AccessMode, ByteT>;
   1386     std::shared_ptr<impl_type> pimpl_;
   1387 
   1388 public:
   1389     using value_type = typename impl_type::value_type;
   1390     using size_type = typename impl_type::size_type;
   1391     using reference = typename impl_type::reference;
   1392     using const_reference = typename impl_type::const_reference;
   1393     using pointer = typename impl_type::pointer;
   1394     using const_pointer = typename impl_type::const_pointer;
   1395     using difference_type = typename impl_type::difference_type;
   1396     using iterator = typename impl_type::iterator;
   1397     using const_iterator = typename impl_type::const_iterator;
   1398     using reverse_iterator = typename impl_type::reverse_iterator;
   1399     using const_reverse_iterator = typename impl_type::const_reverse_iterator;
   1400     using iterator_category = typename impl_type::iterator_category;
   1401     using handle_type = typename impl_type::handle_type;
   1402     using mmap_type = impl_type;
   1403 
   1404     basic_shared_mmap() = default;
   1405     basic_shared_mmap(const basic_shared_mmap&) = default;
   1406     basic_shared_mmap& operator=(const basic_shared_mmap&) = default;
   1407     basic_shared_mmap(basic_shared_mmap&&) = default;
   1408     basic_shared_mmap& operator=(basic_shared_mmap&&) = default;
   1409 
   1410     /** Takes ownership of an existing mmap object. */
   1411     basic_shared_mmap(mmap_type&& mmap)
   1412         : pimpl_(std::make_shared<mmap_type>(std::move(mmap)))
   1413     {}
   1414 
   1415     /** Takes ownership of an existing mmap object. */
   1416     basic_shared_mmap& operator=(mmap_type&& mmap)
   1417     {
   1418         pimpl_ = std::make_shared<mmap_type>(std::move(mmap));
   1419         return *this;
   1420     }
   1421 
   1422     /** Initializes this object with an already established shared mmap. */
   1423     basic_shared_mmap(std::shared_ptr<mmap_type> mmap) : pimpl_(std::move(mmap)) {}
   1424 
   1425     /** Initializes this object with an already established shared mmap. */
   1426     basic_shared_mmap& operator=(std::shared_ptr<mmap_type> mmap)
   1427     {
   1428         pimpl_ = std::move(mmap);
   1429         return *this;
   1430     }
   1431 
   1432 #ifdef __cpp_exceptions
   1433     /**
   1434      * The same as invoking the `map` function, except any error that may occur
   1435      * while establishing the mapping is wrapped in a `std::system_error` and is
   1436      * thrown.
   1437      */
   1438     template<typename String>
   1439     basic_shared_mmap(const String& path, const size_type offset = 0, const size_type length = map_entire_file)
   1440     {
   1441         std::error_code error;
   1442         map(path, offset, length, error);
   1443         if(error) { throw std::system_error(error); }
   1444     }
   1445 
   1446     /**
   1447      * The same as invoking the `map` function, except any error that may occur
   1448      * while establishing the mapping is wrapped in a `std::system_error` and is
   1449      * thrown.
   1450      */
   1451     basic_shared_mmap(const handle_type handle, const size_type offset = 0, const size_type length = map_entire_file)
   1452     {
   1453         std::error_code error;
   1454         map(handle, offset, length, error);
   1455         if(error) { throw std::system_error(error); }
   1456     }
   1457 #endif // __cpp_exceptions
   1458 
   1459     /**
   1460      * If this is a read-write mapping and the last reference to the mapping,
   1461      * the destructor invokes sync. Regardless of the access mode, unmap is
   1462      * invoked as a final step.
   1463      */
   1464     ~basic_shared_mmap() = default;
   1465 
   1466     /** Returns the underlying `std::shared_ptr` instance that holds the mmap. */
   1467     std::shared_ptr<mmap_type> get_shared_ptr() { return pimpl_; }
   1468 
   1469     /**
   1470      * On UNIX systems 'file_handle' and 'mapping_handle' are the same. On Windows,
   1471      * however, a mapped region of a file gets its own handle, which is returned by
   1472      * 'mapping_handle'.
   1473      */
   1474     handle_type file_handle() const noexcept
   1475     {
   1476         return pimpl_ ? pimpl_->file_handle() : invalid_handle;
   1477     }
   1478 
   1479     handle_type mapping_handle() const noexcept
   1480     {
   1481         return pimpl_ ? pimpl_->mapping_handle() : invalid_handle;
   1482     }
   1483 
   1484     /** Returns whether a valid memory mapping has been created. */
   1485     bool is_open() const noexcept { return pimpl_ && pimpl_->is_open(); }
   1486 
   1487     /**
   1488      * Returns true if no mapping was established, that is, conceptually the
   1489      * same as though the length that was mapped was 0. This function is
   1490      * provided so that this class has Container semantics.
   1491      */
   1492     bool empty() const noexcept { return !pimpl_ || pimpl_->empty(); }
   1493 
   1494     /**
   1495      * `size` and `length` both return the logical length, i.e. the number of bytes
   1496      * user requested to be mapped, while `mapped_length` returns the actual number of
   1497      * bytes that were mapped which is a multiple of the underlying operating system's
   1498      * page allocation granularity.
   1499      */
   1500     size_type size() const noexcept { return pimpl_ ? pimpl_->length() : 0; }
   1501     size_type length() const noexcept { return pimpl_ ? pimpl_->length() : 0; }
   1502     size_type mapped_length() const noexcept
   1503     {
   1504         return pimpl_ ? pimpl_->mapped_length() : 0;
   1505     }
   1506 
   1507     /**
   1508      * Returns a pointer to the first requested byte, or `nullptr` if no memory mapping
   1509      * exists.
   1510      */
   1511     template<
   1512         access_mode A = AccessMode,
   1513         typename = typename std::enable_if<A == access_mode::write>::type
   1514     > pointer data() noexcept { return pimpl_->data(); }
   1515     const_pointer data() const noexcept { return pimpl_ ? pimpl_->data() : nullptr; }
   1516 
   1517     /**
   1518      * Returns an iterator to the first requested byte, if a valid memory mapping
   1519      * exists, otherwise this function call is undefined behaviour.
   1520      */
   1521     iterator begin() noexcept { return pimpl_->begin(); }
   1522     const_iterator begin() const noexcept { return pimpl_->begin(); }
   1523     const_iterator cbegin() const noexcept { return pimpl_->cbegin(); }
   1524 
   1525     /**
   1526      * Returns an iterator one past the last requested byte, if a valid memory mapping
   1527      * exists, otherwise this function call is undefined behaviour.
   1528      */
   1529     template<
   1530         access_mode A = AccessMode,
   1531         typename = typename std::enable_if<A == access_mode::write>::type
   1532     > iterator end() noexcept { return pimpl_->end(); }
   1533     const_iterator end() const noexcept { return pimpl_->end(); }
   1534     const_iterator cend() const noexcept { return pimpl_->cend(); }
   1535 
   1536     /**
   1537      * Returns a reverse iterator to the last memory mapped byte, if a valid
   1538      * memory mapping exists, otherwise this function call is undefined
   1539      * behaviour.
   1540      */
   1541     template<
   1542         access_mode A = AccessMode,
   1543         typename = typename std::enable_if<A == access_mode::write>::type
   1544     > reverse_iterator rbegin() noexcept { return pimpl_->rbegin(); }
   1545     const_reverse_iterator rbegin() const noexcept { return pimpl_->rbegin(); }
   1546     const_reverse_iterator crbegin() const noexcept { return pimpl_->crbegin(); }
   1547 
   1548     /**
   1549      * Returns a reverse iterator past the first mapped byte, if a valid memory
   1550      * mapping exists, otherwise this function call is undefined behaviour.
   1551      */
   1552     template<
   1553         access_mode A = AccessMode,
   1554         typename = typename std::enable_if<A == access_mode::write>::type
   1555     > reverse_iterator rend() noexcept { return pimpl_->rend(); }
   1556     const_reverse_iterator rend() const noexcept { return pimpl_->rend(); }
   1557     const_reverse_iterator crend() const noexcept { return pimpl_->crend(); }
   1558 
   1559     /**
   1560      * Returns a reference to the `i`th byte from the first requested byte (as returned
   1561      * by `data`). If this is invoked when no valid memory mapping has been created
   1562      * prior to this call, undefined behaviour ensues.
   1563      */
   1564     reference operator[](const size_type i) noexcept { return (*pimpl_)[i]; }
   1565     const_reference operator[](const size_type i) const noexcept { return (*pimpl_)[i]; }
   1566 
   1567     /**
   1568      * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the
   1569      * reason is reported via `error` and the object remains in a state as if this
   1570      * function hadn't been called.
   1571      *
   1572      * `path`, which must be a path to an existing file, is used to retrieve a file
   1573      * handle (which is closed when the object destructs or `unmap` is called), which is
   1574      * then used to memory map the requested region. Upon failure, `error` is set to
   1575      * indicate the reason and the object remains in an unmapped state.
   1576      *
   1577      * `offset` is the number of bytes, relative to the start of the file, where the
   1578      * mapping should begin. When specifying it, there is no need to worry about
   1579      * providing a value that is aligned with the operating system's page allocation
   1580      * granularity. This is adjusted by the implementation such that the first requested
   1581      * byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at
   1582      * `offset` from the start of the file.
   1583      *
   1584      * `length` is the number of bytes to map. It may be `map_entire_file`, in which
   1585      * case a mapping of the entire file is created.
   1586      */
   1587     template<typename String>
   1588     void map(const String& path, const size_type offset,
   1589         const size_type length, std::error_code& error)
   1590     {
   1591         map_impl(path, offset, length, error);
   1592     }
   1593 
   1594     /**
   1595      * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the
   1596      * reason is reported via `error` and the object remains in a state as if this
   1597      * function hadn't been called.
   1598      *
   1599      * `path`, which must be a path to an existing file, is used to retrieve a file
   1600      * handle (which is closed when the object destructs or `unmap` is called), which is
   1601      * then used to memory map the requested region. Upon failure, `error` is set to
   1602      * indicate the reason and the object remains in an unmapped state.
   1603      *
   1604      * The entire file is mapped.
   1605      */
   1606     template<typename String>
   1607     void map(const String& path, std::error_code& error)
   1608     {
   1609         map_impl(path, 0, map_entire_file, error);
   1610     }
   1611 
   1612     /**
   1613      * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the
   1614      * reason is reported via `error` and the object remains in a state as if this
   1615      * function hadn't been called.
   1616      *
   1617      * `handle`, which must be a valid file handle, which is used to memory map the
   1618      * requested region. Upon failure, `error` is set to indicate the reason and the
   1619      * object remains in an unmapped state.
   1620      *
   1621      * `offset` is the number of bytes, relative to the start of the file, where the
   1622      * mapping should begin. When specifying it, there is no need to worry about
   1623      * providing a value that is aligned with the operating system's page allocation
   1624      * granularity. This is adjusted by the implementation such that the first requested
   1625      * byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at
   1626      * `offset` from the start of the file.
   1627      *
   1628      * `length` is the number of bytes to map. It may be `map_entire_file`, in which
   1629      * case a mapping of the entire file is created.
   1630      */
   1631     void map(const handle_type handle, const size_type offset,
   1632         const size_type length, std::error_code& error)
   1633     {
   1634         map_impl(handle, offset, length, error);
   1635     }
   1636 
   1637     /**
   1638      * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the
   1639      * reason is reported via `error` and the object remains in a state as if this
   1640      * function hadn't been called.
   1641      *
   1642      * `handle`, which must be a valid file handle, which is used to memory map the
   1643      * requested region. Upon failure, `error` is set to indicate the reason and the
   1644      * object remains in an unmapped state.
   1645      *
   1646      * The entire file is mapped.
   1647      */
   1648     void map(const handle_type handle, std::error_code& error)
   1649     {
   1650         map_impl(handle, 0, map_entire_file, error);
   1651     }
   1652 
   1653     /**
   1654      * If a valid memory mapping has been created prior to this call, this call
   1655      * instructs the kernel to unmap the memory region and disassociate this object
   1656      * from the file.
   1657      *
   1658      * The file handle associated with the file that is mapped is only closed if the
   1659      * mapping was created using a file path. If, on the other hand, an existing
   1660      * file handle was used to create the mapping, the file handle is not closed.
   1661      */
   1662     void unmap() { if(pimpl_) pimpl_->unmap(); }
   1663 
   1664     void swap(basic_shared_mmap& other) { pimpl_.swap(other.pimpl_); }
   1665 
   1666     /** Flushes the memory mapped page to disk. Errors are reported via `error`. */
   1667     template<
   1668         access_mode A = AccessMode,
   1669         typename = typename std::enable_if<A == access_mode::write>::type
   1670     > void sync(std::error_code& error) { if(pimpl_) pimpl_->sync(error); }
   1671 
   1672     /** All operators compare the underlying `basic_mmap`'s addresses. */
   1673 
   1674     friend bool operator==(const basic_shared_mmap& a, const basic_shared_mmap& b)
   1675     {
   1676         return a.pimpl_ == b.pimpl_;
   1677     }
   1678 
   1679     friend bool operator!=(const basic_shared_mmap& a, const basic_shared_mmap& b)
   1680     {
   1681         return !(a == b);
   1682     }
   1683 
   1684     friend bool operator<(const basic_shared_mmap& a, const basic_shared_mmap& b)
   1685     {
   1686         return a.pimpl_ < b.pimpl_;
   1687     }
   1688 
   1689     friend bool operator<=(const basic_shared_mmap& a, const basic_shared_mmap& b)
   1690     {
   1691         return a.pimpl_ <= b.pimpl_;
   1692     }
   1693 
   1694     friend bool operator>(const basic_shared_mmap& a, const basic_shared_mmap& b)
   1695     {
   1696         return a.pimpl_ > b.pimpl_;
   1697     }
   1698 
   1699     friend bool operator>=(const basic_shared_mmap& a, const basic_shared_mmap& b)
   1700     {
   1701         return a.pimpl_ >= b.pimpl_;
   1702     }
   1703 
   1704 private:
   1705     template<typename MappingToken>
   1706     void map_impl(const MappingToken& token, const size_type offset,
   1707         const size_type length, std::error_code& error)
   1708     {
   1709         if(!pimpl_)
   1710         {
   1711             mmap_type mmap = make_mmap<mmap_type>(token, offset, length, error);
   1712             if(error) { return; }
   1713             pimpl_ = std::make_shared<mmap_type>(std::move(mmap));
   1714         }
   1715         else
   1716         {
   1717             pimpl_->map(token, offset, length, error);
   1718         }
   1719     }
   1720 };
   1721 
   1722 /**
   1723  * This is the basis for all read-only mmap objects and should be preferred over
   1724  * directly using basic_shared_mmap.
   1725  */
   1726 template<typename ByteT>
   1727 using basic_shared_mmap_source = basic_shared_mmap<access_mode::read, ByteT>;
   1728 
   1729 /**
   1730  * This is the basis for all read-write mmap objects and should be preferred over
   1731  * directly using basic_shared_mmap.
   1732  */
   1733 template<typename ByteT>
   1734 using basic_shared_mmap_sink = basic_shared_mmap<access_mode::write, ByteT>;
   1735 
   1736 /**
   1737  * These aliases cover the most common use cases, both representing a raw byte stream
   1738  * (either with a char or an unsigned char/uint8_t).
   1739  */
   1740 using shared_mmap_source = basic_shared_mmap_source<char>;
   1741 using shared_ummap_source = basic_shared_mmap_source<unsigned char>;
   1742 
   1743 using shared_mmap_sink = basic_shared_mmap_sink<char>;
   1744 using shared_ummap_sink = basic_shared_mmap_sink<unsigned char>;
   1745 
   1746 } // namespace mio
   1747 
   1748 #endif // MIO_SHARED_MMAP_HEADER