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_categoryerror_codeerror_condition
+ file
+ file_stdioflat_bufferhandler_allochandler_ptr
@@ -211,6 +213,7 @@
Constantserrc
+ file_mode
@@ -224,6 +227,7 @@
is_completion_handleris_const_buffer_sequenceis_dynamic_buffer
+ is_fileis_mutable_buffer_sequenceis_sync_read_streamis_sync_stream
@@ -236,6 +240,7 @@
AsyncStreamBufferSequenceDynamicBuffer
+ FileStreamSyncStream
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