Line data Source code
1 : // WARNING: Changes to this file must be contributed back to Sawyer or else they will 2 : // be clobbered by the next update from Sawyer. The Sawyer repository is at 3 : // https://github.com/matzke1/sawyer. 4 : 5 : 6 : 7 : 8 : #ifndef Sawyer_MappedBuffer_H 9 : #define Sawyer_MappedBuffer_H 10 : 11 : #include <Sawyer/AllocatingBuffer.h> 12 : #include <Sawyer/Buffer.h> 13 : #include <Sawyer/Sawyer.h> 14 : #include <Sawyer/StaticBuffer.h> 15 : 16 : #include <boost/algorithm/string/predicate.hpp> 17 : #include <boost/filesystem.hpp> 18 : #include <boost/iostreams/device/mapped_file.hpp> 19 : #include <boost/lexical_cast.hpp> 20 : #include <boost/serialization/access.hpp> 21 : #include <boost/serialization/base_object.hpp> 22 : #include <boost/serialization/nvp.hpp> 23 : #include <boost/serialization/split_member.hpp> 24 : #include <boost/serialization/string.hpp> 25 : 26 : namespace Sawyer { 27 : namespace Container { 28 : 29 : /** Memory mapped file. 30 : * 31 : * This buffer points to a file that is mapped into memory by the operating system. The API supports a common divisor for 32 : * POSIX and Microsoft Windows and is therefore not all that powerful, but it does allow simple maps to be created that have a 33 : * file as backing store. See http://www.boost.org/doc/libs for more information. 34 : * 35 : * Access modes are the following enumerated constants: 36 : * 37 : * @li <code>boost::iostreams::mapped_file::readonly</code>: shared read-only access 38 : * @li <code>boost::iostreams::mapped_file::readwrite</code>: shared read/write access 39 : * @li <code>boost::iostreams::mapped_file::priv</code>: private read/write access 40 : * 41 : * When a file is mapped with private access changes written to the buffer are not reflected in the underlying file. */ 42 : template<class A, class T> 43 : class MappedBuffer: public Buffer<A, T> { 44 : boost::iostreams::mapped_file_params params_; 45 : boost::iostreams::mapped_file device_; 46 : 47 : public: 48 : typedef A Address; /**< Type of addresses. */ 49 : typedef T Value; /**< Type of values. */ 50 : typedef Buffer<A, T> Super; /**< Type of base class. */ 51 : 52 : private: 53 : friend class boost::serialization::access; 54 : 55 : // Users: You'll need to register the subclass once you know its type, such as 56 : // BOOST_CLASS_REGISTER(Sawyer::Container::MappedBuffer<size_t,uint8_t>); 57 : template<class S> 58 : void save(S &s, const unsigned /*version*/) const { 59 : s & BOOST_SERIALIZATION_BASE_OBJECT_NVP(Super); 60 : s & boost::serialization::make_nvp("path", params_.path); 61 : s & boost::serialization::make_nvp("flags", params_.flags); 62 : s & boost::serialization::make_nvp("mode", params_.mode); 63 : s & boost::serialization::make_nvp("offset", params_.offset); 64 : s & boost::serialization::make_nvp("length", params_.length); 65 : s & boost::serialization::make_nvp("new_file_size", params_.new_file_size); 66 : 67 : boost::uint64_t hint; 68 : BOOST_STATIC_ASSERT(sizeof hint >= sizeof params_.hint); 69 : hint = (boost::uint64_t)(params_.hint); 70 : s & BOOST_SERIALIZATION_NVP(hint); 71 : } 72 : 73 : // Users: You'll need to register the subclass once you know its type, such as 74 : // BOOST_CLASS_REGISTER(Sawyer::Container::MappedBuffer<size_t,uint8_t>); 75 : template<class S> 76 : void load(S &s, const unsigned /*version*/) { 77 : s & BOOST_SERIALIZATION_BASE_OBJECT_NVP(Super); 78 : s & boost::serialization::make_nvp("path", params_.path); 79 : s & boost::serialization::make_nvp("flags", params_.flags); 80 : s & boost::serialization::make_nvp("mode", params_.mode); 81 : s & boost::serialization::make_nvp("offset", params_.offset); 82 : s & boost::serialization::make_nvp("length", params_.length); 83 : s & boost::serialization::make_nvp("new_file_size", params_.new_file_size); 84 : 85 : boost::uint64_t hint; 86 : BOOST_STATIC_ASSERT(sizeof hint >= sizeof params_.hint); 87 : s & BOOST_SERIALIZATION_NVP(hint); 88 : params_.hint = (const char*)hint; 89 : 90 : device_.open(params_); 91 : } 92 : 93 : BOOST_SERIALIZATION_SPLIT_MEMBER(); 94 : 95 : protected: 96 : MappedBuffer() 97 : : Super(".MappedBuffer") {} // needed for de-serialization 98 0 : explicit MappedBuffer(const boost::iostreams::mapped_file_params ¶ms) 99 0 : : Super(".MappedBuffer"), params_(params), device_(params) {} 100 : 101 : public: 102 : /** Map a file according to boost parameters. 103 : * 104 : * The parameters describe which file (by name) and part thereof should be mapped into memory. */ 105 0 : static typename Buffer<A, T>::Ptr instance(const boost::iostreams::mapped_file_params ¶ms) { 106 : try { 107 0 : return typename Buffer<A, T>::Ptr(new MappedBuffer(params)); 108 0 : } catch (const std::ios_base::failure &e) { 109 0 : if (boost::contains(e.what(), "Invalid argument") && 110 0 : boost::filesystem::is_regular_file(params.path) && 111 0 : boost::filesystem::is_empty(params.path)) { 112 0 : return StaticBuffer<Address, Value>::instance((const Value*)NULL, 0); 113 : } else { 114 0 : throw; 115 : } 116 : } 117 : } 118 : 119 : /** Map a file by name. 120 : * 121 : * The specified file, which must already exist, is mapped into memory and pointed to by this new buffer. */ 122 : static typename Buffer<A, T>::Ptr 123 0 : instance(const boost::filesystem::path &path, 124 : boost::iostreams::mapped_file::mapmode mode=boost::iostreams::mapped_file::readonly, 125 : boost::intmax_t offset=0, 126 : boost::iostreams::mapped_file::size_type length=boost::iostreams::mapped_file::max_length) { 127 0 : boost::iostreams::mapped_file_params params(path.string()); 128 0 : params.flags = mode; 129 0 : params.length = length; 130 0 : params.offset = offset; 131 0 : return instance(params); 132 : } 133 : 134 : // It doesn't make sense to copy a memory-mapped buffer since the point of copying is to result in two independent buffers 135 : // pointing to non-shared data. If a shared, writable, memory-mapped buffer is backed by a file and we make a new copy also 136 : // backed by the file, then changing one buffer would change the other. Therefore, we allocate new memory that will hold a 137 : // snapshot of the source buffer. 138 0 : typename Buffer<A, T>::Ptr copy() const /*override*/ { 139 0 : typename Buffer<A, T>::Ptr newBuffer = AllocatingBuffer<A, T>::instance(this->size()); 140 0 : Address nWritten = newBuffer->write((const Value*)device_.data(), 0, this->size()); 141 0 : if (nWritten != this->size()) { 142 : throw std::runtime_error("MappedBuffer::copy() failed after copying " + 143 : boost::lexical_cast<std::string>(nWritten) + " of " + 144 0 : boost::lexical_cast<std::string>(this->size()) + 145 0 : (1==this->size()?" value":" values")); 146 : } 147 0 : return newBuffer; 148 : } 149 : 150 0 : Address available(Address address) const /*override*/ { 151 0 : return address >= device_.size() ? Address(0) : (Address(device_.size()) - address) / sizeof(Value); 152 : } 153 : 154 0 : void resize(Address n) /*override*/ { 155 0 : if (n != this->size()) 156 0 : throw std::runtime_error("resizing not allowed for MappedBuffer"); 157 0 : } 158 : 159 0 : Address read(Value *buf, Address address, Address n) const /*override*/ { 160 0 : Address nread = std::min(n, available(address)); 161 0 : memcpy(buf, device_.const_data() + address, nread * sizeof(Value)); 162 0 : return nread; 163 : } 164 : 165 0 : Address write(const Value *buf, Address address, Address n) /*override*/ { 166 0 : Address nwritten = std::min(n, available(address)); 167 0 : memcpy(device_.data() + address, buf, nwritten * sizeof(Value)); 168 0 : return nwritten; 169 : } 170 : 171 0 : const Value* data() const /*override*/ { 172 0 : return (Value*)device_.const_data(); 173 : } 174 : }; 175 : 176 : } // namespace 177 : } // namespace 178 : #endif