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
14 #include <boost/iostreams/detail/config/rtl.hpp>
15 #include <boost/iostreams/detail/config/windows_posix.hpp>
16 #include <boost/iostreams/detail/file_handle.hpp>
17 #include <boost/iostreams/detail/system_failure.hpp>
18 #include <boost/iostreams/device/mapped_file.hpp>
19 #include <boost/throw_exception.hpp>
21 #ifdef BOOST_IOSTREAMS_WINDOWS
22 # define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
27 # include <sys/mman.h> // mmap, munmap.
28 # include <sys/stat.h>
29 # include <sys/types.h> // struct stat.
30 # include <unistd.h> // sysconf.
33 namespace boost
{ namespace iostreams
{
37 // Class containing the platform-sepecific implementation
38 // Invariant: The members params_, data_, size_, handle_ (and mapped_handle_
40 // - all have default values (or INVALID_HANDLE_VALUE for
41 // Windows handles), or
42 // - all have values reflecting a successful mapping.
43 // In the first case, error_ may be true, reflecting a recent unsuccessful
44 // open or close attempt; in the second case, error_ is always false.
45 class mapped_file_impl
{
47 typedef mapped_file_source::size_type size_type
;
48 typedef mapped_file_source::param_type param_type
;
49 typedef mapped_file_source::mapmode mapmode
;
50 BOOST_STATIC_CONSTANT(
51 size_type
, max_length
= mapped_file_source::max_length
);
54 void open(param_type p
);
55 bool is_open() const { return data_
!= 0; }
57 bool error() const { return error_
; }
58 mapmode
flags() const { return params_
.flags
; }
59 std::size_t size() const { return static_cast<std::size_t>(size_
); }
60 char* data() const { return data_
; }
61 void resize(stream_offset new_size
);
62 static int alignment();
64 void open_file(param_type p
);
65 void try_map_file(param_type p
);
66 void map_file(param_type
& p
);
68 void clear(bool error
);
69 void cleanup_and_throw(const char* msg
);
74 #ifdef BOOST_IOSTREAMS_WINDOWS
75 file_handle mapped_handle_
;
80 mapped_file_impl::mapped_file_impl() { clear(false); }
82 mapped_file_impl::~mapped_file_impl()
83 { try { close(); } catch (...) { } }
85 void mapped_file_impl::open(param_type p
)
88 boost::throw_exception(BOOST_IOSTREAMS_FAILURE("file already open"));
91 map_file(p
); // May modify p.hint
95 void mapped_file_impl::close()
100 error
= !unmap_file() || error
;
102 #ifdef BOOST_IOSTREAMS_WINDOWS
103 !::CloseHandle(handle_
)
105 ::close(handle_
) != 0
110 throw_system_failure("failed closing mapped file");
113 void mapped_file_impl::resize(stream_offset new_size
)
116 boost::throw_exception(BOOST_IOSTREAMS_FAILURE("file is closed"));
117 if (flags() & mapped_file::priv
)
118 boost::throw_exception(
119 BOOST_IOSTREAMS_FAILURE("can't resize private mapped file")
121 if (!(flags() & mapped_file::readwrite
))
122 boost::throw_exception(
123 BOOST_IOSTREAMS_FAILURE("can't resize readonly mapped file")
125 if (params_
.offset
>= new_size
)
126 boost::throw_exception(
127 BOOST_IOSTREAMS_FAILURE("can't resize below mapped offset")
130 cleanup_and_throw("failed unmapping file");
131 #ifdef BOOST_IOSTREAMS_WINDOWS
132 stream_offset offset
= ::SetFilePointer(handle_
, 0, NULL
, FILE_CURRENT
);
133 if (offset
== INVALID_SET_FILE_POINTER
&& ::GetLastError() != NO_ERROR
)
134 cleanup_and_throw("failed querying file pointer");
135 LONG sizehigh
= (new_size
>> (sizeof(LONG
) * 8));
136 LONG sizelow
= (new_size
& 0xffffffff);
137 DWORD result
= ::SetFilePointer(handle_
, sizelow
, &sizehigh
, FILE_BEGIN
);
138 if ((result
== INVALID_SET_FILE_POINTER
&& ::GetLastError() != NO_ERROR
)
139 || !::SetEndOfFile(handle_
))
140 cleanup_and_throw("failed resizing mapped file");
141 sizehigh
= (offset
>> (sizeof(LONG
) * 8));
142 sizelow
= (offset
& 0xffffffff);
143 ::SetFilePointer(handle_
, sizelow
, &sizehigh
, FILE_BEGIN
);
145 if (BOOST_IOSTREAMS_FD_TRUNCATE(handle_
, new_size
) == -1)
146 cleanup_and_throw("failed resizing mapped file");
149 param_type
p(params_
);
150 map_file(p
); // May modify p.hint
154 int mapped_file_impl::alignment()
156 #ifdef BOOST_IOSTREAMS_WINDOWS
158 ::GetSystemInfo(&info
);
159 return static_cast<int>(info
.dwAllocationGranularity
);
161 return static_cast<int>(sysconf(_SC_PAGESIZE
));
165 void mapped_file_impl::open_file(param_type p
)
167 bool readonly
= p
.flags
!= mapped_file::readwrite
;
168 #ifdef BOOST_IOSTREAMS_WINDOWS
171 DWORD dwDesiredAccess
=
174 (GENERIC_READ
| GENERIC_WRITE
);
175 DWORD dwCreationDisposition
= (p
.new_file_size
!= 0 && !readonly
) ?
178 DWORD dwFlagsandAttributes
=
180 FILE_ATTRIBUTE_READONLY
:
181 FILE_ATTRIBUTE_TEMPORARY
;
182 handle_
= p
.path
.is_wide() ?
188 dwCreationDisposition
,
189 dwFlagsandAttributes
,
196 dwCreationDisposition
,
197 dwFlagsandAttributes
,
199 if (handle_
== INVALID_HANDLE_VALUE
)
200 cleanup_and_throw("failed opening file");
203 if (p
.new_file_size
!= 0 && !readonly
) {
204 LONG sizehigh
= (p
.new_file_size
>> (sizeof(LONG
) * 8));
205 LONG sizelow
= (p
.new_file_size
& 0xffffffff);
206 DWORD result
= ::SetFilePointer(handle_
, sizelow
, &sizehigh
, FILE_BEGIN
);
207 if ((result
== INVALID_SET_FILE_POINTER
&& ::GetLastError() != NO_ERROR
)
208 || !::SetEndOfFile(handle_
))
209 cleanup_and_throw("failed setting file size");
212 // Determine file size. Dynamically locate GetFileSizeEx for compatibility
213 // with old Platform SDK (thanks to Pavel Vozenilik).
214 typedef BOOL (WINAPI
*func
)(HANDLE
, PLARGE_INTEGER
);
215 HMODULE hmod
= ::GetModuleHandleA("kernel32.dll");
217 reinterpret_cast<func
>(::GetProcAddress(hmod
, "GetFileSizeEx"));
220 if (get_size(handle_
, &info
)) {
221 boost::intmax_t size
=
222 ( (static_cast<boost::intmax_t>(info
.HighPart
) << 32) |
225 static_cast<std::size_t>(
226 p
.length
!= max_length
?
227 std::min
<boost::intmax_t>(p
.length
, size
) :
231 cleanup_and_throw("failed querying file size");
237 if ( (low
= ::GetFileSize(handle_
, &hi
))
241 boost::intmax_t size
=
242 (static_cast<boost::intmax_t>(hi
) << 32) | low
;
244 static_cast<std::size_t>(
245 p
.length
!= max_length
?
246 std::min
<boost::intmax_t>(p
.length
, size
) :
250 cleanup_and_throw("failed querying file size");
254 #else // #ifdef BOOST_IOSTREAMS_WINDOWS
257 int flags
= (readonly
? O_RDONLY
: O_RDWR
);
258 if (p
.new_file_size
!= 0 && !readonly
)
259 flags
|= (O_CREAT
| O_TRUNC
);
260 #ifdef _LARGEFILE64_SOURCE
261 flags
|= O_LARGEFILE
;
264 handle_
= ::open(p
.path
.c_str(), flags
, S_IRWXU
);
266 cleanup_and_throw("failed opening file");
268 //--------------Set file size---------------------------------------------//
270 if (p
.new_file_size
!= 0 && !readonly
)
271 if (BOOST_IOSTREAMS_FD_TRUNCATE(handle_
, p
.new_file_size
) == -1)
272 cleanup_and_throw("failed setting file size");
274 //--------------Determine file size---------------------------------------//
277 if (p
.length
!= max_length
) {
280 struct BOOST_IOSTREAMS_FD_STAT info
;
281 success
= ::BOOST_IOSTREAMS_FD_FSTAT(handle_
, &info
) != -1;
282 size_
= info
.st_size
;
285 cleanup_and_throw("failed querying file size");
286 #endif // #ifdef BOOST_IOSTREAMS_WINDOWS
289 void mapped_file_impl::try_map_file(param_type p
)
291 bool priv
= p
.flags
== mapped_file::priv
;
292 bool readonly
= p
.flags
== mapped_file::readonly
;
293 #ifdef BOOST_IOSTREAMS_WINDOWS
296 DWORD protect
= priv
?
302 ::CreateFileMappingA(
309 if (mapped_handle_
== NULL
)
310 cleanup_and_throw("failed create mapping");
313 DWORD access
= priv
?
322 (DWORD
) (p
.offset
>> 32),
323 (DWORD
) (p
.offset
& 0xffffffff),
324 (SIZE_T
) (size_
!= max_length
? size_
: 0),
327 cleanup_and_throw("failed mapping view");
330 ::BOOST_IOSTREAMS_FD_MMAP(
331 const_cast<char*>(p
.hint
),
333 readonly
? PROT_READ
: (PROT_READ
| PROT_WRITE
),
334 priv
? MAP_PRIVATE
: MAP_SHARED
,
337 if (data
== MAP_FAILED
)
338 cleanup_and_throw("failed mapping file");
340 data_
= static_cast<char*>(data
);
343 void mapped_file_impl::map_file(param_type
& p
)
347 } catch (const std::exception
&) {
357 bool mapped_file_impl::unmap_file()
359 #ifdef BOOST_IOSTREAMS_WINDOWS
361 error
= !::UnmapViewOfFile(data_
) || error
;
362 error
= !::CloseHandle(mapped_handle_
) || error
;
363 mapped_handle_
= NULL
;
366 return ::munmap(data_
, size_
) == 0;
370 void mapped_file_impl::clear(bool error
)
372 params_
= param_type();
375 #ifdef BOOST_IOSTREAMS_WINDOWS
376 handle_
= INVALID_HANDLE_VALUE
;
377 mapped_handle_
= NULL
;
384 // Called when an error is encountered during the execution of open_file or
386 void mapped_file_impl::cleanup_and_throw(const char* msg
)
388 #ifdef BOOST_IOSTREAMS_WINDOWS
389 DWORD error
= GetLastError();
390 if (mapped_handle_
!= NULL
)
391 ::CloseHandle(mapped_handle_
);
392 if (handle_
!= INVALID_HANDLE_VALUE
)
393 ::CloseHandle(handle_
);
402 boost::iostreams::detail::throw_system_failure(msg
);
405 //------------------Implementation of mapped_file_params_base-----------------//
407 void mapped_file_params_base::normalize()
410 boost::throw_exception(BOOST_IOSTREAMS_FAILURE(
411 "at most one of 'mode' and 'flags' may be specified"
415 case mapped_file::readonly
:
416 case mapped_file::readwrite
:
417 case mapped_file::priv
:
420 boost::throw_exception(BOOST_IOSTREAMS_FAILURE("invalid flags"));
423 flags
= (mode
& BOOST_IOS::out
) ?
424 mapped_file::readwrite
:
425 mapped_file::readonly
;
426 mode
= BOOST_IOS::openmode();
429 boost::throw_exception(BOOST_IOSTREAMS_FAILURE("invalid offset"));
430 if (new_file_size
< 0)
431 boost::throw_exception(
432 BOOST_IOSTREAMS_FAILURE("invalid new file size")
436 } // End namespace detail.
438 //------------------Implementation of mapped_file_source----------------------//
440 mapped_file_source::mapped_file_source()
441 : pimpl_(new impl_type
)
444 mapped_file_source::mapped_file_source(const mapped_file_source
& other
)
445 : pimpl_(other
.pimpl_
)
448 bool mapped_file_source::is_open() const
449 { return pimpl_
->is_open(); }
451 void mapped_file_source::close() { pimpl_
->close(); }
453 // safe_bool is explicitly qualified below to please msvc 7.1
454 mapped_file_source::operator mapped_file_source::safe_bool() const
455 { return pimpl_
->error() ? &safe_bool_helper::x
: 0; }
457 bool mapped_file_source::operator!() const
458 { return pimpl_
->error(); }
460 mapped_file_source::mapmode
mapped_file_source::flags() const
461 { return pimpl_
->flags(); }
463 mapped_file_source::size_type
mapped_file_source::size() const
464 { return pimpl_
->size(); }
466 const char* mapped_file_source::data() const { return pimpl_
->data(); }
468 const char* mapped_file_source::begin() const { return data(); }
470 const char* mapped_file_source::end() const { return data() + size(); }
471 int mapped_file_source::alignment()
472 { return detail::mapped_file_impl::alignment(); }
474 void mapped_file_source::init() { pimpl_
.reset(new impl_type
); }
476 void mapped_file_source::open_impl(const param_type
& p
)
479 //------------------Implementation of mapped_file-----------------------------//
481 mapped_file::mapped_file(const mapped_file
& other
)
482 : delegate_(other
.delegate_
)
485 void mapped_file::resize(stream_offset new_size
)
486 { delegate_
.pimpl_
->resize(new_size
); }
488 //------------------Implementation of mapped_file_sink------------------------//
490 mapped_file_sink::mapped_file_sink(const mapped_file_sink
& other
)
491 : mapped_file(static_cast<const mapped_file
&>(other
))
494 //----------------------------------------------------------------------------//
496 } } // End namespaces iostreams, boost.