diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:45:59 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:45:59 +0000 |
commit | 19fcec84d8d7d21e796c7624e521b60d28ee21ed (patch) | |
tree | 42d26aa27d1e3f7c0b8bd3fd14e7d7082f5008dc /src/boost/libs/iostreams/test/large_file_test.cpp | |
parent | Initial commit. (diff) | |
download | ceph-upstream.tar.xz ceph-upstream.zip |
Adding upstream version 16.2.11+ds.upstream/16.2.11+dsupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/boost/libs/iostreams/test/large_file_test.cpp')
-rw-r--r-- | src/boost/libs/iostreams/test/large_file_test.cpp | 446 |
1 files changed, 446 insertions, 0 deletions
diff --git a/src/boost/libs/iostreams/test/large_file_test.cpp b/src/boost/libs/iostreams/test/large_file_test.cpp new file mode 100644 index 000000000..be18b11b9 --- /dev/null +++ b/src/boost/libs/iostreams/test/large_file_test.cpp @@ -0,0 +1,446 @@ +/* + * 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. + * + * Tests seeking with a file_descriptor using large file offsets. + * + * File: libs/iostreams/test/large_file_test.cpp + * Date: Tue Dec 25 21:34:47 MST 2007 + * Copyright: 2007-2008 CodeRage, LLC + * Author: Jonathan Turkanis + * Contact: turkanis at coderage dot com + */ + +#include <cstdio> // SEEK_SET, etc. +#include <ctime> +#include <string> +#include <boost/config.hpp> // BOOST_STRINGIZE +#include <boost/detail/workaround.hpp> +#include <boost/iostreams/detail/config/rtl.hpp> +#include <boost/iostreams/detail/config/windows_posix.hpp> +#include <boost/iostreams/detail/execute.hpp> +#include <boost/iostreams/detail/ios.hpp> +#include <boost/iostreams/device/file_descriptor.hpp> +#include <boost/iostreams/device/mapped_file.hpp> +#include <boost/iostreams/positioning.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/test/test_tools.hpp> +#include <boost/test/unit_test.hpp> +#include <iostream> + + // OS-specific headers for low-level i/o. + +#include <fcntl.h> // file opening flags. +#include <sys/stat.h> // file access permissions. +#ifdef BOOST_IOSTREAMS_WINDOWS +# include <io.h> // low-level file i/o. +# define WINDOWS_LEAN_AND_MEAN +# include <windows.h> +# ifndef INVALID_SET_FILE_POINTER +# define INVALID_SET_FILE_POINTER ((DWORD)-1) +# endif +#else +# include <sys/types.h> // mode_t. +# include <unistd.h> // low-level file i/o. +#endif + +using namespace std; +using namespace boost; +using namespace boost::iostreams; +using boost::unit_test::test_suite; + +//------------------Definition of constants-----------------------------------// + +const stream_offset gigabyte = 1073741824; +const stream_offset file_size = // Some compilers complain about "8589934593" + gigabyte * static_cast<stream_offset>(8) + static_cast<stream_offset>(1); +const int offset_list[] = + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 7, 6, 5, 4, 3, 2, 1, // Seek by 1GB + 0, 2, 1, 3, 2, 4, 3, 5, 4, 6, 5, 7, 6, 8, // Seek by 2GB + 6, 7, 5, 6, 4, 5, 3, 4, 2, 3, 1, 2, + 0, 3, 1, 4, 2, 5, 3, 6, 4, 7, 5, 8, // Seek by 3GB + 5, 7, 4, 6, 3, 5, 2, 4, 1, + 0, 4, 1, 5, 2, 6, 3, 7, 4, 8, // Seek by 4GB + 4, 7, 3, 6, 2, 5, 1, 4, + 0, 5, 1, 6, 2, 7, 3, 8, 3, 7, 2, 6, 1, 5, // Seek by 5GB + 0, 6, 1, 7, 2, 8, 2, 7, 1, 6, // Seek by 6GB + 0, 7, 1, 8, 1, 7, // Seek by 7GB + 0, 8, 0 }; // Seek by 8GB +const int offset_list_length = sizeof(offset_list) / sizeof(int); +#ifdef LARGE_FILE_TEMP +# define BOOST_FILE_NAME BOOST_STRINGIZE(LARGE_FILE_TEMP) +# define BOOST_KEEP_FILE false +#else +# define BOOST_FILE_NAME BOOST_STRINGIZE(LARGE_FILE_KEEP) +# define BOOST_KEEP_FILE true +#endif + +//------------------Definition of remove_large_file---------------------------// + +// Removes the large file +void remove_large_file() +{ +#ifdef BOOST_IOSTREAMS_WINDOWS + DeleteFile(TEXT(BOOST_FILE_NAME)); +#else + unlink(BOOST_FILE_NAME); +#endif +} + +//------------------Definition of large_file_exists---------------------------// + +// Returns true if the large file exists, has the correct size, and has been +// modified since the last commit affecting this source file; if the file exists +// but is invalid, deletes the file. +bool large_file_exists() +{ + // Last mod date + time_t last_mod; + +#ifdef BOOST_IOSTREAMS_WINDOWS + + // Check existence + WIN32_FIND_DATA info; + HANDLE hnd = FindFirstFile(TEXT(BOOST_FILE_NAME), &info); + if (hnd == INVALID_HANDLE_VALUE) + return false; + + // Check size + FindClose(hnd); + stream_offset size = + (static_cast<stream_offset>(info.nFileSizeHigh) << 32) + + static_cast<stream_offset>(info.nFileSizeLow); + if (size != file_size) { + remove_large_file(); + return false; + } + + // Fetch last mod date + SYSTEMTIME stime; + if (!FileTimeToSystemTime(&info.ftLastWriteTime, &stime)) { + remove_large_file(); + return false; + } + tm ctime; + ctime.tm_year = stime.wYear - 1900; + ctime.tm_mon = stime.wMonth - 1; + ctime.tm_mday = stime.wDay; + ctime.tm_hour = stime.wHour; + ctime.tm_min = stime.wMinute; + ctime.tm_sec = stime.wSecond; + ctime.tm_isdst = 0; + last_mod = mktime(&ctime); + +#else + + // Check existence + struct BOOST_IOSTREAMS_FD_STAT info; + if (BOOST_IOSTREAMS_FD_STAT(BOOST_FILE_NAME, &info)) + return false; + + // Check size + if (info.st_size != file_size) { + remove_large_file(); + return false; + } + + // Fetch last mod date + last_mod = info.st_mtime; + +#endif + + // Fetch last mod date of this file ("large_file_test.cpp") + string timestamp = + "$Date$"; + if (timestamp.size() != 53) { // Length of auto-generated SVN timestamp + remove_large_file(); + return false; + } + tm commit; + try { + commit.tm_year = lexical_cast<int>(timestamp.substr(7, 4)) - 1900; + commit.tm_mon = lexical_cast<int>(timestamp.substr(12, 2)) - 1; + commit.tm_mday = lexical_cast<int>(timestamp.substr(15, 2)); + commit.tm_hour = lexical_cast<int>(timestamp.substr(18, 2)); + commit.tm_min = lexical_cast<int>(timestamp.substr(21, 2)); + commit.tm_sec = lexical_cast<int>(timestamp.substr(24, 2)); + } catch (const bad_lexical_cast&) { + remove_large_file(); + return false; + } + + // If last commit was two days or more before file timestamp, existing + // file is okay; otherwise, it must be regenerated (the two-day window + // compensates for time zone differences) + return difftime(last_mod, mktime(&commit)) >= 60 * 60 * 48; +} + +//------------------Definition of map_large_file------------------------------// + +// Initializes the large file by mapping it in small segments. This is an +// optimization for Win32; the straightforward implementation using WriteFile +// and SetFilePointer (see the Borland workaround below) is painfully slow. +bool map_large_file() +{ + for (stream_offset z = 0; z <= 8; ++z) { + try { + mapped_file_params params; + params.path = BOOST_FILE_NAME; + params.offset = z * gigabyte; + params.length = 1; + params.mode = BOOST_IOS::out; + mapped_file file(params); + file.begin()[0] = static_cast<char>(z + 1); + } catch (const std::exception&) { + remove_large_file(); + return false; + } + } + return true; +} + +//------------------Definition of create_large_file---------------------------// + +// Creates and initializes the large file if it does not already exist. The file +// looks like this: +// +// 0 1GB 2GB 3GB 4GB 5GB 6GB 7GB 8GB +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// 1.....2.....3.....4.....5.....6.....7.....8.....9 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +// where the characters 1-9 appear at offsets that are multiples of 1GB and the +// dots represent uninitialized data. +bool create_large_file() +{ + // If file exists, has correct size, and is recent, we're done + if (BOOST_KEEP_FILE && large_file_exists()) + return true; + +#ifdef BOOST_IOSTREAMS_WINDOWS + + // Create file + HANDLE hnd = + CreateFile( + TEXT(BOOST_FILE_NAME), + GENERIC_WRITE, + 0, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL + ); + if (!hnd) + return false; + + // Set file pointer + LONG off_low = static_cast<LONG>(file_size & 0xffffffff); + LONG off_high = static_cast<LONG>(file_size >> 32); + if ( SetFilePointer(hnd, off_low, &off_high, FILE_BEGIN) == + INVALID_SET_FILE_POINTER && + GetLastError() != NO_ERROR ) + { + CloseHandle(hnd); + remove_large_file(); + return false; + } + + // Set file size + if (!SetEndOfFile(hnd)) { + CloseHandle(hnd); + remove_large_file(); + return false; + } + +# if !defined(__BORLANDC__) || __BORLANDC__ < 0x582 || __BORLANDC__ >= 0x592 + + // Close handle; all further access is via mapped_file + CloseHandle(hnd); + + // Initialize file data + return map_large_file(); + +# else // Borland >= 5.8.2 and Borland < 5.9.2 + + // Initialize file data (very slow, even though only 9 writes are required) + for (stream_offset z = 0; z <= 8; ++z) { + + // Seek + LONG off_low = static_cast<LONG>((z * gigabyte) & 0xffffffff); // == 0 + LONG off_high = static_cast<LONG>((z * gigabyte) >> 32); + if ( SetFilePointer(hnd, off_low, &off_high, FILE_BEGIN) == + INVALID_SET_FILE_POINTER && + GetLastError() != NO_ERROR ) + { + CloseHandle(hnd); + remove_large_file(); + return false; + } + + // Write a character + char buf[1] = { z + 1 }; + DWORD result; + BOOL success = WriteFile(hnd, buf, 1, &result, NULL); + if (!success || result != 1) { + CloseHandle(hnd); + remove_large_file(); + return false; + } + } + + // Close file + CloseHandle(hnd); + return true; + +# endif // Borland workaround +#else // #ifdef BOOST_IOSTREAMS_WINDOWS + + // Create file + int oflag = O_WRONLY | O_CREAT; + #ifdef _LARGEFILE64_SOURCE + oflag |= O_LARGEFILE; + #endif + mode_t pmode = + S_IRUSR | S_IWUSR | + S_IRGRP | S_IWGRP | + S_IROTH | S_IWOTH; + int fd = BOOST_IOSTREAMS_FD_OPEN(BOOST_FILE_NAME, oflag, pmode); + if (fd == -1) + return false; + + // Set file size + if (BOOST_IOSTREAMS_FD_TRUNCATE(fd, file_size)) { + BOOST_IOSTREAMS_FD_CLOSE(fd); + return false; + } + +# ifndef __CYGWIN__ + + // Initialize file data + for (int z = 0; z <= 8; ++z) { + + // Seek + BOOST_IOSTREAMS_FD_OFFSET off = + BOOST_IOSTREAMS_FD_SEEK( + fd, + static_cast<BOOST_IOSTREAMS_FD_OFFSET>(z * gigabyte), + SEEK_SET + ); + if (off == -1) { + BOOST_IOSTREAMS_FD_CLOSE(fd); + return false; + } + + // Write a character + char buf[1] = { z + 1 }; + if (BOOST_IOSTREAMS_FD_WRITE(fd, buf, 1) == -1) { + BOOST_IOSTREAMS_FD_CLOSE(fd); + return false; + } + } + + // Close file + BOOST_IOSTREAMS_FD_CLOSE(fd); + return true; + +# else // Cygwin + + // Close descriptor; all further access is via mapped_file + BOOST_IOSTREAMS_FD_CLOSE(fd); + + // Initialize file data + return map_large_file(); + +# endif +#endif // #ifdef BOOST_IOSTREAMS_WINDOWS +} + +//------------------Definition of large_file----------------------------------// + +// RAII utility +class large_file { +public: + large_file() { exists_ = create_large_file(); } + ~large_file() { if (!BOOST_KEEP_FILE) remove_large_file(); } + bool exists() const { return exists_; } + const char* path() const { return BOOST_FILE_NAME; } +private: + bool exists_; +}; + +//------------------Definition of check_character-----------------------------// + +// Verify that the given file contains the given character at the current +// position +bool check_character(file_descriptor_source& file, char value) +{ + char buf[1]; + std::streamsize amt; + BOOST_CHECK_NO_THROW(amt = file.read(buf, 1)); + BOOST_CHECK_MESSAGE(amt == 1, "failed reading character"); + BOOST_CHECK_NO_THROW(file.seek(-1, BOOST_IOS::cur)); + return buf[0] == value; +} + +//------------------Definition of large_file_test-----------------------------// + +void large_file_test() +{ + BOOST_REQUIRE_MESSAGE( + sizeof(stream_offset) >= 8, + "large offsets not supported" + ); + + // Prepare file and file descriptor + large_file large; + file_descriptor_source file; + BOOST_REQUIRE_MESSAGE( + large.exists(), "failed creating file \"" << BOOST_FILE_NAME << '"' + ); + BOOST_CHECK_NO_THROW(file.open(large.path(), BOOST_IOS::binary)); + + // Test seeking using ios_base::beg + for (int z = 0; z < offset_list_length; ++z) { + char value = offset_list[z] + 1; + stream_offset off = + static_cast<stream_offset>(offset_list[z]) * gigabyte; + BOOST_CHECK_NO_THROW(file.seek(off, BOOST_IOS::beg)); + BOOST_CHECK_MESSAGE( + check_character(file, value), + "failed validating seek" + ); + } + + // Test seeking using ios_base::end + for (int z = 0; z < offset_list_length; ++z) { + char value = offset_list[z] + 1; + stream_offset off = + -static_cast<stream_offset>(8 - offset_list[z]) * gigabyte - 1; + BOOST_CHECK_NO_THROW(file.seek(off, BOOST_IOS::end)); + BOOST_CHECK_MESSAGE( + check_character(file, value), + "failed validating seek" + ); + } + + // Test seeking using ios_base::cur + for (int next, cur = 0, z = 0; z < offset_list_length; ++z, cur = next) { + next = offset_list[z]; + char value = offset_list[z] + 1; + stream_offset off = static_cast<stream_offset>(next - cur) * gigabyte; + BOOST_CHECK_NO_THROW(file.seek(off, BOOST_IOS::cur)); + BOOST_CHECK_MESSAGE( + check_character(file, value), + "failed validating seek" + ); + } +} + +test_suite* init_unit_test_suite(int, char* []) +{ + test_suite* test = BOOST_TEST_SUITE("execute test"); + test->add(BOOST_TEST_CASE(&large_file_test)); + return test; +} |