- Removed a bug with attempt to include pure c headers
- Added some more information about the license - fennec::file implementation
This commit is contained in:
@@ -9,16 +9,14 @@ add_subdirectory(external/sdl)
|
|||||||
# CppTrace is a dependency of the project, added as a git submodule
|
# CppTrace is a dependency of the project, added as a git submodule
|
||||||
add_subdirectory(external/cpptrace)
|
add_subdirectory(external/cpptrace)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 26)
|
set(CMAKE_CXX_STANDARD 23)
|
||||||
set(CMAKE_C_STANDARD 26)
|
set(CMAKE_C_STANDARD 23)
|
||||||
|
|
||||||
# find dependencies
|
# find dependencies
|
||||||
find_package(Doxygen)
|
find_package(Doxygen)
|
||||||
|
|
||||||
# any necessary include directories
|
# any necessary include directories
|
||||||
include_directories(include)
|
include_directories(${PROJECT_SOURCE_DIR}/include)
|
||||||
include_directories(external/cpptrace/include)
|
|
||||||
include_directories(external/sdl/include)
|
|
||||||
|
|
||||||
# Metaprogramming is a dependency for generating various type info before compilation of the engine.
|
# Metaprogramming is a dependency for generating various type info before compilation of the engine.
|
||||||
add_subdirectory(metaprogramming)
|
add_subdirectory(metaprogramming)
|
||||||
@@ -32,6 +30,9 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/lib/${FENNEC_BUILD_NAME
|
|||||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/lib/${FENNEC_BUILD_NAME})
|
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/lib/${FENNEC_BUILD_NAME})
|
||||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/bin/${FENNEC_BUILD_NAME})
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/bin/${FENNEC_BUILD_NAME})
|
||||||
|
|
||||||
|
# add the test suite as a sub-project
|
||||||
|
add_subdirectory(test)
|
||||||
|
|
||||||
add_library(fennec STATIC
|
add_library(fennec STATIC
|
||||||
|
|
||||||
# CORE =================================================================================================================
|
# CORE =================================================================================================================
|
||||||
@@ -133,9 +134,9 @@ add_library(fennec STATIC
|
|||||||
|
|
||||||
# add metaprogramming templates as a dependency and also force documentation to be generated when fennec is compiled
|
# add metaprogramming templates as a dependency and also force documentation to be generated when fennec is compiled
|
||||||
if(DOXYGEN_FOUND)
|
if(DOXYGEN_FOUND)
|
||||||
add_dependencies(fennec fennecdocs metaprogramming SDL3-shared cpptrace::cpptrace)
|
add_dependencies(fennec fennecdocs metaprogramming) # SDL3-shared cpptrace::cpptrace)
|
||||||
else()
|
else()
|
||||||
add_dependencies(fennec metaprogramming SDL3-shared cpptrace::cpptrace)
|
add_dependencies(fennec metaprogramming) # SDL3-shared cpptrace::cpptrace)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Compiler Warning Flags
|
# Compiler Warning Flags
|
||||||
@@ -143,10 +144,13 @@ if(MSVC)
|
|||||||
add_compile_options("/W4" "/WX") # All MSVC Warnings throw as Errors
|
add_compile_options("/W4" "/WX") # All MSVC Warnings throw as Errors
|
||||||
else()
|
else()
|
||||||
add_compile_options("-Wall" "-Wextra" "-pedantic" "-Werror") # All gcc/etc. Warnings throw as errors
|
add_compile_options("-Wall" "-Wextra" "-pedantic" "-Werror") # All gcc/etc. Warnings throw as errors
|
||||||
|
|
||||||
|
target_compile_definitions(fennec PRIVATE _GLIBCXX_INCLUDE_NEXT_C_HEADERS __USE_FILE_OFFSET64)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_compile_options(fennec PUBLIC "-mavx" "-mavx2" "-mavx512f") # SIMD Instructions, automatic vectorization will occur
|
target_compile_options(fennec PUBLIC "-mavx" "-mavx2" "-mavx512f") # SIMD Instructions, automatic vectorization will occur
|
||||||
|
|
||||||
|
|
||||||
target_link_options(fennec PRIVATE "-nostdlib" "-fno-exceptions" "-fno-rtti" "-fdiagnostics-all-candidates") # Do not compile base fennec library with c++ stdlib
|
target_link_options(fennec PRIVATE "-nostdlib" "-fno-exceptions" "-fno-rtti" "-fdiagnostics-all-candidates") # Do not compile base fennec library with c++ stdlib
|
||||||
# fennec does not use the C++ stdlib because it is bloated, difficult to read, and implementation defined.
|
# fennec does not use the C++ stdlib because it is bloated, difficult to read, and implementation defined.
|
||||||
# This implementation is designed to be as readable as possible, and expose information that would otherwise be obfuscated
|
# This implementation is designed to be as readable as possible, and expose information that would otherwise be obfuscated
|
||||||
@@ -154,10 +158,6 @@ target_link_options(fennec PRIVATE "-nostdlib" "-fno-exceptions" "-fno-rtti" "-f
|
|||||||
target_link_libraries(fennec PRIVATE SDL3-shared cpptrace::cpptrace)
|
target_link_libraries(fennec PRIVATE SDL3-shared cpptrace::cpptrace)
|
||||||
|
|
||||||
|
|
||||||
# add the test suite as a sub-project
|
|
||||||
add_subdirectory(test)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# DOXYGEN ==============================================================================================================
|
# DOXYGEN ==============================================================================================================
|
||||||
# https://vicrucann.github.io/tutorials/quick-cmake-doxygen/
|
# https://vicrucann.github.io/tutorials/quick-cmake-doxygen/
|
||||||
|
|||||||
31
PLANNING.md
31
PLANNING.md
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
1. [Introduction](#introduction)
|
1. [Introduction](#introduction)
|
||||||
2. [TODO](#todo)
|
2. [TODO](#todo)
|
||||||
1. [Security Ramblings](#security-ramblings)
|
1. [Security Ramblings](#file-security-ramblings)
|
||||||
3. [C++ Language](#c-language-library-lang)
|
3. [C++ Language](#c-language-library-lang)
|
||||||
4. [Math Library](#math-library-math)
|
4. [Math Library](#math-library-math)
|
||||||
5. [Memory Library](#memory-library-memory)
|
5. [Memory Library](#memory-library-memory)
|
||||||
@@ -63,6 +63,7 @@ This however can be achieved using events at different stages of those engines t
|
|||||||
- 2D Physics (`physics2d`)
|
- 2D Physics (`physics2d`)
|
||||||
- 2D & 3D Audio (`audio`)
|
- 2D & 3D Audio (`audio`)
|
||||||
|
|
||||||
|
|
||||||
### File Security Ramblings:
|
### File Security Ramblings:
|
||||||
|
|
||||||
Windows is starting to piss me off, so I am considering dropping official support for MSVC. MinGW and Cygwin
|
Windows is starting to piss me off, so I am considering dropping official support for MSVC. MinGW and Cygwin
|
||||||
@@ -81,9 +82,9 @@ The crux of this issue falls at the following specific behaviour:
|
|||||||
A threat actor can introduce a malicious file or symlink to the file that was attempted access between the check and
|
A threat actor can introduce a malicious file or symlink to the file that was attempted access between the check and
|
||||||
usage of the file. This is called TOCTOU (time of check, time of use).
|
usage of the file. This is called TOCTOU (time of check, time of use).
|
||||||
|
|
||||||
This issue can be solved using `fopen("<file>", "w+")`, however this specific behaviour is not intuitive to those first
|
This issue can be solved using `fopen("<file>", "a+")` and `ftell`, however this specific behaviour is not intuitive to
|
||||||
learning how to work with file systems. We can attempt to abstract this away with another wrapper, or simply write
|
those first learning how to work with file systems. We can attempt to abstract this away with another wrapper, or simply
|
||||||
the file structure to handle this behaviour properly. The downside to this method overall is that it will break
|
write the file structure to handle this behaviour properly. The downside to this method overall is that it will break
|
||||||
common conventions of how humans interpret filesystems and the related control flow logic. What we can do is force the
|
common conventions of how humans interpret filesystems and the related control flow logic. What we can do is force the
|
||||||
`'+'` flag to always be present for write operations, and raise an error when desired, if the file is not empty. This
|
`'+'` flag to always be present for write operations, and raise an error when desired, if the file is not empty. This
|
||||||
unfortunately would have the downside of being unable to open a file as write only.
|
unfortunately would have the downside of being unable to open a file as write only.
|
||||||
@@ -127,16 +128,34 @@ We could also declare the file interface extern so that only internal files know
|
|||||||
not be satisfied by doing this since it would prevent developers from implementing custom file type implementations.
|
not be satisfied by doing this since it would prevent developers from implementing custom file type implementations.
|
||||||
|
|
||||||
Conserving memory is not really an issue here as long as we are smart about our implementation. Files should only be
|
Conserving memory is not really an issue here as long as we are smart about our implementation. Files should only be
|
||||||
open when necessary and be closed when it is no longer necessary to have them open.
|
open when necessary and be closed when it is no longer necessary to have them open. Data should be streamed unless the
|
||||||
|
all the data in the file is required.
|
||||||
|
|
||||||
When built in release mode, we also need to pack static assets into some sort of archive that is mountable to reduce
|
When built in release mode, we also need to pack static assets into some sort of archive that is mountable to reduce
|
||||||
disk space consumption of a program. I am considering encryption for archives, but there likely is not much of a point.
|
disk space consumption of a program.
|
||||||
|
|
||||||
|
I was considering encryption for archives, however it does not make much sense. Assuming someone intends to pirate the
|
||||||
|
game, there is not much stopping them from running the files. I will add Steam support at some point which would allow
|
||||||
|
you to use Steam's DRM to prevent the executable from being run. Otherwise, there is no point in attempting to encrypt
|
||||||
|
game files. Even Unreal PAK files can be cracked in seconds, and even if I managed to write something that cannot be
|
||||||
|
trivially cracked locally, you can scrape most assets from the GPU and Audio Card.
|
||||||
|
|
||||||
|
I have managed to solve the specific case provided at the top of this section, which was done by wrapping C I/O calls
|
||||||
|
into a file wrapper. This wrapper can handle a few different mode flags and has specific conditions for the flags. See
|
||||||
|
the documentation for `fennec/fproc/io/file.h` for more info.
|
||||||
|
|
||||||
|
One question remains unanswered on this front; should a read/write file open as `r+` or `a+`. `rewind` is slightly faster
|
||||||
|
than `fseek(SEEK_END)`, however for the case of save files and editor assets, `r+` makes more sense from a usage perspective
|
||||||
|
|
||||||
|
Directories remain an issue, with `dirent.h` being the only sensible option at time of writing. The issue with using
|
||||||
|
`dirent.h` boils back down to security issues on Windows. However, the only option is to write a custom implementation
|
||||||
|
for MSVC.
|
||||||
|
|
||||||
|
|
||||||
## C++ Language Library (`lang`)
|
## C++ Language Library (`lang`)
|
||||||
|
|
||||||
Implement header files for standard functions relating to the C++ Language.
|
Implement header files for standard functions relating to the C++ Language.
|
||||||
|
|
||||||
So far this is implemented on an as-needed basis. A full implementation should be worked on continuously.
|
So far this is implemented on an as-needed basis. A full implementation should be worked on continuously.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -279,8 +279,8 @@ require legal council to consult with them which may dissuade permission to use
|
|||||||
|
|
||||||
I hold a Bachelor's Degree in Computer Science and Real-Time Interactive Simulation from DigiPen Institute of Technology,
|
I hold a Bachelor's Degree in Computer Science and Real-Time Interactive Simulation from DigiPen Institute of Technology,
|
||||||
so I am familiar with their copyright policy. Ask your professor about usage of my engine, and they or someone with
|
so I am familiar with their copyright policy. Ask your professor about usage of my engine, and they or someone with
|
||||||
appropriate standing will reach out to me. Eventually I may reach out on my own terms to negotiate usage of fennec for
|
appropriate standing will reach out to me. Eventually, with interest, I may reach out on my own terms to negotiate usage
|
||||||
educative purposes at DigiPen while retaining their license on student work.
|
of fennec for educative purposes at DigiPen while retaining their license on student work.
|
||||||
|
|
||||||
If your University is not listed here, reach out to your professor for permission. Ask them to reach out to me, I am
|
If your University is not listed here, reach out to your professor for permission. Ask them to reach out to me, I am
|
||||||
willing to work with educational institutes to protect both fennec and student work in accordance to university policy.
|
willing to work with educational institutes to protect both fennec and student work in accordance to university policy.
|
||||||
|
|||||||
@@ -21,40 +21,13 @@
|
|||||||
|
|
||||||
#include <fennec/fproc/strings/string.h>
|
#include <fennec/fproc/strings/string.h>
|
||||||
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
|
|
||||||
namespace fennec
|
namespace fennec
|
||||||
{
|
{
|
||||||
|
|
||||||
inline string getcwd() {
|
string getcwd();
|
||||||
char cstr[MAX_PATH];
|
string absolute(const cstring& path);
|
||||||
if (GetCurrentDirectory(sizeof(str), str) == 0) {
|
string absolute(const string& path);
|
||||||
return string("");
|
|
||||||
}
|
|
||||||
return string(cstr);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <linux/limits.h>
|
|
||||||
|
|
||||||
namespace fennec
|
|
||||||
{
|
|
||||||
|
|
||||||
inline string getcwd() {
|
|
||||||
char cstr[PATH_MAX];
|
|
||||||
if (::getcwd(cstr, sizeof(cstr)) == NULL) {
|
|
||||||
return string("");
|
|
||||||
}
|
|
||||||
return string(cstr);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // FENNEC_FPROC_IO_COMMON_H
|
#endif // FENNEC_FPROC_IO_COMMON_H
|
||||||
|
|||||||
@@ -19,18 +19,6 @@
|
|||||||
#ifndef FENNEC_FPROC_STRINGS_DETAIL_CTYPE_H
|
#ifndef FENNEC_FPROC_STRINGS_DETAIL_CTYPE_H
|
||||||
#define FENNEC_FPROC_STRINGS_DETAIL_CTYPE_H
|
#define FENNEC_FPROC_STRINGS_DETAIL_CTYPE_H
|
||||||
|
|
||||||
#if _MSC_VER
|
|
||||||
#pragma warning(push)
|
|
||||||
#pragma warning(disable:4117)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#pragma push_macro("__cplusplus")
|
|
||||||
#undef __cplusplus
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#pragma pop_macro("__cplusplus")
|
|
||||||
|
|
||||||
#if _MSC_VER
|
|
||||||
#pragma warning(pop)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // FENNEC_FPROC_STRINGS_DETAIL_CTYPE_H
|
#endif // FENNEC_FPROC_STRINGS_DETAIL_CTYPE_H
|
||||||
|
|||||||
@@ -19,77 +19,259 @@
|
|||||||
#ifndef FENNEC_FPROC_IO_FILE_H
|
#ifndef FENNEC_FPROC_IO_FILE_H
|
||||||
#define FENNEC_FPROC_IO_FILE_H
|
#define FENNEC_FPROC_IO_FILE_H
|
||||||
|
|
||||||
|
#include <asm-generic/errno-base.h>
|
||||||
|
|
||||||
#include <fennec/fproc/strings/cstring.h>
|
#include <fennec/fproc/strings/cstring.h>
|
||||||
#include <fennec/fproc/strings/string.h>
|
#include <fennec/fproc/strings/string.h>
|
||||||
|
#include <fennec/fproc/strings/wstring.h>
|
||||||
struct FILE;
|
|
||||||
|
|
||||||
namespace fennec
|
namespace fennec
|
||||||
{
|
{
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Mode flags for opening a file
|
/// \brief Mode flags for opening a file
|
||||||
enum fmode : uint8_t
|
///
|
||||||
|
/// fmode_binary and fmode_wide are independent of the other modes
|
||||||
|
///
|
||||||
|
/// \details Valid Flag Combinations
|
||||||
|
/// <table width="100%" class="fieldtable" id="table_fennec_fproc_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
|
||||||
{
|
{
|
||||||
read = 0b00000001
|
fmode_read = 0b00000001 ///< Opens file for reading
|
||||||
, write = 0b00000010
|
, fmode_write = 0b00000010 ///< Opens file for writing
|
||||||
, append = 0b00000100
|
, fmode_trunc = 0b00000100 ///< Contents of the file will be destroyed, only compatible with write enabled modes
|
||||||
, noexists = 0b00001000
|
, 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
|
class file
|
||||||
{
|
{
|
||||||
public:
|
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) {
|
||||||
|
bool t = mode & fmode_trunc;
|
||||||
|
bool x = mode & fmode_exclusive;
|
||||||
|
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();
|
file();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief default destructor, cleans up an open stream
|
||||||
~file();
|
~file();
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Opens a file at the provided path using the provided mode
|
/// \brief move constructor
|
||||||
/// \param path the path to the provided file
|
/// \param file the stream to take ownership of
|
||||||
/// \param mode the flags for opening the file
|
file(file&& file) noexcept;
|
||||||
file(const cstring& path, fmode mode);
|
|
||||||
file(const string& path, fmode mode);
|
|
||||||
|
|
||||||
file(file&& file);
|
file& operator=(file&& file) noexcept;
|
||||||
|
|
||||||
|
// don't allow copying streams
|
||||||
file(const file&) = delete;
|
file(const file&) = delete;
|
||||||
|
|
||||||
|
|
||||||
|
// Properties ==========================================================================================================
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \returns the path the stream
|
||||||
|
const string& 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 =========================================================================================================
|
// File Access =========================================================================================================
|
||||||
|
|
||||||
void open(const cstring& filename, fmode mode);
|
///
|
||||||
void open(const string& filename, fmode mode);
|
/// \brief open a file
|
||||||
void close();
|
/// \param path the path to the file
|
||||||
void commit();
|
/// \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 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 =====================================================================================================
|
// File Operations =====================================================================================================
|
||||||
|
|
||||||
void erase();
|
///
|
||||||
void rename(const cstring& str);
|
/// \brief closes the stream and erases the file
|
||||||
void rename(const string& str);
|
/// \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 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);
|
||||||
|
|
||||||
|
|
||||||
// File Positioning ====================================================================================================
|
// File Positioning ====================================================================================================
|
||||||
|
|
||||||
size_t get_pos();
|
size_t get_pos() const;
|
||||||
void set_pos(size_t i);
|
bool set_pos(size_t i);
|
||||||
void rewind();
|
bool rewind();
|
||||||
|
bool eof() const;
|
||||||
|
|
||||||
|
|
||||||
|
// Read Operations =====================================================================================================
|
||||||
|
|
||||||
|
char getc();
|
||||||
|
wchar_t getwc();
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
string getline();
|
||||||
|
wstring getwline();
|
||||||
|
|
||||||
|
|
||||||
|
// Write Operations ====================================================================================================
|
||||||
|
|
||||||
|
bool putc(char c);
|
||||||
|
bool putwc(wchar_t c);
|
||||||
|
|
||||||
|
size_t write(const void* data, size_t size, size_t n);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Error Handling ======================================================================================================
|
// Error Handling ======================================================================================================
|
||||||
|
|
||||||
bool check_error();
|
const char* get_error() const { return _error; }
|
||||||
string get_error();
|
void clear_error() { _error = nullptr; }
|
||||||
void clear_error();
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FILE* _handle;
|
FILE* _handle;
|
||||||
string _path;
|
string _path;
|
||||||
fmode _mode;
|
uint8_t _mode;
|
||||||
|
char* _error;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,18 +19,7 @@
|
|||||||
#ifndef FENNEC_FPROC_STRINGS_DETAIL_CTYPE_H
|
#ifndef FENNEC_FPROC_STRINGS_DETAIL_CTYPE_H
|
||||||
#define FENNEC_FPROC_STRINGS_DETAIL_CTYPE_H
|
#define FENNEC_FPROC_STRINGS_DETAIL_CTYPE_H
|
||||||
|
|
||||||
#if _MSC_VER
|
|
||||||
#pragma warning(push)
|
|
||||||
#pragma warning(disable:4117)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#pragma push_macro("__cplusplus")
|
|
||||||
#undef __cplusplus
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#pragma pop_macro("__cplusplus")
|
#include <wctype.h>
|
||||||
|
|
||||||
#if _MSC_VER
|
|
||||||
#pragma warning(pop)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // FENNEC_FPROC_STRINGS_DETAIL_CTYPE_H
|
#endif // FENNEC_FPROC_STRINGS_DETAIL_CTYPE_H
|
||||||
|
|||||||
@@ -19,18 +19,6 @@
|
|||||||
#ifndef FENNEC_FPROC_STRINGS_DETAIL_CTYPE_H
|
#ifndef FENNEC_FPROC_STRINGS_DETAIL_CTYPE_H
|
||||||
#define FENNEC_FPROC_STRINGS_DETAIL_CTYPE_H
|
#define FENNEC_FPROC_STRINGS_DETAIL_CTYPE_H
|
||||||
|
|
||||||
#if _MSC_VER
|
|
||||||
#pragma warning(push)
|
|
||||||
#pragma warning(disable:4117)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#pragma push_macro("__cplusplus")
|
|
||||||
#undef __cplusplus
|
|
||||||
#include <locale.h>
|
#include <locale.h>
|
||||||
#pragma pop_macro("__cplusplus")
|
|
||||||
|
|
||||||
#if _MSC_VER
|
|
||||||
#pragma warning(pop)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // FENNEC_FPROC_STRINGS_DETAIL_CTYPE_H
|
#endif // FENNEC_FPROC_STRINGS_DETAIL_CTYPE_H
|
||||||
|
|||||||
@@ -75,7 +75,33 @@ public:
|
|||||||
/// \details adds additional character for null termination.
|
/// \details adds additional character for null termination.
|
||||||
constexpr _string(char c, size_t n)
|
constexpr _string(char c, size_t n)
|
||||||
: _str(n + 1) {
|
: _str(n + 1) {
|
||||||
fennec::memset(_str, c, n); _str[n] = '\0';
|
fennec::memset(_str.data(), c, n); _str[n] = '\0';
|
||||||
|
}
|
||||||
|
///
|
||||||
|
/// \brief Buffer Copy Constructor
|
||||||
|
/// \param str the buffer to copy
|
||||||
|
/// \param len number of characters in the buffer
|
||||||
|
///
|
||||||
|
/// \details adds additional character for null termination. Ignores whether str is null-terminated.
|
||||||
|
/// This constructor makes the assumption that `len` is the intended number of characters.
|
||||||
|
template<size_t n>
|
||||||
|
constexpr _string(const char str[n])
|
||||||
|
: _str(str, n + 1) {
|
||||||
|
::strncpy(_str.data(), str, n);
|
||||||
|
_str[n] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Buffer Copy Constructor
|
||||||
|
/// \param str the buffer to copy
|
||||||
|
/// \param len number of characters in the buffer
|
||||||
|
///
|
||||||
|
/// \details adds additional character for null termination. Ignores whether str is null-terminated.
|
||||||
|
/// This constructor makes the assumption that `len` is the intended number of characters.
|
||||||
|
constexpr _string(const char* str, size_t n)
|
||||||
|
: _str(str, n + 1) {
|
||||||
|
::strncpy(_str.data(), str, n);
|
||||||
|
_str[n] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
@@ -94,7 +120,7 @@ public:
|
|||||||
/// \brief String Copy Constructor
|
/// \brief String Copy Constructor
|
||||||
/// \param str the string to copy
|
/// \param str the string to copy
|
||||||
constexpr _string(const string& str)
|
constexpr _string(const string& str)
|
||||||
: _string(str, str.size() - 1) {
|
: _string(str, str.size()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr _string(_string&& str) noexcept
|
constexpr _string(_string&& str) noexcept
|
||||||
@@ -112,6 +138,10 @@ public:
|
|||||||
return _str.capacity() - 1;
|
return _str.capacity() - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr bool empty() const {
|
||||||
|
return size() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Access ==============================================================================================================
|
// Access ==============================================================================================================
|
||||||
|
|
||||||
@@ -187,16 +217,16 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Finds the index of the first occurrence of `x` in the string
|
/// \brief Finds the index of the first occurrence of `c` in the string
|
||||||
/// \param x the character to find
|
/// \param c the character to find
|
||||||
/// \returns The index of `x` if it occurs in the string, otherwise returns `size()`
|
/// \returns The index of `c` if it occurs in the string, otherwise returns `size()`
|
||||||
constexpr size_t find(char c, size_t i = 0) const {
|
constexpr size_t find(char c, size_t i = 0) const {
|
||||||
if (i >= size()) { // bounds check
|
if (i >= size()) { // bounds check
|
||||||
return size();
|
return size();
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* loc = ::strchr(_str + i, c); // get location using strchr
|
const char* loc = ::strchr(_str.data() + i, c); // get location using strchr
|
||||||
return loc ? loc - _str : size(); // return size if not found
|
return loc ? loc - _str.data() : size(); // return size if not found
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
@@ -293,7 +323,18 @@ public:
|
|||||||
constexpr void resize(size_t n) {
|
constexpr void resize(size_t n) {
|
||||||
size_t i = size();
|
size_t i = size();
|
||||||
_str.reallocate(n + 1);
|
_str.reallocate(n + 1);
|
||||||
if (n > i) fennec::memset(*_str + i, '\0', n - i);
|
if (n > i) fennec::memset(_str.data() + i, '\0', n - i);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Copy Assignment Operator
|
||||||
|
/// \param str the string to copy
|
||||||
|
/// \returns a reference to `this`
|
||||||
|
constexpr string& operator=(const cstring& str) {
|
||||||
|
resize(str.size());
|
||||||
|
fennec::memcpy(_str.data(), str, str.size());
|
||||||
|
_str[str.size()] = '\0';
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
@@ -301,8 +342,8 @@ public:
|
|||||||
/// \param str the string to copy
|
/// \param str the string to copy
|
||||||
/// \returns a reference to `this`
|
/// \returns a reference to `this`
|
||||||
constexpr string& operator=(const string& str) {
|
constexpr string& operator=(const string& str) {
|
||||||
if (str.size() > size()) resize(str.size());
|
resize(str.size());
|
||||||
fennec::memcpy(_str, str, str.size());
|
fennec::memcpy(_str.data(), str, str.size());
|
||||||
_str[str.size()] = '\0';
|
_str[str.size()] = '\0';
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
@@ -321,10 +362,12 @@ public:
|
|||||||
/// \param i the start index
|
/// \param i the start index
|
||||||
/// \param n the number of characters
|
/// \param n the number of characters
|
||||||
/// \return
|
/// \return
|
||||||
constexpr string substring(size_t i, size_t n = size()) const {
|
constexpr string substring(size_t i, size_t n = npos) const {
|
||||||
assert(i < size(), "Array Out of Bounds");
|
if (i >= size()) {
|
||||||
|
return string("");
|
||||||
|
}
|
||||||
n = min(n, size() - i);
|
n = min(n, size() - i);
|
||||||
return string(&_str[i], n);
|
return string(_str.data() + i, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
@@ -340,14 +383,14 @@ public:
|
|||||||
|
|
||||||
constexpr string operator+(const cstring& str) const {
|
constexpr string operator+(const cstring& str) const {
|
||||||
string res(size() + str.size()); // Make a new string with the size of this + str
|
string res(size() + str.size()); // Make a new string with the size of this + str
|
||||||
fennec::memcpy(res, _str, size()); // Copy the contents of this
|
fennec::memcpy(&res[0], _str.data(), size()); // Copy the contents of this
|
||||||
fennec::memcpy(&res[size()], str, str.size()); // Append the contents of str
|
fennec::memcpy(&res[size()], str, str.size()); // Append the contents of str
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr string operator+(const string& str) const {
|
constexpr string operator+(const string& str) const {
|
||||||
string res(size() + str.size()); // Make a new string with the size of this + str
|
string res(size() + str.size()); // Make a new string with the size of this + str
|
||||||
fennec::memcpy(res, _str, size()); // Copy the contents of this
|
fennec::memcpy(&res[0], _str.data(), size()); // Copy the contents of this
|
||||||
fennec::memcpy(&res[size()], str, str.size()); // Append the contents of str
|
fennec::memcpy(&res[size()], str, str.size()); // Append the contents of str
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|||||||
309
include/fennec/fproc/strings/wcstring.h
Normal file
309
include/fennec/fproc/strings/wcstring.h
Normal file
@@ -0,0 +1,309 @@
|
|||||||
|
// =====================================================================================================================
|
||||||
|
// 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_FPROC_STRINGS_wcstring_H
|
||||||
|
#define FENNEC_FPROC_STRINGS_wcstring_H
|
||||||
|
|
||||||
|
#include <fennec/fproc/strings/detail/__ctype.h>
|
||||||
|
#include <fennec/memory/detail/__string.h>
|
||||||
|
|
||||||
|
#include <fennec/lang/assert.h>
|
||||||
|
|
||||||
|
#include <fennec/math/common.h>
|
||||||
|
|
||||||
|
namespace fennec
|
||||||
|
{
|
||||||
|
|
||||||
|
// TODO: Document
|
||||||
|
|
||||||
|
using ::iswalnum;
|
||||||
|
using ::iswalpha;
|
||||||
|
using ::iswlower;
|
||||||
|
using ::iswupper;
|
||||||
|
using ::iswdigit;
|
||||||
|
using ::iswxdigit;
|
||||||
|
using ::iswcntrl;
|
||||||
|
using ::iswgraph;
|
||||||
|
using ::iswspace;
|
||||||
|
using ::iswblank;
|
||||||
|
using ::iswprint;
|
||||||
|
using ::iswpunct;
|
||||||
|
|
||||||
|
using ::towlower;
|
||||||
|
using ::towupper;
|
||||||
|
|
||||||
|
using ::towctrans;
|
||||||
|
using ::wctrans;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief This struct wraps c-style strings
|
||||||
|
///
|
||||||
|
/// \details Requires that the string is null-terminated.
|
||||||
|
/// Prevents const qualified memory blocks from being manipulated.
|
||||||
|
/// This struct should be used when fennec::string would make unnecessary dynamic buffers.
|
||||||
|
struct wcstring
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static constexpr size_t npos = -1;
|
||||||
|
|
||||||
|
// Constructors ========================================================================================================
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Default Constructor, initializes with nullptr
|
||||||
|
constexpr wcstring()
|
||||||
|
: _str(nullptr), _size(0), _const(true) {
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Buffer Constructor, wraps the provided C-Style string
|
||||||
|
/// \param str the buffer to wrap
|
||||||
|
/// \param n the number of characters in the buffer plus the null terminator
|
||||||
|
constexpr wcstring(wchar_t* str, size_t n)
|
||||||
|
: _str(str)
|
||||||
|
, _size(n - 1)
|
||||||
|
, _const(false) {
|
||||||
|
assert(_str[n - 1] == '\0', "Invalid NTBS.");
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Buffer Constructor, wraps the provided C-Style string
|
||||||
|
/// \param str the buffer to wrap
|
||||||
|
/// \tparam n the number of characters in the buffer plus the null terminator
|
||||||
|
template<size_t n>
|
||||||
|
constexpr wcstring(wchar_t(&str)[n])
|
||||||
|
: _str(str)
|
||||||
|
, _size(n - 1)
|
||||||
|
, _const(false) {
|
||||||
|
assert(_str[n - 1] == '\0', "Invalid NTBS.");
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Const Buffer Constructor, wraps the provided C-Style string
|
||||||
|
/// \param str the buffer to wrap
|
||||||
|
/// \param n the number of characters in the buffer plus the null terminator
|
||||||
|
constexpr wcstring(const wchar_t* str, size_t n)
|
||||||
|
: _cstr(str)
|
||||||
|
, _size(n - 1)
|
||||||
|
, _const(true) {
|
||||||
|
assert(_cstr[n - 1] == '\0', "Invalid NTBS.");
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Buffer Constructor, wraps the provided C-Style string
|
||||||
|
/// \param str the buffer to wrap
|
||||||
|
/// \tparam n the number of characters in the buffer plus the null terminator
|
||||||
|
template<size_t n>
|
||||||
|
constexpr wcstring(const wchar_t(&str)[n])
|
||||||
|
: _cstr(str)
|
||||||
|
, _size(n - 1)
|
||||||
|
, _const(true) {
|
||||||
|
assert(_cstr[n - 1] == '\0', "Invalid NTBS.");
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Move Constructor
|
||||||
|
/// \param str object to move
|
||||||
|
constexpr wcstring(wcstring&& str) noexcept
|
||||||
|
: _cstr(str._cstr)
|
||||||
|
, _size(str._size)
|
||||||
|
, _const(str._const) {
|
||||||
|
str._cstr = nullptr;
|
||||||
|
str._size = 0;
|
||||||
|
str._const = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Document
|
||||||
|
constexpr wcstring(const wcstring&) = delete;
|
||||||
|
constexpr ~wcstring() = default;
|
||||||
|
|
||||||
|
// TODO: Document
|
||||||
|
template<size_t n>
|
||||||
|
constexpr wcstring& operator=(wchar_t(&str)[n]) {
|
||||||
|
assert(_str[n - 1] == '\0', "Invalid NTBS.");
|
||||||
|
_str = str; _size = n - 1; _const = false;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Document
|
||||||
|
template<size_t n>
|
||||||
|
constexpr wcstring& operator=(const wchar_t(&str)[n]) {
|
||||||
|
assert(str[n - 1] == '\0', "Invalid NTBS.");
|
||||||
|
_cstr = str; _size = n - 1; _const = true;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Document
|
||||||
|
constexpr wcstring& operator=(wcstring&& str) noexcept {
|
||||||
|
_cstr = str._cstr; str._cstr = nullptr;
|
||||||
|
_size = str._size; str._size = 0;
|
||||||
|
_const = str._const; str._const = true;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Properties ==========================================================================================================
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \returns the size of the string excluding its null terminator, i.e. `(*str)[size()] == '\0'`
|
||||||
|
constexpr size_t size() const { return _size; }
|
||||||
|
|
||||||
|
|
||||||
|
// Access ==============================================================================================================
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Array Access Operator
|
||||||
|
/// \param i the index to access
|
||||||
|
/// \returns a reference to the character
|
||||||
|
constexpr wchar_t& operator[](size_t i) {
|
||||||
|
assertd(not _const, "Attempted to Access Const-Qualified Memory as Non-Const");
|
||||||
|
assertd(i < size(), "Array Out of Bounds");
|
||||||
|
return _str[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Const-Array Access Operator
|
||||||
|
/// \param i the index to access
|
||||||
|
/// \returns a copy of the character
|
||||||
|
constexpr wchar_t operator[](size_t i) const {
|
||||||
|
assertd(i < size(), "Array Out of Bounds");
|
||||||
|
return _cstr[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Dereference Operator
|
||||||
|
/// \returns A const qualified pointer to the underlying allocation
|
||||||
|
constexpr const wchar_t* operator*() const {
|
||||||
|
return _cstr;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Implicit Dereference Cast
|
||||||
|
constexpr operator const wchar_t*() const {
|
||||||
|
return _cstr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Examination =========================================================================================================
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \returns The length of the string to the first null-terminator
|
||||||
|
constexpr size_t length() const {
|
||||||
|
return find(L'\0');
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief String Comparison
|
||||||
|
/// \param str the string to compare against
|
||||||
|
/// \returns Zero if both strings are equal, otherwise a negative value if lhs appears before rhs according to the
|
||||||
|
/// current locale, otherwise a positive value.
|
||||||
|
constexpr int compare(const wcstring& str, size_t i = 0, size_t n = npos) const {
|
||||||
|
if (i >= _size) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
n = min(n, max(_size, str._size) + 1);
|
||||||
|
return ::wcsncmp(_cstr + i, str, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief String Equality
|
||||||
|
/// \param str the string to compare against
|
||||||
|
/// \returns True if all characters are equal, false otherwise
|
||||||
|
constexpr bool operator==(const wcstring& str) const {
|
||||||
|
return compare(str) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Finds the index of the first occurrence of `c` in the string
|
||||||
|
/// \param c the character to find
|
||||||
|
/// \param i the index to start at
|
||||||
|
/// \returns The index of `c` if it occurs in the string, otherwise returns `size()`
|
||||||
|
constexpr size_t find(wchar_t c, size_t i = 0) const {
|
||||||
|
if (i >= _size) { // bounds check
|
||||||
|
return _size;
|
||||||
|
}
|
||||||
|
|
||||||
|
const wchar_t* loc = ::wcschr(_cstr + i, c); // get location using strchr
|
||||||
|
return loc ? loc - _cstr : _size; // return size if not found
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Finds the index of the first occurrence of `str` in the string.
|
||||||
|
/// \param str the string to find
|
||||||
|
/// \param i the index to start at
|
||||||
|
/// \returns The index of `str` if it occurs in the string, otherwise returns `size()`
|
||||||
|
constexpr size_t find(const wcstring& str, size_t i = 0) const {
|
||||||
|
if (i + str._size > _size) { // bounds check
|
||||||
|
return _size;
|
||||||
|
}
|
||||||
|
|
||||||
|
const wchar_t* loc = ::wcsstr(_cstr + i, str); // get location using strstr
|
||||||
|
return loc ? loc - _cstr : _size; // return size if not found
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Finds the index of the last occurrence of `c` in the string.
|
||||||
|
/// \param c the string to find
|
||||||
|
/// \param i the index to start at
|
||||||
|
/// \returns The index of `c` if it occurs in the string, otherwise returns `size()`
|
||||||
|
constexpr size_t rfind(wchar_t c, size_t i = npos) const {
|
||||||
|
if (_size == 0) {
|
||||||
|
return _size;
|
||||||
|
}
|
||||||
|
|
||||||
|
i = min(i, _size - 1); // clamp i to bounds
|
||||||
|
do {
|
||||||
|
if (_cstr[i] == c) return i; // loop backwards looking for c
|
||||||
|
} while (i--);
|
||||||
|
return _size; // base case
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Finds the index of the last occurrence of `str` in the string.
|
||||||
|
/// \param str the string to find
|
||||||
|
/// \param i the index to start at
|
||||||
|
/// \returns The index of `str` if it occurs in the string, otherwise returns `size()`
|
||||||
|
constexpr size_t rfind(const wcstring& str, size_t i = npos) const {
|
||||||
|
if (_size == 0) {
|
||||||
|
return _size;
|
||||||
|
}
|
||||||
|
|
||||||
|
const wchar_t first = str[0];
|
||||||
|
i = min(i, _size - str._size);
|
||||||
|
do {
|
||||||
|
if(_cstr[i] == first) {
|
||||||
|
if (compare(str, i, str._size - 1) == 0) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
} // loop backwards looking for str
|
||||||
|
} while (i--);
|
||||||
|
return _size; // base case
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
union { // hack to allow both const qualified and non-const strings
|
||||||
|
wchar_t* _str;
|
||||||
|
const wchar_t* _cstr;
|
||||||
|
};
|
||||||
|
size_t _size;
|
||||||
|
bool _const;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // FENNEC_FPROC_STRINGS_wcstring_H
|
||||||
426
include/fennec/fproc/strings/wstring.h
Normal file
426
include/fennec/fproc/strings/wstring.h
Normal file
@@ -0,0 +1,426 @@
|
|||||||
|
// =====================================================================================================================
|
||||||
|
// 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_FPROC_wstringS_wstring_H
|
||||||
|
#define FENNEC_FPROC_wstringS_wstring_H
|
||||||
|
|
||||||
|
#include <fennec/fproc/strings/detail/__ctype.h>
|
||||||
|
#include <fennec/fproc/strings/wcstring.h>
|
||||||
|
|
||||||
|
#include <fennec/lang/assert.h>
|
||||||
|
|
||||||
|
#include <fennec/memory/allocator.h>
|
||||||
|
#include <fennec/memory/common.h>
|
||||||
|
|
||||||
|
#include <fennec/math/common.h>
|
||||||
|
|
||||||
|
namespace fennec
|
||||||
|
{
|
||||||
|
|
||||||
|
// Forward def
|
||||||
|
template<typename AllocT = allocator<wchar_t>> struct _wstring;
|
||||||
|
|
||||||
|
// Alias for default allocator
|
||||||
|
using wstring = _wstring<>;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Struct for wrapping c-style strings
|
||||||
|
///
|
||||||
|
/// \details behaviour guarantees that the underlying string is null-terminated
|
||||||
|
template<typename AllocT>
|
||||||
|
struct _wstring
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static constexpr size_t npos = -1;
|
||||||
|
using alloc_t = allocation<wchar_t, AllocT>;
|
||||||
|
|
||||||
|
|
||||||
|
// Constructors ========================================================================================================
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Default Constructor, initializes empty string
|
||||||
|
constexpr _wstring()
|
||||||
|
: _str() {
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Sized Constructor, initializes a null-terminated string of size `n` with null wchar_tacters
|
||||||
|
/// \param n the number of wchar_tacters
|
||||||
|
///
|
||||||
|
/// \details adds additional wchar_tacter for null termination.
|
||||||
|
constexpr _wstring(size_t n)
|
||||||
|
: _wstring('\0', n) {
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Sized Constructor, initializes a null-terminated string of size `n` filled with the wchar_tacter `c`
|
||||||
|
/// \param c the wchar_tacter to fill with
|
||||||
|
/// \param n the number of wchar_tacters
|
||||||
|
///
|
||||||
|
/// \details adds additional wchar_tacter for null termination.
|
||||||
|
constexpr _wstring(wchar_t c, size_t n)
|
||||||
|
: _str(n + 1) {
|
||||||
|
fennec::wmemset(_str.data(), c, n); _str[n] = '\0';
|
||||||
|
}
|
||||||
|
///
|
||||||
|
/// \brief Buffer Copy Constructor
|
||||||
|
/// \param str the buffer to copy
|
||||||
|
/// \param len number of wchar_tacters in the buffer
|
||||||
|
///
|
||||||
|
/// \details adds additional wchar_tacter for null termination. Ignores whether str is null-terminated.
|
||||||
|
/// This constructor makes the assumption that `len` is the intended number of wchar_tacters.
|
||||||
|
template<size_t n>
|
||||||
|
constexpr _wstring(const wchar_t str[n])
|
||||||
|
: _str(str, n + 1) {
|
||||||
|
::wcsncpy(_str.data(), str, n);
|
||||||
|
_str[n] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Buffer Copy Constructor
|
||||||
|
/// \param str the buffer to copy
|
||||||
|
/// \param len number of wchar_tacters in the buffer
|
||||||
|
///
|
||||||
|
/// \details adds additional wchar_tacter for null termination. Ignores whether str is null-terminated.
|
||||||
|
/// This constructor makes the assumption that `len` is the intended number of wchar_tacters.
|
||||||
|
constexpr _wstring(const wchar_t* str, size_t n)
|
||||||
|
: _str(str, n + 1) {
|
||||||
|
::wcsncpy(_str.data(), str, n);
|
||||||
|
_str[n] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Buffer Copy Constructor
|
||||||
|
/// \param str the buffer to copy
|
||||||
|
/// \param len number of wchar_tacters in the buffer
|
||||||
|
///
|
||||||
|
/// \details adds additional wchar_tacter for null termination. Ignores whether str is null-terminated.
|
||||||
|
/// This constructor makes the assumption that `len` is the intended number of wchar_tacters.
|
||||||
|
constexpr _wstring(const wcstring& str)
|
||||||
|
: _str(str, str.size() + 1) {
|
||||||
|
_str[str.size()] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief String Copy Constructor
|
||||||
|
/// \param str the string to copy
|
||||||
|
constexpr _wstring(const wstring& str)
|
||||||
|
: _wstring(str, str.size() - 1) {
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr _wstring(_wstring&& str) noexcept
|
||||||
|
: _str(fennec::move(str._str)) { }
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief String Destructor, cleans up the underlying allocation
|
||||||
|
constexpr ~_wstring() = default; // allocation cleans up itself
|
||||||
|
|
||||||
|
// Properties ==========================================================================================================
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \returns The size of the string excluding null terminator
|
||||||
|
constexpr size_t size() const {
|
||||||
|
return _str.capacity() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool empty() const {
|
||||||
|
return size() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Access ==============================================================================================================
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Array Access Operator
|
||||||
|
/// \param i the index to access
|
||||||
|
/// \returns a reference to the wchar_tacter
|
||||||
|
constexpr wchar_t& operator[](int i) {
|
||||||
|
return _str[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Const-Array Access Operator
|
||||||
|
/// \param i the index to access
|
||||||
|
/// \returns a copy of the wchar_tacter
|
||||||
|
constexpr wchar_t operator[](int i) const {
|
||||||
|
assertd(i >= 0 && i < size(), "Array Out of Bounds");
|
||||||
|
return _str[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Dereference Operator
|
||||||
|
/// \returns A const qualified pointer to the underlying allocation
|
||||||
|
constexpr const wchar_t* operator*() const {
|
||||||
|
return _str.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Implicit Dereference Cast
|
||||||
|
constexpr operator const wchar_t*() const {
|
||||||
|
return _str.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Examination =========================================================================================================
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \returns The length of the string to the first null-terminator
|
||||||
|
constexpr size_t length() const {
|
||||||
|
return find('\0');
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief String Comparison
|
||||||
|
/// \param ostr the string to compare against
|
||||||
|
/// \returns Zero if both strings are equal, otherwise a negative value if lhs appears before rhs according to the
|
||||||
|
/// current locale, otherwise a positive value.
|
||||||
|
constexpr int compare(const wcstring& str, size_t i = 0, size_t n = npos) const {
|
||||||
|
if (i >= size()) { // bounds check
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
n = fennec::min(n, fennec::max(_str, str.size()) + 1);
|
||||||
|
|
||||||
|
return ::wcsncmp(_str.data() + i, str, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief String Comparison
|
||||||
|
/// \param ostr the string to compare against
|
||||||
|
/// \returns Zero if both strings are equal, otherwise a negative value if lhs appears before rhs according to the
|
||||||
|
/// current locale, otherwise a positive value.
|
||||||
|
constexpr int compare(const wstring& str, size_t i = 0, size_t n = npos) const {
|
||||||
|
if (i >= size()) { // bounds check
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
n = min(n, max(size(), str.size()) + 1);
|
||||||
|
|
||||||
|
return ::wcsncmp(_str.data() + i, str, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool operator==(const _wstring& str) const {
|
||||||
|
return compare(str) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Finds the index of the first occurrence of `c` in the string
|
||||||
|
/// \param c the wchar_tacter to find
|
||||||
|
/// \returns The index of `c` if it occurs in the string, otherwise returns `size()`
|
||||||
|
constexpr size_t find(wchar_t c, size_t i = 0) const {
|
||||||
|
if (i >= size()) { // bounds check
|
||||||
|
return size();
|
||||||
|
}
|
||||||
|
|
||||||
|
const wchar_t* loc = ::wcschr(_str.data() + i, c); // get location using strchr
|
||||||
|
return loc ? loc - _str.data() : size(); // return size if not found
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Finds the index of the first occurrence of `str` in the string.
|
||||||
|
/// \param str the string to find
|
||||||
|
/// \returns The index of `str` if it occurs in the string, otherwise returns `size()`
|
||||||
|
constexpr size_t find(const wstring& str, size_t i = 0) const { // bounds check
|
||||||
|
if (i >= size()) { // bounds check
|
||||||
|
return size();
|
||||||
|
}
|
||||||
|
|
||||||
|
const wchar_t* loc = ::wcsstr(_str, str);
|
||||||
|
return loc ? loc - _str : size();
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Finds the index of the first occurrence of `str` in the string.
|
||||||
|
/// \param str the string to find
|
||||||
|
/// \returns The index of `str` if it occurs in the string, otherwise returns `size()`
|
||||||
|
constexpr size_t find(const wcstring& str, size_t i = 0) const {
|
||||||
|
if (i + str.size() > size()) { // bounds check
|
||||||
|
return size();
|
||||||
|
}
|
||||||
|
|
||||||
|
const wchar_t* loc = ::wcsstr(_str + i, str); // get location using strstr
|
||||||
|
return loc ? loc - _str : size(); // return size if not found
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Finds the index of the last occurrence of `c` in the string.
|
||||||
|
/// \param c the string to find
|
||||||
|
/// \param i the index to start at
|
||||||
|
/// \returns The index of `c` if it occurs in the string, otherwise returns `size()`
|
||||||
|
constexpr size_t rfind(wchar_t c, size_t i = npos) const {
|
||||||
|
if (size() == 0) {
|
||||||
|
return size();
|
||||||
|
}
|
||||||
|
i = min(i, size() - 1); // clamp i to bounds
|
||||||
|
do {
|
||||||
|
if (_str[i] == c) { // loop backwards looking for c
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
} while (i--);
|
||||||
|
return size(); // base case
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Finds the index of the last occurrence of `str` in the string.
|
||||||
|
/// \param str the string to find
|
||||||
|
/// \param i the index to start at
|
||||||
|
/// \returns The index of `str` if it occurs in the string, otherwise returns `size()`
|
||||||
|
constexpr size_t rfind(const wcstring& str, size_t i = npos) const {
|
||||||
|
if (size() == 0) {
|
||||||
|
return size();
|
||||||
|
}
|
||||||
|
const wchar_t first = str[0];
|
||||||
|
i = min(i, size() - str.size());
|
||||||
|
do {
|
||||||
|
if(_str[i] == first) {
|
||||||
|
if (compare(str, i) == 0) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (i--);
|
||||||
|
return size(); // base case
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Finds the index of the last occurrence of `str` in the string.
|
||||||
|
/// \param str the string to find
|
||||||
|
/// \param i the index to start at
|
||||||
|
/// \returns The index of `str` if it occurs in the string, otherwise returns `size()`
|
||||||
|
constexpr size_t rfind(const wstring& str, size_t i = npos) const {
|
||||||
|
if (size() == 0) {
|
||||||
|
return size();
|
||||||
|
}
|
||||||
|
const wchar_t first = str[0];
|
||||||
|
i = min(i, size() - str.size());
|
||||||
|
do {
|
||||||
|
if(_str[i] == first) {
|
||||||
|
if (compare(str, i) == 0) { // loop backwards looking for str
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (i--);
|
||||||
|
return size(); // base case
|
||||||
|
}
|
||||||
|
|
||||||
|
// Manipulation ========================================================================================================
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Resize the string, filling additional bytes with `'\0'`
|
||||||
|
/// \param n the new size of the string
|
||||||
|
constexpr void resize(size_t n) {
|
||||||
|
size_t i = size();
|
||||||
|
_str.reallocate(n + 1);
|
||||||
|
if (n > i) fennec::wmemset(_str.data() + i, L'\0', n - i);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Copy Assignment Operator
|
||||||
|
/// \param str the string to copy
|
||||||
|
/// \returns a reference to `this`
|
||||||
|
constexpr wstring& operator=(const wcstring& str) {
|
||||||
|
if (str.size() > size()) resize(str.size());
|
||||||
|
fennec::wmemcpy(_str.data(), str, str.size());
|
||||||
|
_str[str.size()] = L'\0';
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Copy Assignment Operator
|
||||||
|
/// \param str the string to copy
|
||||||
|
/// \returns a reference to `this`
|
||||||
|
constexpr wstring& operator=(const wstring& str) {
|
||||||
|
if (str.size() > size()) resize(str.size());
|
||||||
|
fennec::wmemcpy(_str.data(), str, str.size());
|
||||||
|
_str[str.size()] = '\0';
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Move Assignment Operator
|
||||||
|
/// \param str the string to move
|
||||||
|
/// \returns a reference to `this`
|
||||||
|
constexpr wstring& operator=(wstring&& str) noexcept {
|
||||||
|
_str = fennec::move(str._str);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Retrieve a substring of a string
|
||||||
|
/// \param i the start index
|
||||||
|
/// \param n the number of wchar_tacters
|
||||||
|
/// \return
|
||||||
|
constexpr wstring substring(size_t i, size_t n = npos) const {
|
||||||
|
if (i >= size()) {
|
||||||
|
return wstring("");
|
||||||
|
}
|
||||||
|
n = min(n, size() - i);
|
||||||
|
return wstring(_str.data() + i, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Returns a string with `c` appended to it
|
||||||
|
/// \param c
|
||||||
|
/// \returns
|
||||||
|
constexpr wstring operator+(wchar_t c) const {
|
||||||
|
// Copy contents with one additional byte.
|
||||||
|
wstring res(_str, _str.size());
|
||||||
|
res[size()] = c; // Set the last wchar_tacter to c
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr wstring operator+(const wcstring& str) const {
|
||||||
|
wstring res(size() + str.size()); // Make a new string with the size of this + str
|
||||||
|
fennec::wmemcpy(&res[0], _str.data(), size()); // Copy the contents of this
|
||||||
|
fennec::wmemcpy(&res[size()], str, str.size()); // Append the contents of str
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr wstring operator+(const wstring& str) const {
|
||||||
|
wstring res(size() + str.size()); // Make a new string with the size of this + str
|
||||||
|
fennec::wmemcpy(&res[0], _str.data(), size()); // Copy the contents of this
|
||||||
|
fennec::wmemcpy(&res[size()], str, str.size()); // Append the contents of str
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr wstring& operator+=(wchar_t c) {
|
||||||
|
size_t x = size();
|
||||||
|
resize(x + 1);
|
||||||
|
_str[x] = c;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr wstring& operator+=(const wcstring& str) {
|
||||||
|
size_t x = size();
|
||||||
|
resize(x + str.size());
|
||||||
|
fennec::wmemcpy(&_str[x], str, str.size());
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr wstring& operator+=(const wstring& str) {
|
||||||
|
size_t x = size();
|
||||||
|
resize(x + str.size());
|
||||||
|
fennec::wmemcpy(&_str[x], str, str.size());
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
alloc_t _str;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif // FENNEC_FPROC_wstringS_wstring_H
|
||||||
@@ -26,14 +26,7 @@
|
|||||||
#define __PTRDIFF_TYPE__ ptrdiff_t
|
#define __PTRDIFF_TYPE__ ptrdiff_t
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#pragma push_macro("__cplusplus")
|
|
||||||
#undef __cplusplus
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#pragma pop_macro("__cplusplus")
|
|
||||||
|
|
||||||
#if _MSC_VER
|
|
||||||
#pragma warning(pop)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // FENNEC_LANG_DETAIL_INT_H
|
#endif // FENNEC_LANG_DETAIL_INT_H
|
||||||
|
|||||||
@@ -19,20 +19,6 @@
|
|||||||
#ifndef FENNEC_LANG_DETAIL_STDLIB_H
|
#ifndef FENNEC_LANG_DETAIL_STDLIB_H
|
||||||
#define FENNEC_LANG_DETAIL_STDLIB_H
|
#define FENNEC_LANG_DETAIL_STDLIB_H
|
||||||
|
|
||||||
#if _MSC_VER
|
|
||||||
#pragma warning(push)
|
|
||||||
#pragma warning(disable:4117)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#pragma push_macro("__cplusplus")
|
|
||||||
#undef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
}
|
|
||||||
#pragma pop_macro("__cplusplus")
|
|
||||||
|
|
||||||
#if _MSC_VER
|
|
||||||
#pragma warning(pop)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // FENNEC_LANG_DETAIL_STDLIB_H
|
#endif // FENNEC_LANG_DETAIL_STDLIB_H
|
||||||
|
|||||||
@@ -19,20 +19,11 @@
|
|||||||
#ifndef FENNEC_MATH_DETAIL_MATH_H
|
#ifndef FENNEC_MATH_DETAIL_MATH_H
|
||||||
#define FENNEC_MATH_DETAIL_MATH_H
|
#define FENNEC_MATH_DETAIL_MATH_H
|
||||||
|
|
||||||
#if _MSC_VER
|
|
||||||
#pragma warning(push)
|
|
||||||
#pragma warning(disable:4117)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#pragma push_macro("__cplusplus")
|
#pragma push_macro("__cplusplus")
|
||||||
#undef __cplusplus
|
#undef __cplusplus
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#pragma pop_macro("__cplusplus")
|
#pragma pop_macro("__cplusplus")
|
||||||
|
|
||||||
#if _MSC_VER
|
|
||||||
#pragma warning(pop)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#undef div
|
#undef div
|
||||||
#undef acos
|
#undef acos
|
||||||
#undef asin
|
#undef asin
|
||||||
|
|||||||
@@ -691,8 +691,4 @@ template<typename genType> constexpr genType log_phi() { return
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if _MSC_VER
|
|
||||||
#pragma warning(pop)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // FENNEC_MATH_EXT_CONSTANTS_H
|
#endif // FENNEC_MATH_EXT_CONSTANTS_H
|
||||||
|
|||||||
@@ -615,8 +615,4 @@ struct vector_storage<4, SwizzleGenT, DataT>
|
|||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if _MSC_VER
|
|
||||||
#pragma warning(pop)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // FENNEC_MATH_VECTOR_STORAGE_H
|
#endif // FENNEC_MATH_VECTOR_STORAGE_H
|
||||||
|
|||||||
@@ -508,12 +508,7 @@ public:
|
|||||||
return _data[i];
|
return _data[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr const value_t operator[](size_t i) const requires requires { is_fundamental_v<value_t> == true; } {
|
constexpr const value_t operator[](size_t i) const {
|
||||||
assertd(i < size(), "Array Out of Bounds");
|
|
||||||
return _data[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr const value_t& operator[](size_t i) const requires requires { is_fundamental_v<value_t> == false; } {
|
|
||||||
assertd(i < size(), "Array Out of Bounds");
|
assertd(i < size(), "Array Out of Bounds");
|
||||||
return _data[i];
|
return _data[i];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,27 +23,15 @@
|
|||||||
// see https://git.mslockbo.org/mslockbo/fennec/src/commit/0eeb7ae3cff9d78e98dc5d9fc09bcb98b10986b9 for previous
|
// see https://git.mslockbo.org/mslockbo/fennec/src/commit/0eeb7ae3cff9d78e98dc5d9fc09bcb98b10986b9 for previous
|
||||||
// implementation
|
// implementation
|
||||||
|
|
||||||
#if _MSC_VER
|
|
||||||
#pragma warning(push)
|
|
||||||
#pragma warning(disable:4117)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if __GNUC__
|
#if __GNUC__
|
||||||
#define __OPTIMIZE__
|
#define __OPTIMIZE__
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#pragma push_macro("__cplusplus")
|
|
||||||
#undef __cplusplus
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <wchar.h>
|
#include <wchar.h>
|
||||||
#pragma pop_macro("__cplusplus")
|
|
||||||
|
|
||||||
#if __GNUC__
|
#if __GNUC__
|
||||||
#undef __OPTIMIZE__
|
#undef __OPTIMIZE__
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if _MSC_VER
|
|
||||||
#pragma warning(pop)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // FENNEC_MEMORY_DETAIL_MEMORY_H
|
#endif // FENNEC_MEMORY_DETAIL_MEMORY_H
|
||||||
|
|||||||
@@ -102,37 +102,4 @@ void operator delete (void* ptr, fennec::align_t, const fennec::nothrow_t&) noe
|
|||||||
void operator delete[](void* ptr, fennec::align_t, const fennec::nothrow_t&) noexcept;
|
void operator delete[](void* ptr, fennec::align_t, const fennec::nothrow_t&) noexcept;
|
||||||
|
|
||||||
|
|
||||||
// Platform specific code
|
|
||||||
#ifdef _WIN32
|
|
||||||
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
|
||||||
#include <windows.h>
|
|
||||||
|
|
||||||
namespace fennec
|
|
||||||
{
|
|
||||||
|
|
||||||
inline size_T pagesize() {
|
|
||||||
SYSTEM_INFO sysInfo;
|
|
||||||
GetSystemInfo(&sysInfo);
|
|
||||||
return sysInfo.dwPageSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
namespace fennec
|
|
||||||
{
|
|
||||||
|
|
||||||
inline size_t pagesize() {
|
|
||||||
return sysconf(_SC_PAGESIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#endif // FENNEC_MEMORY_NEW_H
|
#endif // FENNEC_MEMORY_NEW_H
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ set(CMAKE_CXX_STANDARD 23)
|
|||||||
add_executable(fennec-metaprogramming main.cpp
|
add_executable(fennec-metaprogramming main.cpp
|
||||||
float.h
|
float.h
|
||||||
integer.h)
|
integer.h)
|
||||||
|
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
OUTPUT .metaprogramming
|
OUTPUT .metaprogramming
|
||||||
COMMAND fennec-metaprogramming
|
COMMAND fennec-metaprogramming
|
||||||
|
|||||||
@@ -16,7 +16,8 @@
|
|||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
// =====================================================================================================================
|
// =====================================================================================================================
|
||||||
|
|
||||||
#include <cpptrace/cpptrace.hpp>
|
#include <stdio.h>
|
||||||
|
//#include <cpptrace/cpptrace.hpp>
|
||||||
|
|
||||||
void __assert_callback(const char* expression, const char* file, int line, const char* function, const char* description)
|
void __assert_callback(const char* expression, const char* file, int line, const char* function, const char* description)
|
||||||
{
|
{
|
||||||
@@ -27,5 +28,5 @@ void __assert_callback(const char* expression, const char* file, int line, const
|
|||||||
"At %s:%d in %s \n"
|
"At %s:%d in %s \n"
|
||||||
"Description: %s \n",
|
"Description: %s \n",
|
||||||
expression, file, line, function, description);
|
expression, file, line, function, description);
|
||||||
cpptrace::generate_trace(2).print();
|
// cpptrace::generate_trace(2).print();
|
||||||
}
|
}
|
||||||
@@ -18,3 +18,250 @@
|
|||||||
|
|
||||||
#include <fennec/fproc/io/common.h>
|
#include <fennec/fproc/io/common.h>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
namespace fennec
|
||||||
|
{
|
||||||
|
|
||||||
|
string getcwd() {
|
||||||
|
char cstr[MAX_PATH];
|
||||||
|
if (GetCurrentDirectory(sizeof(str), str) == 0) {
|
||||||
|
return string("");
|
||||||
|
}
|
||||||
|
string result(cstr);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <linux/limits.h>
|
||||||
|
|
||||||
|
namespace fennec
|
||||||
|
{
|
||||||
|
|
||||||
|
string getcwd() {
|
||||||
|
char cstr[PATH_MAX];
|
||||||
|
if (::getcwd(cstr, sizeof(cstr)) == NULL) {
|
||||||
|
return string("");
|
||||||
|
}
|
||||||
|
return string(cstr, strlen(cstr));
|
||||||
|
}
|
||||||
|
|
||||||
|
string absolute(const cstring& path) {
|
||||||
|
|
||||||
|
// first determine if this is already an absolute path
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (path[1] == ':') {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (path[0] == '/') {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
string parse = path;
|
||||||
|
string res = getcwd();
|
||||||
|
|
||||||
|
size_t len = res.length();
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (res[len - 1] == '/' || res[len - 1] == '\\') {
|
||||||
|
#else
|
||||||
|
if (res[len - 1] == '/') {
|
||||||
|
#endif
|
||||||
|
res = res.substring(0, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (parse.size() > 1) {
|
||||||
|
|
||||||
|
// Parse for relative symbols
|
||||||
|
while (parse[0] == '.') {
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
// up
|
||||||
|
if (parse[1] == '.' && (parse[2] == '/' || parse[2] == '\\')) {
|
||||||
|
size_t r = res.rfind('/');
|
||||||
|
size_t l = res.rfind('\\');
|
||||||
|
size_t size = res.size();
|
||||||
|
if (r == size) {
|
||||||
|
size = l;
|
||||||
|
}
|
||||||
|
else if (l == size) {
|
||||||
|
size = r;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
size = max(r, l);
|
||||||
|
}
|
||||||
|
size = max(size, 1);
|
||||||
|
res = res.substring(0, size);
|
||||||
|
}
|
||||||
|
// rel
|
||||||
|
else if (parse[1] == '/' || parse[1] == '\\') {
|
||||||
|
parse = parse.substring(2);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// up
|
||||||
|
if (parse[1] == '.' && parse[2] == '/') {
|
||||||
|
size_t r = res.rfind('/');
|
||||||
|
res = res.substring(0, max(res.rfind('/'), static_cast<size_t>(1)));
|
||||||
|
parse = parse.substring(2);
|
||||||
|
}
|
||||||
|
// rel
|
||||||
|
else if (parse[1] == '/') {
|
||||||
|
parse = parse.substring(1);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the pos of the first directory separator
|
||||||
|
#ifdef _WIN32
|
||||||
|
size_t pos = min(parse.find('\\', 1), parse.find('/', 1));
|
||||||
|
#else
|
||||||
|
size_t pos = parse.find('/', 1);
|
||||||
|
size_t off = 0;
|
||||||
|
#endif
|
||||||
|
if (pos != parse.size()) {
|
||||||
|
if (parse[pos + 1] == '.') {
|
||||||
|
if (parse[pos + 2] == '/') {
|
||||||
|
off = 1;
|
||||||
|
}
|
||||||
|
else if (parse[pos + 2] == '.' && parse[pos + 3] == '/') {
|
||||||
|
off = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (parse[pos + 1] == '/') {
|
||||||
|
off = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add this part
|
||||||
|
string sub = parse.substring(0, pos);
|
||||||
|
parse = parse.substring(pos + off);
|
||||||
|
res += sub;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
string absolute(const string& path) {
|
||||||
|
|
||||||
|
// first determine if this is already an absolute path
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (path[1] == ':') {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (path[0] == '/') {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
string parse = path;
|
||||||
|
string res = getcwd();
|
||||||
|
|
||||||
|
size_t len = res.length();
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (res[len - 1] == '/' || res[len - 1] == '\\') {
|
||||||
|
#else
|
||||||
|
if (res[len - 1] == '/') {
|
||||||
|
#endif
|
||||||
|
res = res.substring(0, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (parse.size() > 1) {
|
||||||
|
|
||||||
|
// Parse for relative symbols
|
||||||
|
while (parse[0] == '.') {
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
// up
|
||||||
|
if (parse[1] == '.' && (parse[2] == '/' || parse[2] == '\\')) {
|
||||||
|
size_t r = res.rfind('/');
|
||||||
|
size_t l = res.rfind('\\');
|
||||||
|
size_t size = res.size();
|
||||||
|
if (r == size) {
|
||||||
|
size = l;
|
||||||
|
}
|
||||||
|
else if (l == size) {
|
||||||
|
size = r;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
size = max(r, l);
|
||||||
|
}
|
||||||
|
size = max(size, 1);
|
||||||
|
res = res.substring(0, size);
|
||||||
|
}
|
||||||
|
// rel
|
||||||
|
else if (parse[1] == '/' || parse[1] == '\\') {
|
||||||
|
parse = parse.substring(2);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// up
|
||||||
|
if (parse[1] == '.' && parse[2] == '/') {
|
||||||
|
size_t r = res.rfind('/');
|
||||||
|
res = res.substring(0, max(res.rfind('/'), static_cast<size_t>(1)));
|
||||||
|
parse = parse.substring(2);
|
||||||
|
}
|
||||||
|
// rel
|
||||||
|
else if (parse[1] == '/') {
|
||||||
|
parse = parse.substring(1);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the pos of the first directory separator
|
||||||
|
#ifdef _WIN32
|
||||||
|
size_t pos = min(parse.find('\\', 1), parse.find('/', 1));
|
||||||
|
size_t off = 0;
|
||||||
|
if (pos != parse.size()) {
|
||||||
|
if (parse[pos + 1] == '.') {
|
||||||
|
if (parse[pos + 2] == '/' || parse[pos + 2] == '\\') {
|
||||||
|
off = 1;
|
||||||
|
}
|
||||||
|
else if (parse[pos + 2] == '.' && (parse[pos + 3] == '/' || parse[pos + 3] == '\\')) {
|
||||||
|
off = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (parse[pos + 1] == '/' || parse[pos + 1] == '\\') {
|
||||||
|
off = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
size_t pos = parse.find('/', 1);
|
||||||
|
size_t off = 0;
|
||||||
|
if (pos != parse.size()) {
|
||||||
|
if (parse[pos + 1] == '.') {
|
||||||
|
if (parse[pos + 2] == '/') {
|
||||||
|
off = 1;
|
||||||
|
}
|
||||||
|
else if (parse[pos + 2] == '.' && parse[pos + 3] == '/') {
|
||||||
|
off = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (parse[pos + 1] == '/') {
|
||||||
|
off = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// add this part
|
||||||
|
string sub = parse.substring(0, pos);
|
||||||
|
parse = parse.substring(pos + off);
|
||||||
|
res += sub;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,3 +15,826 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
// =====================================================================================================================
|
// =====================================================================================================================
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fennec/fproc/io/file.h>
|
||||||
|
#include <fennec/fproc/io/common.h>
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#include <sys/file.h>
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace fennec
|
||||||
|
{
|
||||||
|
|
||||||
|
constexpr const cstring fmode_translate(uint8_t mode) {
|
||||||
|
if (not file::is_valid(mode)) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
mode &= ~fmode_wide; // Ignore wide bit
|
||||||
|
|
||||||
|
switch (mode) {
|
||||||
|
case fmode_read: return "r";
|
||||||
|
case fmode_write: return "a";
|
||||||
|
|
||||||
|
// I chose r+ because for most read/write assets in the context of the game engine will be setting files, save files,
|
||||||
|
// and editor assets, which require a complete read of the file before any writing is done
|
||||||
|
case fmode_read | fmode_write: return "r+";
|
||||||
|
|
||||||
|
case fmode_write | fmode_trunc: return "w";
|
||||||
|
case fmode_read | fmode_write | fmode_trunc: return "w+";
|
||||||
|
|
||||||
|
case fmode_write | fmode_trunc | fmode_exclusive: return "wx";
|
||||||
|
case fmode_read | fmode_write | fmode_trunc | fmode_exclusive: return "wx+";
|
||||||
|
|
||||||
|
// Binary Files
|
||||||
|
case fmode_binary | fmode_read: return "rb";
|
||||||
|
case fmode_binary | fmode_write: return "ab";
|
||||||
|
|
||||||
|
case fmode_binary | fmode_read | fmode_write: return "rb+";
|
||||||
|
|
||||||
|
case fmode_binary | fmode_write | fmode_trunc: return "wb";
|
||||||
|
case fmode_binary | fmode_read | fmode_write | fmode_trunc: return "wb+";
|
||||||
|
|
||||||
|
case fmode_binary | fmode_write | fmode_trunc | fmode_exclusive: return "wxb";
|
||||||
|
case fmode_binary | fmode_read | fmode_write | fmode_trunc | fmode_exclusive: return "wxb+";
|
||||||
|
|
||||||
|
default: return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
file& file::cout() {
|
||||||
|
static constexpr char path[] = "stdout";
|
||||||
|
static file out = []() -> file {
|
||||||
|
file res;
|
||||||
|
res._mode = fmode_write;
|
||||||
|
res._handle = stdout;
|
||||||
|
res._path = string(path);
|
||||||
|
return res;
|
||||||
|
}();
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
file& file::cin() {
|
||||||
|
static constexpr char path[] = "stdin";
|
||||||
|
static file out = []() -> file {
|
||||||
|
file res;
|
||||||
|
res._mode = fmode_read;
|
||||||
|
res._handle = stdin;
|
||||||
|
res._path = string(path);
|
||||||
|
return res;
|
||||||
|
}();
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
file& file::cerr() {
|
||||||
|
static constexpr char path[] = "stderr";
|
||||||
|
static file out = []() -> file {
|
||||||
|
file res;
|
||||||
|
res._mode = fmode_write;
|
||||||
|
res._handle = stderr;
|
||||||
|
res._path = string(path);
|
||||||
|
return res;
|
||||||
|
}();
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
file::file()
|
||||||
|
: _handle(nullptr)
|
||||||
|
, _path("")
|
||||||
|
, _mode(0)
|
||||||
|
, _error(nullptr) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
file::~file() {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
file::file(file&& file) noexcept
|
||||||
|
: _handle(file._handle), _path(file._path), _mode(file._mode), _error(file._error) {
|
||||||
|
file._handle = nullptr;
|
||||||
|
file._error = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
file& file::operator=(file&& file) noexcept {
|
||||||
|
assert(_error == nullptr, "Attempted Operation on a File in an Errored State");
|
||||||
|
|
||||||
|
close();
|
||||||
|
_handle = file._handle;
|
||||||
|
_path = file._path;
|
||||||
|
_mode = file._mode;
|
||||||
|
_error = file._error;
|
||||||
|
|
||||||
|
file._handle = nullptr;
|
||||||
|
file._path = "";
|
||||||
|
file._error = nullptr;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool file::open(const cstring& filename, uint8_t mode) {
|
||||||
|
assert(_error == nullptr, "Attempted Operation on a File in an Errored State");
|
||||||
|
|
||||||
|
// Ensure validity of the mode
|
||||||
|
if (not is_valid(mode)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the file if already open
|
||||||
|
if (_handle) {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to open the file
|
||||||
|
_handle = fopen(filename, fmode_translate(mode));
|
||||||
|
|
||||||
|
if (_handle == nullptr) {
|
||||||
|
_error = strerror(errno);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to lock the file
|
||||||
|
if (flock(fileno(_handle), LOCK_EX)) {
|
||||||
|
_error = strerror(errno);
|
||||||
|
fclose(_handle);
|
||||||
|
_handle = nullptr;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_mode = mode;
|
||||||
|
_path = absolute(filename);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool file::open(const string& filename, uint8_t mode) {
|
||||||
|
assert(_error == nullptr, "Attempted Operation on a File in an Errored State");
|
||||||
|
|
||||||
|
// Ensure validity of the mode
|
||||||
|
if (not is_valid(mode)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the file if already open
|
||||||
|
if (_handle) {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to open the file
|
||||||
|
_handle = fopen(filename, fmode_translate(mode));
|
||||||
|
|
||||||
|
// Validate the file
|
||||||
|
if (_handle == nullptr) {
|
||||||
|
_error = strerror(errno);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to lock the file
|
||||||
|
if (flock(fileno(_handle), LOCK_EX)) {
|
||||||
|
_error = strerror(errno);
|
||||||
|
fclose(_handle);
|
||||||
|
_handle = nullptr;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the orientation
|
||||||
|
fwide(_handle, mode & fmode_wide ? 1 : -1);
|
||||||
|
|
||||||
|
_mode = mode;
|
||||||
|
_path = absolute(filename);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool file::close() {
|
||||||
|
assert(_error == nullptr, "Attempted an Operation on a File in an Errored State");
|
||||||
|
|
||||||
|
// Check if there is a file
|
||||||
|
if (_handle == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to unlock the file
|
||||||
|
if (flock(fileno(_handle), LOCK_UN)) {
|
||||||
|
_error = strerror(errno);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the file and reset variables
|
||||||
|
fclose(_handle);
|
||||||
|
_mode = 0;
|
||||||
|
_handle = nullptr;
|
||||||
|
_path = "";
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool file::commit() {
|
||||||
|
assert(_error == nullptr, "Attempted an Operation on a File in an Errored State");
|
||||||
|
|
||||||
|
// Check if there is a file
|
||||||
|
if (_handle == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to flush
|
||||||
|
if (fflush(_handle)) {
|
||||||
|
_error = strerror(errno);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool file::erase() {
|
||||||
|
assert(_error == nullptr, "Attempted an Operation on a File in an Errored State");
|
||||||
|
|
||||||
|
// Check if there is a file
|
||||||
|
if (_handle == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the file
|
||||||
|
string path = move(_path);
|
||||||
|
if (close()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Erase the file
|
||||||
|
remove(path);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool file::rename(const cstring& str) {
|
||||||
|
static const size_t page_size = pagesize();
|
||||||
|
assert(_error == nullptr, "Attempted an Operation on a File in an Errored State");
|
||||||
|
|
||||||
|
// Check if there is a file
|
||||||
|
if (_handle == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate path
|
||||||
|
string fpath = absolute(str);
|
||||||
|
if (_path == fpath) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to open the new file
|
||||||
|
FILE* fnew = fopen(fpath, "wx");
|
||||||
|
|
||||||
|
// Check for open failure
|
||||||
|
if (fnew == nullptr) {
|
||||||
|
_error = strerror(errno);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reopen this file as read
|
||||||
|
_handle = freopen(nullptr, "r", _handle);
|
||||||
|
|
||||||
|
// Check if it failed to reopen
|
||||||
|
if (_handle == nullptr) {
|
||||||
|
_error = strerror(errno);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize buffer
|
||||||
|
void* buffer = operator new(page_size, static_cast<align_t>(page_size));
|
||||||
|
|
||||||
|
// Copy contents
|
||||||
|
size_t read = 0;
|
||||||
|
while ((read = fread(buffer, 1, page_size, _handle)) == page_size) {
|
||||||
|
if (fwrite(buffer, 1, page_size, fnew) != page_size) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle eof
|
||||||
|
if (feof(_handle)) {
|
||||||
|
fwrite(buffer, 1, read, fnew);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Check new file for errors
|
||||||
|
if (ferror(fnew)) {
|
||||||
|
_error = strerror(errno);
|
||||||
|
fclose(fnew);
|
||||||
|
_mode = fmode_read;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the original file for errors
|
||||||
|
if (ferror(_handle)) {
|
||||||
|
_error = strerror(errno);
|
||||||
|
fclose(fnew);
|
||||||
|
fclose(_handle);
|
||||||
|
_handle = nullptr;
|
||||||
|
_mode = 0;
|
||||||
|
_path = "";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup the buffer
|
||||||
|
operator delete(buffer, page_size, static_cast<align_t>(page_size));
|
||||||
|
|
||||||
|
// Close old file
|
||||||
|
fclose(_handle);
|
||||||
|
|
||||||
|
// Reopen the new file
|
||||||
|
_handle = freopen(&fpath[0], fmode_translate(_mode), fnew);
|
||||||
|
|
||||||
|
// Check for open failure
|
||||||
|
if (_handle == nullptr) {
|
||||||
|
_error = strerror(errno);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Erase the old file
|
||||||
|
remove(_path);
|
||||||
|
|
||||||
|
// Set the new path
|
||||||
|
_path = fpath;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool file::rename(const string& str) {
|
||||||
|
static const size_t page_size = pagesize();
|
||||||
|
assert(_error == nullptr, "Attempted an Operation on a File in an Errored State");
|
||||||
|
|
||||||
|
// Check if there is a file
|
||||||
|
if (_handle == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate path
|
||||||
|
string fpath = absolute(str);
|
||||||
|
if (_path == fpath) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to open the new file
|
||||||
|
FILE* fnew = fopen(fpath, "wx");
|
||||||
|
|
||||||
|
// Check for open failure
|
||||||
|
if (fnew == nullptr) {
|
||||||
|
_error = strerror(errno);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reopen this file as read
|
||||||
|
_handle = freopen(nullptr, "r", _handle);
|
||||||
|
|
||||||
|
// Check if it failed to reopen
|
||||||
|
if (_handle == nullptr) {
|
||||||
|
_error = strerror(errno);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize buffer
|
||||||
|
void* buffer = operator new(page_size, static_cast<align_t>(page_size));
|
||||||
|
|
||||||
|
// Copy contents
|
||||||
|
size_t read = 0;
|
||||||
|
while ((read = fread(buffer, 1, page_size, _handle)) == page_size) {
|
||||||
|
if (fwrite(buffer, 1, page_size, fnew) != page_size) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle eof
|
||||||
|
if (feof(_handle)) {
|
||||||
|
fwrite(buffer, 1, read, fnew);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check new file for errors
|
||||||
|
if (ferror(fnew)) {
|
||||||
|
_error = strerror(errno);
|
||||||
|
fclose(fnew);
|
||||||
|
_mode = fmode_read;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the original file for errors
|
||||||
|
if (ferror(_handle)) {
|
||||||
|
_error = strerror(errno);
|
||||||
|
fclose(fnew);
|
||||||
|
fclose(_handle);
|
||||||
|
_handle = nullptr;
|
||||||
|
_mode = 0;
|
||||||
|
_path = "";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup the buffer
|
||||||
|
operator delete(buffer, page_size, static_cast<align_t>(page_size));
|
||||||
|
|
||||||
|
// Close old file
|
||||||
|
fclose(_handle);
|
||||||
|
|
||||||
|
// Reopen the new file
|
||||||
|
_handle = freopen(&_path[0], fmode_translate(_mode), fnew);
|
||||||
|
|
||||||
|
// Check for open failure
|
||||||
|
if (_handle == nullptr) {
|
||||||
|
_error = strerror(errno);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the new path
|
||||||
|
_path = fpath;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
file file::copy(const cstring& str) {
|
||||||
|
static const size_t page_size = pagesize();
|
||||||
|
assert(_error == nullptr, "Attempted an Operation on a File in an Errored State");
|
||||||
|
|
||||||
|
// Check if there is a file
|
||||||
|
if (_handle == nullptr) {
|
||||||
|
return file();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate path
|
||||||
|
string fpath = absolute(str);
|
||||||
|
if (_path == fpath) {
|
||||||
|
return file();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to open the new file
|
||||||
|
FILE* fnew = fopen(fpath, "wx");
|
||||||
|
|
||||||
|
// Check for open failure
|
||||||
|
if (fnew == nullptr) {
|
||||||
|
_error = strerror(errno);
|
||||||
|
return file();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reopen this file as read
|
||||||
|
_handle = freopen(nullptr, "r", _handle);
|
||||||
|
|
||||||
|
// Check if it failed to reopen
|
||||||
|
if (_handle == nullptr) {
|
||||||
|
_error = strerror(errno);
|
||||||
|
return file();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize buffer
|
||||||
|
void* buffer = operator new(page_size, static_cast<align_t>(page_size));
|
||||||
|
|
||||||
|
// Copy contents
|
||||||
|
size_t read = 0;
|
||||||
|
while ((read = fread(buffer, 1, page_size, _handle)) == page_size) {
|
||||||
|
if (fwrite(buffer, 1, page_size, fnew) != page_size) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle eof
|
||||||
|
if (feof(_handle)) {
|
||||||
|
fwrite(buffer, 1, read, fnew);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check new file for errors
|
||||||
|
if (ferror(fnew)) {
|
||||||
|
_error = strerror(errno);
|
||||||
|
fclose(fnew);
|
||||||
|
_mode = fmode_read;
|
||||||
|
return file();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the original file for errors
|
||||||
|
if (ferror(_handle)) {
|
||||||
|
_error = strerror(errno);
|
||||||
|
fclose(fnew);
|
||||||
|
fclose(_handle);
|
||||||
|
_handle = nullptr;
|
||||||
|
_mode = 0;
|
||||||
|
_path = "";
|
||||||
|
return file();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup the buffer
|
||||||
|
operator delete(buffer, page_size, static_cast<align_t>(page_size));
|
||||||
|
|
||||||
|
// Reopen the new file
|
||||||
|
_handle = freopen(nullptr, fmode_translate(_mode), _handle);
|
||||||
|
|
||||||
|
// Check for open failure
|
||||||
|
if (_handle == nullptr) {
|
||||||
|
_error = strerror(errno);
|
||||||
|
return file();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the new path
|
||||||
|
file res;
|
||||||
|
res._handle = fnew;
|
||||||
|
res._path = fpath;
|
||||||
|
res._mode = fmode_write;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
file file::copy(const string& str) {
|
||||||
|
static const size_t page_size = pagesize();
|
||||||
|
assert(_error == nullptr, "Attempted an Operation on a File in an Errored State");
|
||||||
|
|
||||||
|
// Check if there is a file
|
||||||
|
if (_handle == nullptr) {
|
||||||
|
return file();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate path
|
||||||
|
string fpath = absolute(str);
|
||||||
|
if (_path == fpath) {
|
||||||
|
return file();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to open the new file
|
||||||
|
FILE* fnew = fopen(fpath, "wx");
|
||||||
|
|
||||||
|
// Check for open failure
|
||||||
|
if (fnew == nullptr) {
|
||||||
|
_error = strerror(errno);
|
||||||
|
return file();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reopen this file as read
|
||||||
|
_handle = freopen(nullptr, "r", _handle);
|
||||||
|
|
||||||
|
// Check if it failed to reopen
|
||||||
|
if (_handle == nullptr) {
|
||||||
|
_error = strerror(errno);
|
||||||
|
return file();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize buffer
|
||||||
|
void* buffer = operator new(page_size, static_cast<align_t>(page_size));
|
||||||
|
|
||||||
|
// Copy contents
|
||||||
|
size_t read = 0;
|
||||||
|
while ((read = fread(buffer, 1, page_size, _handle)) == page_size) {
|
||||||
|
if (fwrite(buffer, 1, page_size, fnew) != page_size) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle eof
|
||||||
|
if (feof(_handle)) {
|
||||||
|
fwrite(buffer, 1, read, fnew);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check new file for errors
|
||||||
|
if (ferror(fnew)) {
|
||||||
|
operator delete(buffer, page_size, static_cast<align_t>(page_size));
|
||||||
|
_error = strerror(errno);
|
||||||
|
fclose(fnew);
|
||||||
|
_mode = fmode_read;
|
||||||
|
return file();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the original file for errors
|
||||||
|
if (ferror(_handle)) {
|
||||||
|
operator delete(buffer, page_size, static_cast<align_t>(page_size));
|
||||||
|
_error = strerror(errno);
|
||||||
|
fclose(fnew);
|
||||||
|
fclose(_handle);
|
||||||
|
_handle = nullptr;
|
||||||
|
_mode = 0;
|
||||||
|
_path = "";
|
||||||
|
return file();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup the buffer
|
||||||
|
operator delete(buffer, page_size, static_cast<align_t>(page_size));
|
||||||
|
|
||||||
|
// Reopen the new file
|
||||||
|
_handle = freopen(nullptr, fmode_translate(_mode), _handle);
|
||||||
|
|
||||||
|
// Check for open failure
|
||||||
|
if (_handle == nullptr) {
|
||||||
|
_error = strerror(errno);
|
||||||
|
return file();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the new path
|
||||||
|
file res;
|
||||||
|
res._handle = fnew;
|
||||||
|
res._path = fpath;
|
||||||
|
res._mode = fmode_write;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t file::get_pos() const {
|
||||||
|
assert(_error == nullptr, "Attempted an Operation on a File in an Errored State");
|
||||||
|
|
||||||
|
// Check if there is a file
|
||||||
|
if (_handle == nullptr) {
|
||||||
|
return npos;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ftell(_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool file::set_pos(size_t i) {
|
||||||
|
assert(_error == nullptr, "Attempted an Operation on a File in an Errored State");
|
||||||
|
|
||||||
|
// Check if there is a file
|
||||||
|
if (_handle == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fseek(_handle, i, SEEK_SET)) {
|
||||||
|
_error = strerror(errno);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool file::rewind() {
|
||||||
|
assert(_error == nullptr, "Attempted an Operation on a File in an Errored State");
|
||||||
|
|
||||||
|
// Check if there is a file
|
||||||
|
if (_handle == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
::rewind(_handle);
|
||||||
|
if (ferror(_handle)) {
|
||||||
|
_error = strerror(errno);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool file::eof() const {
|
||||||
|
assert(_error == nullptr, "Attempted an Operation on a File in an Errored State");
|
||||||
|
|
||||||
|
// Check if there is a file
|
||||||
|
if (_handle == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return feof(_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
char file::getc() {
|
||||||
|
assert(_error == nullptr, "Attempted an Operation on a File in an Errored State");
|
||||||
|
assert(not _mode & fmode_wide, "Attempted Byte Operation on Wide File");
|
||||||
|
|
||||||
|
// Check if there is a file
|
||||||
|
if (_handle == nullptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fgetc(_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
wchar_t file::getwc() {
|
||||||
|
assert(_error == nullptr, "Attempted an Operation on a File in an Errored State");
|
||||||
|
assert(_mode & fmode_wide, "Attempted Wide Operation on Byte File");
|
||||||
|
|
||||||
|
// Check if there is a file
|
||||||
|
if (_handle == nullptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fgetwc(_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t file::read(void* data, size_t size, size_t n) {
|
||||||
|
assert(_error == nullptr, "Attempted an Operation on a File in an Errored State");
|
||||||
|
|
||||||
|
// Check if there is a file
|
||||||
|
if (_handle == nullptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t read = fread(data, size, n, _handle);
|
||||||
|
if (read != size && ferror(_handle)) {
|
||||||
|
_error = strerror(errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
return read;
|
||||||
|
}
|
||||||
|
|
||||||
|
string file::getline() {
|
||||||
|
assert(_error == nullptr, "Attempted an Operation on a File in an Errored State");
|
||||||
|
assert(not _mode & fmode_wide, "Attempted Byte Operation on Wide File");
|
||||||
|
|
||||||
|
// Check if there is a file
|
||||||
|
if (_handle == nullptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the next line;
|
||||||
|
char* line = nullptr;
|
||||||
|
size_t size = 0;
|
||||||
|
size_t read = ::getline(&line, &size, _handle);
|
||||||
|
if (read == npos && ferror(_handle)) {
|
||||||
|
_error = strerror(errno);
|
||||||
|
if (line) free(line);
|
||||||
|
return string("");
|
||||||
|
}
|
||||||
|
|
||||||
|
string res = string(line, read);
|
||||||
|
free(line);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
wstring file::getwline() {
|
||||||
|
assert(_error == nullptr, "Attempted an Operation on a File in an Errored State");
|
||||||
|
assert(_mode & fmode_wide, "Attempted Wide Operation on Byte File");
|
||||||
|
|
||||||
|
// Check if there is a file
|
||||||
|
if (_handle == nullptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the next line;
|
||||||
|
wchar_t arr[257] = { L'\0' };
|
||||||
|
wcstring buff = arr;
|
||||||
|
wstring res{L""};
|
||||||
|
|
||||||
|
// read until first newline or end of file
|
||||||
|
while (fgetws(arr, 257, _handle)) {
|
||||||
|
res += wcstring(arr, buff.length());
|
||||||
|
if (buff.length() < 256 || buff[256] == L'\n') {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_error = strerror(errno);
|
||||||
|
return wstring(L"");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool file::putc(char c) {
|
||||||
|
assert(_error == nullptr, "Attempted an Operation on a File in an Errored State");
|
||||||
|
assert(not _mode & fmode_wide, "Attempted Byte Operation on Wide File");
|
||||||
|
|
||||||
|
// Check if there is a file
|
||||||
|
if (_handle == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int res;
|
||||||
|
if ((res = fputc(c, _handle)) != c && res != EOF) {
|
||||||
|
_error = strerror(errno);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool file::putwc(wchar_t c) {
|
||||||
|
assert(_error == nullptr, "Attempted an Operation on a File in an Errored State");
|
||||||
|
assert(_mode & fmode_wide, "Attempted Wide Operation on Byte File");
|
||||||
|
|
||||||
|
// Check if there is a file
|
||||||
|
if (_handle == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int res;
|
||||||
|
if ((res = fputc(c, _handle)) != c && res != EOF) {
|
||||||
|
_error = strerror(errno);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t file::write(const void* data, size_t size, size_t n) {
|
||||||
|
assert(_error == nullptr, "Attempted an Operation on a File in an Errored State");
|
||||||
|
|
||||||
|
// Check if there is a file
|
||||||
|
if (_handle == nullptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t r = fwrite(data, size, n, _handle);
|
||||||
|
|
||||||
|
if (r != size && ferror(_handle)) {
|
||||||
|
_error = strerror(errno);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -44,6 +44,19 @@ void* operator new[](fennec::size_t size, fennec::align_t align)
|
|||||||
void* operator new (fennec::size_t size, fennec::align_t align, const fennec::nothrow_t&) { return _aligned_malloc(static_cast<size_t>(align), size); }
|
void* operator new (fennec::size_t size, fennec::align_t align, const fennec::nothrow_t&) { return _aligned_malloc(static_cast<size_t>(align), size); }
|
||||||
void* operator new[](fennec::size_t size, fennec::align_t align, const fennec::nothrow_t&) { return _aligned_malloc(static_cast<size_t>(align), size); }
|
void* operator new[](fennec::size_t size, fennec::align_t align, const fennec::nothrow_t&) { return _aligned_malloc(static_cast<size_t>(align), size); }
|
||||||
|
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
namespace fennec
|
||||||
|
{
|
||||||
|
|
||||||
|
inline size_T pagesize() {
|
||||||
|
SYSTEM_INFO sysInfo;
|
||||||
|
GetSystemInfo(&sysInfo);
|
||||||
|
return sysInfo.dwPageSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
|
|
||||||
void operator delete (void* ptr) noexcept { ::free(ptr); }
|
void operator delete (void* ptr) noexcept { ::free(ptr); }
|
||||||
@@ -76,4 +89,14 @@ void operator delete[](void* ptr, const fennec::nothrow_t&) noe
|
|||||||
void operator delete (void* ptr, fennec::size_t, const fennec::nothrow_t&) noexcept { ::free(ptr); }
|
void operator delete (void* ptr, fennec::size_t, const fennec::nothrow_t&) noexcept { ::free(ptr); }
|
||||||
void operator delete[](void* ptr, fennec::size_t, const fennec::nothrow_t&) noexcept { ::free(ptr); }
|
void operator delete[](void* ptr, fennec::size_t, const fennec::nothrow_t&) noexcept { ::free(ptr); }
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
namespace fennec
|
||||||
|
{
|
||||||
|
|
||||||
|
size_t pagesize() {
|
||||||
|
return sysconf(_SC_PAGESIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
cmake_minimum_required(VERSION 3.30)
|
cmake_minimum_required(VERSION 3.30)
|
||||||
project(fennec-test)
|
project(fennec-test)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 26)
|
set(CMAKE_CXX_STANDARD 23)
|
||||||
set(CMAKE_C_STANDARD 26)
|
set(CMAKE_C_STANDARD 23)
|
||||||
|
|
||||||
add_executable(fennec-test main.cpp
|
add_executable(fennec-test main.cpp
|
||||||
test.h
|
test.h
|
||||||
@@ -26,7 +26,9 @@ add_executable(fennec-test main.cpp
|
|||||||
tests/fproc/test_io.h
|
tests/fproc/test_io.h
|
||||||
)
|
)
|
||||||
|
|
||||||
target_compile_definitions(fennec-test PUBLIC FENNEC_TEST_CWD="${CMAKE_RUNTIME_OUTPUT_DIRECTORY}")
|
target_compile_definitions(fennec-test PUBLIC FENNEC_TEST_CWD="${CMAKE_SOURCE_DIR}/bin/${FENNEC_BUILD_NAME}"
|
||||||
|
FENNEC_BUILD_NAME="${FENNEC_BUILD_NAME}"
|
||||||
|
)
|
||||||
|
|
||||||
target_link_libraries(fennec-test PRIVATE
|
target_link_libraries(fennec-test PRIVATE
|
||||||
fennec
|
fennec
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
#include "../../test.h"
|
#include "../../test.h"
|
||||||
|
|
||||||
#include <fennec/fproc/io/common.h>
|
#include <fennec/fproc/io/common.h>
|
||||||
|
#include <fennec/fproc/io/file.h>
|
||||||
|
|
||||||
namespace fennec
|
namespace fennec
|
||||||
{
|
{
|
||||||
@@ -30,7 +31,64 @@ namespace test
|
|||||||
{
|
{
|
||||||
|
|
||||||
inline void fennec_test_fproc_io() {
|
inline void fennec_test_fproc_io() {
|
||||||
fennec_test_run(getcwd(), string(FENNEC_TEST_CWD));
|
|
||||||
|
fennec_test_section("utilities");
|
||||||
|
|
||||||
|
fennec_test_spacer(1);
|
||||||
|
|
||||||
|
fennec_test_run(getcwd(), string(FENNEC_TEST_CWD));
|
||||||
|
fennec_test_run(absolute("../" FENNEC_BUILD_NAME "/./test.sh"), string(FENNEC_TEST_CWD) + "/test.sh");
|
||||||
|
fennec_test_run(absolute("./test/../test.sh"), string(FENNEC_TEST_CWD) + "/test.sh");
|
||||||
|
|
||||||
|
fennec_test_spacer(2);
|
||||||
|
|
||||||
|
|
||||||
|
fennec_test_section("file streams");
|
||||||
|
|
||||||
|
fennec_test_spacer(1);
|
||||||
|
|
||||||
|
file stream;
|
||||||
|
char data[] = "Hello World!";
|
||||||
|
fennec_test_run(stream.open("./test.txt", fmode_write | fmode_trunc), false);
|
||||||
|
fennec_test_run(stream.write(data), sizeof(data));
|
||||||
|
fennec_test_run(stream.close(), false);
|
||||||
|
|
||||||
|
fennec_test_spacer(1);
|
||||||
|
|
||||||
|
fennec_test_run(stream.open("./test.txt", fmode_read), false);
|
||||||
|
fennec_test_run(stream.read(data), sizeof(data));
|
||||||
|
fennec_test_run(cstring(data), cstring("Hello World!"));
|
||||||
|
|
||||||
|
fennec_test_spacer(1);
|
||||||
|
|
||||||
|
fennec_test_run(stream.open("./test.txt", fmode_read), false);
|
||||||
|
fennec_test_run((stream = stream.copy("./copy.txt")).is_open(), true);
|
||||||
|
fennec_test_run(stream.open("./copy.txt", fmode_read), false);
|
||||||
|
fennec_test_run(stream.read(data), sizeof(data));
|
||||||
|
fennec_test_run(cstring(data), cstring("Hello World!"));
|
||||||
|
fennec_test_run(stream.erase(), false);
|
||||||
|
|
||||||
|
fennec_test_spacer(1);
|
||||||
|
|
||||||
|
fennec_test_run(stream.open("./test.txt", fmode_read), false);
|
||||||
|
fennec_test_run(stream.rename("./rename.txt"), false);
|
||||||
|
fennec_test_run(stream.open("./rename.txt", fmode_read), false);
|
||||||
|
fennec_test_run(stream.read(data), sizeof(data));
|
||||||
|
fennec_test_run(cstring(data), cstring("Hello World!"));
|
||||||
|
fennec_test_run(stream.erase(), false);
|
||||||
|
|
||||||
|
fennec_test_spacer(2);
|
||||||
|
|
||||||
|
|
||||||
|
fennec_test_section("std streams");
|
||||||
|
|
||||||
|
fennec_test_spacer(1);
|
||||||
|
|
||||||
|
fennec_test_run(file::cout().write("Hello World!"), sizeof("Hello World!"));
|
||||||
|
fennec_test_run(file::cerr().write("Hello World!"), sizeof("Hello World!"));
|
||||||
|
|
||||||
|
fennec_test_spacer(2);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user