From 764184341ad2600e860510a1fc56afdb38eaa981 Mon Sep 17 00:00:00 2001 From: Danilo Borges Santos Date: Fri, 6 Dec 2024 13:50:32 -0300 Subject: [PATCH] =?UTF-8?q?Corren=C3=A7=C3=B5es=20em=20BinaryReader?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- includes/csharp/exception.hpp | 20 +- includes/csharp/io/binary.hpp | 116 ++++++++++- includes/csharp/io/stream.hpp | 14 +- includes/csharp/sr.hpp | 8 +- samples/01_blank/CMakeLists.txt | 2 +- samples/01_blank/xna.cpp | 16 ++ sources/csharp/CMakeLists.txt | 2 +- sources/csharp/io/binary.cpp | 345 ++++++++++++++++++++++++++++++++ sources/csharp/io/stream.cpp | 3 +- 9 files changed, 509 insertions(+), 17 deletions(-) create mode 100644 sources/csharp/io/binary.cpp diff --git a/includes/csharp/exception.hpp b/includes/csharp/exception.hpp index fd6be00..71282eb 100644 --- a/includes/csharp/exception.hpp +++ b/includes/csharp/exception.hpp @@ -231,8 +231,8 @@ namespace csharp { HRresult = HResults::HR_E_POINTER; } - static void ThrowIfNull(void* argument, OptionalString const& paramName, std::source_location const& source = std::source_location::current()) { - if (!argument) + static void ThrowIfNull(void const* argument, OptionalString const& paramName, std::source_location const& source = std::source_location::current()) { + if (argument == nullptr) { throw ArgumentNullException(paramName, source); } @@ -245,17 +245,23 @@ namespace csharp { ArgumentOutOfRangeException(OptionalString const& message = std::nullopt, std::source_location const& source = std::source_location::current()) : ArgumentOutOfRangeException(message, std::nullopt, nullptr, source) { } - ArgumentOutOfRangeException(OptionalString const& message, OptionalString const& paramName, std::source_location const& source = std::source_location::current()) - : ArgumentOutOfRangeException(message, paramName, nullptr, source) { } + ArgumentOutOfRangeException(OptionalString const& paramName, OptionalString const& message, std::source_location const& source = std::source_location::current()) + : ArgumentOutOfRangeException(paramName, message, nullptr, source) { } ArgumentOutOfRangeException(OptionalString const& message, std::shared_ptr const& innerException, std::source_location const& source = std::source_location::current()) : ArgumentOutOfRangeException(message, std::nullopt, innerException, source) { } - ArgumentOutOfRangeException(OptionalString const& message, OptionalString const& paramName, std::shared_ptr const& innerException, std::source_location const& source = std::source_location::current()) - : ArgumentException(message.value_or(SR::Arg_ArgumentOutOfRangeException), paramName, innerException, source) + ArgumentOutOfRangeException(OptionalString const& paramName, OptionalString const& message, std::shared_ptr const& innerException, std::source_location const& source = std::source_location::current()) + : ArgumentException(paramName, message.value_or(SR::Arg_ArgumentOutOfRangeException), innerException, source) { HRresult = HResults::HR_E_POINTER; - } + } + + template + static void ThrowIfNegative(T& value, OptionalString const& paramName, std::source_location const& source = std::source_location::current()) { + if (value < 0) + throw ArgumentOutOfRangeException(paramName, source); + } }; class SystemException : public Exception { diff --git a/includes/csharp/io/binary.hpp b/includes/csharp/io/binary.hpp index b24c0dd..a04ecc0 100644 --- a/includes/csharp/io/binary.hpp +++ b/includes/csharp/io/binary.hpp @@ -4,25 +4,135 @@ #include "stream.hpp" #include "exception.hpp" #include +#include namespace csharp { + /* + * The BinaryReader class uses byte encodings, by default UTF8. + * This was not implemented, but we tried to follow the same standard. + * Also the reading of primitives was modified. + */ + + //The BinaryReader class uses byte encodings, by default UTF8 class BinaryReader { public: - BinaryReader(std::shared_ptr input, bool leaveOpen = false) + BinaryReader(std::shared_ptr const& input, bool leaveOpen = false) : _stream(input), _leaveOpen(leaveOpen) { - if (input == nullptr) - throw csharp::ArgumentNullException(std::nullopt, "input"); + ArgumentNullException::ThrowIfNull(input.get(), "input"); + + if (!input->CanRead()) + throw ArgumentException(SR::Argument_StreamNotReadable); } + virtual std::shared_ptr BaseStream() const { return _stream; } + virtual void Close() { + if (_disposed) + return; + if (!_leaveOpen) _stream->Close(); + + _disposed = true; + } + + virtual int32_t PeekChar(); + virtual int32_t Read(); + + virtual uint8_t ReadByte() { + return InternalReadByte(); + } + + virtual inline int8_t ReadSByte() { + return static_cast(InternalReadByte()); + } + + virtual inline bool ReadBoolean() { + return InternalReadByte() != 0; + } + + virtual char ReadChar(); + + virtual int16_t ReadInt16() { + return ReadNumeric(); + } + + virtual uint16_t ReadUInt16() { + return ReadNumeric(); + } + + virtual int32_t ReadInt32() { + return ReadNumeric(); + } + + virtual uint32_t ReadUInt32() { + return ReadNumeric(); + } + + virtual int64_t ReadInt64() { + return ReadNumeric(); + } + + virtual uint64_t ReadUInt64() { + return ReadNumeric(); + } + + virtual float ReadSingle() { + return ReadNumeric(); + } + + virtual double ReadDouble() { + return ReadNumeric(); + } + + virtual std::string ReadString(); + + virtual int32_t Read(char* buffer, int32_t bufferLength, int32_t index, int32_t count); + + virtual int32_t Read(char* buffer, int32_t bufferLength); + + virtual std::vector ReadChars(int32_t count); + + virtual int32_t Read(uint8_t* buffer, int32_t bufferLength, int32_t index, int32_t count); + + virtual int32_t Read(uint8_t* buffer, int32_t bufferLength); + + virtual std::vector ReadBytes(int32_t count); + + virtual void ReadExactly(uint8_t* buffer, int32_t bufferLength); + + int32_t Read7BitEncodedInt(); + int64_t Read7BitEncodedInt64(); + + private: + uint8_t InternalReadByte(); + void InternalRead(std::vector& buffer); + int32_t InternalReadChars(char* buffer, int32_t bufferLength); + + template + TNUMERIC ReadNumeric() { + const auto numericSize = sizeof(TNUMERIC); + + if (_auxBuffer.size() != numericSize) + _auxBuffer.resize(numericSize); + + InternalRead(_auxBuffer); + + const auto ptr = reinterpret_cast(_auxBuffer.data()); + const auto value = *ptr; + return value; } private: + static constexpr int MaxCharBytesSize = 128; + std::shared_ptr _stream; bool _leaveOpen; + bool _disposed{false}; + bool _2BytesPerChar{ false }; + + std::vector _auxBuffer; }; } diff --git a/includes/csharp/io/stream.hpp b/includes/csharp/io/stream.hpp index 321be57..9ab1bd6 100644 --- a/includes/csharp/io/stream.hpp +++ b/includes/csharp/io/stream.hpp @@ -60,13 +60,21 @@ namespace csharp { virtual int32_t Read(uint8_t* buffer, int32_t bufferLength); virtual int32_t ReadByte(); - virtual int32_t ReadExactly(uint8_t* buffer, int32_t bufferLength, int32_t offset, int32_t count) { - return ReadAtLeastCore(buffer, bufferLength, bufferLength, true); + void ReadExactly(uint8_t* buffer, int32_t bufferLength) { + ReadAtLeastCore(buffer, bufferLength, bufferLength, true); + } + + virtual void ReadExactly(uint8_t* buffer, int32_t offset, int32_t count) { + ReadAtLeastCore(buffer + offset, count, count, true); + } + + int32_t ReadAtLeast(uint8_t* buffer, int32_t bufferLength, int32_t minimumBytes, bool throwOnEndOfStream = true) { + return ReadAtLeastCore(buffer, bufferLength, minimumBytes, throwOnEndOfStream); } virtual void Write(uint8_t const* buffer, int32_t bufferLength, int32_t offset, int32_t count) = 0; virtual void Write(uint8_t const* buffer, int32_t bufferLength); - virtual void WriteByte(uint8_t value); + virtual void WriteByte(uint8_t value) = 0; protected: void ValidateBuffer(uint8_t const* buffer, int32_t bufferLength); diff --git a/includes/csharp/sr.hpp b/includes/csharp/sr.hpp index c39b76a..ff8e0a8 100644 --- a/includes/csharp/sr.hpp +++ b/includes/csharp/sr.hpp @@ -50,7 +50,13 @@ namespace csharp { inline static const std::string ArgumentOutOfRange_StreamLength = "Stream length must be non-negative and less than 2^31 - 1 - origin."; inline static const std::string net_uri_NotAbsolute - = "This operation is not supported for a relative URI."; + = "This operation is not supported for a relative URI."; + inline static const std::string Argument_StreamNotReadable + = "Stream was not readable."; + inline static const std::string Format_Bad7BitInt + = "Too many bytes in what should have been a 7-bit encoded integer."; + inline static const std::string IO_InvalidStringLen_Len + = "BinaryReader encountered an invalid string length."; }; }; diff --git a/samples/01_blank/CMakeLists.txt b/samples/01_blank/CMakeLists.txt index 9ecdeb3..c3957ab 100644 --- a/samples/01_blank/CMakeLists.txt +++ b/samples/01_blank/CMakeLists.txt @@ -10,4 +10,4 @@ if (CMAKE_VERSION VERSION_GREATER 3.12) endif() # TODO: Add tests and install targets if needed. -target_link_libraries(BlankApp Xn65DX) +target_link_libraries(BlankApp Xn65DX CSharp++) diff --git a/samples/01_blank/xna.cpp b/samples/01_blank/xna.cpp index 6141a87..7e45c24 100644 --- a/samples/01_blank/xna.cpp +++ b/samples/01_blank/xna.cpp @@ -2,6 +2,7 @@ // #include "xna-dx/framework.hpp" +#include "csharp/io/binary.hpp" using namespace std; using namespace xna; @@ -11,6 +12,21 @@ namespace xna { public: Game1() : Game() { Content()->RootDirectory("Content"); + + auto stream = std::make_shared("D:/file.bin", csharp::FileMode::Open); + auto reader = csharp::BinaryReader(stream); + auto bo = reader.ReadBoolean(); //reader.ReadChar() + auto sb = reader.ReadSByte(); //127 + auto by = reader.ReadByte(); //255 + auto ch = reader.ReadChar(); //x + auto i16 = reader.ReadInt16(); //32767 + auto ui16 = reader.ReadUInt16();//65535 + auto i32 = reader.ReadInt32(); //2147483647 + auto ui32 = reader.ReadUInt32(); //4294967295 + auto i64 = reader.ReadInt64(); //9223372036854775807 + auto ui64 = reader.ReadUInt64(); //18446744073709551615 + auto str = reader.ReadString(); //The string in stream. + auto str2 = reader.ReadString(); //Ç de cedilha e ñ com til mas sem ¨ trema. } void Initialize() override { diff --git a/sources/csharp/CMakeLists.txt b/sources/csharp/CMakeLists.txt index c725124..3dc38f9 100644 --- a/sources/csharp/CMakeLists.txt +++ b/sources/csharp/CMakeLists.txt @@ -5,7 +5,7 @@ # Add source to this project's executable. add_library (CSharp++ STATIC "exception.cpp" - "io/stream.cpp") + "io/stream.cpp" "io/binary.cpp") if (CMAKE_VERSION VERSION_GREATER 3.12) set_property(TARGET CSharp++ PROPERTY CXX_STANDARD 20) diff --git a/sources/csharp/io/binary.cpp b/sources/csharp/io/binary.cpp new file mode 100644 index 0000000..f6ea899 --- /dev/null +++ b/sources/csharp/io/binary.cpp @@ -0,0 +1,345 @@ +#include "csharp/io/binary.hpp" +#include +#include + +namespace csharp { + int32_t BinaryReader::PeekChar() { + if (_disposed) + throw InvalidOperationException(); + + if (!_stream->CanSeek()) + { + return -1; + } + + const auto origPos = _stream->Position(); + const auto ch = Read(); + + _stream->Position(origPos); + + return ch; + } + + int32_t BinaryReader::Read() { + if (_disposed) + throw InvalidOperationException(); + + int32_t charsRead = 0; + int32_t numBytes; + int64_t posSav = 0; + + if (_stream->CanSeek()) + { + posSav = _stream->Position(); + } + + auto charBytes = std::vector(MaxCharBytesSize); + char singleChar = '\0'; + + while (charsRead == 0) { + numBytes = _2BytesPerChar ? 2 : 1; + + auto r = _stream->ReadByte(); + charBytes[0] = static_cast(r); + + if (r == -1) + { + numBytes = 0; + } + if (numBytes == 2) + { + r = _stream->ReadByte(); + charBytes[1] = static_cast(r); + + if (r == -1) + { + numBytes = 1; + } + } + + if (numBytes == 0) + { + return -1; + } + + const auto chars = reinterpret_cast(charBytes.data()); + const auto decoder = std::string(chars); + charsRead = decoder.size(); + singleChar = decoder[0]; + } + + return singleChar; + } + + uint8_t BinaryReader::InternalReadByte() { + if (_disposed) + throw InvalidOperationException(); + + const auto b = _stream->ReadByte(); + if (b == -1) + { + throw EndOfStreamException(SR::IO_EOF_ReadBeyondEOF); + } + + return static_cast(b); + } + + char BinaryReader::ReadChar() { + const auto value = Read(); + + if (value == -1) + { + throw EndOfStreamException(SR::IO_EOF_ReadBeyondEOF); + } + + return static_cast(value); + } + + void BinaryReader::InternalRead(std::vector& buffer) { + if (_disposed) + throw InvalidOperationException(); + + _stream->ReadExactly(buffer.data(), buffer.size()); + } + + std::string BinaryReader::ReadString() { + if (_disposed) + throw InvalidOperationException(); + + const auto stringLength = Read7BitEncodedInt(); + if (stringLength < 0) + { + throw IOException(SR::IO_InvalidStringLen_Len); + } + + if (stringLength == 0) + { + return {}; + } + + auto charBytes = std::vector(MaxCharBytesSize); + int32_t currPos = 0; + + std::string sb; + + do + { + const auto readLength = std::min(MaxCharBytesSize, stringLength - currPos); + const auto n = _stream->Read(charBytes.data(), readLength); + + if (n == 0) + { + throw EndOfStreamException(SR::IO_EOF_ReadBeyondEOF); + } + + const auto chars = reinterpret_cast(charBytes.data()); + + if (currPos == 0 && n == stringLength) + { + return std::string(chars); + } + + sb.append(chars); + + currPos += n; + + } while (currPos < stringLength); + + return sb; + } + + int32_t BinaryReader::Read(char* buffer, int32_t bufferLength, int32_t index, int32_t count) { + ArgumentNullException::ThrowIfNull(buffer, "buffer"); + + if (index < 0) + index = 0; + + if (count < 0) + count = 0; + + if (bufferLength - index < count) { + throw ArgumentException(SR::Argument_InvalidOffLen); + } + + if (_disposed) + throw InvalidOperationException(); + + return InternalReadChars(buffer + index, count); + } + + int32_t BinaryReader::Read(char* buffer, int32_t bufferLength) { + if (_disposed) + throw InvalidOperationException(); + + return InternalReadChars(buffer, bufferLength); + } + + int32_t BinaryReader::InternalReadChars(char* buffer, int32_t bufferLength) { + int totalCharsRead = 0; + auto charBytes = std::vector(MaxCharBytesSize); + + auto numBytes = bufferLength; + + while (bufferLength > 0) + { + auto numBytes = bufferLength; + + if (_2BytesPerChar) + { + numBytes <<= 1; + } + + std::vector byteBuffer; + + if (numBytes > MaxCharBytesSize) + { + numBytes = MaxCharBytesSize; + } + + numBytes = _stream->Read(charBytes.data(), numBytes); + byteBuffer = std::vector(charBytes.begin(), charBytes.begin() + numBytes); + + if (byteBuffer.empty()) + { + break; + } + + auto chars = reinterpret_cast(byteBuffer.data()); + auto charsRead = std::string(chars); + + bufferLength = charsRead.length(); + totalCharsRead += charsRead.length(); + } + + // we may have read fewer than the number of characters requested if end of stream reached + // or if the encoding makes the char count too big for the buffer (e.g. fallback sequence) + return totalCharsRead; + } + + std::vector BinaryReader::ReadChars(int32_t count) { + if (count < 0) + throw ArgumentOutOfRangeException(); + + if (_disposed) + throw InvalidOperationException(); + + if (count == 0) + return std::vector(); + + auto chars = std::vector(count); + const auto n = InternalReadChars(chars.data(), chars.size()); + + if (n != count) { + chars = std::vector(chars.begin(), chars.begin() + count); + } + + return chars; + } + + int32_t BinaryReader::Read(uint8_t* buffer, int32_t bufferLength, int32_t index, int32_t count) { + ArgumentNullException::ThrowIfNull(buffer, "buffer"); + ArgumentOutOfRangeException::ThrowIfNegative(index, "index"); + ArgumentOutOfRangeException::ThrowIfNegative(count, "count"); + + if (bufferLength - index < count) { + throw ArgumentException(SR::Argument_InvalidOffLen); + } + + if (_disposed) + throw InvalidOperationException(); + + return _stream->Read(buffer, bufferLength, index, count); + } + + int32_t BinaryReader::Read(uint8_t* buffer, int32_t bufferLength) { + if (_disposed) + throw InvalidOperationException(); + + return _stream->Read(buffer, bufferLength); + } + + std::vector BinaryReader::ReadBytes(int32_t count) { + ArgumentOutOfRangeException::ThrowIfNegative(count, "count"); + + if (_disposed) + throw InvalidOperationException(); + + if (count == 0) + return std::vector(); + + auto result = std::vector(count); + const auto numRead = _stream->ReadAtLeast(result.data(), result.size(), result.size(), false); + + if (numRead != result.size()) + { + result = std::vector(result.begin(), result.begin() + numRead); + } + + return result; + } + + void BinaryReader::ReadExactly(uint8_t* buffer, int32_t bufferLength) { + if (_disposed) + throw InvalidOperationException(); + + _stream->ReadExactly(buffer, bufferLength); + } + + int32_t BinaryReader::Read7BitEncodedInt() { + uint32_t result = 0; + uint8_t byteReadJustNow; + + constexpr int32_t MaxBytesWithoutOverflow = 4; + + for (int32_t shift = 0; shift < MaxBytesWithoutOverflow * 7; shift += 7) + { + byteReadJustNow = ReadByte(); + result |= (byteReadJustNow & 0x7Fu) << shift; + + if (byteReadJustNow <= 0x7Fu) + { + return static_cast(result); + } + } + + byteReadJustNow = ReadByte(); + + if (byteReadJustNow > 15u) + { + //FormatException + throw InvalidOperationException(SR::Format_Bad7BitInt); + } + + result |= static_cast(byteReadJustNow) << (MaxBytesWithoutOverflow * 7); + return static_cast(result); + } + + int64_t BinaryReader::Read7BitEncodedInt64() { + uint64_t result = 0; + uint8_t byteReadJustNow; + + constexpr int32_t MaxBytesWithoutOverflow = 9; + + for (int32_t shift = 0; shift < MaxBytesWithoutOverflow * 7; shift += 7) + { + byteReadJustNow = ReadByte(); + result |= (byteReadJustNow & 0x7Ful) << shift; + + if (byteReadJustNow <= 0x7Fu) + { + return static_cast(result); + } + } + + byteReadJustNow = ReadByte(); + + if (byteReadJustNow > 1u) + { + //FormatException + throw InvalidOperationException(SR::Format_Bad7BitInt); + } + + result |= static_cast(byteReadJustNow) << (MaxBytesWithoutOverflow * 7); + return static_cast(result); + } +} \ No newline at end of file diff --git a/sources/csharp/io/stream.cpp b/sources/csharp/io/stream.cpp index 00ab1b8..0adb879 100644 --- a/sources/csharp/io/stream.cpp +++ b/sources/csharp/io/stream.cpp @@ -684,7 +684,8 @@ namespace csharp { return -1; } - const auto result = static_cast(c); + const auto uchar = static_cast(c); + const auto result = static_cast(uchar); return result; }