Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rewrite ChunkedMemoryStream #6

Open
wants to merge 8 commits into
base: release/3.1.x
Choose a base branch
from
Open

Conversation

lizard-boy
Copy link

@lizard-boy lizard-boy commented Nov 1, 2024

Prerequisites

  • I have written a descriptive pull-request title
  • I have verified that there are no overlapping pull-requests open
  • I have verified that I am following the existing coding patterns and practice as demonstrated in the repository. These follow strict Stylecop rules 👮.
  • I have provided test coverage for my change (where applicable)

Description

Fixes SixLabors#2806

ChunkedMemoryStream contained multiple writing bugs and was too costly to fix/maintain relative to performance benefits so I'm just ditching it.

  • Complete rewrite of ChunkedMemoryStream to simplify it and fix numerous bugs.
  • Ensure ImageEncoder uses the chunked stream when encoding non-seekable streams.

Greptile Summary

Complete rewrite of ChunkedMemoryStream to fix critical bugs and improve non-seekable stream handling in ImageSharp's encoding pipeline.

  • Rewrote ChunkedMemoryStream to use a simpler List-based chunk management system instead of linked lists
  • Fixed ImageEncoder to properly use chunked memory stream for non-seekable stream encoding
  • Added proper async disposal with await using in EncodeWithSeekableStreamAsync
  • Updated NonSeekableStream to correctly delegate write operations to underlying stream
  • Added comprehensive tests verifying WebP encoding works with non-seekable streams

@lizard-boy lizard-boy added the bug Something isn't working label Nov 1, 2024
@lizard-boy lizard-boy changed the title Js/issue 2806 Rewrite ChunkedMemoryStream Nov 1, 2024
Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5 file(s) reviewed, 9 comment(s)
Edit PR Review Bot Settings | Greptile

int offset = 0;
int count = buffer.Length;
while (count > 0)
while (bytesToRead != 0 && this.currentChunk != this.memoryChunkBuffer.Length)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: potential infinite loop if currentChunk reaches memoryChunkBuffer.Length but bytesToRead is still non-zero

int copyCount = Math.Min(count, chunkSize - this.writeOffset);
buffer.Slice(offset, copyCount).CopyTo(chunkBuffer[this.writeOffset..]);
// Write n bytes to the current chunk
buffer.Slice(offset, n).CopyTo(chunk.Buffer.Slice(this.currentChunkIndex, n));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Buffer.Slice() may throw if currentChunkIndex + n exceeds the buffer length

Comment on lines +366 to +371
// If the new position is greater than the length of the stream, set the position to the end of the stream
if (offset > 0 && offset >= this.memoryChunkBuffer.Length)
{
if (this.readOffset == chunkSize)
{
// Exit if no more chunks are currently available
if (this.readChunk.Next is null)
{
break;
}
this.currentChunk = this.memoryChunkBuffer.ChunkCount - 1;
this.currentChunkIndex = this.memoryChunkBuffer[this.currentChunk].Length - 1;
return;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: setting position to Length-1 when seeking beyond stream end differs from standard Stream behavior which allows seeking beyond end

Comment on lines +376 to 387
while (offset != 0)
{
int chunkLength = this.memoryChunkBuffer[currentChunkIndex].Length;
if (offset < chunkLength)
{
// Found the correct chunk and the corresponding index
break;
}

int writeCount = chunkSize - this.readOffset;
stream.Write(chunkBuffer.GetSpan(), this.readOffset, writeCount);
this.readOffset = chunkSize;
offset -= chunkLength;
currentChunkIndex++;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: will throw IndexOutOfRangeException if offset is larger than total chunk lengths

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants