1 // (C) Copyright Craig Henderson 2002 'boost/memmap.hpp' from sandbox
2 // (C) Copyright Jonathan Turkanis 2004.
3 // (C) Copyright Jonathan Graehl 2004.
4 // (C) Copyright Jorge Lodos 2008.
5 // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt.)
8 // Define BOOST_IOSTREAMS_SOURCE so that <boost/iostreams/detail/config.hpp>
9 // knows that we are building the library (possibly exporting code), rather
10 // than using it (possibly importing code).
11 #define BOOST_IOSTREAMS_SOURCE
15 #include <boost/iostreams/detail/config/rtl.hpp>
16 #include <boost/iostreams/detail/config/windows_posix.hpp>
17 #include <boost/iostreams/detail/file_handle.hpp>
18 #include <boost/iostreams/detail/system_failure.hpp>
19 #include <boost/iostreams/device/mapped_file.hpp>
20 #include <boost/throw_exception.hpp>
21 #include <boost/numeric/conversion/cast.hpp>
23 #ifdef BOOST_IOSTREAMS_WINDOWS
24 # define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
29 # include <sys/mman.h> // mmap, munmap.
30 # include <sys/stat.h>
31 # include <sys/types.h> // struct stat.
32 # include <unistd.h> // sysconf.
35 namespace boost
{ namespace iostreams
{
39 // Class containing the platform-sepecific implementation
40 // Invariant: The members params_, data_, size_, handle_ (and mapped_handle_
42 // - all have default values (or INVALID_HANDLE_VALUE for
43 // Windows handles), or
44 // - all have values reflecting a successful mapping.
45 // In the first case, error_ may be true, reflecting a recent unsuccessful
46 // open or close attempt; in the second case, error_ is always false.
47 class mapped_file_impl
{
49 typedef mapped_file_source::size_type size_type
;
50 typedef mapped_file_source::param_type param_type
;
51 typedef mapped_file_source::mapmode mapmode
;
52 BOOST_STATIC_CONSTANT(
53 size_type
, max_length
= mapped_file_source::max_length
);
56 void open(param_type p
);
57 bool is_open() const { return data_
!= 0; }
59 bool error() const { return error_
; }
60 mapmode
flags() const { return params_
.flags
; }
61 std::size_t size() const { return static_cast<std::size_t>(size_
); }
62 char* data() const { return data_
; }
63 void resize(stream_offset new_size
);
64 static int alignment();
66 void open_file(param_type p
);
67 void try_map_file(param_type p
);
68 void map_file(param_type
& p
);
70 void clear(bool error
);
71 void cleanup_and_throw(const char* msg
);
76 #ifdef BOOST_IOSTREAMS_WINDOWS
77 file_handle mapped_handle_
;
82 mapped_file_impl::mapped_file_impl() { clear(false); }
84 mapped_file_impl::~mapped_file_impl()
85 { try { close(); } catch (...) { } }
87 void mapped_file_impl::open(param_type p
)
90 boost::throw_exception(BOOST_IOSTREAMS_FAILURE("file already open"));
93 map_file(p
); // May modify p.hint
97 void mapped_file_impl::close()
102 error
= !unmap_file() || error
;
104 #ifdef BOOST_IOSTREAMS_WINDOWS
105 !::CloseHandle(handle_
)
107 ::close(handle_
) != 0
112 throw_system_failure("failed closing mapped file");
115 void mapped_file_impl::resize(stream_offset new_size
)
118 boost::throw_exception(BOOST_IOSTREAMS_FAILURE("file is closed"));
119 if (flags() & mapped_file::priv
)
120 boost::throw_exception(
121 BOOST_IOSTREAMS_FAILURE("can't resize private mapped file")
123 if (!(flags() & mapped_file::readwrite
))
124 boost::throw_exception(
125 BOOST_IOSTREAMS_FAILURE("can't resize readonly mapped file")
127 if (params_
.offset
>= new_size
)
128 boost::throw_exception(
129 BOOST_IOSTREAMS_FAILURE("can't resize below mapped offset")
132 cleanup_and_throw("failed unmapping file");
133 #ifdef BOOST_IOSTREAMS_WINDOWS
134 stream_offset offset
= ::SetFilePointer(handle_
, 0, NULL
, FILE_CURRENT
);
135 if (offset
== INVALID_SET_FILE_POINTER
&& ::GetLastError() != NO_ERROR
)
136 cleanup_and_throw("failed querying file pointer");
137 LONG sizehigh
= (new_size
>> (sizeof(LONG
) * 8));
138 LONG sizelow
= (new_size
& 0xffffffff);
139 DWORD result
= ::SetFilePointer(handle_
, sizelow
, &sizehigh
, FILE_BEGIN
);
140 if ((result
== INVALID_SET_FILE_POINTER
&& ::GetLastError() != NO_ERROR
)
141 || !::SetEndOfFile(handle_
))
142 cleanup_and_throw("failed resizing mapped file");
143 sizehigh
= (offset
>> (sizeof(LONG
) * 8));
144 sizelow
= (offset
& 0xffffffff);
145 ::SetFilePointer(handle_
, sizelow
, &sizehigh
, FILE_BEGIN
);
147 if (BOOST_IOSTREAMS_FD_TRUNCATE(handle_
, new_size
) == -1)
148 cleanup_and_throw("failed resizing mapped file");
151 param_type
p(params_
);
152 map_file(p
); // May modify p.hint
156 int mapped_file_impl::alignment()
158 #ifdef BOOST_IOSTREAMS_WINDOWS
160 ::GetSystemInfo(&info
);
161 return static_cast<int>(info
.dwAllocationGranularity
);
163 return static_cast<int>(sysconf(_SC_PAGESIZE
));
167 void mapped_file_impl::open_file(param_type p
)
169 bool readonly
= p
.flags
!= mapped_file::readwrite
;
170 #ifdef BOOST_IOSTREAMS_WINDOWS
173 DWORD dwDesiredAccess
=
176 (GENERIC_READ
| GENERIC_WRITE
);
177 DWORD dwCreationDisposition
= (p
.new_file_size
!= 0 && !readonly
) ?
180 DWORD dwFlagsandAttributes
=
182 FILE_ATTRIBUTE_READONLY
:
183 FILE_ATTRIBUTE_TEMPORARY
;
184 handle_
= p
.path
.is_wide() ?
190 dwCreationDisposition
,
191 dwFlagsandAttributes
,
198 dwCreationDisposition
,
199 dwFlagsandAttributes
,
201 if (handle_
== INVALID_HANDLE_VALUE
)
202 cleanup_and_throw("failed opening file");
205 if (p
.new_file_size
!= 0 && !readonly
) {
206 LONG sizehigh
= (p
.new_file_size
>> (sizeof(LONG
) * 8));
207 LONG sizelow
= (p
.new_file_size
& 0xffffffff);
208 DWORD result
= ::SetFilePointer(handle_
, sizelow
, &sizehigh
, FILE_BEGIN
);
209 if ((result
== INVALID_SET_FILE_POINTER
&& ::GetLastError() != NO_ERROR
)
210 || !::SetEndOfFile(handle_
))
211 cleanup_and_throw("failed setting file size");
214 // Determine file size. Dynamically locate GetFileSizeEx for compatibility
215 // with old Platform SDK (thanks to Pavel Vozenilik).
216 typedef BOOL (WINAPI
*func
)(HANDLE
, PLARGE_INTEGER
);
217 HMODULE hmod
= ::GetModuleHandleA("kernel32.dll");
219 reinterpret_cast<func
>(::GetProcAddress(hmod
, "GetFileSizeEx"));
222 if (get_size(handle_
, &info
)) {
223 boost::intmax_t size
=
224 ( (static_cast<boost::intmax_t>(info
.HighPart
) << 32) |
227 static_cast<std::size_t>(
228 p
.length
!= max_length
?
229 std::min
<boost::intmax_t>(p
.length
, size
) :
233 cleanup_and_throw("failed querying file size");
239 if ( (low
= ::GetFileSize(handle_
, &hi
))
243 boost::intmax_t size
=
244 (static_cast<boost::intmax_t>(hi
) << 32) | low
;
246 static_cast<std::size_t>(
247 p
.length
!= max_length
?
248 std::min
<boost::intmax_t>(p
.length
, size
) :
252 cleanup_and_throw("failed querying file size");
256 #else // #ifdef BOOST_IOSTREAMS_WINDOWS
259 int flags
= (readonly
? O_RDONLY
: O_RDWR
);
260 if (p
.new_file_size
!= 0 && !readonly
)
261 flags
|= (O_CREAT
| O_TRUNC
);
262 #ifdef _LARGEFILE64_SOURCE
263 flags
|= O_LARGEFILE
;
266 if (p
.path
.is_wide()) { errno
= EINVAL
; cleanup_and_throw("wide path not supported here"); } // happens on CYGWIN
267 handle_
= ::open(p
.path
.c_str(), flags
, S_IRWXU
);
269 cleanup_and_throw("failed opening file");
271 //--------------Set file size---------------------------------------------//
273 if (p
.new_file_size
!= 0 && !readonly
)
274 if (BOOST_IOSTREAMS_FD_TRUNCATE(handle_
, p
.new_file_size
) == -1)
275 cleanup_and_throw("failed setting file size");
277 //--------------Determine file size---------------------------------------//
280 if (p
.length
!= max_length
) {
283 struct BOOST_IOSTREAMS_FD_STAT info
;
284 success
= ::BOOST_IOSTREAMS_FD_FSTAT(handle_
, &info
) != -1;
285 size_
= info
.st_size
;
288 cleanup_and_throw("failed querying file size");
289 #endif // #ifdef BOOST_IOSTREAMS_WINDOWS
292 void mapped_file_impl::try_map_file(param_type p
)
294 bool priv
= p
.flags
== mapped_file::priv
;
295 bool readonly
= p
.flags
== mapped_file::readonly
;
296 #ifdef BOOST_IOSTREAMS_WINDOWS
299 DWORD protect
= priv
?
305 ::CreateFileMappingA(
312 if (mapped_handle_
== NULL
)
313 cleanup_and_throw("failed create mapping");
316 DWORD access
= priv
?
325 (DWORD
) (p
.offset
>> 32),
326 (DWORD
) (p
.offset
& 0xffffffff),
327 (SIZE_T
) (numeric_cast
<size_type
>(size_
) != max_length
? size_
: 0),
330 cleanup_and_throw("failed mapping view");
333 ::BOOST_IOSTREAMS_FD_MMAP(
334 const_cast<char*>(p
.hint
),
336 readonly
? PROT_READ
: (PROT_READ
| PROT_WRITE
),
337 priv
? MAP_PRIVATE
: MAP_SHARED
,
340 if (data
== MAP_FAILED
)
341 cleanup_and_throw("failed mapping file");
343 data_
= static_cast<char*>(data
);
346 void mapped_file_impl::map_file(param_type
& p
)
350 } catch (const std::exception
&) {
360 bool mapped_file_impl::unmap_file()
362 #ifdef BOOST_IOSTREAMS_WINDOWS
364 error
= !::UnmapViewOfFile(data_
) || error
;
365 error
= !::CloseHandle(mapped_handle_
) || error
;
366 mapped_handle_
= NULL
;
369 return ::munmap(data_
, size_
) == 0;
373 void mapped_file_impl::clear(bool error
)
375 params_
= param_type();
378 #ifdef BOOST_IOSTREAMS_WINDOWS
379 handle_
= INVALID_HANDLE_VALUE
;
380 mapped_handle_
= NULL
;
387 // Called when an error is encountered during the execution of open_file or
389 void mapped_file_impl::cleanup_and_throw(const char* msg
)
391 #ifdef BOOST_IOSTREAMS_WINDOWS
392 DWORD error
= GetLastError();
393 if (mapped_handle_
!= NULL
)
394 ::CloseHandle(mapped_handle_
);
395 if (handle_
!= INVALID_HANDLE_VALUE
)
396 ::CloseHandle(handle_
);
405 boost::iostreams::detail::throw_system_failure(msg
);
408 //------------------Implementation of mapped_file_params_base-----------------//
410 void mapped_file_params_base::normalize()
413 boost::throw_exception(BOOST_IOSTREAMS_FAILURE(
414 "at most one of 'mode' and 'flags' may be specified"
418 case mapped_file::readonly
:
419 case mapped_file::readwrite
:
420 case mapped_file::priv
:
423 boost::throw_exception(BOOST_IOSTREAMS_FAILURE("invalid flags"));
426 flags
= (mode
& BOOST_IOS::out
) ?
427 mapped_file::readwrite
:
428 mapped_file::readonly
;
429 mode
= BOOST_IOS::openmode();
432 boost::throw_exception(BOOST_IOSTREAMS_FAILURE("invalid offset"));
433 if (new_file_size
< 0)
434 boost::throw_exception(
435 BOOST_IOSTREAMS_FAILURE("invalid new file size")
439 } // End namespace detail.
441 //------------------Implementation of mapped_file_source----------------------//
443 mapped_file_source::mapped_file_source()
444 : pimpl_(new impl_type
)
447 mapped_file_source::mapped_file_source(const mapped_file_source
& other
)
448 : pimpl_(other
.pimpl_
)
451 bool mapped_file_source::is_open() const
452 { return pimpl_
->is_open(); }
454 void mapped_file_source::close() { pimpl_
->close(); }
456 // safe_bool is explicitly qualified below to please msvc 7.1
457 mapped_file_source::operator mapped_file_source::safe_bool() const
458 { return pimpl_
->error() ? &safe_bool_helper::x
: 0; }
460 bool mapped_file_source::operator!() const
461 { return pimpl_
->error(); }
463 mapped_file_source::mapmode
mapped_file_source::flags() const
464 { return pimpl_
->flags(); }
466 mapped_file_source::size_type
mapped_file_source::size() const
467 { return pimpl_
->size(); }
469 const char* mapped_file_source::data() const { return pimpl_
->data(); }
471 const char* mapped_file_source::begin() const { return data(); }
473 const char* mapped_file_source::end() const { return data() + size(); }
474 int mapped_file_source::alignment()
475 { return detail::mapped_file_impl::alignment(); }
477 void mapped_file_source::init() { pimpl_
.reset(new impl_type
); }
479 void mapped_file_source::open_impl(const param_type
& p
)
482 //------------------Implementation of mapped_file-----------------------------//
484 mapped_file::mapped_file(const mapped_file
& other
)
485 : delegate_(other
.delegate_
)
488 void mapped_file::resize(stream_offset new_size
)
489 { delegate_
.pimpl_
->resize(new_size
); }
491 //------------------Implementation of mapped_file_sink------------------------//
493 mapped_file_sink::mapped_file_sink(const mapped_file_sink
& other
)
494 : mapped_file(static_cast<const mapped_file
&>(other
))
497 //----------------------------------------------------------------------------//
499 } } // End namespaces iostreams, boost.