diff --git a/includes/csharp/io/exception.hpp b/includes/csharp/io/exception.hpp new file mode 100644 index 0000000..bcf329d --- /dev/null +++ b/includes/csharp/io/exception.hpp @@ -0,0 +1,57 @@ +#ifndef CSHARP_IO_EXCEPTION_HPP +#define CSHARP_IO_EXCEPTION_HPP + +#include "../exception.hpp" +#include + +namespace csharp { + class IOException : public SystemException { + public: + IOException(std::source_location const& source = std::source_location::current()) + : SystemException(SR::Arg_IOException, source) + { + HRresult = HResults::HR_COR_E_IO; + } + + IOException(std::string const& message, std::source_location const& source = std::source_location::current()) + : SystemException(!message.empty() ? message : SR::Arg_IOException, source) + { + HRresult = HResults::HR_COR_E_IO; + } + + IOException(std::string const& message, size_t hresult, std::source_location const& source = std::source_location::current()) + : SystemException(!message.empty() ? message : SR::Arg_IOException, source) + { + HRresult = hresult; + } + + IOException(std::string const& message, std::shared_ptr& innerException, std::source_location const& source = std::source_location::current()) + : SystemException(!message.empty() ? message : SR::Arg_IOException, innerException, source) + { + HRresult = HResults::HR_COR_E_IO; + } + }; + + class EndOfStreamException : public IOException { + public: + EndOfStreamException(std::source_location const& source = std::source_location::current()) + : IOException(SR::Arg_IOException, source) + { + HRresult = HResults::HR_COR_E_ENDOFSTREAM; + } + + EndOfStreamException(std::string const& message, std::source_location const& source = std::source_location::current()) + : IOException(!message.empty() ? message : SR::Arg_IOException, source) + { + HRresult = HResults::HR_COR_E_ENDOFSTREAM; + } + + EndOfStreamException(std::string const& message, std::shared_ptr& innerException, std::source_location const& source = std::source_location::current()) + : IOException(!message.empty() ? message : SR::Arg_IOException, innerException, source) + { + HRresult = HResults::HR_COR_E_ENDOFSTREAM; + } + }; +} + +#endif \ No newline at end of file diff --git a/includes/csharp/io/stream.hpp b/includes/csharp/io/stream.hpp new file mode 100644 index 0000000..321be57 --- /dev/null +++ b/includes/csharp/io/stream.hpp @@ -0,0 +1,337 @@ +#ifndef CSHARP_IO_STREAM_HPP +#define CSHARP_IO_STREAM_HPP + +#include "../exception.hpp" +#include +#include +#include +#include +#include + +namespace csharp { + // Provides seek reference points. To seek to the end of a stream, + // call stream.Seek(0, SeekOrigin.End). + enum class SeekOrigin + { + // These constants match Win32's FILE_BEGIN, FILE_CURRENT, and FILE_END + Begin = 0, + Current = 1, + End = 2, + }; + + class Stream { + public: + virtual bool CanRead() const = 0; + virtual bool CanWrite() const = 0; + virtual bool CanSeek() const = 0; + virtual bool CanTimeout() const { return false; } + virtual int64_t Length() const = 0; + virtual int64_t Position() const = 0; + virtual void Position(int64_t value) = 0; + + virtual int32_t ReadTimeout() { + throw InvalidOperationException(SR::InvalidOperation_TimeoutsNotSupported); + return 0; + } + + virtual void ReadTimeout(int32_t value) { + throw InvalidOperationException(SR::InvalidOperation_TimeoutsNotSupported); + } + + virtual int32_t WriteTimeout() { + throw InvalidOperationException(SR::InvalidOperation_TimeoutsNotSupported); + return 0; + } + + virtual void WriteTimeout(int32_t value) { + throw InvalidOperationException(SR::InvalidOperation_TimeoutsNotSupported); + } + + void CopyTo(Stream& destination) { + CopyTo(destination, GetCopybufferLength()); + } + + virtual void CopyTo(Stream& destination, int32_t bufferLength); + virtual void Close() {} + virtual void Flush() = 0; + virtual int64_t Seek(int64_t offset, SeekOrigin origin) = 0; + virtual void SetLength(int64_t value) = 0; + virtual int32_t Read(uint8_t* buffer, int32_t bufferLength, int32_t offset, int32_t count) = 0; + 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); + } + + 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); + + protected: + void ValidateBuffer(uint8_t const* buffer, int32_t bufferLength); + + private: + int32_t ReadAtLeastCore(uint8_t* buffer, int32_t bufferLength, int32_t minimumBytes, bool throwOnEndOfStream); + + private: + int32_t GetCopybufferLength() const; + }; + + class MemoryStream : public Stream { + public: + MemoryStream() : MemoryStream(0) {} + + MemoryStream(int32_t capacity) + { + _buffer.resize(static_cast(capacity)); + _capacity = capacity; + _expandable = true; + _writable = true; + _exposable = true; + _isOpen = true; + } + + MemoryStream(std::vector const buffer) + : MemoryStream(buffer, true) {} + + MemoryStream(std::vector const buffer, bool writable) { + _buffer = buffer; + _length = _capacity = static_cast(buffer.size()); + _writable = writable; + _isOpen = true; + } + + MemoryStream(std::vector const buffer, int32_t index, int32_t count) + : MemoryStream(buffer, index, count, true, false) {} + + MemoryStream(std::vector const buffer, int32_t index, int32_t count, bool writable) + : MemoryStream(buffer, index, count, writable, false) {} + + MemoryStream(std::vector const buffer, int32_t index, int32_t count, bool writable, bool publiclyVisible) + { + if (index < 0) + index = 0; + + if (count < 0) + count = 0; + + if (buffer.size() - static_cast(index < count)) + throw ArgumentException(SR::Argument_InvalidOffLen); + + _buffer = buffer; + _origin = _position = index; + _length = _capacity = index + count; + _writable = writable; + _exposable = publiclyVisible; + _isOpen = true; + } + + bool CanRead() const override { return _isOpen; } + bool CanSeek() const override { return _isOpen; } + bool CanWrite() const override { return _writable; } + void Flush() override {} + virtual std::vector& GetBuffer(); + virtual bool TryGetBuffer(std::vector& buffer); + virtual int32_t Capacity() const; + virtual void Capacity(int32_t value); + int64_t Length() const override; + int64_t Position() const override; + void Position(int64_t value) override; + int32_t Read(uint8_t* buffer, int32_t bufferLength, int32_t offset, int32_t count) override; + int32_t Read(uint8_t* buffer, int32_t bufferLength) override; + int32_t ReadByte() override; + void CopyTo(Stream& destination, int32_t bufferLength) override; + int32_t InternalEmulateRead(int32_t count); + int64_t Seek(int64_t offset, SeekOrigin loc) override; + void SetLength(int64_t value) override; + void Write(uint8_t const* buffer, int32_t bufferLength, int32_t offset, int32_t count) override; + void Write(uint8_t const* buffer, int32_t bufferLength) override; + void WriteByte(uint8_t value) override; + virtual void WriteTo(Stream& stream); + + constexpr void Close() override { + if (!_isOpen) + return; + + _isOpen = false; + _writable = false; + _expandable = false; + _buffer.clear(); + } + + private: + void EnsureNotClosed() const; + void EnsureWriteable() const; + bool EnsureCapacity(int32_t value); + int64_t SeekCore(int64_t offset, int32_t loc); + + public: + std::vector _buffer; + + private: + int32_t _origin{ 0 }; + int32_t _position{ 0 }; + int32_t _length{ 0 }; + int32_t _capacity{ 0 }; + bool _expandable{ false }; + bool _writable{ false }; + bool _exposable{ false }; + bool _isOpen{ false }; + + inline static constexpr int32_t MemStreamMaxLength = std::numeric_limits::max(); + }; + + // Contains constants for specifying how the OS should open a file. + // These will control whether you overwrite a file, open an existing + // file, or some combination thereof. + // To append to a file, use Append (which maps to OpenOrCreate then we seek + // to the end of the file). To truncate a file or create it if it doesn't + // exist, use Create. + enum class FileMode + { + // Creates a new file. An exception is raised if the file already exists. + CreateNew = 1, + // Creates a new file. If the file already exists, it is overwritten. + Create = 2, + // Opens an existing file. An exception is raised if the file does not exist. + Open = 3, + // Opens the file if it exists. Otherwise, creates a new file. + OpenOrCreate = 4, + // Opens an existing file. Once opened, the file is truncated so that its + // size is zero bytes. The calling process must open the file with at least + // WRITE access. An exception is raised if the file does not exist. + Truncate = 5, + // Opens the file if it exists and seeks to the end. Otherwise, + // creates a new file. + Append = 6, + }; + + // Contains constants for specifying the access you want for a file. + // You can have Read, Write or ReadWrite access. + // + enum class FileAccess + { + // Specifies read access to the file. Data can be read from the file and + // the file pointer can be moved. Combine with WRITE for read-write access. + Read = 1, + + // Specifies write access to the file. Data can be written to the file and + // the file pointer can be moved. Combine with READ for read-write access. + Write = 2, + + // Specifies read and write access to the file. Data can be written to the + // file and the file pointer can be moved. Data can also be read from the + // file. + ReadWrite = 3, + }; + + // Contains constants for controlling file sharing options while + // opening files. You can specify what access other processes trying + // to open the same file concurrently can have. + // + // Note these values currently match the values for FILE_SHARE_READ, + // FILE_SHARE_WRITE, and FILE_SHARE_DELETE in winnt.h + // + enum class FileShare + { + // No sharing. Any request to open the file (by this process or another + // process) will fail until the file is closed. + None = 0, + + // Allows subsequent opening of the file for reading. If this flag is not + // specified, any request to open the file for reading (by this process or + // another process) will fail until the file is closed. + Read = 1, + + // Allows subsequent opening of the file for writing. If this flag is not + // specified, any request to open the file for writing (by this process or + // another process) will fail until the file is closed. + Write = 2, + + // Allows subsequent opening of the file for writing or reading. If this flag + // is not specified, any request to open the file for writing or reading (by + // this process or another process) will fail until the file is closed. + ReadWrite = 3, + + // Open the file, but allow someone else to delete the file. + Delete = 4, + + // Whether the file handle should be inheritable by child processes. + // Note this is not directly supported like this by Win32. + Inheritable = 0x10, + }; + + // Maps to FILE_FLAG_DELETE_ON_CLOSE and similar values from winbase.h. + // We didn't expose a number of these values because we didn't believe + // a number of them made sense in managed code, at least not yet. + enum class FileOptions + { + // NOTE: any change to FileOptions enum needs to be + // matched in the FileStream ctor for error validation + None = 0, + WriteThrough = static_cast(0x80000000), + Asynchronous = static_cast(0x40000000), // FILE_FLAG_OVERLAPPED + // NoBuffering = 0x20000000, // FILE_FLAG_NO_BUFFERING + RandomAccess = 0x10000000, + DeleteOnClose = 0x04000000, + SequentialScan = 0x08000000, + // AllowPosix = 0x01000000, // FILE_FLAG_POSIX_SEMANTICS + // BackupOrRestore = 0x02000000, // FILE_FLAG_BACKUP_SEMANTICS + // DisallowReparsePoint = 0x00200000, // FILE_FLAG_OPEN_REPARSE_POINT + // NoRemoteRecall = 0x00100000, // FILE_FLAG_OPEN_NO_RECALL + // FirstPipeInstance = 0x00080000, // FILE_FLAG_FIRST_PIPE_INSTANCE + Encrypted = 0x00004000, // FILE_ATTRIBUTE_ENCRYPTED + }; + + class FileStream : public Stream { + public: + FileStream(std::string const path, FileMode mode) : FileStream(path, mode, FileShare::None, 0) {} + FileStream(std::string const path, FileMode mode, FileAccess access) : FileStream(path, mode, FileShare::None, 0) {} + FileStream(std::string const path, FileMode mode, FileShare shared) : FileStream(path, mode, shared, 0) {} + FileStream(std::string const path, FileMode mode, FileShare shared, int32_t bufferLength); + + constexpr bool CanRead() const override { + return true; + } + + constexpr bool CanWrite() const override { + return true; + } + + constexpr bool CanSeek() const override { + return true; + } + + int64_t Length() const override; + int64_t Position() const override; + void Position(int64_t value) override; + void CopyTo(Stream& destination, int32_t bufferLength) override; + void Close() override; + void Flush() override { stream.flush(); } + int64_t Seek(int64_t offset, SeekOrigin origin) override; + void SetLength(int64_t value) override; + int32_t Read(uint8_t* buffer, int32_t bufferLength, int32_t offset, int32_t count) override; + int32_t Read(uint8_t* buffer, int32_t bufferLength) override; + int32_t ReadByte() override; + void Write(uint8_t const* buffer, int32_t bufferLength, int32_t offset, int32_t count) override; + void Write(uint8_t const* buffer, int32_t bufferLength) override; + void WriteByte(uint8_t value) override; + + virtual std::fstream& GetBuffer(); + + private: + void SetStreamLength(); + void EnsureNotClosed() const; + void EnsureWriteable() const; + + public: + std::fstream stream; + + private: + std::streampos _length{ 0 }; + std::streampos _position{ 0 }; + }; +} + +#endif \ No newline at end of file diff --git a/sources/csharp/CMakeLists.txt b/sources/csharp/CMakeLists.txt index 9c9a2a0..c725124 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") if (CMAKE_VERSION VERSION_GREATER 3.12) set_property(TARGET CSharp++ PROPERTY CXX_STANDARD 20) diff --git a/sources/csharp/io/stream.cpp b/sources/csharp/io/stream.cpp new file mode 100644 index 0000000..00ab1b8 --- /dev/null +++ b/sources/csharp/io/stream.cpp @@ -0,0 +1,729 @@ +#include "csharp/io/stream.hpp" +#include "csharp/io/exception.hpp" +#include +#include +#include +#include +#include + +using byte = uint8_t; + +namespace csharp { + void Stream::CopyTo(Stream& destination, int32_t bufferLength) + { + if (!CanRead()) + { + if (CanWrite()) + { + throw NotSupportedException(SR::NotSupported_UnreadableStream); + } + + throw InvalidOperationException(SR::ObjectDisposed_StreamClosed); + } + + auto buffer = std::vector(bufferLength); + + int32_t bytesRead = 0; + + while ((bytesRead = Read(buffer.data(), static_cast(buffer.size()), 0, static_cast(buffer.size()))) != 0) + { + destination.Write(buffer.data(), static_cast(buffer.size()), 0, bytesRead); + } + } + + int32_t Stream::GetCopybufferLength() const + { + constexpr auto DefaultCopybufferLength = 81920; + auto bufferLength = DefaultCopybufferLength; + + if (CanSeek()) + { + auto length = Length(); + auto position = Position(); + + if (length <= position) + { + bufferLength = 1; + } + else + { + auto remaining = length - position; + if (remaining > 0) + { + bufferLength = static_cast(std::min(static_cast(bufferLength), remaining)); + } + } + } + + return bufferLength; + } + + int32_t Stream::Read(uint8_t* buffer, int32_t bufferLength) { + ValidateBuffer(buffer, bufferLength); + + auto sharedBuffer = std::vector(bufferLength); + auto numRead = Read(sharedBuffer.data(), bufferLength, 0, bufferLength); + + if (numRead > bufferLength) + { + throw IOException(SR::IO_StreamTooLong); + } + + for (size_t i = 0; i < numRead; ++i) + buffer[i] = sharedBuffer[i]; + + return numRead; + } + + int32_t Stream::ReadByte() { + byte oneByteArray = 0; + auto r = Read(&oneByteArray, 1, 0, 1); + + return r == 0 ? -1 : oneByteArray; + } + + int32_t Stream::ReadAtLeastCore(uint8_t* buffer, int32_t bufferLength, int32_t minimumBytes, bool throwOnEndOfStream) { + ValidateBuffer(buffer, bufferLength); + + int32_t totalRead = 0; + while (totalRead < minimumBytes) + { + auto read = Read(buffer, bufferLength); + if (read == 0) + { + if (throwOnEndOfStream) + { + throw EndOfStreamException(SR::IO_EOF_ReadBeyondEOF); + } + + return totalRead; + } + + totalRead += read; + } + + return totalRead; + } + + void Stream::Write(uint8_t const* buffer, int32_t bufferLength) { + ValidateBuffer(buffer, bufferLength); + + auto sharedBuffer = std::vector(bufferLength); + + for (size_t i = 0; i < bufferLength; ++i) + sharedBuffer[i] = buffer[i]; + + Write(sharedBuffer.data(), bufferLength, 0, bufferLength); + } + + void Stream::ValidateBuffer(uint8_t const* buffer, int32_t bufferLength) { + if (!buffer) { + throw ArgumentNullException("buffer"); + } + + if (bufferLength < 0) { + throw ArgumentException("bufferLength"); + } + } + + // + //---------------------------------------------------------------- + // MemoryStream + //---------------------------------------------------------------- + // + + void MemoryStream::EnsureNotClosed() const { + if (!_isOpen) + throw InvalidOperationException(SR::ObjectDisposed_StreamClosed); + } + + void MemoryStream::EnsureWriteable() const { + if (!CanWrite()) + throw NotSupportedException(SR::NotSupported_UnwritableStream); + } + + bool MemoryStream::EnsureCapacity(int32_t value) { + if (value < 0) + throw IOException(SR::IO_StreamTooLong); + + if (value > _capacity) + { + auto newCapacity = std::max(value, 256); + + if (newCapacity < _capacity * 2) + { + newCapacity = _capacity * 2; + } + + + if ((_capacity * 2) > MemStreamMaxLength) + { + newCapacity = std::max(value, MemStreamMaxLength); + } + + Capacity(newCapacity); + + return true; + } + return false; + } + + std::vector& MemoryStream::GetBuffer() { + if (!_exposable) { + throw UnauthorizedAccessException(SR::UnauthorizedAccess_MemStreamBuffer); + } + + return _buffer; + } + + bool MemoryStream::TryGetBuffer(std::vector& buffer) { + if (!_exposable) + { + return false; + } + + buffer = _buffer; + return true; + } + + int32_t MemoryStream::Capacity() const { + EnsureNotClosed(); + return _capacity - _origin; + } + + void MemoryStream::Capacity(int32_t value) { + if (value < Length()) + throw ArgumentOutOfRangeException("value", SR::ArgumentOutOfRange_SmallCapacity); + + EnsureNotClosed(); + + if (!_expandable && (value != Capacity())) + throw NotSupportedException(SR::NotSupported_MemStreamNotExpandable); + + if (_expandable && value != _capacity) + { + if (value > 0) + { + auto newBuffer = std::vector(value); + + if (_length > 0) + { + //Buffer.BlockCopy(_buffer, 0, newBuffer, 0, _length); + std::memmove(newBuffer.data(), _buffer.data(), _length); + } + + _buffer = newBuffer; + } + else + { + _buffer = std::vector(); + } + _capacity = value; + } + } + + int64_t MemoryStream::Length() const { + EnsureNotClosed(); + return _length - _origin; + } + + int64_t MemoryStream::Position() const { + EnsureNotClosed(); + return _position - _origin; + } + + void MemoryStream::Position(int64_t value) { + EnsureNotClosed(); + + if (value > MemStreamMaxLength - _origin) + throw ArgumentOutOfRangeException("value", SR::ArgumentOutOfRange_StreamLength); + + _position = static_cast(_origin + value); + } + + int32_t MemoryStream::Read(uint8_t* buffer, int32_t bufferLength, int32_t offset, int32_t count) { + ValidateBuffer(buffer, bufferLength); + EnsureNotClosed(); + + auto n = _length - _position; + + if (n > count) + n = count; + if (n <= 0) + return 0; + + if (n <= 8) + { + auto byteCount = n; + while (--byteCount >= 0) + buffer[offset + byteCount] = _buffer[_position + byteCount]; + } + else { + //Buffer.BlockCopy(_buffer, _position, buffer, offset, n); + std::memmove(buffer + offset, _buffer.data() + _position, n); + } + + _position += n; + + return static_cast(n); + } + + int32_t MemoryStream::Read(uint8_t* buffer, int32_t bufferLength) { + ValidateBuffer(buffer, bufferLength); + EnsureNotClosed(); + + int n = std::min(_length - _position, bufferLength); + + if (n <= 0) + return 0; + + for (size_t i = 0; i < n; ++i) + buffer[i] = _buffer[i]; + + _position += n; + + return n; + } + + int32_t MemoryStream::ReadByte() { + EnsureNotClosed(); + + if (_position >= _length) + return -1; + + return _buffer[_position++]; + } + + void MemoryStream::CopyTo(Stream& destination, int32_t bufferLength) { + EnsureNotClosed(); + + auto originalPosition = _position; + + auto remaining = InternalEmulateRead(_length - originalPosition); + + if (remaining > 0) + { + destination.Write(_buffer.data(), static_cast(_buffer.size()), originalPosition, remaining); + } + } + + int32_t MemoryStream::InternalEmulateRead(int32_t count) { + EnsureNotClosed(); + + auto n = _length - _position; + + if (n > count) + n = count; + if (n < 0) + n = 0; + _position += n; + return n; + } + + int64_t MemoryStream::Seek(int64_t offset, SeekOrigin loc) { + EnsureNotClosed(); + + int32_t _loc = 0; + + switch (loc) + { + case csharp::SeekOrigin::Begin: + _loc = _origin; + break; + case csharp::SeekOrigin::Current: + _loc = _position; + break; + case csharp::SeekOrigin::End: + _loc = _length; + break; + default: + throw ArgumentException(SR::Argument_InvalidSeekOrigin); + break; + } + + return SeekCore(offset, _loc); + } + + int64_t MemoryStream::SeekCore(int64_t offset, int32_t loc) { + if (offset > MemStreamMaxLength - loc) + throw ArgumentOutOfRangeException("offset", SR::ArgumentOutOfRange_StreamLength); + + auto tempPosition = loc + static_cast(offset); + + if ((loc + offset) < _origin || tempPosition < _origin) + throw IOException(SR::IO_SeekBeforeBegin); + + _position = tempPosition; + + return _position - _origin; + } + + void MemoryStream::SetLength(int64_t value) { + if (value < 0 || value > MemStreamMaxLength) + throw ArgumentOutOfRangeException("value", SR::ArgumentOutOfRange_StreamLength); + + EnsureWriteable(); + + if (value > (MemStreamMaxLength - _origin)) + throw ArgumentOutOfRangeException("value", SR::ArgumentOutOfRange_StreamLength); + + int newLength = _origin + (int)value; + bool allocatedNewArray = EnsureCapacity(newLength); + + if (!allocatedNewArray && newLength > _length) + { + //Array.Clear(_buffer, _length, newLength - _length); + for (size_t i = _length; i < newLength - _length; ++i) { + _buffer[i] = 0; + } + } + + _length = newLength; + + if (_position > newLength) + _position = newLength; + } + + void MemoryStream::Write(uint8_t const* buffer, int32_t bufferLength, int32_t offset, int32_t count) { + ValidateBuffer(buffer, bufferLength); + EnsureNotClosed(); + EnsureWriteable(); + + auto i = _position + count; + + if (i < 0) + throw IOException(SR::IO_StreamTooLong); + + if (i > _length) + { + auto mustZero = _position > _length; + + if (i > _capacity) + { + bool allocatedNewArray = EnsureCapacity(i); + if (allocatedNewArray) + { + mustZero = false; + } + } + if (mustZero) + { + //Array.Clear(_buffer, _length, i - _length); + for (size_t j = _length; j < i - _length; ++j) { + _buffer[j] = 0; + } + } + + _length = i; + } + + if ((count <= 8) && (buffer != _buffer.data())) + { + auto byteCount = count; + while (--byteCount >= 0) + { + _buffer[_position + byteCount] = buffer[offset + byteCount]; + } + } + else + { + //Buffer.BlockCopy(buffer, offset, _buffer, _position, count); + std::memmove(_buffer.data() + _position, buffer + offset, count); + } + _position = i; + } + + void MemoryStream::Write(uint8_t const* buffer, int32_t bufferLength) { + EnsureNotClosed(); + EnsureWriteable(); + + // Check for overflow + auto i = _position + bufferLength; + + if (i < 0) + throw IOException(SR::IO_StreamTooLong); + + if (i > _length) + { + auto mustZero = _position > _length; + + if (i > _capacity) + { + auto allocatedNewArray = EnsureCapacity(i); + if (allocatedNewArray) + { + mustZero = false; + } + } + + if (mustZero) + { + //Array.Clear(_buffer, _length, i - _length); + for (size_t j = _length; j < i - _length; ++j) { + _buffer[j] = 0; + } + } + + _length = i; + } + + //TODO: Verificar se está correto e verificar os std::memmove + //buffer.CopyTo(new Span(_buffer, _position, buffer.Length)); + for (size_t x = _position; x < bufferLength; ++i) { + _buffer[x] = buffer[x]; + } + + _position = i; + } + + void MemoryStream::WriteByte(uint8_t value) { + EnsureNotClosed(); + EnsureWriteable(); + + if (_position >= _length) + { + auto newLength = _position + 1; + auto mustZero = _position > _length; + + if (newLength >= _capacity) + { + bool allocatedNewArray = EnsureCapacity(newLength); + if (allocatedNewArray) + { + mustZero = false; + } + } + if (mustZero) + { + //Array.Clear(_buffer, _length, _position - _length); + for (size_t j = _length; j < _position - _length; ++j) { + _buffer[j] = 0; + } + } + _length = newLength; + } + _buffer[_position++] = value; + } + + void MemoryStream::WriteTo(Stream& stream) { + EnsureNotClosed(); + + stream.Write(_buffer.data(), static_cast(_buffer.size()), _origin, _length - _origin); + } + + // + //---------------------------------------------------------------- + // FileStream + //---------------------------------------------------------------- + // + + FileStream::FileStream(std::string const path, FileMode mode, FileShare shared, int32_t bufferLength) { + auto flags = std::fstream::in + | std::fstream::out + | std::fstream::binary; + + const auto exists = std::filesystem::exists(path); + + switch (mode) + { + //Especifica se deve abrir um arquivo existente. + case FileMode::Open: + if (!exists) + throw InvalidOperationException("The specified file does not exist."); + break; + //Especifica que se deve abrir um arquivo, se existir; + // caso contrário, um novo arquivo deverá ser criado. + case FileMode::OpenOrCreate: + case FileMode::Create: + if (!exists) + flags |= std::fstream::trunc; + break; + //Especifica que o sistema operacional deve criar um novo arquivo. + //Se o arquivo já existir, não abre o arquivo. + case FileMode::CreateNew: + if (!exists) + flags |= std::fstream::trunc; + else + throw InvalidOperationException("The specified file already exists."); + break; + //Abre o arquivo, se existir, e busca o final do arquivo ou cria um novo arquivo. + case FileMode::Append: + if (!exists) + flags |= std::fstream::trunc; + else + flags |= std::fstream::app; + break; + //Especifica que se deve abrir um arquivo existente. + //Quando o arquivo for aberto, ele deverá ser truncado + //para que seu tamanho seja zero bytes. + case FileMode::Truncate: + if (!exists) + throw InvalidOperationException("The specified file does not exist."); + + flags |= std::fstream::trunc; + break; + default: + throw InvalidOperationException(); + break; + } + + stream.open(path.c_str(), flags); + + if (!stream.good()) + throw InvalidOperationException("Failed to open file: " + path); + + SetStreamLength(); + _position = stream.tellg(); + } + + void FileStream::SetStreamLength() { + const auto pos = stream.tellg(); + stream.seekg(0, std::ios_base::end); + + const auto end = stream.tellg(); + stream.seekg(pos); + + _length = end; + } + + int64_t FileStream::Length() const { + EnsureNotClosed(); + return static_cast(_length); + } + + void FileStream::EnsureNotClosed() const { + if (!stream.is_open()) + throw InvalidOperationException(SR::ObjectDisposed_StreamClosed); + } + + int64_t FileStream::Position() const { + EnsureNotClosed(); + return static_cast(_position); + } + + void FileStream::Position(int64_t value) { + EnsureNotClosed(); + const auto _position = static_cast(value); + stream.seekg(_position); + } + + void FileStream::CopyTo(Stream& destination, int32_t bufferLength) { + if (!CanRead()) + { + if (CanWrite()) + { + throw NotSupportedException(SR::NotSupported_UnreadableStream); + } + + throw InvalidOperationException(SR::ObjectDisposed_StreamClosed); + } + + auto buffer = std::vector(bufferLength); + int32_t bytesRead = 0; + + while ((bytesRead = Read(buffer.data(), bufferLength, 0, bufferLength)) != 0) + { + destination.Write(buffer.data(), bufferLength, 0, bytesRead); + } + } + + void FileStream::Close() { + if (!stream.is_open()) + return; + + stream.close(); + _position = 0; + _length = 0; + } + + int64_t FileStream::Seek(int64_t offset, SeekOrigin origin) { + EnsureNotClosed(); + stream.seekg(static_cast(offset), static_cast(origin)); + _position = stream.tellg(); + return static_cast(_position); + } + + void FileStream::SetLength(int64_t value) { + EnsureNotClosed(); + EnsureWriteable(); + + throw NotSupportedException(); + } + + void FileStream::EnsureWriteable() const { + if (!CanWrite()) + throw NotSupportedException(SR::NotSupported_UnwritableStream); + } + + int32_t FileStream::Read(uint8_t* buffer, int32_t bufferLength, int32_t offset, int32_t count) { + EnsureNotClosed(); + ValidateBuffer(buffer, bufferLength); + + auto buff = reinterpret_cast(buffer); + stream.read(buff + offset, count); + + if (stream.rdstate() != std::fstream::goodbit) { + return -1; + } + + return static_cast(stream.gcount()); + } + + int32_t FileStream::Read(uint8_t* buffer, int32_t bufferLength) { + return Read(buffer, bufferLength, 0, bufferLength); + } + + int32_t FileStream::ReadByte() { + EnsureNotClosed(); + + char c = 0; + + stream.read(&c, 1); + + if (stream.rdstate() != std::fstream::goodbit) { + return -1; + } + + const auto result = static_cast(c); + + return result; + } + + void FileStream::Write(uint8_t const* buffer, int32_t bufferLength, int32_t offset, int32_t count) { + EnsureNotClosed(); + EnsureWriteable(); + + auto buff = reinterpret_cast(buffer); + + stream.write(buff + offset, count); + + if (stream.rdstate() != std::fstream::goodbit) { + throw InvalidOperationException(); + } + + SetStreamLength(); + } + + void FileStream::Write(uint8_t const* buffer, int32_t bufferLength) { + Write(buffer, bufferLength, 0, bufferLength); + } + + void FileStream::WriteByte(uint8_t value) { + EnsureNotClosed(); + EnsureWriteable(); + + const char c = static_cast(value); + + stream.write(&c, 1); + + if (stream.rdstate() != std::fstream::goodbit) { + throw InvalidOperationException(); + } + + SetStreamLength(); + } + + std::fstream& FileStream::GetBuffer() { + return stream; + } +} \ No newline at end of file