diff --git a/src/Internal/LazySeekStream.cs b/src/Internal/LazySeekStream.cs deleted file mode 100644 index 24739b8..0000000 --- a/src/Internal/LazySeekStream.cs +++ /dev/null @@ -1,188 +0,0 @@ -using System; -using System.IO; - -namespace OwlCore.Storage; - -/// -/// Wraps around a non-seekable stream to enable seeking functionality with lazy loading of the source. -/// -internal class LazySeekStream : Stream -{ - private readonly Stream _originalStream; - private readonly MemoryStream _memoryStream; - - /// - /// Creates a new instance of . - /// - /// - public LazySeekStream(Stream stream) - { - _originalStream = stream; - - _memoryStream = new MemoryStream() - { - Capacity = (int)Length, - }; - } - - /// - public override bool CanRead => _memoryStream.CanRead; - - /// - public override bool CanSeek => _memoryStream.CanSeek; - - /// - public override bool CanWrite => false; - - /// - public override long Length => _originalStream.Length; - - /// - public override long Position - { - get => _memoryStream.Position; - set - { - if (value < 0) - throw new IOException("An attempt was made to move the position before the beginning of the stream."); - - // Check if the requested position is beyond the current length of the memory stream - if (value > _memoryStream.Length) - { - long additionalBytesNeeded = value - _memoryStream.Length; - var buffer = new byte[additionalBytesNeeded]; - long totalBytesRead = 0; - - while (totalBytesRead < additionalBytesNeeded) - { - int bytesRead = _originalStream.Read(buffer, (int)totalBytesRead, (int)(additionalBytesNeeded - totalBytesRead)); - if (bytesRead == 0) - break; // End of the original stream reached - - totalBytesRead += bytesRead; - } - - // Write the newly read bytes to the end of the memory stream - _memoryStream.Seek(0, SeekOrigin.End); - _memoryStream.Write(buffer, 0, (int)totalBytesRead); - } - - // Set the new position of the memory stream - _memoryStream.Position = value; - } - } - - /// - public override void Flush() => _memoryStream.Flush(); - - /// - public override int Read(byte[] buffer, int offset, int count) - { - int totalBytesRead = 0; - - // Read from memory stream first - if (_memoryStream.Position < _memoryStream.Length) - { - totalBytesRead = _memoryStream.Read(buffer, offset, count); - if (totalBytesRead == count) - { - return totalBytesRead; // Complete read from memory stream - } - - // Prepare to read the remaining data from the original stream - offset += totalBytesRead; - count -= totalBytesRead; - } - - // Read the remaining data directly into the provided buffer - while (count > 0) - { - int bytesReadFromOriginalStream = _originalStream.Read(buffer, offset, count); - if (bytesReadFromOriginalStream == 0) - { - break; // End of the original stream reached - } - - // Write the new data from the original stream into the memory stream - _memoryStream.Seek(0, SeekOrigin.End); - _memoryStream.Write(buffer, offset, bytesReadFromOriginalStream); - - totalBytesRead += bytesReadFromOriginalStream; - offset += bytesReadFromOriginalStream; - count -= bytesReadFromOriginalStream; - } - - return totalBytesRead; - } - - /// - public override long Seek(long offset, SeekOrigin origin) - { - switch (origin) - { - case SeekOrigin.Begin: - Position = offset; - break; - case SeekOrigin.Current: - Position = _memoryStream.Position + offset; - break; - case SeekOrigin.End: - Position = _originalStream.Length + offset; - break; - default: - throw new ArgumentOutOfRangeException(nameof(origin), "Invalid seek origin."); - } - - return Position; - } - - /// - public override void SetLength(long value) - { - if (value < 0) - throw new ArgumentOutOfRangeException(nameof(value), "Length must be non-negative."); - - if (value < _memoryStream.Length) - { - // Truncate the memory stream - _memoryStream.SetLength(value); - } - else if (value > _memoryStream.Length) - { - long additionalBytesNeeded = value - _memoryStream.Length; - - // Extend the memory stream with zeros or additional data from the original stream - if (_originalStream.CanRead && additionalBytesNeeded > 0) - { - var buffer = new byte[additionalBytesNeeded]; - int bytesRead = _originalStream.Read(buffer, 0, buffer.Length); - - _memoryStream.Seek(0, SeekOrigin.End); - _memoryStream.Write(buffer, 0, bytesRead); - - if (bytesRead < additionalBytesNeeded) - { - // Fill the rest with zeros if the original stream didn't have enough data - var zeroFill = new byte[additionalBytesNeeded - bytesRead]; - _memoryStream.Write(zeroFill, 0, zeroFill.Length); - } - } - else - { - // Fill with zeros if the original stream can't be read or no additional bytes are needed - _memoryStream.SetLength(value); - } - } - } - - /// - public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException($"Writing not supported by {nameof(LazySeekStream)}."); - - /// - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - _memoryStream.Dispose(); - _originalStream.Dispose(); - } -} diff --git a/src/OwlCore.Storage.csproj b/src/OwlCore.Storage.csproj index c928859..3b01941 100644 --- a/src/OwlCore.Storage.csproj +++ b/src/OwlCore.Storage.csproj @@ -23,6 +23,13 @@ LICENSE.txt logo.png +--- 0.12.0 --- +[Breaking] +The Stream opened from HttpFile is no longer seekable, as the internal LazySeekStream and all usages have been removed. + +[Fixes] +Fixes an issue where files larger than 2GB couldn't be fully read via HttpFile. + --- 0.11.3 --- [Improvement] Removed redundant validation when enumerating files and folders to improve performance in SystemFile and SystemFolder. diff --git a/src/System/Net/Http/HttpFile.cs b/src/System/Net/Http/HttpFile.cs index 7068bec..e89dccc 100644 --- a/src/System/Net/Http/HttpFile.cs +++ b/src/System/Net/Http/HttpFile.cs @@ -89,10 +89,9 @@ public async Task OpenStreamAsync(FileAccess accessMode = FileAccess.Rea // Extract the content length if available long? length = response.Content.Headers.ContentLength; - if (length is long notNullLength) + if (length is { } notNullLength) contentStream = new LengthOverrideStream(contentStream, notNullLength); - // Return in a lazy seek-able wrapper. - return new LazySeekStream(contentStream); + return contentStream; } } \ No newline at end of file