Skip to content

Commit

Permalink
File: Add read / write uint8_t overloads and make readAppend private
Browse files Browse the repository at this point in the history
  • Loading branch information
Pagghiu committed Mar 9, 2024
1 parent f1c770a commit 0e8137c
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 43 deletions.
1 change: 0 additions & 1 deletion Documentation/Libraries/File.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ File library allows synchronous I/O operations on files and pipes.
| SC::FileDescriptor::read | @copybrief SC::FileDescriptor::read |
| SC::FileDescriptor::write | @copybrief SC::FileDescriptor::write |
| SC::FileDescriptor::seek | @copybrief SC::FileDescriptor::seek |
| SC::FileDescriptor::readAppend | @copybrief SC::FileDescriptor::readAppend |
| SC::FileDescriptor::readUntilEOF | @copybrief SC::FileDescriptor::readUntilEOF |

| SC::PipeDescriptor | @copybrief SC::PipeDescriptor |
Expand Down
47 changes: 43 additions & 4 deletions Libraries/File/FileDescriptor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
// SPDX-License-Identifier: MIT
#include "FileDescriptor.h"

struct SC::FileDescriptor::ReadResult
{
size_t actuallyRead = 0;
bool isEOF = false;
};

#if SC_PLATFORM_WINDOWS
#include "Internal/FileDescriptorWindows.inl"
#else
Expand All @@ -18,22 +24,55 @@ SC::Result SC::PipeDescriptor::close()

SC::Result SC::FileDescriptor::open(StringView path, OpenMode mode) { return open(path, mode, OpenOptions()); }

SC::Result SC::FileDescriptor::readUntilEOF(Vector<char>& destination)
template <typename T>
SC::Result SC::FileDescriptor::readUntilEOFTemplate(Vector<T>& destination)
{
char buffer[1024];
T buffer[1024];
SC_TRY(isValid());
ReadResult readResult;
ReadResult readResult;
FileDescriptor::Handle fileDescriptor;
SC_TRY(get(fileDescriptor, Result::Error("FileDescriptor::readAppend - Invalid Handle")));
while (not readResult.isEOF)
{
SC_TRY(readAppend(destination, {buffer, sizeof(buffer)}, readResult));
SC_TRY(Internal::readAppend(fileDescriptor, destination, {buffer, sizeof(buffer)}, readResult));
}
return Result(true);
}

SC::Result SC::FileDescriptor::readUntilEOF(Vector<char>& destination) { return readUntilEOFTemplate(destination); }

SC::Result SC::FileDescriptor::readUntilEOF(Vector<uint8_t>& destination) { return readUntilEOFTemplate(destination); }

SC::Result SC::FileDescriptor::readUntilEOF(String& destination)
{
SC_TRY(readUntilEOF(destination.data));
if (destination.isEmpty())
return Result(true);
return Result(StringConverter::pushNullTerm(destination.data, destination.encoding));
}

SC::Result SC::FileDescriptor::write(Span<const uint8_t> data, uint64_t offset)
{
return write({reinterpret_cast<const char*>(data.data()), data.sizeInBytes()}, offset);
}

SC::Result SC::FileDescriptor::read(Span<uint8_t> data, Span<uint8_t>& actuallyRead)
{
Span<char> readBytes;
SC_TRY(read({reinterpret_cast<char*>(data.data()), data.sizeInBytes()}, readBytes));
actuallyRead = {reinterpret_cast<uint8_t*>(readBytes.data()), readBytes.sizeInBytes()};
return Result(true);
}

SC::Result SC::FileDescriptor::read(Span<uint8_t> data, Span<uint8_t>& actuallyRead, uint64_t offset)
{
Span<char> readBytes;
SC_TRY(read({reinterpret_cast<char*>(data.data()), data.sizeInBytes()}, readBytes, offset));
actuallyRead = {reinterpret_cast<uint8_t*>(readBytes.data()), readBytes.sizeInBytes()};
return Result(true);
}

SC::Result SC::FileDescriptor::write(Span<const uint8_t> data)
{
return write({reinterpret_cast<const char*>(data.data()), data.sizeInBytes()});
}
49 changes: 33 additions & 16 deletions Libraries/File/FileDescriptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,23 +107,47 @@ struct SC::FileDescriptor : public SC::UniqueHandle<SC::detail::FileDescriptorDe
/// @return Valid result if read succeeded
[[nodiscard]] Result read(Span<char> data, Span<char>& actuallyRead, uint64_t offset);

