Skip to content

feat(io_interface): Port Reader and Writer interfaces from CLP with minimal compilation fixes. #48

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

Merged
merged 3 commits into from
Mar 28, 2025
Merged
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/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
add_subdirectory(containers)
add_subdirectory(error_handling)
add_subdirectory(io_interface)
14 changes: 14 additions & 0 deletions src/ystdlib/io_interface/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
cpp_library(
NAME io_interface
NAMESPACE ystdlib
PUBLIC_HEADERS
ErrorCode.hpp
ReaderInterface.hpp
WriterInterface.hpp
PRIVATE_SOURCES
ReaderInterface.cpp
WriterInterface.cpp
TESTS_SOURCES
test/test_ReaderInterface.cpp
test/test_WriterInterface.cpp
)
33 changes: 33 additions & 0 deletions src/ystdlib/io_interface/ErrorCode.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#ifndef YSTDLIB_IO_INTERFACE_ERRORCODE_HPP
#define YSTDLIB_IO_INTERFACE_ERRORCODE_HPP

// NOLINTBEGIN

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;
} // namespace ystdlib::io_interface

// NOLINTEND
#endif // YSTDLIB_IO_INTERFACE_ERRORCODE_HPP
129 changes: 129 additions & 0 deletions src/ystdlib/io_interface/ReaderInterface.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// NOLINTBEGIN
#include "ReaderInterface.hpp"

using std::string;

namespace ystdlib::io_interface {
ErrorCode ReaderInterface::try_read_to_delimiter(
char delim,
bool keep_delimiter,
bool append,
std::string& str
) {
if (false == append) {
str.clear();
}

size_t original_str_length = str.length();

// Read character by character into str, until we find a delimiter
char c;
size_t num_bytes_read;
while (true) {
auto error_code = try_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;
}

str += c;
}

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

return ErrorCode_Success;
}

bool ReaderInterface::read(char* buf, size_t num_bytes_to_read, size_t& num_bytes_read) {
ErrorCode error_code = try_read(buf, num_bytes_to_read, num_bytes_read);
if (ErrorCode_EndOfFile == error_code) {
return false;
}
if (ErrorCode_Success != error_code) {
throw OperationFailed(error_code);
}
return true;
}

bool ReaderInterface::read_to_delimiter(char delim, bool keep_delimiter, bool append, string& str) {
ErrorCode error_code = try_read_to_delimiter(delim, keep_delimiter, append, str);
if (ErrorCode_EndOfFile == error_code) {
return false;
}
if (ErrorCode_Success != error_code) {
throw OperationFailed(error_code);
}

return true;
}

ErrorCode ReaderInterface::try_read_exact_length(char* buf, size_t num_bytes) {
size_t num_bytes_read;
auto error_code = try_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;
}

bool ReaderInterface::read_exact_length(char* buf, size_t num_bytes, bool eof_possible) {
ErrorCode error_code = try_read_exact_length(buf, num_bytes);
if (eof_possible && ErrorCode_EndOfFile == error_code) {
return false;
}
if (ErrorCode_Success != error_code) {
throw OperationFailed(error_code);
}
return true;
}

ErrorCode ReaderInterface::try_read_string(size_t const str_length, string& str) {
// Resize string to fit str_length
str.resize(str_length);

return try_read_exact_length(&str[0], str_length);
}

bool ReaderInterface::read_string(size_t const str_length, string& str, bool eof_possible) {
ErrorCode error_code = try_read_string(str_length, str);
if (eof_possible && ErrorCode_EndOfFile == error_code) {
return false;
}
if (ErrorCode_Success != error_code) {
throw OperationFailed(error_code);
}
return true;
}

void ReaderInterface::seek_from_begin(size_t pos) {
ErrorCode error_code = try_seek_from_begin(pos);
if (ErrorCode_Success != error_code) {
throw OperationFailed(error_code);
}
}

size_t ReaderInterface::get_pos() {
size_t pos;
ErrorCode error_code = try_get_pos(pos);
if (ErrorCode_Success != error_code) {
throw OperationFailed(error_code);
}

return pos;
}
} // namespace ystdlib::io_interface

// NOLINTEND
149 changes: 149 additions & 0 deletions src/ystdlib/io_interface/ReaderInterface.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
#ifndef YSTDLIB_IO_INTERFACE_READERINTERFACE_HPP
#define YSTDLIB_IO_INTERFACE_READERINTERFACE_HPP
// NOLINTBEGIN

#include <cstddef>
#include <string>

#include "ErrorCode.hpp"

