312 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			312 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| 
 | |
| #include "./stream.hpp"
 | |
| 
 | |
| #include <algorithm>
 | |
| #include <cassert>
 | |
| #include <limits>
 | |
| 
 | |
| namespace mijin
 | |
| {
 | |
| 
 | |
| //
 | |
| // internal defines
 | |
| //
 | |
| 
 | |
| //
 | |
| // internal constants
 | |
| //
 | |
| 
 | |
| //
 | |
| // internal types
 | |
| //
 | |
| 
 | |
| //
 | |
| // internal variables
 | |
| //
 | |
| 
 | |
| //
 | |
| // internal functions
 | |
| //
 | |
| 
 | |
| //
 | |
| // public functions
 | |
| //
 | |
| 
 | |
| void Stream::flush() {}
 | |
| 
 | |
| StreamError Stream::readString(std::string& outString)
 | |
| {
 | |
|     std::uint32_t length; // NOLINT(cppcoreguidelines-init-variables)
 | |
|     StreamError error = read(length);
 | |
|     if (error != StreamError::SUCCESS) {
 | |
|         return error;
 | |
|     }
 | |
| 
 | |
|     std::string result;
 | |
|     result.resize(length);
 | |
|     error = readSpan(result.begin(), result.end());
 | |
|     if (error != StreamError::SUCCESS) {
 | |
|         return error;
 | |
|     }
 | |
|     outString = std::move(result);
 | |
|     return StreamError::SUCCESS;
 | |
| }
 | |
| 
 | |
| StreamError Stream::writeString(std::string_view str)
 | |
| {
 | |
|     assert(str.length() <= std::numeric_limits<std::uint32_t>::max());
 | |
|     const std::uint32_t length = static_cast<std::uint32_t>(str.length());
 | |
|     StreamError error = write(length);
 | |
|     if (error != StreamError::SUCCESS) {
 | |
|         return error;
 | |
|     }
 | |
|     return writeSpan(str.begin(), str.end());
 | |
| }
 | |
| 
 | |
| FileStream::~FileStream()
 | |
| {
 | |
|     if (handle) {
 | |
|         close();
 | |
|     }
 | |
| }
 | |
| 
 | |
| StreamError FileStream::open(const char* path, FileOpenMode mode_)
 | |
| {
 | |
|     mode = mode_;
 | |
|     
 | |
|     const char* modeStr; // NOLINT(cppcoreguidelines-init-variables)
 | |
|     switch (mode_)
 | |
|     {
 | |
|         case FileOpenMode::READ:
 | |
|             modeStr = "rb";
 | |
|             break;
 | |
|         case FileOpenMode::WRITE:
 | |
|             modeStr = "wb";
 | |
|             break;
 | |
|         case FileOpenMode::APPEND:
 | |
|             modeStr = "ab";
 | |
|             break;
 | |
|         case FileOpenMode::READ_WRITE:
 | |
|             modeStr = "r+b";
 | |
|             break;
 | |
|     }
 | |
|     handle = std::fopen(path, modeStr); // NOLINT(cppcoreguidelines-owning-memory)
 | |
|     if (!handle && mode_ == FileOpenMode::READ_WRITE) {
 | |
|         handle = std::fopen(path, "w+b"); // NOLINT(cppcoreguidelines-owning-memory)
 | |
|     }
 | |
|     if (!handle) {
 | |
|         return StreamError::IO_ERROR;
 | |
|     }
 | |
| 
 | |
|     int result = std::fseek(handle, 0, SEEK_END);
 | |
|     assert(result == 0);
 | |
|     length = std::ftell(handle);
 | |
|     result = std::fseek(handle, 0, SEEK_SET);
 | |
|     assert(result == 0);
 | |
| 
 | |
|     return StreamError::SUCCESS;
 | |
| }
 | |
| 
 | |
| void FileStream::close()
 | |
| {
 | |
|     assert(handle);
 | |
|     const int result = std::fclose(handle); // NOLINT(cppcoreguidelines-owning-memory)
 | |
|     assert(result == 0);
 | |
| }
 | |
| 
 | |
| StreamError FileStream::readRaw(std::span<std::uint8_t> buffer, bool partial, std::size_t* outBytesRead)
 | |
| {
 | |
|     assert(handle);
 | |
|     assert(mode == FileOpenMode::READ || mode == FileOpenMode::READ_WRITE);
 | |
| 
 | |
|     const std::size_t readBytes = std::fread(buffer.data(), 1, buffer.size(), handle);
 | |
|     if (std::ferror(handle)) {
 | |
|         return StreamError::IO_ERROR;
 | |
|     }
 | |
|     if (!partial && readBytes < buffer.size()) {
 | |
|         return StreamError::IO_ERROR;
 | |
|     }
 | |
|     if (outBytesRead != nullptr) {
 | |
|         *outBytesRead = readBytes;
 | |
|     }
 | |
|     return StreamError::SUCCESS;
 | |
| }
 | |
| 
 | |
| StreamError FileStream::writeRaw(std::span<const std::uint8_t> buffer)
 | |
| {
 | |
|     assert(handle);
 | |
|     assert(mode == FileOpenMode::WRITE || mode == FileOpenMode::APPEND || mode == FileOpenMode::READ_WRITE);
 | |
| 
 | |
|     const std::size_t written = std::fwrite(buffer.data(), 1, buffer.size(), handle);
 | |
|     if (written != buffer.size() || std::ferror(handle)) {
 | |
|         return StreamError::IO_ERROR;
 | |
|     }
 | |
| 
 | |
|     length = std::max<std::size_t>(length, std::ftell(handle));
 | |
|     return StreamError::SUCCESS;
 | |
| }
 | |
| 
 | |
| std::size_t FileStream::tell()
 | |
| {
 | |
|     assert(handle);
 | |
| 
 | |
|     return std::ftell(handle);
 | |
| }
 | |
| 
 | |
| StreamError FileStream::seek(std::intptr_t pos, SeekMode seekMode)
 | |
| {
 | |
|     assert(handle);
 | |
|     
 | |
|     int origin; // NOLINT(cppcoreguidelines-init-variables)
 | |
|     switch (seekMode)
 | |
|     {
 | |
|         case SeekMode::ABSOLUTE:
 | |
|             origin = SEEK_SET;
 | |
|             break;
 | |
|         case SeekMode::RELATIVE:
 | |
|             origin = SEEK_CUR;
 | |
|             break;
 | |
|         case SeekMode::RELATIVE_TO_END:
 | |
|             origin = SEEK_END;
 | |
|             break;
 | |
|         default:
 | |
|             assert(!"Invalid value passed as seekMode!");
 | |
|             return StreamError::UNKNOWN_ERROR;
 | |
|     }
 | |
|     const int result = std::fseek(handle, static_cast<long>(pos), origin);
 | |
|     if (result != 0 || std::ferror(handle)) {
 | |
|         return StreamError::IO_ERROR;
 | |
|     }
 | |
|     return StreamError::SUCCESS;
 | |
| }
 | |
| 
 | |
| void FileStream::flush()
 | |
| {
 | |
|     const int result = std::fflush(handle);
 | |
|     assert(result == 0);
 | |
| }
 | |
| 
 | |
| bool FileStream::isAtEnd()
 | |
| {
 | |
|     assert(handle);
 | |
| 
 | |
|     (void) std::fgetc(handle);
 | |
|     if (std::feof(handle)) {
 | |
|         return true;
 | |
|     }
 | |
|     const int result = std::fseek(handle, -1, SEEK_CUR);
 | |
|     assert(result == 0);
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| StreamFeatures FileStream::getFeatures()
 | |
| {
 | |
|     if (handle)
 | |
|     {
 | |
|         return {
 | |
|             .read = (mode == FileOpenMode::READ),
 | |
|             .write = (mode == FileOpenMode::WRITE || mode == FileOpenMode::APPEND || mode == FileOpenMode::READ_WRITE),
 | |
|             .tell = true,
 | |
|             .seek = true
 | |
|         };
 | |
|     }
 | |
|     return {};
 | |
| }
 | |
| 
 | |
| void MemoryStream::openRW(std::span<std::uint8_t> data)
 | |
| {
 | |
|     assert(!isOpen());
 | |
|     data_ = data;
 | |
|     pos_ = 0;
 | |
|     canWrite_ = true;
 | |
| }
 | |
| 
 | |
| void MemoryStream::openRO(std::span<const std::uint8_t> data)
 | |
| {
 | |
|     assert(!isOpen());
 | |
|     data_ = std::span<std::uint8_t>(const_cast<std::uint8_t*>(data.data()), data.size()); // NOLINT(cppcoreguidelines-pro-type-const-cast) we'll be fine
 | |
|     pos_ = 0;
 | |
|     canWrite_ = false;
 | |
| }
 | |
| 
 | |
| void MemoryStream::close()
 | |
| {
 | |
|     assert(isOpen());
 | |
|     data_ = {};
 | |
| }
 | |
| 
 | |
| StreamError MemoryStream::readRaw(std::span<std::uint8_t> buffer, bool partial, std::size_t* outBytesRead)
 | |
| {
 | |
|     assert(isOpen());
 | |
|     if (!partial && availableBytes() < buffer.size()) {
 | |
|         return StreamError::IO_ERROR; // TODO: need more errors?
 | |
|     }
 | |
|     const std::size_t numBytes = std::min(buffer.size(), availableBytes());
 | |
|     std::copy_n(data_.begin() + static_cast<long>(pos_), numBytes, buffer.begin());
 | |
|     if (outBytesRead) {
 | |
|         *outBytesRead = numBytes;
 | |
|     }
 | |
|     pos_ += numBytes;
 | |
|     return StreamError::SUCCESS;
 | |
| }
 | |
| 
 | |
| StreamError MemoryStream::writeRaw(std::span<const std::uint8_t> buffer)
 | |
| {
 | |
|     assert(isOpen());
 | |
|     assert(canWrite_);
 | |
| 
 | |
|     if (availableBytes() < buffer.size()) {
 | |
|         return StreamError::IO_ERROR;
 | |
|     }
 | |
|     std::copy(buffer.begin(), buffer.end(), data_.begin() + static_cast<long>(pos_));
 | |
|     pos_ += buffer.size();
 | |
|     return StreamError::SUCCESS;
 | |
| }
 | |
| 
 | |
| std::size_t MemoryStream::tell()
 | |
| {
 | |
|     assert(isOpen());
 | |
|     return pos_;
 | |
| }
 | |
| 
 | |
| StreamError MemoryStream::seek(std::intptr_t pos, SeekMode seekMode)
 | |
| {
 | |
|     assert(isOpen());
 | |
|     std::intptr_t newPos = -1;
 | |
|     switch (seekMode)
 | |
|     {
 | |
|         case SeekMode::ABSOLUTE:
 | |
|             newPos = pos;
 | |
|             break;
 | |
|         case SeekMode::RELATIVE:
 | |
|             newPos = static_cast<std::intptr_t>(pos_) + pos;
 | |
|             break;
 | |
|         case SeekMode::RELATIVE_TO_END:
 | |
|             newPos = static_cast<std::intptr_t>(data_.size()) + pos;
 | |
|             break;
 | |
|     }
 | |
|     if (newPos < 0 || newPos > static_cast<std::intptr_t>(data_.size())) {
 | |
|         return StreamError::IO_ERROR;
 | |
|     }
 | |
|     pos_ = newPos;
 | |
|     return StreamError::SUCCESS;
 | |
| }
 | |
| 
 | |
| bool MemoryStream::isAtEnd()
 | |
| {
 | |
|     assert(isOpen());
 | |
|     return pos_ == data_.size();
 | |
| }
 | |
| 
 | |
| StreamFeatures MemoryStream::getFeatures()
 | |
| {
 | |
|     return {
 | |
|         .read  = true,
 | |
|         .write = canWrite_,
 | |
|         .tell  = true,
 | |
|         .seek  = true
 | |
|     };
 | |
| }
 | |
| 
 | |
| } // namespace mijin
 |