Files
fennec/include/fennec/langproc/filesystem/file.h
Medusa Slockbower 3565bbbc52 - Fixed some logic errors with assertions for wide vs. byte files.
- Changed getline and getwline to use gets instead of the experimental functions. These, in theory, will be slightly faster.
2025-09-11 16:58:32 -04:00

323 lines
9.7 KiB
C++

// =====================================================================================================================
// fennec, a free and open source game engine
// Copyright © 2025 Medusa Slockbower
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// =====================================================================================================================
#ifndef FENNEC_LANGPROC_IO_FILE_H
#define FENNEC_LANGPROC_IO_FILE_H
#include <fennec/langproc/filesystem/path.h>
#include <fennec/langproc/strings/cstring.h>
#include <fennec/langproc/strings/string.h>
#include <fennec/langproc/strings/wstring.h>
namespace fennec
{
///
/// \brief Mode flags for opening a file
///
/// fmode_binary and fmode_wide are independent of the other modes
///
/// \details Valid Flag Combinations
/// <table width="100%" class="fieldtable" id="table_fennec_LANGPROC_io_fmode">
/// <tr><th style="vertical-align: top">Flags
/// <th style="vertical-align: top">Description
///
/// <tr><td style="vertical-align: top">`read`
/// <td style="vertical-align: top">Opens file as read-only, reading from start
///
/// <tr><td style="vertical-align: top">`write`
/// <td style="vertical-align: top">Opens file as write-only, writing to end
///
/// <tr><td style="vertical-align: top">`read | write`
/// <td style="vertical-align: top">Opens file as read-write, reading from start
///
/// <tr><td style="vertical-align: top">`write | trunc`
/// <td style="vertical-align: top">Opens file as write-only, destroying contents
///
/// <tr><td style="vertical-align: top">`read | write | trunc`
/// <td style="vertical-align: top">Opens file as read-write, destroying contents
/// </table>
enum fmode_ : uint8_t
{
fmode_read = 0b00000001 ///< Opens file for reading
, fmode_write = 0b00000010 ///< Opens file for writing
, fmode_trunc = 0b00000100 ///< Contents of the file will be destroyed, only compatible with write enabled modes
, fmode_exclusive = 0b00001000 ///< Generates an error if the opened file is not empty
, fmode_binary = 0b00010000 ///< Open in binary mode
, fmode_wide = 0b00100000 ///< Opens a file in wide mode
};
///
/// \brief Structure for handling streams of data
///
/// \details operations, when errored, will return a corresponding error.
/// Use file::get_error() to check if an error is present and return a corresponding string.
/// Use file::clear_error() to clear the errored state.
/// Some operations, specifically file::rename() and file::copy().
class file
{
public:
/// \brief value of an invalid position
static constexpr size_t npos = -1;
///
/// \brief Check if the provided mode bitflags are a valid combination
/// \param mode the bitfield
/// \returns true if the combination of flags is valid, false otherwise
static constexpr bool is_valid(uint8_t mode) {
const bool t = mode & fmode_trunc;
const bool x = mode & fmode_exclusive;
const bool w = mode & fmode_write;
// when x is true, t must be true
// when t is true, w must be true
return (t && x && w)
|| (t && w)
|| !(t || x);
}
///
/// \returns the c stdout
static file& cout();
///
/// \returns the c stdin
static file& cin();
///
/// \returns the c stderr
static file& cerr();
///
/// \brief default constructor, initializes an empty stream
file();
///
/// \brief default destructor, cleans up an open stream
~file();
///
/// \brief move constructor
/// \param file the stream to take ownership of
file(file&& file) noexcept;
file& operator=(file&& file) noexcept;
// don't allow copying streams
file(const file&) = delete;
// Properties ==========================================================================================================
///
/// \returns the path the stream
const path& get_path() const {
return _path;
}
///
/// \returns the mode of the stream
uint8_t mode() const {
return _mode;
}
///
/// \returns true if there is a valid, open stream.
bool is_open() const {
return _handle != nullptr;
}
// File Access =========================================================================================================
///
/// \brief open a file
/// \param path the path to the file
/// \param mode the mode flags to open the file with
/// \returns false on success, true on error
bool open(const cstring& path, uint8_t mode);
///
/// \brief open a file
/// \param path the path to the file
/// \param mode the mode flags to open the file with
/// \returns false on success, true on error
bool open(const string& path, uint8_t mode);
///
/// \brief open a file
/// \param path the path to the file
/// \param mode the mode flags to open the file with
/// \returns false on success, true on error
bool open(const path& path, uint8_t mode);
///
/// \brief close a stream
/// \returns false on success, true on error
bool close();
///
/// \brief commit the streams buffer to the file
/// \returns false on success, true on error
bool commit();
// File Operations =====================================================================================================
///
/// \brief closes the stream and erases the file
/// \returns false on success, true on error
bool erase();
///
/// \brief rebinds the stream, copying contents to path, and erasing the old file
/// \param path the new path
/// \returns false on success, true on error
///
/// \details attempts to open a write-only stream at path,
/// attempts to reopen this file as read-only,
/// copies the contents of this file to the new stream,
/// reopen the new stream with the flags of this file and binds to it,
/// closes the old file.
bool rename(const cstring& path);
///
/// \brief rebinds the stream, copying contents to path, and erasing the old file
/// \param path the new path
/// \returns false on success, true on error
///
/// \details attempts to open a write-only stream at path,
/// attempts to reopen this file as read-only,
/// copies the contents of this file to the new stream,
/// reopen the new stream with the flags of this file and binds to it,
/// closes the old file.
bool rename(const string& path);
///
/// \brief rebinds the stream, copying contents to path, and erasing the old file
/// \param path the new path
/// \returns false on success, true on error
///
/// \details attempts to open a write-only stream at path,
/// attempts to reopen this file as read-only,
/// copies the contents of this file to the new stream,
/// reopen the new stream with the flags of this file and binds to it,
/// closes the old file.
bool rename(const path& path);
///
/// \brief copies the contents of this file to path.
/// \param path the path to copy to
/// \returns a file at the new path with the copied contents
file copy(const cstring& path);
///
/// \brief copies the contents of this file to path.
/// \param path the path to copy to
/// \returns a file at the new path with the copied contents
file copy(const string& path);
///
/// \brief copies the contents of this file to path.
/// \param path the path to copy to
/// \returns a file at the new path with the copied contents
file copy(const path& path);
// File Positioning ====================================================================================================
size_t get_pos() const;
bool set_pos(size_t i);
bool rewind();
bool eof() const;
// Binary Read Operations ==============================================================================================
size_t read(void* data, size_t size, size_t n);
template<typename T>
size_t read(T* data, size_t n) {
return read(static_cast<void*>(data), sizeof(T), n);
}
template<typename T, size_t n>
size_t read(T (&data)[n]) {
return read(static_cast<void*>(data), sizeof(T), n);
}
// Binary Write Operations =============================================================================================
bool putc(char c);
bool putwc(wchar_t c);
size_t write(const void* data, size_t size, size_t n);
template<size_t n>
size_t write(const char (&data)[n]) {
return write(data, sizeof(char), n - 1);
}
template<size_t n>
size_t write(const wchar_t (&data)[n]) {
return write(data, sizeof(wchar_t), n - 1);
}
template<typename T>
size_t write(const T* data, size_t n) {
return write(static_cast<const void*>(data), sizeof(T), n);
}
template<typename T, size_t n>
size_t write(const T (&data)[n]) {
return write(static_cast<const void*>(data), sizeof(T), n);
}
// Read Operations =====================================================================================================
char getc();
wchar_t getwc();
string getline();
wstring getwline();
// Printing Operations =================================================================================================
// Error Handling ======================================================================================================
const char* get_error() const { return _error; }
void clear_error() { _error = nullptr; }
private:
FILE* _handle;
path _path;
uint8_t _mode;
char* _error;
};
}
#endif // FENNEC_LANGPROC_IO_FILE_H