diff --git a/CHANGELOG.md b/CHANGELOG.md index defe0966..4c802a95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +Version 74: + +* Add file_stdio and File concept + +-------------------------------------------------------------------------------- + Version 73: * Jamroot tweak diff --git a/doc/0_main.qbk b/doc/0_main.qbk index 7e01cac4..658e9cbf 100644 --- a/doc/0_main.qbk +++ b/doc/0_main.qbk @@ -60,6 +60,7 @@ [def __DynamicBuffer__ [link beast.concept.DynamicBuffer [*DynamicBuffer]]] [def __Fields__ [link beast.concept.Fields [*Fields]]] [def __FieldsReader__ [link beast.concept.FieldsReader [*FieldsReader]]] +[def __File__ [link beast.concept.File [*File]]] [def __Stream__ [link beast.concept.streams [*Stream]]] [def __SyncStream__ [link beast.concept.streams.SyncStream [*SyncStream]]] @@ -77,13 +78,14 @@ [def __static_buffer__ [link beast.ref.beast__static_buffer `static_buffer`]] [def __static_buffer_n__ [link beast.ref.beast__static_buffer_n `static_buffer_n`]] -[import ../example/doc/http_examples.hpp] -[import ../example/echo-op/echo_op.cpp] [import ../example/common/detect_ssl.hpp] [import ../example/common/file_body.hpp] +[import ../example/doc/http_examples.hpp] +[import ../example/echo-op/echo_op.cpp] [import ../example/http-client/http_client.cpp] [import ../example/websocket-client/websocket_client.cpp] + [import ../test/exemplars.cpp] [import ../test/core/doc_snippets.cpp] [import ../test/http/doc_snippets.cpp] diff --git a/doc/8_concepts.qbk b/doc/8_concepts.qbk index 57ec4d35..894553a0 100644 --- a/doc/8_concepts.qbk +++ b/doc/8_concepts.qbk @@ -13,6 +13,7 @@ This section describes all of the concepts defined by the library. [include concept/BodyReader.qbk] [include concept/BodyWriter.qbk] [include concept/BufferSequence.qbk] +[include concept/File.qbk] [include concept/DynamicBuffer.qbk] [include concept/Fields.qbk] [include concept/FieldsReader.qbk] diff --git a/doc/concept/File.qbk b/doc/concept/File.qbk new file mode 100644 index 00000000..35decc65 --- /dev/null +++ b/doc/concept/File.qbk @@ -0,0 +1,173 @@ +[/ + 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) +] + +[section:File File] + +The [*File] concept abstracts access to files in the underlying file system. +To support other platform interfaces, users may author their own [*File] +types which meet these requirements. + +In this table: + +* `F` is a [*File] type +* `f` is an instance of `F` +* `p` is a value of type `char const*` which points to a null + terminated utf-8 encoded string. +* `m` is an instance of [link beast.ref.beast__file_mode `file_mode`] +* `n` is a number of bytes, convertible to `std::size_t` +* `o` is a byte offset in the file, convertible to `std::uint64_t` +* `b` is any non-const pointer to memory +* `c` is any possibly-const pointer to memory +* `ec` is a reference of type [link beast.ref.beast__error_code `error_code`] + +[heading Associated Types] + +* [link beast.ref.beast__file_mode `file_mode`] +* [link beast.ref.beast__is_file `is_file`] + +[heading File Requirements] +[table Valid Expressions +[[Operation] [Return Type] [Semantics, Pre/Post-conditions]] +[ + [`F()`] + [ ] + [ + Default constructable + ] +] +[ + [`f.~F()`] + [ ] + [ + Destructible. + If `f` refers to an open file, it is first closed + as if by a call to `close` with the error ignored. + ] +] +[ + [`f.is_open()`] + [`bool`] + [ + Returns `true` if `f` refers to an open file, `false` otherwise. + ] +] +[ + [`f.close(ec)`] + [] + [ + If `f` refers to an open file, thie function attempts to + close the file. + Regardless of whether an error occurs or not, a subsequent + call to `f.is_open()` will return `false`. + The function will ensure that `!ec` is `true` if there was + no error or set to the appropriate error code if an error + occurred. + ] +] +[ + [`f.open(p,m,ec)`] + [] + [ + Attempts to open the file at the path specified by `p` + with the mode specified by `m`. + Upon success, a subsequent call to `f.is_open()` will + return `true`. + If `f` refers to an open file, it is first closed + as if by a call to `close` with the error ignored. + The function will ensure that `!ec` is `true` if there was + no error or set to the appropriate error code if an error + occurred. + + ] +] +[ + [`f.size(ec)`] + [`std::uint64_t`] + [ + If `f` refers to an open file, this function attempts to + determine the file size and return its value. + If `f` does not refer to an open file, the function will + set `ec` to `errc::invalid_argument` and return 0. + The function will ensure that `!ec` is `true` if there was + no error or set to the appropriate error code if an error + occurred. + ] +] +[ + [`f.pos(ec)`] + [`std::uint64_t`] + [ + If `f` refers to an open file, this function attempts to + determine the current file offset and return it. + If `f` does not refer to an open file, the function will + set `ec` to `errc::invalid_argument` and return 0. + The function will ensure that `!ec` is `true` if there was + no error or set to the appropriate error code if an error + occurred. + ] +] +[ + [`f.seek(o,ec)`] + [] + [ + Attempts to reposition the current file offset to the value + `o`, which represents a byte offset relative to the beginning + of the file. + If `f` does not refer to an open file, the function will + set `ec` to `errc::invalid_argument` and return immediately. + The function will ensure that `!ec` is `true` if there was + no error or set to the appropriate error code if an error + occurred. + ] +] +[ + [`f.read(b,n,ec)`] + [`std::size_t`] + [ + Attempts to read `n` bytes starting at the current file offset + from the open file referred to by `f`. + Bytes read are stored in the memory buffer at address `b` which + must be at least `n` bytes in size. + The function advances the file offset by the amount read, and + returns the number of bytes actually read, which may be less + than `n`. + If `f` does not refer to an open file, the function will + set `ec` to `errc::invalid_argument` and return immediately. + The function will ensure that `!ec` is `true` if there was + no error or set to the appropriate error code if an error + occurred. + ] +] +[ + [`f.write(c,n,ec)`] + [`std::size_t`] + [ + Attempts to write `n` bytes from the buffer pointed to by `c` to + the current file offset of the open file referred to by `f`. + The memory buffer at `c` must point to storage of at least `n` + bytes meant to be copied to the file. + The function advances the file offset by the amount written, + and returns the number of bytes actually written, which may be + less than `n`. + If `f` does not refer to an open file, the function will + set `ec` to `errc::invalid_argument` and return immediately. + The function will ensure that `!ec` is `true` if there was + no error or set to the appropriate error code if an error + occurred. + ] +] +] + +[heading Exemplar] + +[concept_File] + +[heading Models] + +* [link beast.ref.beast__file_stdio `file_stdio`] + +[endsect] diff --git a/doc/quickref.xml b/doc/quickref.xml index 3e27d12a..affa6d26 100644 --- a/doc/quickref.xml +++ b/doc/quickref.xml @@ -173,11 +173,13 @@ error_category error_code error_condition + file   + file_stdio flat_buffer handler_alloc handler_ptr @@ -211,6 +213,7 @@ Constants errc + file_mode @@ -224,6 +227,7 @@ is_completion_handler is_const_buffer_sequence is_dynamic_buffer + is_file is_mutable_buffer_sequence is_sync_read_stream is_sync_stream @@ -236,6 +240,7 @@ AsyncStream BufferSequence DynamicBuffer + File Stream SyncStream diff --git a/include/beast/core.hpp b/include/beast/core.hpp index 2960e542..1a1c49c6 100644 --- a/include/beast/core.hpp +++ b/include/beast/core.hpp @@ -19,6 +19,9 @@ #include #include #include +#include +#include +#include #include #include #include diff --git a/include/beast/core/file.hpp b/include/beast/core/file.hpp new file mode 100644 index 00000000..200b2c90 --- /dev/null +++ b/include/beast/core/file.hpp @@ -0,0 +1,25 @@ +// +// 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_HPP +#define BEAST_CORE_FILE_HPP + +#include +#include + +namespace beast { + +/** An implementation of File. + + This alias is set to the best available implementation + of @b File given the platform and build settings. +*/ +using file = file_stdio; + +} // beast + +#endif diff --git a/include/beast/core/file_base.hpp b/include/beast/core/file_base.hpp new file mode 100644 index 00000000..71f3cd1a --- /dev/null +++ b/include/beast/core/file_base.hpp @@ -0,0 +1,87 @@ +// +// 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_BASE_HPP +#define BEAST_CORE_FILE_BASE_HPP + +#include + +namespace beast { + +/// The type of file path used by the library +using file_path = string_view; + +/** File open modes + + These modes are used when opening files using + instances of the @b File concept. + + @see file_stdio +*/ +enum class file_mode +{ + /// Random reading + read, + + /// Sequential reading + scan, + + /** Random writing to a new or truncated file + + @li If the file does not exist, it is created. + + @li If the file exists, it is truncated to + zero size upon opening. + */ + write, + + /** Random writing to new file only + + If the file exists, an error is generated. + */ + write_new, + + /** Random writing to existing file + + If the file does not exist, an error is generated. + */ + write_existing, + + /** Appending to a new or truncated file + + The current file position shall be set to the end of + the file prior to each write. + + @li If the file does not exist, it is created. + + @li If the file exists, it is truncated to + zero size upon opening. + */ + append, + + /** Appending to a new file only + + The current file position shall be set to the end of + the file prior to each write. + + If the file exists, an error is generated. + */ + append_new, + + /** Appending to an existing file + + The current file position shall be set to the end of + the file prior to each write. + + If the file does not exist, an error is generated. + */ + append_existing +}; + +} // beast + +#endif diff --git a/include/beast/core/file_stdio.hpp b/include/beast/core/file_stdio.hpp new file mode 100644 index 00000000..cb1972f4 --- /dev/null +++ b/include/beast/core/file_stdio.hpp @@ -0,0 +1,153 @@ +// +// 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_STDIO_HPP +#define BEAST_CORE_FILE_STDIO_HPP + +#include +#include +#include +#include + +namespace beast { + +/** An implementation of File which uses cstdio. + + This class implements a file using the interfaces present + in the C++ Standard Library, in ``. +*/ +class file_stdio +{ + FILE* f_ = nullptr; + +public: + /** The type of the underlying file handle. + + This is platform-specific. + */ + using native_handle_type = FILE*; + + /** Destructor + + If the file is open it is first closed. + */ + ~file_stdio(); + + /** Constructor + + There is no open file initially. + */ + file_stdio() = default; + + /** Constructor + + The moved-from object behaves as if default constructed. + */ + file_stdio(file_stdio&& other); + + /** Assignment + + The moved-from object behaves as if default constructed. + */ + file_stdio& operator=(file_stdio&& other); + + /// Returns the native handle associated with the file. + FILE* + native_handle() const + { + return f_; + } + + /** Set the native handle associated with the file. + + If the file is open it is first closed. + + @param f The native file handle to assign. + */ + void + native_handle(FILE* f); + + /// Returns `true` if the file is open + bool + is_open() const + { + return f_ != nullptr; + } + + /** 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 diff --git a/include/beast/core/impl/file_stdio.ipp b/include/beast/core/impl/file_stdio.ipp new file mode 100644 index 00000000..1738e48d --- /dev/null +++ b/include/beast/core/impl/file_stdio.ipp @@ -0,0 +1,225 @@ +// +// 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_STDIO_IPP +#define BEAST_CORE_IMPL_FILE_STDIO_IPP + +#include + +namespace beast { + +inline +file_stdio:: +~file_stdio() +{ + if(f_) + fclose(f_); +} + +inline +file_stdio:: +file_stdio(file_stdio&& other) + : f_(other.f_) +{ + other.f_ = nullptr; +} + +inline +file_stdio& +file_stdio:: +operator=(file_stdio&& other) +{ + if(&other == this) + return *this; + if(f_) + fclose(f_); + f_ = other.f_; + other.f_ = nullptr; + return *this; +} + +inline +void +file_stdio:: +native_handle(FILE* f) +{ + if(f_) + fclose(f_); + f_ = f; +} + +inline +void +file_stdio:: +close(error_code& ec) +{ + if(f_) + { + int failed = fclose(f_); + f_ = nullptr; + if(failed) + { + ec.assign(errno, generic_category()); + return; + } + } + ec.assign(0, ec.category()); +} + +inline +void +file_stdio:: +open(char const* path, file_mode mode, error_code& ec) +{ + if(f_) + { + fclose(f_); + f_ = nullptr; + } + char const* s; + switch(mode) + { + default: + case file_mode::read: s = "rb"; break; + case file_mode::scan: s = "rb"; break; + case file_mode::write: s = "wb"; break; + case file_mode::write_new: s = "wbx"; break; + case file_mode::write_existing: s = "wb"; break; + case file_mode::append: s = "ab"; break; + case file_mode::append_new: s = "abx"; break; + case file_mode::append_existing: s = "ab"; break; + } + f_ = std::fopen(path, s); + if(! f_) + { + ec.assign(errno, generic_category()); + return; + } + ec.assign(0, ec.category()); +} + +inline +std::uint64_t +file_stdio:: +size(error_code& ec) const +{ + if(! f_) + { + ec.assign(errc::invalid_argument, generic_category()); + return 0; + } + long pos = std::ftell(f_); + if(pos == -1L) + { + ec.assign(errno, generic_category()); + return 0; + } + int result = std::fseek(f_, 0, SEEK_END); + if(result != 0) + { + ec.assign(errno, generic_category()); + return 0; + } + long size = std::ftell(f_); + if(size == -1L) + { + ec.assign(errno, generic_category()); + std::fseek(f_, pos, SEEK_SET); + return 0; + } + result = std::fseek(f_, pos, SEEK_SET); + if(result != 0) + ec.assign(errno, generic_category()); + else + ec.assign(0, ec.category()); + return size; +} + +inline +std::uint64_t +file_stdio:: +pos(error_code& ec) const +{ + if(! f_) + { + ec.assign(errc::invalid_argument, generic_category()); + return 0; + } + long pos = std::ftell(f_); + if(pos == -1L) + { + ec.assign(errno, generic_category()); + return 0; + } + ec.assign(0, ec.category()); + return pos; +} + +inline +void +file_stdio:: +seek(std::uint64_t offset, error_code& ec) +{ + if(! f_) + { + ec.assign(errc::invalid_argument, generic_category()); + return; + } + if(offset > (std::numeric_limits::max)()) + { + ec = make_error_code(errc::invalid_seek); + return; + } + int result = std::fseek(f_, + static_cast(offset), SEEK_SET); + if(result != 0) + ec.assign(errno, generic_category()); + else + ec.assign(0, ec.category()); +} + +inline +std::size_t +file_stdio:: +read(void* buffer, std::size_t n, error_code& ec) const +{ + if(! f_) + { + ec.assign(errc::invalid_argument, generic_category()); + return 0; + } + auto nread = std::fread(buffer, 1, n, f_); + if(std::ferror(f_)) + { + ec.assign(errno, generic_category()); + return 0; + } + return nread; +} + +inline +std::size_t +file_stdio:: +write(void const* buffer, std::size_t n, error_code& ec) +{ + if(! f_) + { + ec.assign(errc::invalid_argument, generic_category()); + return 0; + } + auto nwritten = std::fwrite(buffer, 1, n, f_); + if(std::ferror(f_)) + { + ec.assign(errno, generic_category()); + return 0; + } + return nwritten; +} + +} // beast + +#endif diff --git a/include/beast/core/type_traits.hpp b/include/beast/core/type_traits.hpp index beebe9b8..2624ee66 100644 --- a/include/beast/core/type_traits.hpp +++ b/include/beast/core/type_traits.hpp @@ -9,6 +9,7 @@ #define BEAST_TYPE_TRAITS_HPP #include +#include #include #include #include @@ -558,6 +559,75 @@ using is_sync_stream = std::integral_constant::value && is_sync_write_stream::value>; #endif +//------------------------------------------------------------------------------ +// +// File concepts +// +//------------------------------------------------------------------------------ + +/** Determine if `T` meets the requirements of @b File. + + Metafunctions are used to perform compile time checking of template + types. This type will be `std::true_type` if `T` meets the requirements, + else the type will be `std::false_type`. + + @par Example + + Use with `static_assert`: + + @code + template + void f(File& file) + { + static_assert(is_file::value, + "File requirements not met"); + ... + @endcode + + Use with `std::enable_if` (SFINAE): + + @code + template + typename std::enable_if::value>::type + f(File& file); + @endcode +*/ +#if BEAST_DOXYGEN +template +struct is_file : std::integral_constant{}; +#else +template +struct is_file : std::false_type {}; + +template +struct is_file() = std::declval().is_open(), + std::declval().close(std::declval()), + std::declval().open( + std::declval(), + std::declval(), + std::declval()), + std::declval() = std::declval().size( + std::declval()), + std::declval() = std::declval().pos( + std::declval()), + std::declval().seek( + std::declval(), + std::declval()), + std::declval() = std::declval().read( + std::declval(), + std::declval(), + std::declval()), + std::declval() = std::declval().write( + std::declval(), + std::declval(), + std::declval()), + (void)0)>> : std::integral_constant::value && + std::is_destructible::value + > {}; +#endif + } // beast #endif diff --git a/test/core/CMakeLists.txt b/test/core/CMakeLists.txt index f69d7580..7eac1631 100644 --- a/test/core/CMakeLists.txt +++ b/test/core/CMakeLists.txt @@ -24,6 +24,9 @@ add_executable (core-tests doc_snippets.cpp drain_buffer.cpp error.cpp + file.cpp + file_stdio.cpp + file_test.hpp flat_buffer.cpp handler_alloc.cpp handler_ptr.cpp diff --git a/test/core/Jamfile b/test/core/Jamfile index ab2d5a2a..d8845617 100644 --- a/test/core/Jamfile +++ b/test/core/Jamfile @@ -19,6 +19,8 @@ unit-test core-tests : doc_snippets.cpp drain_buffer.cpp error.cpp + file.cpp + file_stdio.cpp flat_buffer.cpp handler_alloc.cpp handler_ptr.cpp diff --git a/test/core/file.cpp b/test/core/file.cpp new file mode 100644 index 00000000..f483142b --- /dev/null +++ b/test/core/file.cpp @@ -0,0 +1,9 @@ +// +// Copyright (c) 2013-2017 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 diff --git a/test/core/file_stdio.cpp b/test/core/file_stdio.cpp new file mode 100644 index 00000000..3bb61e24 --- /dev/null +++ b/test/core/file_stdio.cpp @@ -0,0 +1,31 @@ +// +// 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 + +#include "file_test.hpp" + +#include +#include + +namespace beast { + +class file_stdio_test + : public beast::unit_test::suite +{ +public: + void + run() + { + doTestFile(*this); + } +}; + +BEAST_DEFINE_TESTSUITE(file_stdio,core,beast); + +} // beast diff --git a/test/core/file_test.hpp b/test/core/file_test.hpp new file mode 100644 index 00000000..2249a4c0 --- /dev/null +++ b/test/core/file_test.hpp @@ -0,0 +1,122 @@ +// +// 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) +// + +#ifndef BEAST_TEST_CORE_FILE_TEST_HPP +#define BEAST_TEST_CORE_FILE_TEST_HPP + +#include +#include +#include +#include +#include + +namespace beast { + +template +void +doTestFile(beast::unit_test::suite& test) +{ + BOOST_STATIC_ASSERT(is_file::value); + + error_code ec; + auto const temp = boost::filesystem::unique_path(); + + { + { + File f1; + test.BEAST_EXPECT(! f1.is_open()); + f1.open(temp.string().c_str(), file_mode::write, ec); + test.BEAST_EXPECT(! ec); + File f2{std::move(f1)}; + test.BEAST_EXPECT(! f1.is_open()); + test.BEAST_EXPECT(f2.is_open()); + File f3; + f3 = std::move(f2); + test.BEAST_EXPECT(! f2.is_open()); + test.BEAST_EXPECT(f3.is_open()); + f1.close(ec); + test.BEAST_EXPECT(! ec); + auto const temp2 = boost::filesystem::unique_path(); + f3.open(temp2.string().c_str(), file_mode::read, ec); + test.BEAST_EXPECT(ec); + ec.assign(0, ec.category()); + } + boost::filesystem::remove(temp, ec); + test.BEAST_EXPECT(! ec); + } + + File f; + + test.BEAST_EXPECT(! f.is_open()); + + f.size(ec); + test.BEAST_EXPECT(ec == errc::invalid_argument); + ec.assign(0, ec.category()); + + f.pos(ec); + test.BEAST_EXPECT(ec == errc::invalid_argument); + ec.assign(0, ec.category()); + + f.seek(0, ec); + test.BEAST_EXPECT(ec == errc::invalid_argument); + ec.assign(0, ec.category()); + + f.read(nullptr, 0, ec); + test.BEAST_EXPECT(ec == errc::invalid_argument); + ec.assign(0, ec.category()); + + f.write(nullptr, 0, ec); + test.BEAST_EXPECT(ec == errc::invalid_argument); + ec.assign(0, ec.category()); + + f.open(temp.string().c_str(), file_mode::write, ec); + test.BEAST_EXPECT(! ec); + + std::string const s = "Hello, world!"; + f.write(s.data(), s.size(), ec); + test.BEAST_EXPECT(! ec); + + auto size = f.size(ec); + test.BEAST_EXPECT(! ec); + test.BEAST_EXPECT(size == s.size()); + + auto pos = f.pos(ec); + test.BEAST_EXPECT(! ec); + test.BEAST_EXPECT(pos == size); + + f.close(ec); + test.BEAST_EXPECT(! ec); + + f.open(temp.string().c_str(), file_mode::read, ec); + test.BEAST_EXPECT(! ec); + + std::string buf; + buf.resize(s.size()); + f.read(&buf[0], buf.size(), ec); + test.BEAST_EXPECT(! ec); + test.BEAST_EXPECT(buf == s); + + f.seek(1, ec); + test.BEAST_EXPECT(! ec); + buf.resize(3); + f.read(&buf[0], buf.size(), ec); + test.BEAST_EXPECT(! ec); + test.BEAST_EXPECT(buf == "ell"); + + pos = f.pos(ec); + test.BEAST_EXPECT(! ec); + test.BEAST_EXPECT(pos == 4); + + f.close(ec); + test.BEAST_EXPECT(! ec); + boost::filesystem::remove(temp, ec); + test.BEAST_EXPECT(! ec); +} + +} // beast + +#endif diff --git a/test/exemplars.cpp b/test/exemplars.cpp index d92dd56e..1079475a 100644 --- a/test/exemplars.cpp +++ b/test/exemplars.cpp @@ -6,6 +6,7 @@ // #include +#include #include #include #include @@ -244,5 +245,56 @@ struct FieldsReader //] }; +//[concept_File + +struct File +{ + /** Default constructor + + There is no open file initially. + */ + File(); + + /** Destructor + + If the file is open it is first closed. + */ + ~File(); + + /// Returns `true` if the file is open + bool + is_open() const; + + /// Close the file if open + void + close(error_code& ec); + + /// Open a file at the given path with the specified mode + void + open(char const* path, file_mode mode, error_code& ec); + + /// Return the size of the open file + std::uint64_t + size(error_code& ec) const; + + /// Return the current position in the open file + std::uint64_t + pos(error_code& ec) const; + + /// Adjust the current position in the open file + void + seek(std::uint64_t offset, error_code& ec); + + /// Read from the open file + std::size_t + read(void* buffer, std::size_t n, error_code& ec) const; + + /// Write to the open file + std::size_t + write(void const* buffer, std::size_t n, error_code& ec); +}; + +//] + } // http } // beast