Put everything back.

This commit is contained in:
jakcron
2022-04-16 21:27:49 +08:00
parent 5d62e839e7
commit bc04de6d09
844 changed files with 114383 additions and 29 deletions
@@ -0,0 +1,109 @@
/**
* @file BasicPathResolver.h
* @brief Declaration of tc::io::BasicPathResolver
* @author Jack (jakcron)
* @version 0.3
* @date 2022/02/25
**/
#pragma once
#include <tc/io/IPortablePathResolver.h>
#include <tc/ArgumentOutOfRangeException.h>
namespace tc { namespace io {
/**
* @class BasicPathResolver
* @brief This implementation of IPortablePathResolver resolves a path and current directory to canonical path, resolving only '`.`', '`..`' and empty path elements.
* @details
* This does not consider the local file-system/environment, so links or '`~`' will not be resolved properly. It is intended for processing archived/portable filesystems.
*
* This supports custom root labels. A root label being the first element of absolute/canonical paths.
* * For POSIX this is an empty string, e.g. the empty string prior to / in "/some/path/to/a/file"
* * For Windows systems this is the drive letter, e.g. "C:" in "C:\some\path\to\a\file"
*
* BasicPathResolver maintains a list of root labels to determine if a path being resolved is absolute or relative, including:
* * Implicit Root Label : This is the first element in the current working directory, will change if the current working directory changes.
* * Explicit Root Labels : This is a list of root labels supplied separately via @ref setExplicitRootLabels().
*/
class BasicPathResolver : public tc::io::IPortablePathResolver
{
public:
/**
* @brief Default Constructor
*
* @post The current directory will be "/" and the list of explicit root labels will be {}.
*/
BasicPathResolver();
/**
* @brief Create BasicPathResolver
*
* @param[in] current_directory_path Canonical path for the current directory.
*
* @post The current directory will be @p current_directory_path and the list of explicit root labels will be {}.
*/
BasicPathResolver(const tc::io::Path& current_directory_path);
/**
* @brief Create BasicPathResolver
*
* @param[in] current_directory_path Canonical path for the current directory.
* @param[in] root_labels Vector of valid root path names for this path resolver.
*
* @post The current directory will be @p current_directory_path and the list of explicit root labels will be @p root_labels .
*/
BasicPathResolver(const tc::io::Path& current_directory_path, const std::vector<std::string>& root_labels);
/**
* @brief Set the current directory path
*
* @param path Canonical current directory path.
*
* @throws tc::ArgumentOutOfRangeException @p path was an empty path.
*/
void setCurrentDirectory(const tc::io::Path& path);
/**
* @brief Get the current directory path
*
* @return tc::io::Path Canonical current directory path.
*/
const tc::io::Path& getCurrentDirectory() const;
/**
* @brief Set explicit root labels
* @details
* This is only required where multiple root labels need to be registered.
*
* @param root_labels Vector of root labels
*/
void setExplicitRootLabels(const std::vector<std::string>& root_labels);
/// Get explicit root labels
const std::vector<std::string>& getExplicitRootLabels() const;
/**
* @brief Resolve path to its canonical path
*
* @param path Input path.
* @param canonical_path Output path to write resolved canonical path.
*/
void resolveCanonicalPath(const tc::io::Path& path, tc::io::Path& canonical_path) const;
/**
* @brief Resolve path to its canonical path
*
* @param path Input path.
*
* @return Resolved canonical path.
*/
tc::io::Path resolveCanonicalPath(const tc::io::Path& path) const;
private:
static const std::string kClassName;
tc::io::Path mCurrentDirPath;
std::vector<std::string> mExplicitRootLabels;
};
}} // namespace tc::io
@@ -0,0 +1,205 @@
/**
* @file ConcatenatedStream.h
* @brief Declaration of tc::io::ConcatenatedStream
* @author Jack (jakcron)
* @version 0.1
* @date 2022/02/28
**/
#pragma once
#include <tc/io/IStream.h>
#include <tc/Optional.h>
#include <tc/ArgumentOutOfRangeException.h>
#include <tc/NotSupportedException.h>
#include <tc/NotImplementedException.h>
#include <tc/ObjectDisposedException.h>
#include <tc/io/IOException.h>
namespace tc { namespace io {
/**
* @class ConcatenatedStream
* @brief A stream that concatenates multiple streams into a single stream.
**/
class ConcatenatedStream : public tc::io::IStream
{
public:
/**
* @brief Default Constructor
* @post This will create an unusable ConcatenatedStream, it will have to be assigned from a valid ConcatenatedStream object to be usable.
**/
ConcatenatedStream();
/**
* @brief Move constructor
**/
ConcatenatedStream(ConcatenatedStream&& other);
/**
* @brief Create ConcatenatedStream
*
* @param[in] stream_list The list IStream objects to concatenate in this stream.
*
* @pre All base streams must support seeking for @ref seek() to work
* @pre All base streams must support reading for @ref read() to work.
* @pre All base streams must support writing for @ref write() to work.
*
* @throw tc::NotSupportedException List of streams did not all support either read or write.
* @throw tc::NotSupportedException List of streams combined to a stream with no length.
**/
ConcatenatedStream(const std::vector<std::shared_ptr<tc::io::IStream>>& stream_list);
/// Destructor
~ConcatenatedStream();
/**
* @brief Move assignment
**/
ConcatenatedStream& operator=(ConcatenatedStream&& other);
/**
* @brief Indicates whether the current stream supports reading.
**/
bool canRead() const;
/**
* @brief Indicates whether the current stream supports writing.
**/
bool canWrite() const;
/**
* @brief Indicates whether the current stream supports seeking.
**/
bool canSeek() const;
/**
* @brief Gets the length in bytes of the stream.
**/
int64_t length();
/**
* @brief Gets the position within the current stream.
*
* @return This is returns the current position within the stream.
**/
int64_t position();
/**
* @brief Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read.
*
* @param[out] ptr Pointer to an array of bytes. When this method returns, @p ptr contains the specified byte array with the values between 0 and (@p count - 1) replaced by the bytes read from the current source.
* @param[in] count The maximum number of bytes to be read from the current stream.
*
* @return The total number of bytes read into @p ptr. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached.
*
* @pre A stream must support reading for @ref read to work.
* @note Use @ref canRead to determine if this stream supports reading.
* @note Exceptions thrown by the base stream are not altered/intercepted, refer to that module's documentation for those exceptions.
*
* @throw tc::io::IOException Failed to read data from an underlying stream.
* @throw tc::NotSupportedException Stream does not support reading.
* @throw tc::ObjectDisposedException Methods were called after the stream was closed.
**/
size_t read(byte_t* ptr, size_t count);
/**
* @brief Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written.
*
* @param[in] ptr Pointer to an array of bytes. This method copies @p count bytes from @p ptr to the current stream.
* @param[in] count The number of bytes to be written to the current stream.
*
* @return The total number of bytes written to the stream. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached.
*
* @pre A stream must support writing for @ref write to work.
* @note Use @ref canWrite to determine if this stream supports writing.
* @note Exceptions thrown by the base stream are not altered/intercepted, refer to that module's documentation for those exceptions.
*
* @throw tc::io::IOException Failed to write data to an underlying stream.
* @throw tc::NotSupportedException Stream does not support writing.
* @throw tc::ObjectDisposedException Methods were called after the stream was closed.
**/
size_t write(const byte_t* ptr, size_t count);
/**
* @brief Sets the position within the current stream.
*
* @param[in] offset A byte offset relative to the origin parameter.
* @param[in] origin A value of type @ref tc::io::SeekOrigin indicating the reference point used to obtain the new position.
*
* @return The new position within the current stream.
*
* @pre A stream must support seeking for @ref seek to work.
* @note Use @ref canSeek to determine if this stream supports seeking.
* @note Exceptions thrown by the base stream are not altered/intercepted, refer to that module's documentation for those exceptions.
*
* @throw tc::io::IOException Failed to seek because underlying stream could not be determined.
* @throw tc::ArgumentOutOfRangeException @p origin contains an invalid value.
* @throw tc::NotSupportedException Stream does not support seeking.
* @throw tc::ObjectDisposedException Methods were called after the stream was closed.
**/
int64_t seek(int64_t offset, SeekOrigin origin);
/**
* @brief Sets the length of the current stream. This is not implemented for @ref ConcatenatedStream.
* @throw tc::NotImplementedException @ref setLength is not implemented for @ref ConcatenatedStream
* @throw tc::ObjectDisposedException Methods were called after the stream was closed.
**/
void setLength(int64_t length);
/**
* @brief Clears all buffers for this and the base stream and causes any buffered data to be written to the underlying device.
*
* @throw tc::io::IOException An I/O error occurs.
* @throw tc::ObjectDisposedException Methods were called after the stream was closed.
**/
void flush();
/**
* @brief Releases internal resources including base stream and clears internal state.
**/
void dispose();
private:
static const std::string kClassName;
// delete copy constructor
ConcatenatedStream(const ConcatenatedStream&);
// delete copy assignment
ConcatenatedStream& operator=(const ConcatenatedStream&);
struct StreamRange
{
int64_t offset;
int64_t length;
StreamRange() : offset(0), length(0) {}
StreamRange(int64_t offset) : offset(offset), length(0) {}
StreamRange(int64_t offset, int64_t length) : offset(offset), length(length) {}
bool operator<(const StreamRange& other) const
{
return (this->offset < other.offset && (this->offset + this->length) <= other.offset);
}
};
struct StreamInfo
{
StreamRange range;
std::shared_ptr<tc::io::IStream> stream;
};
std::vector<StreamInfo> mStreamList;
std::map<StreamRange, size_t> mStreamListMap;
tc::Optional<std::vector<StreamInfo>::iterator> mCurrentStream;
inline bool isStreamDisposed() const { return mStreamList.empty() || mCurrentStream.isNull() || mCurrentStream.get() == mStreamList.end(); }
void updateCurrentStream(std::vector<StreamInfo>::iterator stream_itr);
// static stream properties
bool mCanRead;
bool mCanWrite;
bool mCanSeek;
int64_t mStreamLength;
};
}} // namespace tc::io
@@ -0,0 +1,57 @@
/**
* @file DirectoryNotEmptyException.h
* @brief Declaration of tc::io::DirectoryNotEmptyException
* @author Jack (jakcron)
* @version 0.1
* @date 2020/01/22
**/
#pragma once
#include <tc/io/IOException.h>
namespace tc { namespace io {
/**
* @class DirectoryNotEmptyException
* @brief The exception that is thrown when a directory is not empty.
**/
class DirectoryNotEmptyException : public tc::io::IOException
{
public:
/// Default Constructor
DirectoryNotEmptyException() noexcept :
tc::io::IOException()
{
}
/**
* @brief Basic Parameterized Constructor
*
* @param[in] what Explanation for exception
*
* @post
* - what() == what
* - module() == ""
* - error() == what
**/
DirectoryNotEmptyException(const std::string& what) noexcept :
tc::io::IOException(what)
{}
/**
* @brief Parameterized Constructor
*
* @param[in] module Name of module that threw the exception
* @param[in] what Explanation for exception
*
* @post
* - what() == "[" + module + " ERROR] " + what
* - module() == module
* - error() == what
**/
DirectoryNotEmptyException(const std::string& module, const std::string& what) noexcept :
tc::io::IOException(module, what)
{
}
};
}} // namespace tc::io
@@ -0,0 +1,57 @@
/**
* @file DirectoryNotFoundException.h
* @brief Declaration of tc::io::DirectoryNotFoundException
* @author Jack (jakcron)
* @version 0.1
* @date 2020/01/22
**/
#pragma once
#include <tc/io/IOException.h>
namespace tc { namespace io {
/**
* @class DirectoryNotFoundException
* @brief The exception that is thrown when part of a file or directory cannot be found.
**/
class DirectoryNotFoundException : public tc::io::IOException
{
public:
/// Default Constructor
DirectoryNotFoundException() noexcept :
tc::io::IOException()
{
}
/**
* @brief Basic Parameterized Constructor
*
* @param[in] what Explanation for exception
*
* @post
* - what() == what
* - module() == ""
* - error() == what
**/
DirectoryNotFoundException(const std::string& what) noexcept :
tc::io::IOException(what)
{}
/**
* @brief Parameterized Constructor
*
* @param[in] module Name of module that threw the exception
* @param[in] what Explanation for exception
*
* @post
* - what() == "[" + module + " ERROR] " + what
* - module() == module
* - error() == what
**/
DirectoryNotFoundException(const std::string& module, const std::string& what) noexcept :
tc::io::IOException(module, what)
{
}
};
}} // namespace tc::io
@@ -0,0 +1,23 @@
/**
* @file FileMode.h
* @brief Declaration of tc::io::FileMode
* @author Jack (jakcron)
* @version 0.1
* @date 2020/01/22
**/
#pragma once
namespace tc { namespace io {
/**
* @enum FileAccess
* @brief Defines constants for read, write, or read/write access to a file.
**/
enum class FileAccess
{
Read = 1, /**< Read access to the file. Data can be read from the file. Combine with Write for read/write access. */
Write = 2, /**< Write access to the file. Data can be written to the file. Combine with Read for read/write access. */
ReadWrite = Read|Write /**< Read and write access to the file. Data can be written to and read from the file. */
};
}} // namespace tc::io
@@ -0,0 +1,57 @@
/**
* @file FileExistsException.h
* @brief Declaration of tc::io::FileExistsException
* @author Jack (jakcron)
* @version 0.1
* @date 2020/01/26
**/
#pragma once
#include <tc/io/IOException.h>
namespace tc { namespace io {
/**
* @class FileExistsException
* @brief The exception that is thrown when an attempt to overwrite/remove a file that exists on disk fails.
**/
class FileExistsException : public tc::io::IOException
{
public:
/// Default Constructor
FileExistsException() noexcept :
tc::io::IOException()
{
}
/**
* @brief Basic Parameterized Constructor
*
* @param[in] what Explanation for exception
*
* @post
* - what() == what
* - module() == ""
* - error() == what
**/
FileExistsException(const std::string& what) noexcept :
tc::io::IOException(what)
{}
/**
* @brief Parameterized Constructor
*
* @param[in] module Name of module that threw the exception
* @param[in] what Explanation for exception
*
* @post
* - what() == "[" + module + " ERROR] " + what
* - module() == module
* - error() == what
**/
FileExistsException(const std::string& module, const std::string& what) noexcept :
tc::io::IOException(module, what)
{
}
};
}} // namespace tc::io
@@ -0,0 +1,26 @@
/**
* @file FileMode.h
* @brief Declaration of tc::io::FileMode
* @author Jack (jakcron)
* @version 0.1
* @date 2020/01/22
**/
#pragma once
namespace tc { namespace io {
/**
* @enum FileMode
* @brief Specifies how the operating system should open a file.
**/
enum class FileMode
{
CreateNew = 1, /**< Specifies that the operating system should create a new file. This requires Write permission. If the file already exists, an @ref tc::io::IOException exception is thrown. */
Create = 2, /**< Specifies that the operating system should create a new file. If the file already exists, it will be overwritten. This requires Write permission. FileMode.Create is equivalent to requesting that if the file does not exist, use CreateNew; otherwise, use Truncate. If the file already exists but is a hidden file, an @ref tc::UnauthorisedAccessException exception is thrown. */
Open = 3, /**< Specifies that the operating system should open an existing file. The ability to open the file is dependent on the value specified by the FileAccess enumeration. A @ref tc::io::FileNotFoundException exception is thrown if the file does not exist. */
OpenOrCreate = 4, /**< Specifies that the operating system should open a file if it exists; otherwise, a new file should be created. If the file is opened with FileAccess.Read, Read permission is required. If the file access is FileAccess.Write, Write permission is required. If the file is opened with FileAccess.ReadWrite, both Read and Write permissions are required. */
Truncate = 5, /**< Specifies that the operating system should open an existing file. When the file is opened, it should be truncated so that its size is zero bytes. This requires Write permission. Attempts to read from a file opened with FileMode.Truncate cause an @ref tc::ArgumentException exception. */
Append = 6 /**< Opens the file if it exists and seeks to the end of the file, or creates a new file. This requires Append permission. FileMode.Append can be used only in conjunction with FileAccess.Write. Trying to seek to a position before the end of the file throws an @ref tc::io::IOException exception, and any attempt to read fails and throws a @ref tc::NotSupportedException exception. */
};
}} // namespace tc::io
@@ -0,0 +1,57 @@
/**
* @file FileNotFoundException.h
* @brief Declaration of tc::io::FileNotFoundException
* @author Jack (jakcron)
* @version 0.1
* @date 2020/01/22
**/
#pragma once
#include <tc/io/IOException.h>
namespace tc { namespace io {
/**
* @class FileNotFoundException
* @brief The exception that is thrown when an attempt to access a file that does not exist on disk fails.
**/
class FileNotFoundException : public tc::io::IOException
{
public:
/// Default Constructor
FileNotFoundException() noexcept :
tc::io::IOException()
{
}
/**
* @brief Basic Parameterized Constructor
*
* @param[in] what Explanation for exception
*
* @post
* - what() == what
* - module() == ""
* - error() == what
**/
FileNotFoundException(const std::string& what) noexcept :
tc::io::IOException(what)
{}
/**
* @brief Parameterized Constructor
*
* @param[in] module Name of module that threw the exception
* @param[in] what Explanation for exception
*
* @post
* - what() == "[" + module + " ERROR] " + what
* - module() == module
* - error() == what
**/
FileNotFoundException(const std::string& module, const std::string& what) noexcept :
tc::io::IOException(module, what)
{
}
};
}} // namespace tc::io
@@ -0,0 +1,240 @@
/**
* @file FileStream.h
* @brief Declaration of tc::io::FileStream
* @author Jack (jakcron)
* @version 0.4
* @date 2020/01/23
**/
#pragma once
#include <tc/io/IStream.h>
#include <tc/io/Path.h>
#include <tc/io/FileMode.h>
#include <tc/io/FileAccess.h>
#include <tc/AccessViolationException.h>
#include <tc/ArgumentNullException.h>
#include <tc/ArgumentOutOfRangeException.h>
#include <tc/NotSupportedException.h>
#include <tc/NotImplementedException.h>
#include <tc/ObjectDisposedException.h>
#include <tc/OverflowException.h>
#include <tc/UnauthorisedAccessException.h>
#include <tc/SecurityException.h>
#include <tc/io/IOException.h>
#include <tc/io/FileExistsException.h>
#include <tc/io/FileNotFoundException.h>
#include <tc/io/PathTooLongException.h>
#ifdef _WIN32
#include <Windows.h>
#else
#include <cstdio>
#endif
namespace tc { namespace io {
/**
* @class FileStream
* @brief An implementation of IStream as a wrapper to local OS file access functions.
**/
class FileStream : public tc::io::IStream
{
public:
/**
* @brief Default constuctor
**/
FileStream();
/**
* @brief Move constructor
**/
FileStream(FileStream&& other);
/**
* @brief Open file stream
*
* @param[in] path A relative or absolute path for the file that the current FileStream object will encapsulate.
* @param[in] mode One of the enumeration values that determines how to open or create the file.
* @param[in] access One of the enumeration values that determines how the file can be accessed by the FileStream object. This also determines the values returned by the @ref canRead and @ref canWrite methods of the FileStream object. @ref canSeek is true if path specifies a disk file.
*
* @throw tc::ArgumentException @p path contains invalid characters or is empty.
* @throw tc::NotSupportedException @p path refers to an unsupported non-file device.
* @throw tc::io::IOException An I/O error, such as specifying @p mode @a CreateNew when the file specified by @p path already exists, occurred. Or the stream has been closed.
* @throw tc::SecurityException The caller does not have the required permission.
* @throw tc::io::DirectoryNotFoundException The specified path is invalid, such as being on an unmapped drive.
* @throw tc::UnauthorisedAccessException The @p access requested is not permitted by the operating system for the specified @p path, such as when @p access is @a Write or @a ReadWrite and the file or directory is set for read-only access.
* @throw tc::io::PathTooLongException The specified @p path, file name, or both exceed the system-defined maximum length.
* @throw tc::ArgumentOutOfRangeException @p mode contains an invalid value.
* @throw tc::ArgumentOutOfRangeException @p access contains an invalid value.
**/
FileStream(const tc::io::Path& path, FileMode mode, FileAccess access);
/**
* @brief Move assignment
**/
FileStream& operator=(FileStream&& other);
/**
* @brief Indicates whether the current stream supports reading.
**/
bool canRead() const;
/**
* @brief Indicates whether the current stream supports writing.
**/
bool canWrite() const;
/**
* @brief Indicates whether the current stream supports seeking.
**/
bool canSeek() const;
/**
* @brief Gets the length in bytes of the stream.
**/
int64_t length();
/**
* @brief Gets the position within the current stream.
*
* @return This is returns the result of seek(0, SeekOrigin::Current);
*
* @throw tc::NotSupportedException The stream does not support seeking.
**/
int64_t position();
/**
* @brief Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read.
*
* @param[out] ptr Pointer to an array of bytes. When this method returns, @p ptr contains the specified byte array with the values between 0 and (@p count - 1) replaced by the bytes read from the current source.
* @param[in] count The maximum number of bytes to be read from the current stream.
*
* @return The total number of bytes read into @p ptr. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached.
*
* @pre A stream must support reading for @ref read to work.
* @note Use @ref canRead to determine if this stream supports reading.
*
* @throw tc::AccessViolation @p ptr refers to inaccessible/protected memory.
* @throw tc::ArgumentNullException @p ptr is @a nullptr.
* @throw tc::ArgumentOutOfRangeException @p count is negative.
* @throw tc::io::IOException An I/O error occurred.
* @throw tc::NotSupportedException The stream does not support reading.
* @throw tc::ObjectDisposedException Methods were called after the stream was closed.
**/
size_t read(byte_t* ptr, size_t count);
/**
* @brief Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written.
*
* @param[in] ptr Pointer to an array of bytes. This method copies @p count bytes from @p ptr to the current stream.
* @param[in] count The number of bytes to be written to the current stream.
*
* @return The total number of bytes written to the stream. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached.
*
* @pre A stream must support writing for @ref write to work.
* @note Use @ref canWrite to determine if this stream supports writing.
*
* @throw tc::AccessViolation @p ptr refers to inaccessible/protected memory.
* @throw tc::ArgumentNullException @p ptr is @a nullptr.
* @throw tc::ArgumentOutOfRangeException @p count is negative.
* @throw tc::io::IOException An I/O error occurred.
* @throw tc::NotSupportedException The stream does not support writing.
* @throw tc::ObjectDisposedException Methods were called after the stream was closed.
**/
size_t write(const byte_t* ptr, size_t count);
/**
* @brief Sets the position within the current stream.
*
* @param[in] offset A byte offset relative to the origin parameter.
* @param[in] origin A value of type @ref tc::io::SeekOrigin indicating the reference point used to obtain the new position.
*
* @return The new position within the current stream.
*
* @pre A stream must support seeking for @ref seek to work.
* @note Use @ref canSeek to determine if this stream supports seeking.
*
* @throw tc::ArgumentOutOfRangeException @p offset or @p origin contains an invalid value.
* @throw tc::io::IOException An I/O error occurs.
* @throw tc::NotSupportedException The stream does not support seeking, such as if the stream is constructed from a pipe or console output.
* @throw tc::ObjectDisposedException Methods were called after the stream was closed.
* @throw tc::OverflowException The new stream position could not be represented in int64_t without overflowing.
**/
int64_t seek(int64_t offset, SeekOrigin origin);
/**
* @brief Sets the length of the current stream. THIS IS NOT IMPLEMENTED FOR @ref FileStream.
*
* @param[in] length The desired length of the current stream in bytes.
*
* @pre A stream must support both writing and seeking for @ref setLength to work.
* @note Use @ref canWrite to determine if this stream supports writing.
* @note Use @ref canSeek to determine if this stream supports seeking.
*
* @throw tc::NotSupportedException The stream does not support both writing and seeking, such as if the stream is constructed from a pipe or console output.
* @throw tc::ObjectDisposedException Methods were called after the stream was closed.
**/
void setLength(int64_t length);
/**
* @brief Clears all buffers for this stream and causes any buffered data to be written to the underlying device.
*
* @throw tc::io::IOException An I/O error occurs.
* @throw tc::ObjectDisposedException Methods were called after the stream was closed.
**/
void flush();
void dispose();
private:
static const std::string kClassName;
// delete copy constructor
FileStream(const FileStream&);
// delete copy assignment
FileStream& operator=(const FileStream&);
struct FileHandle
{
#ifdef _WIN32
HANDLE handle;
FileHandle(HANDLE h) : handle(h) {}
#else
int handle;
FileHandle(int h) : handle(h) {}
#endif
FileHandle() : handle(0) {}
FileHandle(FileHandle&& other) {handle = other.handle; other.handle = 0;}
~FileHandle();
private:
FileHandle(const FileHandle& other);
};
bool mCanRead;
bool mCanWrite;
bool mCanSeek;
bool mIsAppendRestrictSeekCall;
std::unique_ptr<tc::io::FileStream::FileHandle> mFileHandle;
#ifdef _WIN32
void open_impl(const tc::io::Path& path, FileMode mode, FileAccess access);
int64_t length_impl();
size_t read_impl(byte_t* ptr, size_t count);
size_t write_impl(const byte_t* ptr, size_t count);
int64_t seek_impl(int64_t offset, SeekOrigin origin);
void setLength_impl(int64_t length);
void flush_impl();
#else
void open_impl(const tc::io::Path& path, FileMode mode, FileAccess access);
int64_t length_impl();
size_t read_impl(byte_t* ptr, size_t count);
size_t write_impl(const byte_t* ptr, size_t count);
int64_t seek_impl(int64_t offset, SeekOrigin origin);
void setLength_impl(int64_t length);
void flush_impl();
#endif
};
}} // namespace tc::io
@@ -0,0 +1,124 @@
/**
* @file IFileSystem.h
* @brief Declaration of tc::io::IFileSystem
* @author Jack (jakcron)
* @version 0.6
* @date 2019/06/18
**/
#pragma once
#include <tc/types.h>
#include <tc/ResourceStatus.h>
#include <tc/io/IStream.h>
#include <tc/io/Path.h>
#include <tc/io/FileMode.h>
#include <tc/io/FileAccess.h>
namespace tc { namespace io {
/**
* @struct sDirectoryListing
* @brief Contains basic info about a directory with-in a FileSystem
*
* The includes the absolute path of the directory, and lists of child directory and file names.
*
* @note All strings are UTF-8
**/
struct sDirectoryListing
{
/// Absolute Path
tc::io::Path abs_path;
/// List of child directory names
std::vector<std::string> dir_list;
/// List of child file names
std::vector<std::string> file_list;
};
/**
* @class IFileSystem
*
* @brief An interface for implementing a basic FileSystem handler.
*
* Defines expected functionality including:
* - File access (open)
* - File management (create, remove)
* - Directory traversal (get current directory, change current directory)
* - Directory management (create, remove)
* - Directory listing
*
* @note IFileSystem uses the tc::io::Path class to represent a path, not as a literal string.
* @note It is up to the implementation of IFileSystem to validate and process tc::io::Path objects.
* @note It is up to the implementation to enforce tc::io::FileMode & tc::io::FileAccess.
**/
class IFileSystem
{
public:
/**
* @brief Destructor
**/
virtual ~IFileSystem() = default;
/**
* @brief Get state of IFileSystem
* @return ResourceStatus
**/
virtual tc::ResourceStatus state() = 0;
/**
* @brief Close the filesystem
**/
virtual void dispose() = 0;
/**
* @brief Create a new file
* @param[in] path A relative or absolute path to file.
**/
virtual void createFile(const tc::io::Path& path) = 0;
/**
* @brief Remove a file
* @param[in] path A relative or absolute path to file.
**/
virtual void removeFile(const tc::io::Path& path) = 0;
/**
* @brief Open a file
* @param[in] path A relative or absolute path to file.
* @param[in] mode One of the enumeration values that determines how to open or create the file.
* @param[in] access One of the enumeration values that determines how the file can be accessed by the @ref IStream object. This also determines the values returned by the @ref IStream::canRead and @ref IStream::canWrite methods of the IStream object. @ref IStream::canSeek is true if path specifies a disk file.
* @param[out] stream Pointer to IStream object to be instantiated
**/
virtual void openFile(const tc::io::Path& path, tc::io::FileMode mode, tc::io::FileAccess access, std::shared_ptr<tc::io::IStream>& stream) = 0;
/**
* @brief Create a new directory
* @param[in] path A relative or absolute path to directory.
**/
virtual void createDirectory(const tc::io::Path& path) = 0;
/**
* @brief Remove a directory
* @param[in] path A relative or absolute path to directory.
**/
virtual void removeDirectory(const tc::io::Path& path) = 0;
/**
* @brief Get the full path of the working directory
* @param[out] path Path object to populate
**/
virtual void getWorkingDirectory(tc::io::Path& path) = 0;
/**
* @brief Change the working directory
* @param[in] path A relative or absolute path to directory.
**/
virtual void setWorkingDirectory(const tc::io::Path& path) = 0;
/**
* @brief Get directory listing a directory
* @param[in] path A relative or absolute path to directory.
* @param[out] info sDirectoryListing object to populate
**/
virtual void getDirectoryListing(const tc::io::Path& path, tc::io::sDirectoryListing& info) = 0;
};
}} // namespace tc::io
@@ -0,0 +1,57 @@
/**
* @file IOException.h
* @brief Declaration of tc::io::IOException
* @author Jack (jakcron)
* @version 0.1
* @date 2020/01/22
**/
#pragma once
#include <tc/Exception.h>
namespace tc { namespace io {
/**
* @class IOException
* @brief The exception that is thrown when an I/O error occurs.
**/
class IOException : public tc::Exception
{
public:
/// Default Constructor
IOException() noexcept :
tc::Exception()
{
}
/**
* @brief Basic Parameterized Constructor
*
* @param[in] what Explanation for exception
*
* @post
* - what() == what
* - module() == ""
* - error() == what
**/
IOException(const std::string& what) noexcept :
tc::Exception(what)
{}
/**
* @brief Parameterized Constructor
*
* @param[in] module Name of module that threw the exception
* @param[in] what Explanation for exception
*
* @post
* - what() == "[" + module + " ERROR] " + what
* - module() == module
* - error() == what
**/
IOException(const std::string& module, const std::string& what) noexcept :
tc::Exception(module, what)
{
}
};
}} // namespace tc::io
@@ -0,0 +1,63 @@
/**
* @file IOUtil.h
* @brief Declaration of tc::io::IOUtil
* @author Jack (jakcron)
* @version 0.1
* @date 2020/04/10
**/
#pragma once
#include <tc/types.h>
namespace tc { namespace io {
/**
* @class IOUtil
* @brief Utility functions for IO based classes.
**/
class IOUtil
{
public:
/**
* @brief Convert size_t to int64_t safely.
**/
static int64_t castSizeToInt64(size_t size);
/**
* @brief Convert int64_t to size_t safely.
**/
static size_t castInt64ToSize(int64_t length);
/**
* @brief Get size of available data for an IO class performing a read or write operation.
*
* @param[in] data_length Total length of data.
* @param[in] data_offset Byte offset in data to begin operation from.
*
* @return Largest possible operable data size.
**/
static size_t getAvailableSize(int64_t data_length, int64_t data_offset);
/**
* @brief Get size of writeable data for an IO class, given the data length, desired read offset and count.
*
* @param[in] data_length Total length of data.
* @param[in] data_offset Byte offset in data to begin reading from.
* @param[in] requested_read_count Number of bytes to read.
*
* @return Largest possible readable count.
**/
static size_t getReadableCount(int64_t data_length, int64_t data_offset, size_t requested_read_count);
/**
* @brief Get size of writeable data for an IO class, given the data length, desired write offset and count.
*
* @param[in] data_length Total length of data.
* @param[in] data_offset Byte offset in data to begin writing from.
* @param[in] requested_write_count Number of bytes to write.
*
* @return Largest possible writeable count.
**/
static size_t getWritableCount(int64_t data_length, int64_t data_offset, size_t requested_write_count);
};
}} // namespace tc::io
@@ -0,0 +1,40 @@
/**
* @file IPathResolver.h
* @brief Declaration of tc::io::IPathResolver
* @author Jack (jakcron)
* @version 0.2
* @date 2022/02/22
**/
#pragma once
#include <tc/io/Path.h>
namespace tc { namespace io {
/**
* @class IPathResolver
* @brief This is an interface for a class that resolves relative paths to canonical paths.
*/
class IPathResolver
{
public:
virtual ~IPathResolver() = default;
/**
* @brief Resolve path to its canonical path
*
* @param path Input path.
* @param canonical_path Output path to write resolved canonical path.
*/
virtual void resolveCanonicalPath(const tc::io::Path& path, tc::io::Path& canonical_path) const = 0;
/**
* @brief Resolve path to its canonical path
*
* @param path Input path.
*
* @return Resolved canonical path.
*/
virtual tc::io::Path resolveCanonicalPath(const tc::io::Path& path) const = 0;
};
}} // namespace tc::io
@@ -0,0 +1,37 @@
/**
* @file IPortablePathResolver.h
* @brief Declaration of tc::io::IPortablePathResolver
* @author Jack (jakcron)
* @version 0.1
* @date 2022/02/22
**/
#pragma once
#include <tc/io/IPathResolver.h>
namespace tc { namespace io {
/**
* @class IPortablePathResolver
* @brief This is an extension for the IPathResolver interface that specifies additional methods for setting the current directory. For more information see @ref tc::io::IPathResolver.
*/
class IPortablePathResolver : public IPathResolver
{
public:
virtual ~IPortablePathResolver() = default;
/**
* @brief Set the current directory path
*
* @param path Canonical current directory path.
*/
virtual void setCurrentDirectory(const tc::io::Path& path) = 0;
/**
* @brief Get the current directory path
*
* @return tc::io::Path Canonical current directory path.
*/
virtual const tc::io::Path& getCurrentDirectory() const = 0;
};
}} // namespace tc::io
@@ -0,0 +1,28 @@
/**
* @file IReadableSink.h
* @brief Declaration of tc::io::IReadableSink
* @author Jack (jakcron)
* @version 0.1
* @date 2020/02/07
**/
#pragma once
#include <tc/io/ISink.h>
#include <tc/io/ISource.h>
namespace tc { namespace io {
/**
* @class IReadableSink
* @brief An interface defining a byte data sink that can be also provide an ISource.
**/
class IReadableSink : public tc::io::ISink
{
public:
/// Destructor
virtual ~IReadableSink() = default;
/// Convert to ISource
virtual std::shared_ptr<tc::io::ISource>& toSource() = 0;
};
}} // namespace tc::io
@@ -0,0 +1,46 @@
/**
* @file ISink.h
* @brief Declaration of tc::io::ISink
* @author Jack (jakcron)
* @version 0.2
* @date 2020/04/10
**/
#pragma once
#include <tc/types.h>
#include <tc/ResourceStatus.h>
#include <tc/ByteData.h>
namespace tc { namespace io {
/**
* @class ISink
* @brief An interface defining a byte data sink.
**/
class ISink
{
public:
/// Destructor
virtual ~ISink() = default;
/// Gets the length of the sink.
virtual int64_t length() = 0;
/**
* @brief Sets the length of the sink.
*
* @param[in] length The desired length of the sink in bytes.
**/
virtual void setLength(int64_t length) = 0;
/**
* @brief Push data to the sink.
*
* @param[in] data Data to be pushed to the sink.
* @param[in] offset Zero-based offset in sink to push data.
*
* @return Number of bytes pushed to sink.
**/
virtual size_t pushData(const tc::ByteData& data, int64_t offset) = 0;
};
}} // namespace tc::io
@@ -0,0 +1,39 @@
/**
* @file ISource.h
* @brief Declaration of tc::io::ISource
* @author Jack (jakcron)
* @version 0.1
* @date 2020/01/27
**/
#pragma once
#include <tc/types.h>
#include <tc/ResourceStatus.h>
#include <tc/ByteData.h>
namespace tc { namespace io {
/**
* @class ISource
* @brief An interface defining a byte data source.
**/
class ISource
{
public:
/// Destructor
virtual ~ISource() = default;
/// Gets the length of the source.
virtual int64_t length() = 0;
/**
* @brief Pull data from the source.
*
* @param[in] offset Zero-based offset in source to pull data.
* @param[in] count The maximum number of bytes to be pull from the source.
*
* @return ByteData containing data pulled from source.
**/
virtual tc::ByteData pullData(int64_t offset, size_t count) = 0;
};
}} // namespace tc::io
@@ -0,0 +1,117 @@
/**
* @file IStream.h
* @brief Declaration of tc::io::IStream
* @author Jack (jakcron)
* @version 0.4
* @date 2020/01/22
**/
#pragma once
#include <tc/types.h>
#include <tc/io/SeekOrigin.h>
namespace tc { namespace io {
/**
* @class IStream
* @brief An interface for implementing a basic data stream handler.
*
* Defines expcted functionality required to process/access a data stream.
*
* Usage of size_t with offsets and lengths reflect run-time memory allocation limits
* Usage of int64_t with offsets and lengths reflect more closely the natural size limits of a stream
**/
class IStream
{
public:
/**
* @brief Destructor
**/
virtual ~IStream() = default;
/**
* @brief Indicates whether the current stream supports reading.
**/
virtual bool canRead() const = 0;
/**
* @brief Indicates whether the current stream supports writing.
**/
virtual bool canWrite() const = 0;
/**
* @brief Indicates whether the current stream supports seeking.
**/
virtual bool canSeek() const = 0;
/**
* @brief Gets the length in bytes of the stream.
**/
virtual int64_t length() = 0;
/**
* @brief Gets the position within the current stream.
**/
virtual int64_t position() = 0;
/**
* @brief Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read.
*
* @param[out] ptr Pointer to an array of bytes. When this method returns, @p ptr contains the specified byte array with the values between 0 and (@p count - 1) replaced by the bytes read from the current source.
* @param[in] count The maximum number of bytes to be read from the current stream.
*
* @return The total number of bytes read into @p ptr. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached.
*
* @pre A stream must support reading for @ref read to work.
* @note Use @ref canRead to determine if this stream supports reading.
**/
virtual size_t read(byte_t* ptr, size_t count) = 0;
/**
* @brief Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written.
*
* @param[in] ptr Pointer to an array of bytes. This method copies @p count bytes from @p ptr to the current stream.
* @param[in] count The number of bytes to be written to the current stream.
*
* @return The total number of bytes written to the stream. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached.
*
* @pre A stream must support writing for @ref write to work.
* @note Use @ref canWrite to determine if this stream supports writing.
**/
virtual size_t write(const byte_t* ptr, size_t count) = 0;
/**
* @brief Sets the position within the current stream.
*
* @param[in] offset A byte offset relative to the origin parameter.
* @param[in] origin A value of type @ref tc::io::SeekOrigin indicating the reference point used to obtain the new position.
*
* @return The new position within the current stream.
*
* @pre A stream must support seeking for @ref seek to work.
* @note Use @ref canSeek to determine if this stream supports seeking.
**/
virtual int64_t seek(int64_t offset, SeekOrigin origin) = 0;
/**
* @brief Sets the length of the current stream.
*
* @param[in] length The desired length of the current stream in bytes.
*
* @pre A stream must support both writing and seeking for @ref setLength to work.
* @note Use @ref canWrite to determine if this stream supports writing.
* @note Use @ref canSeek to determine if this stream supports seeking.
**/
virtual void setLength(int64_t length) = 0;
/**
* @brief Clears all buffers for this stream and causes any buffered data to be written to the underlying device.
**/
virtual void flush() = 0;
/**
* @brief Releases all resources used by the Stream.
**/
virtual void dispose() = 0;
};
}}
@@ -0,0 +1,143 @@
/**
* @file LocalFileSystem.h
* @brief Declaration of tc::io::LocalFileSystem
* @author Jack (jakcron)
* @version 0.6
* @date 2022/01/23
**/
#pragma once
#include <tc/io/IFileSystem.h>
#include <tc/io/IOException.h>
#include <tc/io/DirectoryNotEmptyException.h>
#include <tc/io/DirectoryNotFoundException.h>
#include <tc/io/FileNotFoundException.h>
#include <tc/io/PathTooLongException.h>
#include <tc/UnauthorisedAccessException.h>
#ifdef _WIN32
#include <windows.h>
#endif
namespace tc { namespace io {
/**
* @class LocalFileSystem
* @brief An IFileSystem wrapper around the existing OS FileSystem API.
**/
class LocalFileSystem : public tc::io::IFileSystem
{
public:
/// Default Constructor
LocalFileSystem();
tc::ResourceStatus state();
void dispose();
/**
* @brief Create a new file
*
* @param[in] path A relative or absolute path to file.
*
* @throw tc::ArgumentException @p path contains invalid characters or is empty.
* @throw tc::NotSupportedException @p path refers to an unsupported non-file device.
* @throw tc::io::IOException An unspecfied I/O error occurred. Or the stream has been closed.
* @throw tc::SecurityException The caller does not have the required permission.
* @throw tc::io::DirectoryNotFoundException The specified path is invalid, such as being on an unmapped drive.
* @throw tc::UnauthorisedAccessException The @p access requested is not permitted by the operating system for the specified @p path.
* @throw tc::io::PathTooLongException The specified @p path, file name, or both exceed the system-defined maximum length.
**/
void createFile(const tc::io::Path& path);
/**
* @brief Remove a file
* @param[in] path A relative or absolute path for the file that the current @ref IFileSystem object will remove.
*
* @throw tc::UnauthorisedAccessException @p path specified a read-only file.
* @throw tc::UnauthorisedAccessException @p path is a directory.
* @throw tc::UnauthorisedAccessException The caller does not have the required permission.
* @throw tc::UnauthorisedAccessException The file is currently in use.
* @throw tc::io::IOException File An I/O error has occured.
* @throw tc::io::PathTooLongException The specified path, file name, or both exceed the system-defined maximum length.
* @throw tc::io::DirectoryNotFoundException A component of the path prefix is not a directory.
* @throw tc::io::FileNotFoundException The specifed file does not exist.
**/
void removeFile(const tc::io::Path& path);
/**
* @brief Open a file
* @param[in] path A relative or absolute path for the file that the current @ref IFileSystem object will open an @ref IStream for.
* @param[in] mode One of the enumeration values that determines how to open or create the file.
* @param[in] access One of the enumeration values that determines how the file can be accessed by the @ref IStream object. This also determines the values returned by the @ref IStream::canRead and @ref IStream::canWrite methods of the IStream object. @ref IStream::canSeek is true if path specifies a disk file.
* @param[out] stream Pointer to IStream object to be instantiated
**/
void openFile(const tc::io::Path& path, tc::io::FileMode mode, tc::io::FileAccess access, std::shared_ptr<tc::io::IStream>& stream);
/**
* @brief Create a new directory
* @param[in] path Path to directory
*
* @throw tc::UnauthorisedAccessException Write permission is denied for a parent direcory.
* @throw tc::UnauthorisedAccessException Parent directory resides in a read-only file system.
* @throw tc::UnauthorisedAccessException The caller does not have the required permission.
* @throw tc::io::IOException An I/O error has occured.
* @throw tc::io::PathTooLongException The specified path, directory name, or both exceed the system-defined maximum length.
* @throw tc::io::DirectoryNotFoundException A component of the path prefix is not a directory or does not exist.
**/
void createDirectory(const tc::io::Path& path);
/**
* @brief Remove a directory
* @param[in] path Path to directory
*
* @throw tc::UnauthorisedAccessException Directory resides in a read-only file system.
* @throw tc::UnauthorisedAccessException The caller does not have the required permission.
* @throw tc::UnauthorisedAccessException The directory is a mount point.
* @throw tc::io::IOException An I/O error has occured.
* @throw tc::io::PathTooLongException The specified path, directory name, or both exceed the system-defined maximum length.
* @throw tc::io::DirectoryNotEmptyException The named directory is not empty.
* @throw tc::io::DirectoryNotFoundException A component of the path prefix is not a directory.
* @throw tc::io::DirectoryNotFoundException The named directory does not exist.
**/
void removeDirectory(const tc::io::Path& path);
/**
* @brief Get the full path of the working directory
* @param[out] path Path object to populate
*
* @throw tc::UnauthorisedAccessException Read or search permission was denied for a component of the pathname.
* @throw tc::io::IOException An I/O error has occured.
**/
void getWorkingDirectory(tc::io::Path& path);
/**
* @brief Change the working directory
* @param[in] path Path to directory
*
* @throw tc::UnauthorisedAccessException Search permission was denied for a component of the pathname.
* @throw tc::io::IOException An I/O error has occured.
* @throw tc::io::PathTooLongException The specified path, directory name, or both exceed the system-defined maximum length.
* @throw tc::io::DirectoryNotFoundException A component of the path prefix is not a directory.
* @throw tc::io::DirectoryNotFoundException The named directory does not exist.
**/
void setWorkingDirectory(const tc::io::Path& path);
/**
* @brief Get directory listing a directory
* @param[in] path Path to directory
* @param[out] info sDirectoryListing object to populate
*
* @throw tc::UnauthorisedAccessException Permission denied.
* @throw tc::io::IOException An I/O error has occured.
* @throw tc::io::DirectoryNotFoundException A component of the path prefix is not a directory.
* @throw tc::io::DirectoryNotFoundException The named directory does not exist.
**/
void getDirectoryListing(const tc::io::Path& path, tc::io::sDirectoryListing& info);
private:
static const std::string kClassName;
tc::ResourceStatus mState;
};
}} // namespace tc::io
@@ -0,0 +1,68 @@
/**
* @file MemorySource.h
* @brief Declaration of tc::io::MemorySource
* @author Jack (jakcron)
* @version 0.1
* @date 2020/03/20
**/
#pragma once
#include <tc/io/ISource.h>
namespace tc { namespace io {
/**
* @class MemorySource
* @brief A block of run-time memory wrapped as an ISource object.
**/
class MemorySource : public tc::io::ISource
{
public:
/**
* @brief Default constructor
* @post This will create a MemorySource with length() == 0.
**/
MemorySource();
/**
* @brief Initialize MemorySource (by copy) with a tc::ByteData object.
*
* @param[in] byte_data The base data to create the MemorySource from.
**/
MemorySource(const tc::ByteData& byte_data);
/**
* @brief Initialize MemorySource (by move) with a tc::ByteData object.
*
* @param[in] byte_data The base data to create the MemorySource from.
**/
MemorySource(tc::ByteData&& byte_data);
/**
* @brief Initialize MemorySource (by copy) with a block of memory.
*
* @param[in] data Pointer to block of memory which will populate this MemorySource.
* @param[in] len Length of data to copy into this MemorySource.
**/
MemorySource(const byte_t* data, size_t len);
/**
* @brief Gets the length of the source.
**/
int64_t length();
/**
* @brief Pull data from source
*
* @param[in] offset Zero-based offset in source to pull data.
* @param[in] count The maximum number of bytes to be pull from the source.
*
* @return ByteData containing data pulled from source
**/
tc::ByteData pullData(int64_t offset, size_t count);
private:
static const std::string kClassName;
tc::ByteData mData;
};
}} // namespace tc::io
@@ -0,0 +1,155 @@
/**
* @file MemoryStream.h
* @brief Declaration of tc::io::MemoryStream
* @author Jack (jakcron)
* @version 0.1
* @date 2020/02/16
**/
#pragma once
#include <tc/io/IStream.h>
#include <tc/ByteData.h>
#include <tc/ArgumentNullException.h>
#include <tc/ArgumentOutOfRangeException.h>
#ifdef _WIN32
#include <Windows.h>
#else
#include <cstdio>
#endif
namespace tc { namespace io {
/**
* @class MemoryStream
* @brief A block of run-time memory wrapped as an IStream object.
**/
class MemoryStream : public tc::io::IStream
{
public:
/**
* @brief Default constuctor
**/
MemoryStream();
/**
* @brief Create MemoryStream
*
* @param[in] length Length of the stream in bytes.
**/
MemoryStream(size_t length);
/**
* @brief Initialize MemoryStream (by copy) with a tc::ByteData object.
*
* @param[in] byte_data The base data to create the MemoryStream from.
**/
MemoryStream(const tc::ByteData& byte_data);
/**
* @brief Initialize MemoryStream (by move) with a tc::ByteData object.
*
* @param[in] byte_data The base data to create the MemoryStream from.
**/
MemoryStream(tc::ByteData&& byte_data);
/**
* @brief Initialize MemoryStream (by copy) with a block of memory.
*
* @param[in] data Pointer to memory block which will populate this MemoryStream.
* @param[in] len Length of data to copy int this MemoryStream.
**/
MemoryStream(const byte_t* data, size_t len);
/**
* @brief Indicates whether the current stream supports reading.
**/
bool canRead() const;
/**
* @brief Indicates whether the current stream supports writing.
**/
bool canWrite() const;
/**
* @brief Indicates whether the current stream supports seeking.
**/
bool canSeek() const;
/**
* @brief Gets the length in bytes of the stream.
**/
int64_t length();
/**
* @brief Gets the position within the current stream.
**/
int64_t position();
/**
* @brief Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read.
*
* @param[out] ptr Pointer to an array of bytes. When this method returns, @p ptr contains the specified byte array with the values between 0 and (@p count - 1) replaced by the bytes read from the current source.
* @param[in] count The maximum number of bytes to be read from the current stream.
*
* @return The total number of bytes read into @p ptr. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached.
*
* @throw tc::ArgumentNullException @p ptr is @a nullptr.
**/
size_t read(byte_t* ptr, size_t count);
/**
* @brief Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written.
*
* @param[in] ptr Pointer to an array of bytes. This method copies @p count bytes from @p ptr to the current stream.
* @param[in] count The number of bytes to be written to the current stream.
*
* @return The total number of bytes written to the stream. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached.
*
* @throw tc::ArgumentNullException @p ptr is @a nullptr.
**/
size_t write(const byte_t* ptr, size_t count);
/**
* @brief Sets the position within the current stream.
*
* @param[in] offset A byte offset relative to the origin parameter.
* @param[in] origin A value of type @ref tc::io::SeekOrigin indicating the reference point used to obtain the new position.
*
* @return The new position within the current stream.
*
* @throw tc::ArgumentOutOfRangeException @p offset and @p origin indicate an invalid stream position.
* @throw tc::ArgumentOutOfRangeException @p origin contains an invalid value.
**/
int64_t seek(int64_t offset, SeekOrigin origin);
/**
* @brief Sets the length of the current stream.
*
* @param[in] length The desired length of the current stream in bytes.
*
* @post If the new length is smaller than the current length, the data will be truncated.
*
* @throw tc::ArgumentOutOfRangeException @p length exceeds the maximum possible value for a MemoryStream.
* @throw tc::ArgumentOutOfRangeException @p length is negative.
**/
void setLength(int64_t length);
/**
* @brief This does nothing for tc::io::MemoryStream
**/
void flush();
/**
* @brief Release internal memory. This will make this stream length 0.
**/
void dispose();
private:
static const std::string kClassName;
tc::ByteData mData;
int64_t mPosition;
};
}} // namespace tc::io
@@ -0,0 +1,96 @@
/**
* @file OverlayedSource.h
* @brief Declaration of tc::io::OverlayedSource
* @author Jack (jakcron)
* @version 0.1
* @date 2020/03/21
**/
#pragma once
#include <tc/io/ISource.h>
#include <vector>
#include <tc/ArgumentNullException.h>
#include <tc/ArgumentOutOfRangeException.h>
namespace tc { namespace io {
/**
* @class OverlayedSource
* @brief This will replaces regions within a base source with one or more other sources, so as to override those regions in the base source when pullData() is called.
**/
class OverlayedSource : public tc::io::ISource
{
public:
/**
* @struct OverlaySourceInfo
* @brief This contains information about an overlay region. Used with @ref OverlayedSource::OverlayedSource()
**/
struct OverlaySourceInfo
{
/// ISource to overlay with
std::shared_ptr<tc::io::ISource> overlay_source;
/// Offset in base source to overlay
int64_t offset;
/// Length of region in base source to overlay
int64_t length;
};
/**
* @brief Default constructor
* @post This will create a OverlayedSource with length() == 0.
**/
OverlayedSource();
/**
* @brief Overlay base source with one source
*
* @param[in] base_source Base source to be overlayed.
* @param[in] overlay_source Source to overlay onto the base source.
* @param[in] offset Offset in base source to overlay
* @param[in] length Length in base source to overlay
*
* @throw tc::ArgumentNullException @p base_source or @p overlay_source was null.
* @throw tc::ArgumentOutOfRangeException @p length was greater than the length of @p overlay_source .
* @throw tc::ArgumentOutOfRangeException The overlay region offset is negative or the total size exceeded the length of @p base_source .
**/
OverlayedSource(const std::shared_ptr<tc::io::ISource>& base_source, const std::shared_ptr<tc::io::ISource>& overlay_source, int64_t offset, int64_t length);
/**
* @brief Overlay base source with multiple sources
*
* @param[in] base_source Base source to be overlayed.
* @param[in] overlay_source_infos Vector of sources to overlay onto the base source. @ref OverlaySourceInfo
*
* @throw tc::ArgumentNullException @p base_source or one of the overlay sources was null.
* @throw tc::ArgumentOutOfRangeException An overlay source was smaller than the region in the base source it was supposed to overlay.
* @throw tc::ArgumentOutOfRangeException A region to overlay in the base source either partly or entirely does not exist.
**/
OverlayedSource(const std::shared_ptr<tc::io::ISource>& base_source, const std::vector<OverlaySourceInfo>& overlay_source_infos);
/**
* @brief Gets the length of the source.
**/
int64_t length();
/**
* @brief Pull data from source
*
* @param[in] offset Zero-based offset in source to pull data.
* @param[in] count The maximum number of bytes to be pull from the source.
*
* @return ByteData containing data pulled from source.
**/
tc::ByteData pullData(int64_t offset, size_t count);
private:
static const std::string kClassName;
std::shared_ptr<tc::io::ISource> mBaseSource;
std::vector<OverlaySourceInfo> mOverlaySourceInfos;
void getOverlaySourcePullableRegion(int64_t base_pull_offset, size_t base_pull_count, const OverlaySourceInfo& overlay_info, int64_t& overlay_pull_offset, size_t& overlay_pull_count);
};
}} // namespace tc::io
@@ -0,0 +1,59 @@
/**
* @file PaddingSource.h
* @brief Declaration of tc::io::PaddingSource
* @author Jack (jakcron)
* @version 0.1
* @date 2020/02/07
**/
#pragma once
#include <tc/io/ISource.h>
#include <tc/ArgumentOutOfRangeException.h>
namespace tc { namespace io {
/**
* @class PaddingSource
* @brief A source that provides dummy/filler data.
**/
class PaddingSource : public tc::io::ISource
{
public:
/**
* @brief Default constructor
* @post This will create a PaddingSource with length() == 0.
**/
PaddingSource();
/**
* @brief Create PaddingSource
*
* @param[in] padding_byte Byte to fill data pulled using @ref pullData.
* @param[in] length Length of source.
*
* @throw tc::ArgumentOutOfRangeException @p length is negative.
**/
PaddingSource(byte_t padding_byte, int64_t length);
/// Get length of source
int64_t length();
/**
* @brief Pull data from source
*
* @param[in] offset Zero-based offset in source to pull data.
* @param[in] count The maximum number of bytes to be pull from the source.
*
* @return ByteData containing data pulled from source
*
* @throw tc::OutOfMemoryException The @ref tc::ByteData object could not be created due to insuffient memory.
**/
tc::ByteData pullData(int64_t offset, size_t count);
private:
static const std::string kClassName;
int64_t mSourceLength;
byte_t mPaddingByte;
};
}} // namespace tc::io
@@ -0,0 +1,253 @@
/**
* @file Path.h
* @brief Declaration of tc::io::Path
* @author Jack (jakcron)
* @version 0.5
* @date 2022/02/27
*/
#pragma once
#include <list>
#include <tc/types.h>
#include <tc/ArgumentException.h>
namespace tc { namespace io {
/**
* @class Path
* @brief Represents a unicode path for a filesystem
*
* This stores a path as a list of path elements.
**/
class Path
{
public:
/**
* @brief This enum defines the Path format type, used for encoding to string.
* @details
* This defines the encoding type used when encoding Path as a string.
* See @ref to_string(), @ref to_u16string() and @ref to_u32string() for more info.
*/
enum class Format
{
Native = 0, /**< Path format for the native environment */
POSIX = 1, /**< Path format for POSIX based environments (Unix, Linux, macOS, WSL, etc) */
Win32 = 2, /**< Path format for Microsoft Windows based environments */
};
/// Type of const_iterator for Path
using const_iterator = typename std::list<std::string>::const_iterator;
/// Type of iterator for Path
using iterator = typename std::list<std::string>::iterator;
/// Maximum value for size_t, used to indicated unbounded length for @ref subpath()
static const size_t npos = -1;
/// Default Constructor
Path();
/// Initalizer List Constructor
Path(std::initializer_list<std::string> list);
/**
* @brief Create Path from a literal UTF-8 encoded string
*
* @param[in] path UTF-8 encoded path
*
* @pre
* - @p path can have either forward or backward slash path separators ('/' or '\') but not both
*
* @throws tc::ArgumentException Path literal has both forward ('/') and backward ('\') path separators.
*
* @note No filtering or processing of special characters is done (e.g. '.', '~')
**/
Path(const std::string& path);
/**
* @brief Create Path from a literal UTF-16 encoded string
*
* @param[in] path UTF-16 encoded path
*
* @pre
* - @p path can have either forward or backward slash path separators ('/' or '\') but not both
*
* @throws tc::ArgumentException Path literal has both forward ('/') and backward ('\') path separators.
*
* @note
* No filtering or processing of special characters is done (e.g. '.', '~')
**/
Path(const std::u16string& path);
/**
* @brief Create Path from a literal UTF-32 encoded string
*
* @param[in] path UTF-32 encoded path
*
* @pre
* - @p path can have either forward or backward slash path separators ('/' or '\') but not both
*
* @throws tc::ArgumentException Path literal has both forward ('/') and backward ('\') path separators.
*
* @note
* No filtering or processing of special characters is done (e.g. '.', '~')
**/
Path(const std::u32string& path);
/// Addition operator
Path operator+(const Path& other) const;
/// Append operator
void operator+=(const Path& other);
/// Equality operator
bool operator==(const Path& other) const;
/// Inequality operator
bool operator!=(const Path& other) const;
/// Comparison operator
bool operator<(const Path& other) const;
/**
* @brief Returns a reference to the first element in the container.
*
* @return reference to the first element
*
* @note Calling front on an empty container is undefined.
* @note For a Path p, the expression p.front() is equivalent to *p.begin().
**/
std::string& front();
/**
* @brief Returns a const reference to the first element in the container.
*
* @return const reference to the first element
*
* @note Calling front on an empty container is undefined.
* @note For a Path p, the expression p.front() is equivalent to *p.begin().
**/
const std::string& front() const;
/**
* @brief Returns a reference to the last element in the container.
*
* @return reference to the last element
*
* @note Calling back on an empty container is undefined.
* @note For a Path p, the expression p.back() is equivalent to *(--p.end()).
**/
std::string& back();
/**
* @brief Returns a const reference to the last element in the container.
*
* @return const reference to the last element
*
* @note Calling back on an empty container is undefined.
* @note For a Path p, the expression p.back() is equivalent to *(--p.end()).
**/
const std::string& back() const;
/// Begin Iterator, points to front element
iterator begin();
/// Const Begin Iterator, points to front element
const_iterator begin() const;
/// End Iterator, points to after the last element
iterator end();
/// Const End Iterator, points to after the last element
const_iterator end() const;
/**
* @brief Remove element at the front of the path
*
* @note Calling pop_front on an empty container is undefined.
**/
void pop_front();
/**
* @brief Remove element at the back of the path
*
* @note Calling pop_back on an empty container is undefined.
**/
void pop_back();
/// Insert path element at the front of the path
void push_front(const std::string& str);
/// Insert path element at the back of the path
void push_back(const std::string& str);
/// Clear all elements from the path
void clear();
/// Get number of path elements
size_t size() const;
/// Checks whether the path is empty
bool empty() const;
/**
* @brief Create a path from a subset of this path
*
* @param[in] pos Position of first path element
* @param[in] len Number of path elements. Default value is @ref npos, indicating include all path elements after @p pos.
* @return tc::io::Path Sub-path created from this path.
*/
tc::io::Path subpath(size_t pos, size_t len = npos) const;
/**
* @brief Create a path from a subset of this path
*
* @param[in] begin Iterator pointing to the first element
* @param[in] end Iterator
* @return tc::io::Path Sub-path created from this path.
*/
tc::io::Path subpath(const_iterator begin, const_iterator end) const;
/**
* @brief Convert path to std::string
*
* @param[in] format @ref tc::io::Path::Format format to encode path as string. Default is @ref tc::io::Path::Format::Native.
* @return std::string UTF-8 encoded path string
*/
std::string to_string(Format format = Format::Native) const;
/**
* @brief Convert path to std::u16string
*
* @param[in] format @ref tc::io::Path::Format format to encode path as string. Default is @ref tc::io::Path::Format::Native.
* @return std::u16string UTF-16 encoded path string
*/
std::u16string to_u16string(Format format = Format::Native) const;
/**
* @brief Convert path to std::u32string
*
* @param[in] format @ref tc::io::Path::Format format to encode path as string. Default is @ref tc::io::Path::Format::Native.
* @return std::u32string UTF-32 encoded path string
*/
std::u32string to_u32string(Format format = Format::Native) const;
/// Implicit conversion to a natively formatted std::string
operator std::string() const;
/// Implicit conversion to a natively formatted std::u16string
operator std::u16string() const;
/// Implicit conversion to a natively formatted std::u32string
operator std::u32string() const;
private:
static const std::string kClassName;
std::list<std::string> mUnicodePath;
void initializePath(const std::string& src);
void appendPath(const std::list<std::string>& other);
};
}} // namespace tc::io
@@ -0,0 +1,57 @@
/**
* @file PathTooLongException.h
* @brief Declaration of tc::io::PathTooLongException
* @author Jack (jakcron)
* @version 0.1
* @date 2020/01/22
**/
#pragma once
#include <tc/io/IOException.h>
namespace tc { namespace io {
/**
* @class PathTooLongException
* @brief The exception that is thrown when a path or fully qualified file name is longer than the system-defined maximum length.
**/
class PathTooLongException : public tc::io::IOException
{
public:
/// Default Constructor
PathTooLongException() noexcept :
tc::io::IOException()
{
}
/**
* @brief Basic Parameterized Constructor
*
* @param[in] what Explanation for exception
*
* @post
* - what() == what
* - module() == ""
* - error() == what
**/
PathTooLongException(const std::string& what) noexcept :
tc::io::IOException(what)
{}
/**
* @brief Parameterized Constructor
*
* @param[in] module Name of module that threw the exception
* @param[in] what Explanation for exception
*
* @post
* - what() == "[" + module + " ERROR] " + what
* - module() == module
* - error() == what
**/
PathTooLongException(const std::string& module, const std::string& what) noexcept :
tc::io::IOException(module, what)
{
}
};
}} // namespace tc::io
@@ -0,0 +1,39 @@
/**
* @file PathUtil.h
* @brief Declaration of tc::io::PathUtil
* @author Jack (jakcron)
* @version 0.2
* @date 2020/03/22
*/
#pragma once
#include <list>
#include <tc/types.h>
#include <tc/io/Path.h>
namespace tc { namespace io {
/**
* @class PathUtil
* @brief Collection of utilities related to tc::io::Path
**/
class PathUtil
{
public:
/**
* @brief Format a Path as a Windows style UTF-16 string
* @param[in] path Source Path
* @param[out] out Destination UTF-16 string
* @note See @ref tc::io::Path
**/
static void pathToWindowsUTF16(const tc::io::Path& path, std::u16string& out);
/**
* @brief Format a Path as a Unix/Linux style UTF-8 string
* @param[in] path Source Path
* @param[out] out Destination UTF-8 string
* @note See @ref tc::io::Path
**/
static void pathToUnixUTF8(const tc::io::Path& path, std::string& out);
};
}} // namespace tc::io
@@ -0,0 +1,23 @@
/**
* @file SeekOrigin.h
* @brief Declaration of tc::io::SeekOrigin
* @author Jack (jakcron)
* @version 0.1
* @date 2020/01/22
**/
#pragma once
namespace tc { namespace io {
/**
* @enum SeekOrigin
* @brief Specifies the position in a stream to use for seeking.
**/
enum class SeekOrigin
{
Begin = 0, /**< Specifies the beginning of a stream. */
Current = 1, /**< Specifies the current position within a stream. */
End = 2 /**< Specifies the end of a stream. */
};
}} // namespace tc::io
@@ -0,0 +1,76 @@
/**
* @file StreamSink.h
* @brief Declaration of tc::io::StreamSink
* @author Jack (jakcron)
* @version 0.2
* @date 2020/04/10
**/
#pragma once
#include <tc/io/ISink.h>
#include <tc/io/IStream.h>
#include <tc/ArgumentNullException.h>
#include <tc/ObjectDisposedException.h>
#include <tc/NotSupportedException.h>
#include <tc/io/IOException.h>
namespace tc { namespace io {
/**
* @class StreamSink
* @brief An IStream wrapped in an ISink.
**/
class StreamSink : public tc::io::ISink
{
public:
/**
* @brief Default constructor
* @post This will create an unusable StreamSink, it will have to be assigned from a valid StreamSink object to be usable.
**/
StreamSink();
/**
* @brief Create StreamSink
*
* @param[in] stream The base IStream object which this sink will derive from.
*
* @pre The base stream must support writing.
*
* @throw tc::ArgumentNullException @p stream is a @p nullptr.
* @throw tc::NotSupportedException @p stream does not support writing.
**/
StreamSink(const std::shared_ptr<tc::io::IStream>& stream);
/**
* @brief Gets the length of the sink.
**/
int64_t length();
/**
* @brief Sets the length of the sink.
*
* @param[in] length The desired length of the sink in bytes.
*
* @throw tc::ObjectDisposedException The base stream was not initialized.
**/
void setLength(int64_t length);
/**
* @brief Push data to the sink.
*
* @param[in] data Data to be pushed to the sink.
* @param[in] offset Zero-based offset in sink to push data.
*
* @return Number of bytes pushed to sink.
*
* @throw tc::ObjectDisposedException The base stream was not initialized.
* @throw tc::io::IOException Data was not written to base stream successfully.
**/
size_t pushData(const tc::ByteData& data, int64_t offset);
private:
static const std::string kClassName;
std::shared_ptr<tc::io::IStream> mBaseStream;
};
}} // namespace tc::io
@@ -0,0 +1,66 @@
/**
* @file StreamSource.h
* @brief Declaration of tc::io::StreamSource
* @author Jack (jakcron)
* @version 0.2
* @date 2020/03/29
**/
#pragma once
#include <tc/io/ISource.h>
#include <tc/io/IStream.h>
#include <tc/ArgumentNullException.h>
#include <tc/NotSupportedException.h>
namespace tc { namespace io {
/**
* @class StreamSource
* @brief An IStream wrapped in an ISource.
**/
class StreamSource : public tc::io::ISource
{
public:
/**
* @brief Default constructor
* @post This will create an unusable StreamSource, it will have to be assigned from a valid StreamSource object to be usable.
**/
StreamSource();
/**
* @brief Create StreamSource
*
* @param[in] stream The base IStream object which this sub source will derive from.
*
* @pre The base stream must support reading.
*
* @throw tc::ArgumentNullException @p stream is a @p nullptr.
* @throw tc::NotSupportedException @p stream does not support reading.
* @throw tc::NotSupportedException @p stream does not support seeking.
**/
StreamSource(const std::shared_ptr<tc::io::IStream>& stream);
/**
* @brief Gets the length of the source.
**/
int64_t length();
/**
* @brief Pull data from source
*
* @param[in] offset Zero-based offset in source to pull data.
* @param[in] count The maximum number of bytes to be pull from the source.
*
* @return ByteData containing data pulled from source
**/
tc::ByteData pullData(int64_t offset, size_t count);
private:
static const std::string kClassName;
std::shared_ptr<tc::io::IStream> mBaseStream;
int64_t mBaseSourceOffset;
int64_t mStreamSourceLength;
};
}} // namespace tc::io
@@ -0,0 +1,37 @@
/**
* @file StreamUtil.h
* @brief Declaration of tc::io::StreamUtil
* @author Jack (jakcron)
* @version 0.2
* @date 2020/04/06
**/
#pragma once
#include <tc/io/IStream.h>
#include <tc/ArgumentOutOfRangeException.h>
namespace tc { namespace io {
/**
* @class StreamUtil
* @brief Utility class for IStream objects.
**/
class StreamUtil
{
public:
/**
* @brief Get the logical result for seek(), given the current position and stream length.
*
* @param[in] offset A byte offset relative to the origin parameter.
* @param[in] origin A value of type @ref tc::io::SeekOrigin indicating the reference point used to obtain the new position.
* @param[in] current_position The current byte offset relative to the beginning of the stream.
* @param[in] stream_length The length of the stream.
*
* @return The new position within the stream.
*
* @throw tc::ArgumentOutOfRangeException @p origin has an illegal value.
**/
static int64_t getSeekResult(int64_t offset, tc::io::SeekOrigin origin, int64_t current_position, int64_t stream_length);
};
}} // namespace tc::io
@@ -0,0 +1,136 @@
/**
* @file SubFileSystem.h
* @brief Declaration of tc::io::SubFileSystem
* @author Jack (jakcron)
* @version 0.5
* @date 2022/01/23
**/
#pragma once
#include <tc/io/IFileSystem.h>
#include <tc/io/BasicPathResolver.h>
#include <tc/ArgumentNullException.h>
#include <tc/InvalidOperationException.h>
#include <tc/ObjectDisposedException.h>
#include <tc/UnauthorisedAccessException.h>
namespace tc { namespace io {
/**
* @class SubFileSystem
* @brief A wrapper around an existing IFileSystem object that exposes a subset of the base IFileSystem directory tree.
**/
class SubFileSystem : public tc::io::IFileSystem
{
public:
/**
* @brief Default constructor
* @post This will create an unusable SubFileSystem, it will have to be assigned from a valid SubFileSystem object to be usable.
**/
SubFileSystem();
/**
* @brief Create SubFileSystem
*
* @param[in] file_system The base IFileSystem object which this sub file-system will derive from.
* @param[in] base_path The path to the subdirectory used as the substream root directory.
*
* @throw tc::ArgumentNullException @p file_system is @a nullptr.
* @throw tc::InvalidOperationException @p file_system was not in a ready state.
*
* @note This will temporarily change and then restore the base file-system current working directory.
**/
SubFileSystem(const std::shared_ptr<tc::io::IFileSystem>& file_system, const tc::io::Path& base_path);
tc::ResourceStatus state();
void dispose();
/**
* @brief Create a new file
*
* @param[in] path A relative or absolute path to file.
*
* @throw tc::ObjectDisposedException Methods were called after the file-system was closed.
**/
void createFile(const tc::io::Path& path);
/**
* @brief Remove a file
*
* @param[in] path A relative or absolute path to file.
*
* @throw tc::ObjectDisposedException Methods were called after the file-system was closed.
**/
void removeFile(const tc::io::Path& path);
/**
* @brief Open a file
*
* @param[in] path A relative or absolute path to file.
* @param[in] mode One of the enumeration values that determines how to open or create the file.
* @param[in] access One of the enumeration values that determines how the file can be accessed by the @ref IStream object. This also determines the values returned by the @ref IStream::canRead and @ref IStream::canWrite methods of the IStream object. @ref IStream::canSeek is true if path specifies a disk file.
* @param[out] stream Pointer to IStream object to be instantiated
*
* @throw tc::ObjectDisposedException Methods were called after the file-system was closed.
**/
void openFile(const tc::io::Path& path, tc::io::FileMode mode, tc::io::FileAccess access, std::shared_ptr<tc::io::IStream>& stream);
/**
* @brief Create a new directory
*
* @param[in] path A relative or absolute path to directory.
*
* @throw tc::ObjectDisposedException Methods were called after the file-system was closed.
**/
void createDirectory(const tc::io::Path& path);
/**
* @brief Remove a directory
* @param[in] path A relative or absolute path to directory.
*
* @throw tc::ObjectDisposedException Methods were called after the file-system was closed.
**/
void removeDirectory(const tc::io::Path& path);
/**
* @brief Get the full path of the working directory
* @param[out] path Path object to populate
*
* @throw tc::ObjectDisposedException Methods were called after the file-system was closed.
*
* @note This will temporarily change and then restore the base file-system current working directory.
**/
void getWorkingDirectory(tc::io::Path& path);
/**
* @brief Change the working directory
* @param[in] path A relative or absolute path to directory.
*
* @throw tc::ObjectDisposedException Methods were called after the file-system was closed.
* @throw tc::UnauthorisedAccessException Sub file-system escape detected.
**/
void setWorkingDirectory(const tc::io::Path& path);
/**
* @brief Get directory listing a directory
* @param[in] path A relative or absolute path to directory.
* @param[out] info The sDirectoryListing object to populate
*
* @throw tc::ObjectDisposedException Methods were called after the file-system was closed.
* @throw tc::UnauthorisedAccessException Sub file-system escape detected.
**/
void getDirectoryListing(const tc::io::Path& path, tc::io::sDirectoryListing& info);
private:
static const std::string kClassName;
std::shared_ptr<tc::io::IFileSystem> mBaseFileSystem;
tc::io::BasicPathResolver mBasePathResolver;
tc::io::BasicPathResolver mSubPathResolver;
void subPathToRealPath(const tc::io::Path& sub_path, tc::io::Path& real_path);
void realPathToSubPath(const tc::io::Path& real_path, tc::io::Path& sub_path);
};
}} // namespace tc::io
@@ -0,0 +1,77 @@
/**
* @file SubSink.h
* @brief Declaration of tc::io::SubSink
* @author Jack (jakcron)
* @version 0.2
* @date 2020/04/10
**/
#pragma once
#include <tc/io/ISink.h>
#include <tc/ArgumentNullException.h>
#include <tc/ArgumentOutOfRangeException.h>
#include <tc/OutOfMemoryException.h>
#include <tc/ObjectDisposedException.h>
#include <tc/NotSupportedException.h>
#include <tc/NotImplementedException.h>
namespace tc { namespace io {
/**
* @class SubSink
* @brief A ISink that exposes a subset of a base ISink.
**/
class SubSink : public tc::io::ISink
{
public:
/**
* @brief Default constructor
* @post This will create an unusable SubSink, it will have to be assigned from a valid SubSink object to be usable.
**/
SubSink();
/**
* @brief Create SubSink
*
* @param[in] sink The base ISink object which this sub sink will derive from.
* @param[in] offset The zero-based byte offset in sink at which to begin the sub sink.
* @param[in] length Length of the sub sink.
*
* @pre The sub sink must be a subset of the base sink.
*
* @throw tc::ArgumentNullException @p sink is a @p nullptr.
* @throw tc::ArgumentOutOfRangeException @p offset or @p length is negative or otherwise invalid given the length of the base sink.
**/
SubSink(const std::shared_ptr<tc::io::ISink>& sink, int64_t offset, int64_t length);
/// Gets the length of the sink.
int64_t length();
/**
* @brief Sets the length of the sink. This is not supported for SubSink.
* @throw tc::NotImplemented setLength is not implemented for SubSink.
**/
void setLength(int64_t length);
/**
* @brief Push data to the sink.
*
* @param[in] data Data to be pushed to the sink.
* @param[in] offset Zero-based offset in sink to push data.
*
* @return Number of bytes pushed to sink.
*
* @throw tc::ObjectDisposedException The base sink was not initialized.
* @throw tc::ArgumentOutOfRangeException @p data was too large to be pushed to the sink.
**/
size_t pushData(const tc::ByteData& data, int64_t offset);
private:
static const std::string kClassName;
std::shared_ptr<tc::io::ISink> mBaseSink;
int64_t mBaseSinkOffset;
int64_t mSubSinkLength;
};
}} // namespace tc::io
@@ -0,0 +1,64 @@
/**
* @file SubSource.h
* @brief Declaration of tc::io::SubSource
* @author Jack (jakcron)
* @version 0.1
* @date 2020/02/08
**/
#pragma once
#include <tc/io/ISource.h>
#include <tc/ArgumentNullException.h>
#include <tc/ArgumentOutOfRangeException.h>
namespace tc { namespace io {
/**
* @class SubSource
* @brief A ISource that exposes a subset of a base ISource.
**/
class SubSource : public tc::io::ISource
{
public:
/**
* @brief Default constructor
* @post This will create an unusable SubSource, it will have to be assigned from a valid SubSource object to be usable.
**/
SubSource();
/**
* @brief Create SubSource
*
* @param[in] source The base ISource object which this sub source will derive from.
* @param[in] offset The zero-based byte offset in source at which to begin the sub source.
* @param[in] length Length of the sub source.
*
* @pre The sub source must be a subset of the base source.
*
* @throw tc::ArgumentNullException @p source is a @p nullptr.
* @throw tc::ArgumentOutOfRangeException @p offset or @p length is negative or otherwise invalid given the length of the base source.
**/
SubSource(const std::shared_ptr<tc::io::ISource>& source, int64_t offset, int64_t length);
/// Get length of source
int64_t length();
/**
* @brief Pull data from source
*
* @param[in] offset Zero-based offset in source to pull data.
* @param[in] count The maximum number of bytes to be pull from the source.
*
* @return ByteData containing data pulled from source
**/
tc::ByteData pullData(int64_t offset, size_t count);
private:
static const std::string kClassName;
std::shared_ptr<tc::io::ISource> mBaseSource;
int64_t mBaseSourceOffset;
int64_t mSubSourceLength;
};
}} // namespace tc::io
@@ -0,0 +1,155 @@
/**
* @file SubStream.h
* @brief Declaration of tc::io::SubStream
* @author Jack (jakcron)
* @version 0.4
* @date 2020/01/26
**/
#pragma once
#include <tc/io/IStream.h>
#include <tc/ArgumentNullException.h>
#include <tc/ArgumentOutOfRangeException.h>
#include <tc/NotSupportedException.h>
#include <tc/NotImplementedException.h>
#include <tc/ObjectDisposedException.h>
namespace tc { namespace io {
/**
* @class SubStream
* @brief A wrapper around an existing IStream object that exposes a subset of the base the IStream object.
**/
class SubStream : public tc::io::IStream
{
public:
/**
* @brief Default Constructor
* @post This will create an unusable SubStream, it will have to be assigned from a valid SubStream object to be usable.
**/
SubStream();
/**
* @brief Create SubStream
*
* @param[in] stream The base IStream object which this sub stream will derive from.
* @param[in] offset The zero-based byte offset in stream at which to begin the sub stream.
* @param[in] length Length of the sub stream.
*
* @pre The sub stream must be a subset of the base stream.
* @pre A stream must support seeking for @ref seek to work.
*
* @throw tc::ArgumentNullException @p stream is a @p nullptr.
* @throw tc::NotSupportedException The base stream does not support seeking.
* @throw tc::ArgumentOutOfRangeException @p offset or @p length is negative or otherwise invalid given the length of the base stream.
**/
SubStream(const std::shared_ptr<tc::io::IStream>& stream, int64_t offset, int64_t length);
/**
* @brief Indicates whether the current stream supports reading.
**/
bool canRead() const;
/**
* @brief Indicates whether the current stream supports writing.
**/
bool canWrite() const;
/**
* @brief Indicates whether the current stream supports seeking.
**/
bool canSeek() const;
/**
* @brief Gets the length in bytes of the stream.
**/
int64_t length();
/**
* @brief Gets the position within the current stream.
*
* @return This is returns the current position within the stream.
**/
int64_t position();
/**
* @brief Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read.
*
* @param[out] ptr Pointer to an array of bytes. When this method returns, @p ptr contains the specified byte array with the values between 0 and (@p count - 1) replaced by the bytes read from the current source.
* @param[in] count The maximum number of bytes to be read from the current stream.
*
* @return The total number of bytes read into @p ptr. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached.
*
* @pre A stream must support reading for @ref read to work.
* @note Use @ref canRead to determine if this stream supports reading.
* @note Exceptions thrown by the base stream are not altered/intercepted, refer to that module's documentation for those exceptions.
*
* @throw tc::ArgumentOutOfRangeException @p count exceeds the length of readable data in the sub stream.
* @throw tc::ObjectDisposedException Methods were called after the stream was closed.
**/
size_t read(byte_t* ptr, size_t count);
/**
* @brief Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written.
*
* @param[in] ptr Pointer to an array of bytes. This method copies @p count bytes from @p ptr to the current stream.
* @param[in] count The number of bytes to be written to the current stream.
*
* @return The total number of bytes written to the stream. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached.
*
* @pre A stream must support writing for @ref write to work.
* @note Use @ref canWrite to determine if this stream supports writing.
* @note Exceptions thrown by the base stream are not altered/intercepted, refer to that module's documentation for those exceptions.
*
* @throw tc::ArgumentOutOfRangeException @p count exceeds the length of writeable data in the sub stream.
* @throw tc::ObjectDisposedException Methods were called after the stream was closed.
**/
size_t write(const byte_t* ptr, size_t count);
/**
* @brief Sets the position within the current stream.
*
* @param[in] offset A byte offset relative to the origin parameter.
* @param[in] origin A value of type @ref tc::io::SeekOrigin indicating the reference point used to obtain the new position.
*
* @return The new position within the current stream.
*
* @pre A stream must support seeking for @ref seek to work.
* @note Use @ref canSeek to determine if this stream supports seeking.
* @note Exceptions thrown by the base stream are not altered/intercepted, refer to that module's documentation for those exceptions.
*
* @throw tc::ArgumentOutOfRangeException @p origin contains an invalid value.
* @throw tc::ObjectDisposedException Methods were called after the stream was closed.
**/
int64_t seek(int64_t offset, SeekOrigin origin);
/**
* @brief Sets the length of the current stream. This is not implemented for @ref SubStream.
* @throw tc::NotImplementedException @ref setLength is not implemented for @ref SubStream
* @throw tc::ObjectDisposedException Methods were called after the stream was closed.
**/
void setLength(int64_t length);
/**
* @brief Clears all buffers for this and the base stream and causes any buffered data to be written to the underlying device.
*
* @throw tc::io::IOException An I/O error occurs.
* @throw tc::ObjectDisposedException Methods were called after the stream was closed.
**/
void flush();
/**
* @brief Releases internal resources including base stream and clears internal state.
**/
void dispose();
private:
static const std::string kClassName;
std::shared_ptr<tc::io::IStream> mBaseStream;
int64_t mBaseStreamOffset;
int64_t mSubStreamLength;
int64_t mSubStreamPosition;
};
}} // namespace tc::io
@@ -0,0 +1,212 @@
/**
* @file VirtualFileSystem.h
* @brief Declaration of tc::io::VirtualFileSystem
* @author Jack (jakcron)
* @version 0.2
* @date 2022/02/08
**/
#pragma once
#include <tc/io/IFileSystem.h>
#include <tc/io/BasicPathResolver.h>
#include <tc/ObjectDisposedException.h>
#include <tc/NotImplementedException.h>
#include <tc/NotSupportedException.h>
#include <tc/io/DirectoryNotFoundException.h>
#include <tc/io/FileNotFoundException.h>
namespace tc { namespace io {
/**
* @class VirtualFileSystem
* @brief A virtual read-only file-system created using a file-system snapshot.
*
* @details
* The intended use-case for VirtualFileSystem is for when the user must read/parse the file-system structures directly instead of using the OS to mount it.
* The user generates a file-system snapshot from the file-system structures (see @ref VirtualFileSystem::FileSystemSnapshot) and supplies that to VirtualFileSystem to create a IFileSystem shim.
*
* User supplies:
* * a @ref VirtualFileSystem::FileSystemSnapshot struct which contains vectors of directory and file entries, including mapping between absolute tc::io::Path and a dir/file entry.
* * optionally an implementation of @ref tc::io::IPortablePathResolver to determine the absolute path from a relative path and the current directory. Providing a custom IPortablePathResolver implementation is only required when special logic (like case insensitivity) is required to resolve the correct absolute path.
**/
class VirtualFileSystem : public tc::io::IFileSystem
{
public:
/**
* @struct FileSystemSnapshot
* @brief A struct which contains vectors of directory and file entries, including mapping between absolute tc::io::Path and a dir/file entry.
*
* @details
* FileSystemSnapshot is snapshot of a FileSystem directory tree. It only has to support read-only access to directory information and file streams.
*
* VirtualFileSystem considers a directory to exist if the following conditions are satisfied:
* * The absolute tc::io::Path to the directory exists in @b dir_entry_path_map
* * The index from looking up the absolute path in @b dir_entry_path_map is a valid index for @b dir_entries
*
* VirtualFileSystem considers a file to exist if the following conditions are satisfied:
* * The absolute tc::io::Path to the file exists in @b file_entry_path_map
* * The index from looking up the absolute path in @b file_entry_path_map is a valid index for @b file_entries
* * The FileEntry in @b file_entries has a @b stream member that is not @b nullptr
* * The IStream has properties: @b .canRead() == @b true, @b .canWrite() == @b false
*/
struct FileSystemSnapshot
{
FileSystemSnapshot() :
dir_entries(),
file_entries(),
dir_entry_path_map(),
file_entry_path_map()
{
}
/**
* @struct DirEntry
* @brief This struct contains data for the directory entry.
* @details
* This currently contains only a @ref tc::io::sDirectoryListing.
*
* DirEntries are used to confirm that a directory exists, and also to provide the sDirectoryListing for getDirectoryListing()
*/
struct DirEntry
{
tc::io::sDirectoryListing dir_listing;
};
/**
* @struct FileEntry
* @brief This struct contains data for the file entry.
* @details
* This currently contains only a @ref tc::io::IStream pointer.
*
* FileEntry objects are used to confirm that a files exists, and also to provide the IStream for openFile()
* IStream objects should have .canRead() == true AND .canWrite() == false, canSeek() is preferred to be true, but it isn't required.
*/
struct FileEntry
{
std::shared_ptr<tc::io::IStream> stream;
};
/// Vector of directory entries
std::vector<DirEntry> dir_entries;
/// Vector of file entries
std::vector<FileEntry> file_entries;
/// Mapping of absolute path to index of DirEntry in dir_entries
std::map<tc::io::Path, size_t> dir_entry_path_map;
/// Mapping of absolute path to index of FileEntry in file_entries
std::map<tc::io::Path, size_t> file_entry_path_map;
};
/**
* @brief Default constructor
* @post This will create an unusable VirtualFileSystem, it will have to be assigned from a valid VirtualFileSystem object to be usable.
**/
VirtualFileSystem();
/**
* @brief Create VirtualFileSystem
*
* @param[in] fs_snapshot The FileSystemSnapshot object which this VirtualFileSystem will use to process file-system requests.
* @param[in] path_resolver Pointer to @ref tc::io::IPortablePathResolver object that resolves relative paths to absolute paths. If @p nullptr, @ref tc::io::BasicPathResolver will be used.
*
* @throw tc::InvalidOperationException @p fs_snapshot Did not contain a root directory entry.
**/
VirtualFileSystem(const FileSystemSnapshot& fs_snapshot, const std::shared_ptr<tc::io::IPortablePathResolver>& path_resolver = nullptr);
tc::ResourceStatus state();
/// This will release the underlying FileSystemSnapshot and PathResolver
void dispose();
/**
* @brief Create a new file
* @details This method is not implemented for VirtualFileSystem.
*
* @param[in] path A relative or absolute path to file.
*
* @throw tc::NotImplementedException This method is not implemented for VirtualFileSystem.
* @throw tc::ObjectDisposedException Methods were called after the file-system was closed.
**/
void createFile(const tc::io::Path& path);
/**
* @brief Remove a file
* @details This method is not implemented for VirtualFileSystem.
*
* @param[in] path A relative or absolute path to file.
*
* @throw tc::NotImplementedException This method is not implemented for VirtualFileSystem.
* @throw tc::ObjectDisposedException Methods were called after the file-system was closed.
**/
void removeFile(const tc::io::Path& path);
/**
* @brief Open a file
*
* @param[in] path A relative or absolute path to file.
* @param[in] mode One of the enumeration values that determines how to open or create the file. This must be @ref tc::io::FileMode::Open for VirtualFileSystem.
* @param[in] access One of the enumeration values that determines how the file can be accessed by the @ref IStream object. This must be @ref tc::io::FileAccess::Read for VirtualFileSystem.
* @param[out] stream Pointer to IStream object to be instantiated.
*
* @throw tc::ObjectDisposedException Methods were called after the file-system was closed.
* @throw tc::NotSupportedException Unsupported access/mode ( @p mode was not @ref tc::io::FileMode::Open, or @p access was not @ref tc::io::FileAccess::Read).
* @throw tc::io::FileNotFoundException File was not found.
**/
void openFile(const tc::io::Path& path, tc::io::FileMode mode, tc::io::FileAccess access, std::shared_ptr<tc::io::IStream>& stream);
/**
* @brief Create a new directory
* @details This method is not implemented for VirtualFileSystem.
*
* @param[in] path A relative or absolute path to directory.
*
* @throw tc::NotImplementedException This method is not implemented for VirtualFileSystem.
* @throw tc::ObjectDisposedException Methods were called after the file-system was closed.
**/
void createDirectory(const tc::io::Path& path);
/**
* @brief Remove a directory
* @details This method is not implemented for VirtualFileSystem.
*
* @param[in] path A relative or absolute path to directory.
*
* @throw tc::NotImplementedException This method is not implemented for VirtualFileSystem.
* @throw tc::ObjectDisposedException Methods were called after the file-system was closed.
**/
void removeDirectory(const tc::io::Path& path);
/**
* @brief Get the full path of the working directory
* @param[out] path Path object to populate.
*
* @throw tc::ObjectDisposedException Methods were called after the file-system was closed.
**/
void getWorkingDirectory(tc::io::Path& path);
/**
* @brief Change the working directory
* @param[in] path A relative or absolute path to directory.
*
* @throw tc::ObjectDisposedException Methods were called after the file-system was closed.
* @throw tc::io::DirectoryNotFoundException Directory was not found.
**/
void setWorkingDirectory(const tc::io::Path& path);
/**
* @brief Get directory listing a directory
* @param[in] path A relative or absolute path to directory.
* @param[out] info The sDirectoryListing object to populate.
*
* @throw tc::ObjectDisposedException Methods were called after the file-system was closed.
* @throw tc::io::DirectoryNotFoundException Directory was not found.
**/
void getDirectoryListing(const tc::io::Path& path, tc::io::sDirectoryListing& info);
private:
static const std::string kClassName;
FileSystemSnapshot::DirEntry* mCurDir;
FileSystemSnapshot mFsSnapshot;
std::shared_ptr<tc::io::IPortablePathResolver> mPathResolver;
};
}} // namespace tc::io