Add file_stdio and File concept

This commit is contained in:
Vinnie Falco committed 2017-07-20 08:15:30 -07:00
1 parent aff5f392e2
commit ddc3099281
17 files changed
+971 -2

No files matched your search

+3
View File
@@ -19,6 +19,9 @@
#include <beast/core/consuming_buffers.hpp>
#include <beast/core/drain_buffer.hpp>
#include <beast/core/error.hpp>
#include <beast/core/file.hpp>
#include <beast/core/file_base.hpp>
#include <beast/core/file_stdio.hpp>
#include <beast/core/flat_buffer.hpp>
#include <beast/core/handler_alloc.hpp>
#include <beast/core/handler_ptr.hpp>
+25
View File
@@ -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 <beast/core/file_base.hpp>
#include <beast/core/file_stdio.hpp>
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
+87
View File
@@ -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 <beast/core/string.hpp>
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
+153
View File
@@ -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 <beast/core/error.hpp>
#include <beast/core/file_base.hpp>
#include <cstdio>
#include <cstdint>
namespace beast {
/** An implementation of File which uses cstdio.
This class implements a file using the interfaces present
in the C++ Standard Library, in `<stdio>`.
*/
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 <beast/core/impl/file_stdio.ipp>
#endif
+225
View File
@@ -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 <limits>
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<long>::max)())
{
ec = make_error_code(errc::invalid_seek);
return;
}
int result = std::fseek(f_,
static_cast<long>(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
+70
View File
@@ -9,6 +9,7 @@
#define BEAST_TYPE_TRAITS_HPP
#include <beast/config.hpp>
#include <beast/core/file_base.hpp>
#include <beast/core/detail/type_traits.hpp>
#include <boost/asio/buffer.hpp>
#include <type_traits>
@@ -558,6 +559,75 @@ using is_sync_stream = std::integral_constant<bool,
is_sync_read_stream<T>::value && is_sync_write_stream<T>::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<class File>
void f(File& file)
{
static_assert(is_file<File>::value,
"File requirements not met");
...
@endcode
Use with `std::enable_if` (SFINAE):
@code
template<class File>
typename std::enable_if<is_file<File>::value>::type
f(File& file);
@endcode
*/
#if BEAST_DOXYGEN
template<class T>
struct is_file : std::integral_constant<bool, ...>{};
#else
template<class T, class = void>
struct is_file : std::false_type {};
template<class T>
struct is_file<T, detail::void_t<decltype(
std::declval<bool&>() = std::declval<T const&>().is_open(),
std::declval<T&>().close(std::declval<error_code&>()),
std::declval<T&>().open(
std::declval<char const*>(),
std::declval<file_mode>(),
std::declval<error_code&>()),
std::declval<std::uint64_t&>() = std::declval<T&>().size(
std::declval<error_code&>()),
std::declval<std::uint64_t&>() = std::declval<T&>().pos(
std::declval<error_code&>()),
std::declval<T&>().seek(
std::declval<std::uint64_t>(),
std::declval<error_code&>()),
std::declval<std::size_t&>() = std::declval<T&>().read(
std::declval<void*>(),
std::declval<std::size_t>(),
std::declval<error_code&>()),
std::declval<std::size_t&>() = std::declval<T&>().write(
std::declval<void const*>(),
std::declval<std::size_t>(),
std::declval<error_code&>()),
(void)0)>> : std::integral_constant<bool,
std::is_default_constructible<T>::value &&
std::is_destructible<T>::value
> {};
#endif
} // beast
#endif