Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(io_interface): Use Result with API. #57

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/ystdlib/io_interface/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ cpp_library(
PRIVATE_SOURCES
ReaderInterface.cpp
PUBLIC_LINK_LIBRARIES
ystdlib::error_handling
ystdlib::wrapped_facade_headers
TESTS_SOURCES
test/test_ReaderInterface.cpp
Expand Down
37 changes: 37 additions & 0 deletions src/ystdlib/io_interface/ErrorCode.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#include "ErrorCode.hpp"

#include <string>

#include <ystdlib/error_handling/ErrorCode.hpp>

using ystdlib::io_interface::ErrorCodeEnum;
using ErrorCategory = ystdlib::error_handling::ErrorCategory<ErrorCodeEnum>;

template <>
auto ErrorCategory::name() const noexcept -> char const* {
return "ystdlib::io_interface::ErrorCode";
}

template <>
auto ErrorCategory::message(ErrorCodeEnum error_enum) const -> std::string {
switch (error_enum) {
case ErrorCodeEnum::BadParam:
return "Supplied parameters are invalid.";
case ErrorCodeEnum::BufferFull:
return "The output buffer is full and cannot accept more data.";
case ErrorCodeEnum::Corrupt:
return "The data is corrupted or malformed.";
case ErrorCodeEnum::EndOfFile:
return "End of file reached.";
case ErrorCodeEnum::EndOfStream:
return "End of the data stream reached.";
case ErrorCodeEnum::OutOfBounds:
return "Attempted to access data outside of valid bounds.";
case ErrorCodeEnum::OutOfMemory:
return "Memory allocation failed.";
case ErrorCodeEnum::Truncated:
return "The data was unexpectedly truncated or cut off.";
default:
return "Unrecognized ystdlib::io_interface::ErrorCodeEnum.";
}
}
42 changes: 17 additions & 25 deletions src/ystdlib/io_interface/ErrorCode.hpp
Original file line number Diff line number Diff line change
@@ -1,33 +1,25 @@
#ifndef YSTDLIB_IO_INTERFACE_ERRORCODE_HPP
#define YSTDLIB_IO_INTERFACE_ERRORCODE_HPP

// NOLINTBEGIN
#include <cstdint>

#include <ystdlib/error_handling/ErrorCode.hpp>

namespace ystdlib::io_interface {
typedef enum {
ErrorCode_Success = 0,
ErrorCode_BadParam,
ErrorCode_BadParam_DB_URI,
ErrorCode_Corrupt,
ErrorCode_errno,
ErrorCode_EndOfFile,
ErrorCode_FileExists,
ErrorCode_FileNotFound,
ErrorCode_NoMem,
ErrorCode_NotInit,
ErrorCode_NotReady,
ErrorCode_OutOfBounds,
ErrorCode_TooLong,
ErrorCode_Truncated,
ErrorCode_Unsupported,
ErrorCode_NoAccess,
ErrorCode_Failure,
ErrorCode_Failure_Metadata_Corrupted,
ErrorCode_MetadataCorrupted,
ErrorCode_Failure_DB_Bulk_Write,
ErrorCode_Failure_Network,
} ErrorCode;
enum class ErrorCodeEnum : uint8_t {
BadParam,
BufferFull,
Corrupt,
EndOfFile,
EndOfStream,
OutOfBounds,
OutOfMemory,
Truncated
};

using ErrorCode = ystdlib::error_handling::ErrorCode<ErrorCodeEnum>;
} // namespace ystdlib::io_interface

// NOLINTEND
YSTDLIB_ERROR_HANDLING_MARK_AS_ERROR_CODE_ENUM(ystdlib::io_interface::ErrorCodeEnum);

#endif // YSTDLIB_IO_INTERFACE_ERRORCODE_HPP
85 changes: 41 additions & 44 deletions src/ystdlib/io_interface/ReaderInterface.cpp
Original file line number Diff line number Diff line change
@@ -1,64 +1,61 @@
#include "ReaderInterface.hpp"

#include <cstddef>
#include <span>
#include <string>

#include <ystdlib/error_handling/Result.hpp>

#include "ErrorCode.hpp"

namespace ystdlib::io_interface {
auto
ReaderInterface::read_to_delimiter(char delim, bool keep_delimiter, bool append, std::string& str)
-> ErrorCode {
if (false == append) {
str.clear();
auto ReaderInterface::read(size_t num_bytes) -> Result<std::string> {
std::string str(num_bytes, 0);
auto const num_bytes_read{YSTDLIB_ERROR_HANDLING_TRYX(read({str.data(), num_bytes}))};
str.resize(num_bytes_read);
return str;
}

auto ReaderInterface::read_exact_length(std::span<char> buf) -> Result<void> {
auto const num_bytes_read{YSTDLIB_ERROR_HANDLING_TRYX(read(buf))};
if (0 == num_bytes_read) {
return ErrorCode{ErrorCodeEnum::EndOfStream};
}
if (num_bytes_read < buf.size()) {
return ErrorCode{ErrorCodeEnum::Truncated};
}
return ystdlib::error_handling::success();
}

auto const original_str_length{str.length()};
auto ReaderInterface::read_exact_length(size_t num_bytes) -> Result<std::string> {
std::string str(num_bytes, 0);
YSTDLIB_ERROR_HANDLING_TRYV(read_exact_length({str.data(), num_bytes}));
return str;
}

// Read character by character into str, until we find a delimiter
auto ReaderInterface::read_to_delimiter(char delim, bool keep_delimiter) -> Result<std::string> {
char c{0};
size_t num_bytes_read{0};
while (true) {
auto const error_code{read(&c, 1, num_bytes_read)};
if (ErrorCode_Success != error_code) {
if (ErrorCode_EndOfFile == error_code && str.length() > original_str_length) {
return ErrorCode_Success;
}
return error_code;
}

if (delim == c) {
break;
}
std::string str;

// The first read needs to succeed
c = YSTDLIB_ERROR_HANDLING_TRYX(read_numeric_value<char>());
while (delim != c) {
str += c;
auto const result{read_numeric_value<char>()};
if (result.has_error()) {
auto const error{result.error()};
if (ErrorCode{ErrorCodeEnum::EndOfStream} == error
|| ErrorCode{ErrorCodeEnum::EndOfFile} == error)
{
return str;
}
return error;
}
c = result.value();
}

// Add delimiter if necessary
if (keep_delimiter) {
str += delim;
}

return ErrorCode_Success;
}

auto ReaderInterface::read_exact_length(char* buf, size_t num_bytes) -> ErrorCode {
size_t num_bytes_read{0};
auto const error_code{read(buf, num_bytes, num_bytes_read)};
if (ErrorCode_Success != error_code) {
return error_code;
}
if (num_bytes_read < num_bytes) {
return ErrorCode_Truncated;
}

return ErrorCode_Success;
}

auto ReaderInterface::read_string(size_t const str_length, std::string& str) -> ErrorCode {
// Resize string to fit str_length
str.resize(str_length);

return read_exact_length(str.data(), str_length);
return str;
}
} // namespace ystdlib::io_interface
71 changes: 40 additions & 31 deletions src/ystdlib/io_interface/ReaderInterface.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@
#define YSTDLIB_IO_INTERFACE_READERINTERFACE_HPP

#include <cstddef>
#include <span>
#include <string>

#include <ystdlib/error_handling/Result.hpp>
#include <ystdlib/wrapped_facade_headers/sys/types.h>

#include "ErrorCode.hpp"

namespace ystdlib::io_interface {
class ReaderInterface {
public:
template <typename ReturnType>
using Result = ystdlib::error_handling::Result<ReturnType>;

// Constructor
ReaderInterface() = default;

Expand All @@ -26,66 +29,72 @@ class ReaderInterface {
virtual ~ReaderInterface() = default;

// Methods
/*
* Reads up to the given number of bytes from the underlying medium into the given buffer.
/**
* Performs a read that attempts to fill the given buffer.
* @param buf
* @param num_bytes_to_read
* @param num_bytes_read Returns the actual number of bytes read.
* @return The number of bytes read.
*/
[[nodiscard]] virtual auto read(char* buf, size_t num_bytes_to_read, size_t& num_bytes_read)
-> ErrorCode
= 0;
[[nodiscard]] virtual auto read(std::span<char> buf) -> Result<size_t> = 0;

/**
* Reads up to the next delimiter from the underlying medium into the given string.
* @param delim The delimiter to stop at.
* @param keep_delimiter Whether to include the delimiter in the output string or not.
* @param append Whether to append to the given string or replace its contents.
* @param str Returns the string read.
* Performs a read of up to the requested byte count.
* @param num_bytes
* @return The data read as a string.
*/
[[nodiscard]] virtual auto
read_to_delimiter(char delim, bool keep_delimiter, bool append, std::string& str) -> ErrorCode;
[[nodiscard]] virtual auto read(size_t num_bytes) -> Result<std::string>;

/**
* Reads the given number of bytes from the underlying medium into the given buffer.
* Performs a read that completely fills the given buffer.
* @param buf
* @param num_bytes Number of bytes to read.
*/
[[nodiscard]] virtual auto read_exact_length(char* buf, size_t num_bytes) -> ErrorCode;
[[nodiscard]] virtual auto read_exact_length(std::span<char> buf) -> Result<void>;

/**
* @param value Returns the read numeric value.
* Performs a read that must return exactly the requested byte count.
* @param num_bytes
* @return The data read as a string.
*/
template <typename ValueType>
[[nodiscard]] auto read_numeric_value(ValueType& value) -> ErrorCode;
[[nodiscard]] virtual auto read_exact_length(size_t num_bytes) -> Result<std::string>;

/**
* @param str_length
* @param str Returns the string read.
* Performs a reads of up to the next delimiter.
* @param buf
* @param delim The delimiter to stop at.
* @param keep_delimiter Whether to include the delimiter in the output.
* @return The data read as a string.
*/
[[nodiscard]] virtual auto read_string(size_t str_length, std::string& str) -> ErrorCode;
[[nodiscard]] virtual auto read_to_delimiter(char delim, bool keep_delimiter)
-> Result<std::string>;

/**
* @return The numeric value read.
*/
template <typename ValueType>
[[nodiscard]] auto read_numeric_value() -> Result<ValueType>;

/**
* Seeks from the beginning to the given position.
* @param pos
*/
[[nodiscard]] virtual auto seek_from_begin(size_t pos) -> ErrorCode = 0;
[[nodiscard]] virtual auto seek_from_begin(size_t pos) -> Result<void> = 0;

/**
* Seeks from the current position to the next position by the given offset amount.
* @param offset
*/
[[nodiscard]] virtual auto seek_from_current(off_t offset) -> ErrorCode = 0;
[[nodiscard]] virtual auto seek_from_current(off_t offset) -> Result<void> = 0;

/**
* @param pos Returns the current position of the read pointer.
* @return The current position of the read pointer.
*/
[[nodiscard]] virtual auto get_pos(size_t& pos) -> ErrorCode = 0;
[[nodiscard]] virtual auto get_pos() -> Result<size_t> = 0;
};

template <typename ValueType>
auto ReaderInterface::read_numeric_value(ValueType& value) -> ErrorCode {
return read_exact_length(static_cast<char*>(&value), sizeof(ValueType));
auto ReaderInterface::read_numeric_value() -> Result<ValueType> {
ValueType value{};
YSTDLIB_ERROR_HANDLING_TRYV(read_exact_length({static_cast<char*>(&value), sizeof(ValueType)}));
return value;
}
} // namespace ystdlib::io_interface

Expand Down
Loading