/// @brief Reads bytes at offset into user supplied span
/// @param data Span of bytes where data should be written to
/// @param actuallyRead A sub-span of data of the actually read bytes. A zero sized span means EOF.
/// @param offset Offset from begin of the file descriptor where read should be started
/// @return Valid result if read succeeded
[[nodiscard]] Result read(Span<uint8_t> data, Span<uint8_t>& actuallyRead, uint64_t offset);

/// @brief Reads bytes from current position (FileDescriptor::seek) into user supplied Span
/// @param data Span of bytes where data should be written to
/// @param actuallyRead A sub-span of data of the actually read bytes. A zero sized span means EOF.
/// @return Valid result if read succeeded
[[nodiscard]] Result read(Span<char> data, Span<char>& actuallyRead);

/// @brief Reads bytes from current position (FileDescriptor::seek) into user supplied Span
/// @param data Span of bytes where data should be written to
/// @param actuallyRead A sub-span of data of the actually read bytes. A zero sized span means EOF.
/// @return Valid result if read succeeded
[[nodiscard]] Result read(Span<uint8_t> data, Span<uint8_t>& actuallyRead);

/// @brief Writes bytes at offset from start of the file descriptor
/// @param data Span of bytes containing the data to write
/// @param offset Offset from begin of file descriptor to start writing
/// @return Valid result if write succeeded
[[nodiscard]] Result write(Span<const char> data, uint64_t offset);

/// @brief Writes bytes at offset from start of the file descriptor
/// @param data Span of bytes containing the data to write
/// @param offset Offset from begin of file descriptor to start writing
/// @return Valid result if write succeeded
[[nodiscard]] Result write(Span<const uint8_t> data, uint64_t offset);

/// @brief Writes bytes from current position (FileDescriptor::seek) of the file descriptor
/// @param data Span of bytes containing the data to write
/// @return Valid result if write succeeded
[[nodiscard]] Result write(Span<const char> data);

/// @brief Writes bytes from current position (FileDescriptor::seek) of the file descriptor
/// @param data Span of bytes containing the data to write
/// @return Valid result if write succeeded
[[nodiscard]] Result write(Span<const uint8_t> data);

/// @brief How the offset to FileDescriptor::seek is defined
enum SeekMode
{
Expand Down Expand Up @@ -154,30 +178,23 @@ struct SC::FileDescriptor : public SC::UniqueHandle<SC::detail::FileDescriptorDe
/// @return Valid result if read succeeded until EOF
[[nodiscard]] Result readUntilEOF(Vector<char>& destination);

/// @brief Reads into a given dynamic buffer until End of File (EOF) is signaled.
/// Works also for non-seekable file descriptors (stdout / in / err).
/// @param destination A destination buffer to write to (it will be resized as needed)
/// @return Valid result if read succeeded until EOF
[[nodiscard]] Result readUntilEOF(Vector<uint8_t>& destination);

/// @brief Reads into a given string until End of File (EOF) is signaled
/// Works also for non-seekable file descriptors (stdout / in / err).
/// @param destination A destination string to write to (it will be sized as needed)
/// @return Valid result if read succeeded until EOF
[[nodiscard]] Result readUntilEOF(String& destination);

/// @brief Results of readAppend function
struct ReadResult
{
size_t actuallyRead = 0;
bool isEOF = false;
};

/// @brief Reads bytes appending to buffer.
/// @param buffer Destination buffer where bytes will be written to
/// @param fallbackBuffer if buffer is full (capacity() == size()) then data will be read in fallbackBuffer first
/// and only later appended to buffer. This allows sizing the buffer only to the actually needed size without
/// having to resize the buffer beforehand.
/// @param result returns information about EOF being reached and actuallyRead bytes
/// @return Valid result if read succeeded
[[nodiscard]] Result readAppend(Vector<char>& buffer, Span<char> fallbackBuffer, ReadResult& result);

private:
struct Internal;
struct ReadResult;
template <typename T>
Result readUntilEOFTemplate(Vector<T>& destination);
};

/// @brief Descriptor representing a Pipe used for InterProcess Communication (IPC)
Expand Down
35 changes: 17 additions & 18 deletions Libraries/File/Internal/FileDescriptorPosix.inl
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,13 @@
#include <sys/stat.h> // fstat
#include <unistd.h> // close

namespace SC
{
struct FileDescriptorPosixHelpers;
}