namespace ystdlib::io_interface {
class ReaderInterface {
public:
// Types
class OperationFailed : public std::exception {
public:
OperationFailed(ErrorCode error_code) {}
};

// Destructor
virtual ~ReaderInterface() = default;

// Methods
virtual ErrorCode try_read(char* buf, size_t num_bytes_to_read, size_t& num_bytes_read) = 0;
virtual ErrorCode try_seek_from_begin(size_t pos) = 0;
virtual ErrorCode try_get_pos(size_t& pos) = 0;

/**
* Tries to read up to the next delimiter and stores it in the given string.
* NOTE: Implementations should override this if they can achieve better performance.
* @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 The string read
* @return ErrorCode_Success on success
* @return Same as ReaderInterface::try_read otherwise
*/
virtual ErrorCode
try_read_to_delimiter(char delim, bool keep_delimiter, bool append, std::string& str);

/**
* Reads up to a given number of bytes
* @param buf
* @param num_bytes_to_read The number of bytes to try and read
* @param num_bytes_read The actual number of bytes read
* @return false on EOF
* @return true otherwise
*/
bool read(char* buf, size_t num_bytes_to_read, size_t& num_bytes_read);

/**
* Reads up to the next delimiter and stores it in 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 The string read
* @return false on EOF
* @return true on success
*/
bool read_to_delimiter(char delim, bool keep_delimiter, bool append, std::string& str);

/**
* Tries to read a number of bytes
* @param buf
* @param num_bytes Number of bytes to read
* @return Same as the underlying medium's try_read method
* @return ErrorCode_Truncated if 0 < # bytes read < num_bytes
*/
ErrorCode try_read_exact_length(char* buf, size_t num_bytes);
/**
* Reads a number of bytes
* @param buf
* @param num_bytes Number of bytes to read
* @param eof_possible If EOF should be possible (without reading any bytes)
* @return false if EOF is possible and EOF was hit
* @return true on success
*/
bool read_exact_length(char* buf, size_t num_bytes, bool eof_possible);

/**
* Tries to read a numeric value from a file
* @param value The read value
* @return Same as FileReader::try_read_exact_length's return values
*/
template <typename ValueType>
ErrorCode try_read_numeric_value(ValueType& value);
/**
* Reads a numeric value
* @param value The read value
* @param eof_possible If EOF should be possible (without reading any bytes)
* @return false if EOF is possible and EOF was hit
* @return true on success
*/
template <typename ValueType>
bool read_numeric_value(ValueType& value, bool eof_possible);

/**
* Tries to read a string
* @param str_length
* @param str The string read
* @return Same as ReaderInterface::try_read_exact_length
*/
ErrorCode try_read_string(size_t str_length, std::string& str);
/**
* Reads a string
* @param str_length
* @param str The string read
* @param eof_possible If EOF should be possible (without reading any bytes)
* @return false if EOF is possible and EOF was hit
* @return true on success
*/
bool read_string(size_t str_length, std::string& str, bool eof_possible);

/**
* Seeks from the beginning to the given position
* @param pos
*/
void seek_from_begin(size_t pos);

/**
* Gets the current position of the read head
* @return Position of the read head
*/
size_t get_pos();
};

template <typename ValueType>
ErrorCode ReaderInterface::try_read_numeric_value(ValueType& value) {
ErrorCode error_code = try_read_exact_length(reinterpret_cast<char*>(&value), sizeof(value));
if (ErrorCode_Success != error_code) {
return error_code;
}
return ErrorCode_Success;
}

template <typename ValueType>
bool ReaderInterface::read_numeric_value(ValueType& value, bool eof_possible) {
ErrorCode error_code = try_read_numeric_value(value);
if (ErrorCode_EndOfFile == error_code && eof_possible) {
return false;
}
if (ErrorCode_Success != error_code) {
throw OperationFailed(error_code);
}
return true;
}
} // namespace ystdlib::io_interface

// NOLINTEND
#endif // YSTDLIB_IO_INTERFACE_READERINTERFACE_HPP
38 changes: 38 additions & 0 deletions src/ystdlib/io_interface/WriterInterface.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// NOLINTBEGIN
#include "WriterInterface.hpp"

namespace ystdlib::io_interface {
void WriterInterface::write_char(char c) {
write(&c, 1);
}

void WriterInterface::write_string(std::string const& str) {
write(str.c_str(), str.length());
}

void WriterInterface::seek_from_begin(size_t pos) {
auto error_code = try_seek_from_begin(pos);
if (ErrorCode_Success != error_code) {
throw OperationFailed(error_code);
}
}

void WriterInterface::seek_from_current(off_t offset) {
auto error_code = try_seek_from_current(offset);
if (ErrorCode_Success != error_code) {
throw OperationFailed(error_code);
}
}

size_t WriterInterface::get_pos() const {
size_t pos;
ErrorCode error_code = try_get_pos(pos);
if (ErrorCode_Success != error_code) {
throw OperationFailed(error_code);
}

return pos;
}
} // namespace ystdlib::io_interface

// NOLINTEND
Loading