Skip to content
This repository was archived by the owner on Jun 28, 2026. It is now read-only.

Feat/container streaming#2

Open
u202F wants to merge 18 commits into
masterfrom
feat/container-streaming
Open

Feat/container streaming#2
u202F wants to merge 18 commits into
masterfrom
feat/container-streaming

Conversation

@u202F

@u202F u202F commented Jun 14, 2026

Copy link
Copy Markdown
Member

No description provided.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request migrates the AnimeStudio CLI to .NET 10, removes the desktop GUI and patcher, and introduces a low-memory streaming container storage mechanism to optimize resource usage. Feedback on the changes suggests guarding managed field cleanup in FbxExporterContext.Dispose to ensure finalizer safety, optimizing directory measurement in RunSummary by using DirectoryInfo.EnumerateFiles to avoid redundant metadata queries, and improving stream reading robustness in SevenZipHelper using Stream.ReadExactly and BinaryPrimitives.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +56 to +68
if (Interlocked.Exchange(ref _disposeState, 1) != 0)
{
return;
}

_frameToNode.Clear();
_createdMaterials.Clear();
_createdTextures.Clear();
_frameToNode?.Clear();
_createdMaterials?.Clear();
_createdTextures?.Clear();

AsFbxDisposeContext(ref _pContext);
if (_pContext != IntPtr.Zero)
{
AsFbxDisposeContext(ref _pContext);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

In the standard .NET Dispose pattern, accessing managed fields (such as _frameToNode, _createdMaterials, and _createdTextures) during finalization (when disposing is false) is unsafe because those managed objects may have already been finalized or are in an unstable state. These operations should be guarded with if (disposing).

            if (Interlocked.Exchange(ref _disposeState, 1) != 0)
            {
                return;
            }

            if (disposing)
            {
                _frameToNode?.Clear();
                _createdMaterials?.Clear();
                _createdTextures?.Clear();
            }

            if (_pContext != IntPtr.Zero)
            {
                AsFbxDisposeContext(ref _pContext);
            }

Comment on lines +88 to +105
internal static FileTreeStatistics MeasureDirectory(string directoryPath)
{
long fileCount = 0;
long totalBytes = 0;
foreach (var filePath in Directory.EnumerateFiles(
directoryPath,
"*",
OutputEnumerationOptions))
{
checked
{
fileCount++;
totalBytes += new FileInfo(filePath).Length;
}
}

return new FileTreeStatistics(fileCount, totalBytes);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Using Directory.EnumerateFiles followed by new FileInfo(filePath).Length for each file is inefficient because it performs an extra OS metadata query for every single file. In .NET, DirectoryInfo.EnumerateFiles returns FileInfo objects directly, which avoids manual allocations and redundant system calls.

        internal static FileTreeStatistics MeasureDirectory(string directoryPath)
        {
            long fileCount = 0;
            long totalBytes = 0;
            var directoryInfo = new DirectoryInfo(directoryPath);
            foreach (var fileInfo in directoryInfo.EnumerateFiles(
                "*",
                OutputEnumerationOptions))
            {
                checked
                {
                    fileCount++;
                    totalBytes += fileInfo.Length;
                }
            }

            return new FileTreeStatistics(fileCount, totalBytes);
        }

Comment on lines +39 to +61
public static void StreamDecompressWithHeader(Stream inStream, Stream outStream)
{
var decoder = new Decoder();
var properties = new byte[5];
if (inStream.Read(properties, 0, properties.Length) != properties.Length)
throw new InvalidDataException("LZMA input is too short.");

long outSize = 0;
for (var index = 0; index < sizeof(long); index++)
{
var value = inStream.ReadByte();
if (value < 0)
throw new InvalidDataException("LZMA input does not contain an output length.");
outSize |= (long)(byte)value << (8 * index);
}

if (outSize < 0)
throw new InvalidDataException($"LZMA output length cannot be negative: {outSize}.");

decoder.SetDecoderProperties(properties);
var compressedSize = inStream.Length - inStream.Position;
decoder.Code(inStream, outStream, compressedSize, outSize, null);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Using Stream.Read without guaranteeing that the full buffer is read can lead to partial reads and corrupt data. In modern .NET, Stream.ReadExactly is available and should be used to ensure the full buffer is read. Additionally, we can stack-allocate the 8-byte buffer for the size and use BinaryPrimitives.ReadInt64LittleEndian to read the size efficiently without 8 separate ReadByte calls.

        public static void StreamDecompressWithHeader(Stream inStream, Stream outStream)
        {
            var decoder = new Decoder();
            var properties = new byte[5];
            try
            {
                inStream.ReadExactly(properties);
            }
            catch (EndOfStreamException)
            {
                throw new InvalidDataException("LZMA input is too short.");
            }

            Span<byte> sizeBuffer = stackalloc byte[8];
            try
            {
                inStream.ReadExactly(sizeBuffer);
            }
            catch (EndOfStreamException)
            {
                throw new InvalidDataException("LZMA input does not contain an output length.");
            }

            long outSize = System.Buffers.Binary.BinaryPrimitives.ReadInt64LittleEndian(sizeBuffer);
            if (outSize < 0)
                throw new InvalidDataException($"LZMA output length cannot be negative: {outSize}.");

            decoder.SetDecoderProperties(properties);
            var compressedSize = inStream.Length - inStream.Position;
            decoder.Code(inStream, outStream, compressedSize, outSize, null);
        }

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants