// (C) Copyright Craig Henderson 2002 'boost/memmap.hpp' from sandbox // (C) Copyright Jonathan Turkanis 2004. // (C) Copyright Jonathan Graehl 2004. // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt.) // See http://www.boost.org/libs/iostreams for documentation. // Define BOOST_IOSTREAMS_SOURCE so that // knows that we are building the library (possibly exporting code), rather // than using it (possibly importing code). #define BOOST_IOSTREAMS_SOURCE #include #include #include #include // failure. #include #include #ifdef BOOST_IOSTREAMS_WINDOWS # define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers # include #else # include # include # include // mmap, munmap. # include # include // struct stat. # include // sysconf. #endif #include namespace boost { namespace iostreams { namespace detail { struct mapped_file_impl { mapped_file_impl() { clear(false); } ~mapped_file_impl() { try { close(); } catch (std::exception&) { } } void clear(bool error) { data_ = 0; size_ = 0; mode_ = BOOST_IOS::openmode(); error_ = error; #ifdef BOOST_IOSTREAMS_WINDOWS handle_ = INVALID_HANDLE_VALUE; mapped_handle_ = NULL; #else handle_ = 0; #endif } void close() { bool error = false; #ifdef BOOST_IOSTREAMS_WINDOWS if (handle_ == INVALID_HANDLE_VALUE) return; error = !::UnmapViewOfFile(data_) || error; error = !::CloseHandle(mapped_handle_) || error; error = !::CloseHandle(handle_) || error; handle_ = INVALID_HANDLE_VALUE; mapped_handle_ = NULL; #else if (!handle_) return; error = ::munmap(reinterpret_cast(data_), size_) != 0 || error; error = ::close(handle_) != 0 || error; handle_ = 0; #endif data_ = 0; size_ = 0; mode_ = BOOST_IOS::openmode(); if (error) throw_system_failure("error closing mapped file"); } char* data_; std::size_t size_; BOOST_IOS::openmode mode_; bool error_; #ifdef BOOST_IOSTREAMS_WINDOWS HANDLE handle_; HANDLE mapped_handle_; #else int handle_; #endif }; } // End namespace detail. //------------------Definition of mapped_file_source--------------------------// mapped_file_source::mapped_file_source(mapped_file_params p) { open(p); } mapped_file_source::mapped_file_source( const std::string& path, mapped_file_source::size_type length, boost::intmax_t offset ) { open(path, length, offset); } void mapped_file_source::open(mapped_file_params p) { p.mode &= ~BOOST_IOS::out; open_impl(p); } void mapped_file_source::open( const std::string& path, mapped_file_source::size_type length, boost::intmax_t offset ) { mapped_file_params p(path); p.mode = BOOST_IOS::in; p.length = length; p.offset = offset; open_impl(p); } mapped_file_source::size_type mapped_file_source::size() const { return pimpl_->size_; } bool mapped_file_source::is_open() const { return !!pimpl_ && pimpl_->handle_ != 0; } void mapped_file_source::close() { pimpl_->close(); } mapped_file_source::operator mapped_file_source::safe_bool() const { return !!pimpl_ && pimpl_->error_ == false ? &safe_bool_helper::x : 0; } bool mapped_file_source::operator!() const { return !!pimpl_ || pimpl_->error_; } BOOST_IOS::openmode mapped_file_source::mode() const { return pimpl_->mode_; } const char* mapped_file_source::data() const { return pimpl_->data_; } const char* mapped_file_source::begin() const { return data(); } const char* mapped_file_source::end() const { return data() + size(); } #ifdef BOOST_IOSTREAMS_WINDOWS //---------------------------------------------// namespace detail { void cleanup_and_throw(detail::mapped_file_impl& impl, const char* msg) { if (impl.mapped_handle_ != INVALID_HANDLE_VALUE) ::CloseHandle(impl.mapped_handle_); if (impl.handle_ != NULL) ::CloseHandle(impl.handle_); impl.clear(true); throw_system_failure(msg); } } // End namespace detail. void mapped_file_source::open_impl(mapped_file_params p) { using namespace std; if (is_open()) throw BOOST_IOSTREAMS_FAILURE("file already open"); if (!pimpl_) pimpl_.reset(new impl_type); else pimpl_->clear(false); bool readonly = (p.mode & BOOST_IOS::out) == 0; pimpl_->mode_ = readonly ? BOOST_IOS::in : (BOOST_IOS::in | BOOST_IOS::out); //--------------Open underlying file--------------------------------------// pimpl_->handle_ = ::CreateFileA( p.path.c_str(), readonly ? GENERIC_READ : GENERIC_ALL, FILE_SHARE_READ, NULL, (p.new_file_size != 0 && !readonly) ? CREATE_ALWAYS : OPEN_EXISTING, FILE_ATTRIBUTE_TEMPORARY, NULL ); if (pimpl_->handle_ == INVALID_HANDLE_VALUE) detail::cleanup_and_throw(*pimpl_, "failed opening file"); //--------------Set file size---------------------------------------------// if (p.new_file_size != 0 && !readonly) { LONG sizehigh = (p.new_file_size >> (sizeof(LONG) * 8)); LONG sizelow = (p.new_file_size & 0xffffffff); ::SetFilePointer(pimpl_->handle_, sizelow, &sizehigh, FILE_BEGIN); if (::GetLastError() != NO_ERROR || !::SetEndOfFile(pimpl_->handle_)) detail::cleanup_and_throw(*pimpl_, "failed setting file size"); } //--------------Create mapping--------------------------------------------// try_again: // Target of goto in following section. pimpl_->mapped_handle_ = ::CreateFileMappingA( pimpl_->handle_, NULL, readonly ? PAGE_READONLY : PAGE_READWRITE, 0, 0, NULL ); if (pimpl_->mapped_handle_ == NULL) { detail::cleanup_and_throw(*pimpl_, "couldn't create mapping"); } //--------------Access data-----------------------------------------------// void* data = ::MapViewOfFileEx( pimpl_->mapped_handle_, readonly ? FILE_MAP_READ : FILE_MAP_WRITE, (DWORD) (p.offset >> 32), (DWORD) (p.offset & 0xffffffff), p.length != max_length ? p.length : 0, (LPVOID) p.hint ); if (!data) { if (p.hint != 0) { p.hint = 0; goto try_again; } detail::cleanup_and_throw(*pimpl_, "failed mapping view"); } //--------------Determing file size---------------------------------------// // Dynamically locate GetFileSizeEx (thanks to Pavel Vozenilik). typedef BOOL (WINAPI *func)(HANDLE, PLARGE_INTEGER); HMODULE hmod = ::GetModuleHandleA("kernel32.dll"); func get_size = reinterpret_cast(::GetProcAddress(hmod, "GetFileSizeEx")); if (get_size) { LARGE_INTEGER info; if (get_size(pimpl_->handle_, &info)) { boost::intmax_t size = ( (static_cast(info.HighPart) << 32) | info.LowPart ); pimpl_->size_ = static_cast( p.length != max_length ? std::min(p.length, size) : size ); } else { detail::cleanup_and_throw(*pimpl_, "failed getting file size"); return; } } else { DWORD hi; DWORD low; if ( (low = ::GetFileSize(pimpl_->handle_, &hi)) != INVALID_FILE_SIZE ) { boost::intmax_t size = (static_cast(hi) << 32) | low; pimpl_->size_ = static_cast( p.length != max_length ? std::min(p.length, size) : size ); } else { detail::cleanup_and_throw(*pimpl_, "failed getting file size"); return; } } pimpl_->data_ = reinterpret_cast(data); } int mapped_file_source::alignment() { SYSTEM_INFO info; ::GetSystemInfo(&info); return static_cast(info.dwAllocationGranularity); } #else // #ifdef BOOST_IOSTREAMS_WINDOWS //------------------------------------// namespace detail { void cleanup_and_throw(detail::mapped_file_impl& impl, const char* msg) { if (impl.handle_ != 0) ::close(impl.handle_); impl.clear(true); throw_system_failure( msg ); } } // End namespace detail. void mapped_file_source::open_impl(mapped_file_params p) { using namespace std; if (is_open()) throw BOOST_IOSTREAMS_FAILURE("file already open"); if (!pimpl_) pimpl_.reset(new impl_type); else pimpl_->clear(false); bool readonly = (p.mode & BOOST_IOS::out) == 0; pimpl_->mode_ = readonly ? BOOST_IOS::in : (BOOST_IOS::in | BOOST_IOS::out); //--------------Open underlying file--------------------------------------// int flags = (readonly ? O_RDONLY : O_RDWR); if (p.new_file_size != 0 && !readonly) flags |= (O_CREAT | O_TRUNC); errno = 0; pimpl_->handle_ = ::open(p.path.c_str(), flags, S_IRWXU); if (errno != 0) detail::cleanup_and_throw(*pimpl_, "failed opening file"); //--------------Set file size---------------------------------------------// if (p.new_file_size != 0 && !readonly) if (ftruncate(pimpl_->handle_, p.new_file_size) == -1) detail::cleanup_and_throw(*pimpl_, "failed setting file size"); //--------------Determine file size---------------------------------------// bool success = true; struct stat info; if (p.length != max_length) pimpl_->size_ = p.length; else { success = ::fstat(pimpl_->handle_, &info) != -1; pimpl_->size_ = info.st_size; } if (!success) detail::cleanup_and_throw(*pimpl_, "failed getting file size"); //--------------Create mapping--------------------------------------------// try_again: // Target of goto in following section. char* hint = const_cast(p.hint); void* data = ::mmap( hint, pimpl_->size_, readonly ? PROT_READ : (PROT_READ | PROT_WRITE), readonly ? MAP_PRIVATE : MAP_SHARED, pimpl_->handle_, p.offset ); if (data == MAP_FAILED) { if (hint != 0) { hint = 0; goto try_again; } detail::cleanup_and_throw(*pimpl_, "failed mapping file"); } pimpl_->data_ = reinterpret_cast(data); return; } int mapped_file_source::alignment() { return static_cast(sysconf(_SC_PAGESIZE)); } #endif // #ifdef BOOST_IOSTREAMS_WINDOWS //-----------------------------------// //------------------Implementation of mapped_file-----------------------------// mapped_file::mapped_file(mapped_file_params p) { delegate_.open_impl(p); } mapped_file::mapped_file( const std::string& path, BOOST_IOS::openmode mode, size_type length, stream_offset offset ) { open(path, mode, length, offset); } void mapped_file::open(mapped_file_params p) { delegate_.open_impl(p); } void mapped_file::open( const std::string& path, BOOST_IOS::openmode mode, size_type length, stream_offset offset ) { mapped_file_params p(path); p.mode = mode; p.length = length; p.offset = offset; open(p); } //------------------Implementation of mapped_file_sink------------------------// mapped_file_sink::mapped_file_sink(mapped_file_params p) { open(p); } mapped_file_sink::mapped_file_sink( const std::string& path, size_type length, stream_offset offset ) { open(path, length, offset); } void mapped_file_sink::open(mapped_file_params p) { p.mode |= BOOST_IOS::out; p.mode &= ~BOOST_IOS::in; mapped_file::open(p); } void mapped_file_sink::open( const std::string& path, size_type length, stream_offset offset ) { mapped_file_params p(path); p.mode = BOOST_IOS::out; p.length = length; p.offset = offset; open(p); } //----------------------------------------------------------------------------// } } // End namespaces iostreams, boost. #include