From eec5f1d8b69e1d09452ac046f1e28c48c05cfb20 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Thu, 6 Jul 2017 03:59:33 -0700 Subject: [PATCH] Add file_posix --- CHANGELOG.md | 1 + doc/quickref.xml | 1 + doc/source.dox | 1 + include/beast/core/file.hpp | 11 +- include/beast/core/file_posix.hpp | 171 +++++++++++++ include/beast/core/impl/file_posix.ipp | 331 +++++++++++++++++++++++++ test/core/CMakeLists.txt | 1 + test/core/Jamfile | 1 + test/core/file_posix.cpp | 35 +++ 9 files changed, 550 insertions(+), 3 deletions(-) create mode 100644 include/beast/core/file_posix.hpp create mode 100644 include/beast/core/impl/file_posix.ipp create mode 100644 test/core/file_posix.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 698ada85..3b08913a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ Version 74: * Add file_win32 * Add file_body * Remove common/file_body.hpp +* Add file_posix -------------------------------------------------------------------------------- diff --git a/doc/quickref.xml b/doc/quickref.xml index 1c899955..b9384156 100644 --- a/doc/quickref.xml +++ b/doc/quickref.xml @@ -180,6 +180,7 @@   + file_posix file_stdio file_win32 flat_buffer diff --git a/doc/source.dox b/doc/source.dox index 0cb3812b..7ead3bb0 100644 --- a/doc/source.dox +++ b/doc/source.dox @@ -285,6 +285,7 @@ INCLUDE_FILE_PATTERNS = PREDEFINED = \ BEAST_DOXYGEN \ + BEAST_USE_POSIX_FILE=1 \ BEAST_USE_WIN32_FILE=1 EXPAND_AS_DEFINED = diff --git a/include/beast/core/file.hpp b/include/beast/core/file.hpp index 34817a28..40dcf065 100644 --- a/include/beast/core/file.hpp +++ b/include/beast/core/file.hpp @@ -9,6 +9,7 @@ #define BEAST_CORE_FILE_HPP #include +#include #include #include #include @@ -20,12 +21,16 @@ namespace beast { This alias is set to the best available implementation of @b File given the platform and build settings. */ -#if defined(BOOST_MSVC) && BEAST_USE_WIN32_FILE +#if BEAST_DOXYGEN +using file = implementation_defined; +#else +#if BEAST_USE_WIN32_FILE using file = file_win32; - +#elif BEAST_USE_POSIX_FILE +using file = file_posix; #else using file = file_stdio; - +#endif #endif } // beast diff --git a/include/beast/core/file_posix.hpp b/include/beast/core/file_posix.hpp new file mode 100644 index 00000000..5a597149 --- /dev/null +++ b/include/beast/core/file_posix.hpp @@ -0,0 +1,171 @@ +// +// Copyright (c) 2015-2016 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// 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) +// + +#ifndef BEAST_CORE_FILE_POSIX_HPP +#define BEAST_CORE_FILE_POSIX_HPP + +#include + +#if ! defined(BEAST_NO_POSIX_FILE) +# if ! defined(__APPLE__) && ! defined(__linux__) +# define BEAST_NO_POSIX_FILE +# endif +#endif + +#if ! defined(BEAST_USE_POSIX_FILE) +# if ! defined(BEAST_NO_POSIX_FILE) +# define BEAST_USE_POSIX_FILE 1 +# else +# define BEAST_USE_POSIX_FILE 0 +# endif +#endif + +#if BEAST_USE_POSIX_FILE + +#include +#include +#include + +namespace beast { + +/** An implementation of File for Win32. + + This class implements a @b File using Win32 native interfaces. +*/ +class file_posix +{ + int fd_ = -1; + +public: + /** The type of the underlying file handle. + + This is platform-specific. + */ + using native_handle_type = int; + + /** Destructor + + If the file is open it is first closed. + */ + ~file_posix(); + + /** Constructor + + There is no open file initially. + */ + file_posix() = default; + + /** Constructor + + The moved-from object behaves as if default constructed. + */ + file_posix(file_posix&& other); + + /** Assignment + + The moved-from object behaves as if default constructed. + */ + file_posix& operator=(file_posix&& other); + + /// Returns the native handle associated with the file. + native_handle_type + native_handle() const + { + return fd_; + } + + /** Set the native handle associated with the file. + + If the file is open it is first closed. + + @param fd The native file handle to assign. + */ + void + native_handle(native_handle_type fd); + + /// Returns `true` if the file is open + bool + is_open() const + { + return fd_ != -1; + } + + /** Close the file if open + + @param ec Set to the error, if any occurred. + */ + void + close(error_code& ec); + + /** Open a file at the given path with the specified mode + + @param path The utf-8 encoded path to the file + + @param mode The file mode to use + + @param ec Set to the error, if any occurred + */ + void + open(char const* path, file_mode mode, error_code& ec); + + /** Return the size of the open file + + @param ec Set to the error, if any occurred + + @return The size in bytes + */ + std::uint64_t + size(error_code& ec) const; + + /** Return the current position in the open file + + @param ec Set to the error, if any occurred + + @return The offset in bytes from the beginning of the file + */ + std::uint64_t + pos(error_code& ec) const; + + /** Adjust the current position in the open file + + @param offset The offset in bytes from the beginning of the file + + @param ec Set to the error, if any occurred + */ + void + seek(std::uint64_t offset, error_code& ec); + + /** Read from the open file + + @param buffer The buffer for storing the result of the read + + @param n The number of bytes to read + + @param ec Set to the error, if any occurred + */ + std::size_t + read(void* buffer, std::size_t n, error_code& ec) const; + + /** Write to the open file + + @param buffer The buffer holding the data to write + + @param n The number of bytes to write + + @param ec Set to the error, if any occurred + */ + std::size_t + write(void const* buffer, std::size_t n, error_code& ec); +}; + +} // beast + +#include + +#endif + +#endif diff --git a/include/beast/core/impl/file_posix.ipp b/include/beast/core/impl/file_posix.ipp new file mode 100644 index 00000000..072ee9af --- /dev/null +++ b/include/beast/core/impl/file_posix.ipp @@ -0,0 +1,331 @@ +// +// Copyright (c) 2015-2016 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// 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) +// + +#ifndef BEAST_CORE_IMPL_FILE_POSIX_IPP +#define BEAST_CORE_IMPL_FILE_POSIX_IPP + +#include +#include +#include +#include +#include +#include +#include + +namespace beast { + +namespace detail { + +inline +int +file_posix_close(int fd) +{ + for(;;) + { + if(! ::close(fd)) + break; + int const ev = errno; + if(errno != EINTR) + return ev; + } + return 0; +} + +} // detail + +inline +file_posix:: +~file_posix() +{ + if(fd_ != -1) + detail::file_posix_close(fd_); +} + +inline +file_posix:: +file_posix(file_posix&& other) + : fd_(other.fd_) +{ + other.fd_ = -1; +} + +inline +file_posix& +file_posix:: +operator=(file_posix&& other) +{ + if(&other == this) + return *this; + if(fd_ != -1) + detail::file_posix_close(fd_); + fd_ = other.fd_; + other.fd_ = -1; + return *this; +} + +inline +void +file_posix:: +native_handle(native_handle_type fd) +{ + if(fd_ != -1) + detail::file_posix_close(fd_); + fd_ = fd; +} + +inline +void +file_posix:: +close(error_code& ec) +{ + if(fd_ != -1) + { + auto const ev = + detail::file_posix_close(fd_); + if(ev) + ec.assign(ev, generic_category()); + else + ec.assign(0, ec.category()); + fd_ = -1; + } + else + { + ec.assign(0, ec.category()); + } +} + +inline +void +file_posix:: +open(char const* path, file_mode mode, error_code& ec) +{ + if(fd_ != -1) + { + auto const ev = + detail::file_posix_close(fd_); + if(ev) + ec.assign(ev, generic_category()); + else + ec.assign(0, ec.category()); + fd_ = -1; + } + int f = 0; + int advise = 0; + switch(mode) + { + default: + case file_mode::read: + f = O_RDONLY; + #ifndef __APPLE__ + advise = POSIX_FADV_RANDOM; + #endif + break; + case file_mode::scan: + f = O_RDONLY; + #ifndef __APPLE__ + advise = POSIX_FADV_SEQUENTIAL; + #endif + break; + + case file_mode::write: + f = O_RDWR | O_CREAT | O_TRUNC; + #ifndef __APPLE__ + advise = POSIX_FADV_RANDOM; + #endif + break; + + case file_mode::write_new: + f = O_RDWR | O_CREAT | O_EXCL; + #ifndef __APPLE__ + advise = POSIX_FADV_RANDOM; + #endif + break; + + case file_mode::write_existing: + f = O_RDWR | O_EXCL; + #ifndef __APPLE__ + advise = POSIX_FADV_RANDOM; + #endif + break; + + case file_mode::append: + f = O_RDWR | O_CREAT | O_TRUNC; + #ifndef __APPLE__ + advise = POSIX_FADV_SEQUENTIAL; + #endif + break; + + case file_mode::append_new: + f = O_RDWR | O_CREAT | O_EXCL; + #ifndef __APPLE__ + advise = POSIX_FADV_SEQUENTIAL; + #endif + break; + + case file_mode::append_existing: + f = O_RDWR | O_EXCL; + #ifndef __APPLE__ + advise = POSIX_FADV_SEQUENTIAL; + #endif + break; + } + for(;;) + { + fd_ = ::open(path, f, 0644); + if(fd_ != -1) + break; + auto const ev = errno; + if(ev != EINTR) + { + ec.assign(ev, generic_category()); + return; + } + } +#ifndef __APPLE__ + if(::posix_fadvise(fd_, 0, 0, advise)) + { + auto const ev = errno; + detail::file_posix_close(fd_); + fd_ = -1; + ec.assign(ev, generic_category()); + return; + } +#endif + ec.assign(0, ec.category()); +} + +inline +std::uint64_t +file_posix:: +size(error_code& ec) const +{ + if(fd_ == -1) + { + ec.assign(errc::invalid_argument, generic_category()); + return 0; + } + BOOST_STATIC_ASSERT( + sizeof(stat::st_size) == sizeof(std::uint64_t)); + struct stat st; + if(::fstat(fd_, &st) != 0) + { + ec.assign(errno, generic_category()); + return 0; + } + ec.assign(0, ec.category()); + return st.st_size; +} + +inline +std::uint64_t +file_posix:: +pos(error_code& ec) const +{ + if(fd_ == -1) + { + ec.assign(errc::invalid_argument, generic_category()); + return 0; + } + auto const result = ::lseek(fd_, 0, SEEK_CUR); + if(result == (off_t)-1) + { + ec.assign(errno, generic_category()); + return 0; + } + ec.assign(0, ec.category()); + return result; +} + +inline +void +file_posix:: +seek(std::uint64_t offset, error_code& ec) +{ + if(fd_ == -1) + { + ec.assign(errc::invalid_argument, generic_category()); + return; + } + auto const result = ::lseek(fd_, offset, SEEK_SET); + if(result == static_cast(-1)) + { + ec.assign(errno, generic_category()); + return; + } + ec.assign(0, ec.category()); +} + +inline +std::size_t +file_posix:: +read(void* buffer, std::size_t n, error_code& ec) const +{ + if(fd_ == -1) + { + ec.assign(errc::invalid_argument, generic_category()); + return 0; + } + std::size_t nread = 0; + while(n > 0) + { + auto const amount = static_cast((std::min)( + n, static_cast(SSIZE_MAX))); + auto const result = ::read(fd_, buffer, amount); + if(result == -1) + { + auto const ev = errno; + if(ev == EINTR) + continue; + ec.assign(ev, generic_category()); + return nread; + } + if(result == 0) + { + // short read + return nread; + } + n -= result; + nread += result; + buffer = reinterpret_cast(buffer) + result; + } + return nread; +} + +inline +std::size_t +file_posix:: +write(void const* buffer, std::size_t n, error_code& ec) +{ + if(fd_ == -1) + { + ec.assign(errc::invalid_argument, generic_category()); + return 0; + } + std::size_t nwritten = 0; + while(n > 0) + { + auto const amount = static_cast((std::min)( + n, static_cast(SSIZE_MAX))); + auto const result = ::write(fd_, buffer, amount); + if(result == -1) + { + auto const ev = errno; + if(ev == EINTR) + continue; + ec.assign(ev, generic_category()); + return nwritten; + } + n -= result; + nwritten += result; + buffer = reinterpret_cast(buffer) + result; + } + return nwritten; +} + +} // beast + +#endif diff --git a/test/core/CMakeLists.txt b/test/core/CMakeLists.txt index 7ae4cdcc..595032bc 100644 --- a/test/core/CMakeLists.txt +++ b/test/core/CMakeLists.txt @@ -26,6 +26,7 @@ add_executable (core-tests drain_buffer.cpp error.cpp file.cpp + file_posix.cpp file_stdio.cpp file_win32.cpp flat_buffer.cpp diff --git a/test/core/Jamfile b/test/core/Jamfile index 0170d7b4..52218baa 100644 --- a/test/core/Jamfile +++ b/test/core/Jamfile @@ -20,6 +20,7 @@ unit-test core-tests : drain_buffer.cpp error.cpp file.cpp + file_posix.cpp file_stdio.cpp file_win32.cpp flat_buffer.cpp diff --git a/test/core/file_posix.cpp b/test/core/file_posix.cpp new file mode 100644 index 00000000..2a8bd046 --- /dev/null +++ b/test/core/file_posix.cpp @@ -0,0 +1,35 @@ +// +// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// 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) +// + +// Test that header file is self-contained. +#include + +#if BEAST_USE_POSIX_FILE + +#include "file_test.hpp" + +#include +#include + +namespace beast { + +class file_posix_test + : public beast::unit_test::suite +{ +public: + void + run() + { + doTestFile(*this); + } +}; + +BEAST_DEFINE_TESTSUITE(file_posix,core,beast); + +} // beast + +#endif