2 * Distributed under the Boost Software License, Version 1.0.(See accompanying
3 * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt.)
5 * See http://www.boost.org/libs/iostreams for documentation.
7 * Tests seeking with a file_descriptor using large file offsets.
9 * File: libs/iostreams/test/large_file_test.cpp
10 * Date: Tue Dec 25 21:34:47 MST 2007
11 * Copyright: 2007-2008 CodeRage, LLC
12 * Author: Jonathan Turkanis
13 * Contact: turkanis at coderage dot com
16 #include <cstdio> // SEEK_SET, etc.
19 #include <boost/config.hpp> // BOOST_STRINGIZE
20 #include <boost/detail/workaround.hpp>
21 #include <boost/iostreams/detail/config/rtl.hpp>
22 #include <boost/iostreams/detail/config/windows_posix.hpp>
23 #include <boost/iostreams/detail/execute.hpp>
24 #include <boost/iostreams/detail/ios.hpp>
25 #include <boost/iostreams/device/file_descriptor.hpp>
26 #include <boost/iostreams/device/mapped_file.hpp>
27 #include <boost/iostreams/positioning.hpp>
28 #include <boost/lexical_cast.hpp>
29 #include <boost/test/test_tools.hpp>
30 #include <boost/test/unit_test.hpp>
33 // OS-specific headers for low-level i/o.
35 #include <fcntl.h> // file opening flags.
36 #include <sys/stat.h> // file access permissions.
37 #ifdef BOOST_IOSTREAMS_WINDOWS
38 # include <io.h> // low-level file i/o.
39 # define WINDOWS_LEAN_AND_MEAN
41 # ifndef INVALID_SET_FILE_POINTER
42 # define INVALID_SET_FILE_POINTER ((DWORD)-1)
45 # include <sys/types.h> // mode_t.
46 # include <unistd.h> // low-level file i/o.
50 using namespace boost
;
51 using namespace boost::iostreams
;
52 using boost::unit_test::test_suite
;
54 //------------------Definition of constants-----------------------------------//
56 const stream_offset gigabyte
= 1073741824;
57 const stream_offset file_size
= // Some compilers complain about "8589934593"
58 gigabyte
* static_cast<stream_offset
>(8) + static_cast<stream_offset
>(1);
59 const int offset_list
[] =
60 { 0, 1, 2, 3, 4, 5, 6, 7, 8, 7, 6, 5, 4, 3, 2, 1, // Seek by 1GB
61 0, 2, 1, 3, 2, 4, 3, 5, 4, 6, 5, 7, 6, 8, // Seek by 2GB
62 6, 7, 5, 6, 4, 5, 3, 4, 2, 3, 1, 2,
63 0, 3, 1, 4, 2, 5, 3, 6, 4, 7, 5, 8, // Seek by 3GB
64 5, 7, 4, 6, 3, 5, 2, 4, 1,
65 0, 4, 1, 5, 2, 6, 3, 7, 4, 8, // Seek by 4GB
66 4, 7, 3, 6, 2, 5, 1, 4,
67 0, 5, 1, 6, 2, 7, 3, 8, 3, 7, 2, 6, 1, 5, // Seek by 5GB
68 0, 6, 1, 7, 2, 8, 2, 7, 1, 6, // Seek by 6GB
69 0, 7, 1, 8, 1, 7, // Seek by 7GB
70 0, 8, 0 }; // Seek by 8GB
71 const int offset_list_length
= sizeof(offset_list
) / sizeof(int);
72 #ifdef LARGE_FILE_TEMP
73 # define BOOST_FILE_NAME BOOST_STRINGIZE(LARGE_FILE_TEMP)
74 # define BOOST_KEEP_FILE false
76 # define BOOST_FILE_NAME BOOST_STRINGIZE(LARGE_FILE_KEEP)
77 # define BOOST_KEEP_FILE true
80 //------------------Definition of remove_large_file---------------------------//
82 // Removes the large file
83 void remove_large_file()
85 #ifdef BOOST_IOSTREAMS_WINDOWS
86 DeleteFile(TEXT(BOOST_FILE_NAME
));
88 unlink(BOOST_FILE_NAME
);
92 //------------------Definition of large_file_exists---------------------------//
94 // Returns true if the large file exists, has the correct size, and has been
95 // modified since the last commit affecting this source file; if the file exists
96 // but is invalid, deletes the file.
97 bool large_file_exists()
102 #ifdef BOOST_IOSTREAMS_WINDOWS
105 WIN32_FIND_DATA info
;
106 HANDLE hnd
= FindFirstFile(TEXT(BOOST_FILE_NAME
), &info
);
107 if (hnd
== INVALID_HANDLE_VALUE
)
113 (static_cast<stream_offset
>(info
.nFileSizeHigh
) << 32) +
114 static_cast<stream_offset
>(info
.nFileSizeLow
);
115 if (size
!= file_size
) {
120 // Fetch last mod date
122 if (!FileTimeToSystemTime(&info
.ftLastWriteTime
, &stime
)) {
127 ctime
.tm_year
= stime
.wYear
- 1900;
128 ctime
.tm_mon
= stime
.wMonth
- 1;
129 ctime
.tm_mday
= stime
.wDay
;
130 ctime
.tm_hour
= stime
.wHour
;
131 ctime
.tm_min
= stime
.wMinute
;
132 ctime
.tm_sec
= stime
.wSecond
;
134 last_mod
= mktime(&ctime
);
139 struct BOOST_IOSTREAMS_FD_STAT info
;
140 if (BOOST_IOSTREAMS_FD_STAT(BOOST_FILE_NAME
, &info
))
144 if (info
.st_size
!= file_size
) {
149 // Fetch last mod date
150 last_mod
= info
.st_mtime
;
154 // Fetch last mod date of this file ("large_file_test.cpp")
157 if (timestamp
.size() != 53) { // Length of auto-generated SVN timestamp
163 commit
.tm_year
= lexical_cast
<int>(timestamp
.substr(7, 4)) - 1900;
164 commit
.tm_mon
= lexical_cast
<int>(timestamp
.substr(12, 2)) - 1;
165 commit
.tm_mday
= lexical_cast
<int>(timestamp
.substr(15, 2));
166 commit
.tm_hour
= lexical_cast
<int>(timestamp
.substr(18, 2));
167 commit
.tm_min
= lexical_cast
<int>(timestamp
.substr(21, 2));
168 commit
.tm_sec
= lexical_cast
<int>(timestamp
.substr(24, 2));
169 } catch (const bad_lexical_cast
&) {
174 // If last commit was two days or more before file timestamp, existing
175 // file is okay; otherwise, it must be regenerated (the two-day window
176 // compensates for time zone differences)
177 return difftime(last_mod
, mktime(&commit
)) >= 60 * 60 * 48;
180 //------------------Definition of map_large_file------------------------------//
182 // Initializes the large file by mapping it in small segments. This is an
183 // optimization for Win32; the straightforward implementation using WriteFile
184 // and SetFilePointer (see the Borland workaround below) is painfully slow.
185 bool map_large_file()
187 for (stream_offset z
= 0; z
<= 8; ++z
) {
189 mapped_file_params params
;
190 params
.path
= BOOST_FILE_NAME
;
191 params
.offset
= z
* gigabyte
;
193 params
.mode
= BOOST_IOS::out
;
194 mapped_file
file(params
);
195 file
.begin()[0] = static_cast<char>(z
+ 1);
196 } catch (const std::exception
&) {
204 //------------------Definition of create_large_file---------------------------//
206 // Creates and initializes the large file if it does not already exist. The file
209 // 0 1GB 2GB 3GB 4GB 5GB 6GB 7GB 8GB
210 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
211 // 1.....2.....3.....4.....5.....6.....7.....8.....9
212 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
214 // where the characters 1-9 appear at offsets that are multiples of 1GB and the
215 // dots represent uninitialized data.
216 bool create_large_file()
218 // If file exists, has correct size, and is recent, we're done
219 if (BOOST_KEEP_FILE
&& large_file_exists())
222 #ifdef BOOST_IOSTREAMS_WINDOWS
227 TEXT(BOOST_FILE_NAME
),
232 FILE_ATTRIBUTE_NORMAL
,
239 LONG off_low
= static_cast<LONG
>(file_size
& 0xffffffff);
240 LONG off_high
= static_cast<LONG
>(file_size
>> 32);
241 if ( SetFilePointer(hnd
, off_low
, &off_high
, FILE_BEGIN
) ==
242 INVALID_SET_FILE_POINTER
&&
243 GetLastError() != NO_ERROR
)
251 if (!SetEndOfFile(hnd
)) {
257 # if !defined(__BORLANDC__) || __BORLANDC__ < 0x582 || __BORLANDC__ >= 0x592
259 // Close handle; all further access is via mapped_file
262 // Initialize file data
263 return map_large_file();
265 # else // Borland >= 5.8.2 and Borland < 5.9.2
267 // Initialize file data (very slow, even though only 9 writes are required)
268 for (stream_offset z
= 0; z
<= 8; ++z
) {
271 LONG off_low
= static_cast<LONG
>((z
* gigabyte
) & 0xffffffff); // == 0
272 LONG off_high
= static_cast<LONG
>((z
* gigabyte
) >> 32);
273 if ( SetFilePointer(hnd
, off_low
, &off_high
, FILE_BEGIN
) ==
274 INVALID_SET_FILE_POINTER
&&
275 GetLastError() != NO_ERROR
)
283 char buf
[1] = { z
+ 1 };
285 BOOL success
= WriteFile(hnd
, buf
, 1, &result
, NULL
);
286 if (!success
|| result
!= 1) {
297 # endif // Borland workaround
298 #else // #ifdef BOOST_IOSTREAMS_WINDOWS
301 int oflag
= O_WRONLY
| O_CREAT
;
302 #ifdef _LARGEFILE64_SOURCE
303 oflag
|= O_LARGEFILE
;
309 int fd
= BOOST_IOSTREAMS_FD_OPEN(BOOST_FILE_NAME
, oflag
, pmode
);
314 if (BOOST_IOSTREAMS_FD_TRUNCATE(fd
, file_size
)) {
315 BOOST_IOSTREAMS_FD_CLOSE(fd
);
321 // Initialize file data
322 for (int z
= 0; z
<= 8; ++z
) {
325 BOOST_IOSTREAMS_FD_OFFSET off
=
326 BOOST_IOSTREAMS_FD_SEEK(
328 static_cast<BOOST_IOSTREAMS_FD_OFFSET
>(z
* gigabyte
),
332 BOOST_IOSTREAMS_FD_CLOSE(fd
);
337 char buf
[1] = { z
+ 1 };
338 if (BOOST_IOSTREAMS_FD_WRITE(fd
, buf
, 1) == -1) {
339 BOOST_IOSTREAMS_FD_CLOSE(fd
);
345 BOOST_IOSTREAMS_FD_CLOSE(fd
);
350 // Close descriptor; all further access is via mapped_file
351 BOOST_IOSTREAMS_FD_CLOSE(fd
);
353 // Initialize file data
354 return map_large_file();
357 #endif // #ifdef BOOST_IOSTREAMS_WINDOWS
360 //------------------Definition of large_file----------------------------------//
365 large_file() { exists_
= create_large_file(); }
366 ~large_file() { if (!BOOST_KEEP_FILE
) remove_large_file(); }
367 bool exists() const { return exists_
; }
368 const char* path() const { return BOOST_FILE_NAME
; }
373 //------------------Definition of check_character-----------------------------//
375 // Verify that the given file contains the given character at the current
377 bool check_character(file_descriptor_source
& file
, char value
)
381 BOOST_CHECK_NO_THROW(amt
= file
.read(buf
, 1));
382 BOOST_CHECK_MESSAGE(amt
== 1, "failed reading character");
383 BOOST_CHECK_NO_THROW(file
.seek(-1, BOOST_IOS::cur
));
384 return buf
[0] == value
;
387 //------------------Definition of large_file_test-----------------------------//
389 void large_file_test()
391 BOOST_REQUIRE_MESSAGE(
392 sizeof(stream_offset
) >= 8,
393 "large offsets not supported"
396 // Prepare file and file descriptor
398 file_descriptor_source file
;
399 BOOST_REQUIRE_MESSAGE(
400 large
.exists(), "failed creating file \"" << BOOST_FILE_NAME
<< '"'
402 BOOST_CHECK_NO_THROW(file
.open(large
.path(), BOOST_IOS::binary
));
404 // Test seeking using ios_base::beg
405 for (int z
= 0; z
< offset_list_length
; ++z
) {
406 char value
= offset_list
[z
] + 1;
408 static_cast<stream_offset
>(offset_list
[z
]) * gigabyte
;
409 BOOST_CHECK_NO_THROW(file
.seek(off
, BOOST_IOS::beg
));
411 check_character(file
, value
),
412 "failed validating seek"
416 // Test seeking using ios_base::end
417 for (int z
= 0; z
< offset_list_length
; ++z
) {
418 char value
= offset_list
[z
] + 1;
420 -static_cast<stream_offset
>(8 - offset_list
[z
]) * gigabyte
- 1;
421 BOOST_CHECK_NO_THROW(file
.seek(off
, BOOST_IOS::end
));
423 check_character(file
, value
),
424 "failed validating seek"
428 // Test seeking using ios_base::cur
429 for (int next
, cur
= 0, z
= 0; z
< offset_list_length
; ++z
, cur
= next
) {
430 next
= offset_list
[z
];
431 char value
= offset_list
[z
] + 1;
432 stream_offset off
= static_cast<stream_offset
>(next
- cur
) * gigabyte
;
433 BOOST_CHECK_NO_THROW(file
.seek(off
, BOOST_IOS::cur
));
435 check_character(file
, value
),
436 "failed validating seek"
441 test_suite
* init_unit_test_suite(int, char* [])
443 test_suite
* test
= BOOST_TEST_SUITE("execute test");
444 test
->add(BOOST_TEST_CASE(&large_file_test
));