// TODO: Add check all posix calls here for EINTR
struct SC::FileDescriptorPosixHelpers
struct SC::FileDescriptor::Internal
{
private:
template <typename T>
[[nodiscard]] static Result readAppend(FileDescriptor::Handle fileDescriptor, Vector<T>& output,
Span<T> fallbackBuffer, ReadResult& result);

static Result getFileFlags(int flagRead, const int fileDescriptor, int& outFlags)
{
do
Expand Down Expand Up @@ -141,32 +139,33 @@ SC::Result SC::FileDescriptor::open(StringView path, OpenMode mode, OpenOptions

SC::Result SC::FileDescriptor::setBlocking(bool blocking)
{
return FileDescriptorPosixHelpers::setFileStatusFlags<O_NONBLOCK>(handle, not blocking);
return Internal::setFileStatusFlags<O_NONBLOCK>(handle, not blocking);
}

SC::Result SC::FileDescriptor::setInheritable(bool inheritable)
{
return FileDescriptorPosixHelpers::setFileDescriptorFlags<FD_CLOEXEC>(handle, not inheritable);
return Internal::setFileDescriptorFlags<FD_CLOEXEC>(handle, not inheritable);
}

SC::Result SC::FileDescriptor::isInheritable(bool& hasValue) const
{
auto res = FileDescriptorPosixHelpers::hasFileDescriptorFlags<FD_CLOEXEC>(handle, hasValue);
auto res = Internal::hasFileDescriptorFlags<FD_CLOEXEC>(handle, hasValue);
hasValue = not hasValue;
return res;
}

SC::Result SC::FileDescriptor::readAppend(Vector<char>& output, Span<char> fallbackBuffer, ReadResult& result)
template <typename T>
SC::Result SC::FileDescriptor::Internal::readAppend(FileDescriptor::Handle fileDescriptor, Vector<T>& output,
Span<T> fallbackBuffer, ReadResult& result)
{
ssize_t numReadBytes;
const bool useVector = output.capacity() > output.size();
FileDescriptor::Handle fileDescriptor;
SC_TRY(get(fileDescriptor, Result::Error("FileDescriptor::readAppend - Invalid Handle")));
ssize_t numReadBytes;
const bool useVector = output.capacity() > output.size();
if (useVector)
{
do
{
numReadBytes = ::read(fileDescriptor, output.data() + output.size(), output.capacity() - output.size());
const size_t bytesToRead = (output.capacity() - output.size()) * sizeof(T);
numReadBytes = ::read(fileDescriptor, output.data() + output.size(), bytesToRead);
} while (numReadBytes == -1 && errno == EINTR); // Syscall may be interrupted and userspace must retry
}
else
Expand All @@ -181,13 +180,13 @@ SC::Result SC::FileDescriptor::readAppend(Vector<char>& output, Span<char> fallb
{
if (useVector)
{
SC_TRY_MSG(output.resizeWithoutInitializing(output.size() + static_cast<size_t>(numReadBytes)),
SC_TRY_MSG(output.resizeWithoutInitializing(output.size() + static_cast<size_t>(numReadBytes) / sizeof(T)),
"FileDescriptor::readAppend - resize failed");
}
else
{
SC_TRY_MSG(
output.append({fallbackBuffer.data(), static_cast<size_t>(numReadBytes)}),
output.append({fallbackBuffer.data(), static_cast<size_t>(numReadBytes) / sizeof(T)}),
"FileDescriptor::readAppend - append failed. Bytes have been read from stream and will get lost");
}
result = ReadResult{static_cast<size_t>(numReadBytes), false};
Expand Down
11 changes: 7 additions & 4 deletions Libraries/File/Internal/FileDescriptorWindows.inl
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ SC::Result SC::FileDescriptor::isInheritable(bool& hasValue) const

struct SC::FileDescriptor::Internal
{
template <typename T>
[[nodiscard]] static Result readAppend(FileDescriptor::Handle fileDescriptor, Vector<T>& output,
Span<T> fallbackBuffer, ReadResult& result);

[[nodiscard]] static bool isActualError(BOOL success, DWORD numReadBytes, FileDescriptor::Handle fileDescriptor)
{
if (success == FALSE && numReadBytes == 0 && GetFileType(fileDescriptor) == FILE_TYPE_PIPE &&
Expand All @@ -120,11 +124,10 @@ struct SC::FileDescriptor::Internal
}
};

SC::Result SC::FileDescriptor::readAppend(Vector<char>& output, Span<char> fallbackBuffer, ReadResult& result)
template <typename T>
SC::Result SC::FileDescriptor::Internal::readAppend(FileDescriptor::Handle fileDescriptor, Vector<T>& output,
Span<T> fallbackBuffer, ReadResult& result)
{
FileDescriptor::Handle fileDescriptor;
SC_TRY(get(fileDescriptor, Result::Error("FileDescriptor::readAppend - Invalid Handle")));

const bool useVector = output.capacity() > output.size();

DWORD numReadBytes = 0xffffffff;
Expand Down

0 comments on commit 0e8137c

Please sign in to comment.