Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -127,23 +127,30 @@ public static ZstandardDictionary Train(ReadOnlySpan<byte> samples, ReadOnlySpan

ArgumentOutOfRangeException.ThrowIfLessThan(maxDictionarySize, 256, nameof(maxDictionarySize));

byte[] dictionaryBuffer = new byte[maxDictionarySize];

nuint dictSize;

unsafe
byte[] dictionaryBuffer = ArrayPool<byte>.Shared.Rent(maxDictionarySize);
try
{
fixed (byte* samplesPtr = &MemoryMarshal.GetReference(samples))
fixed (byte* dictPtr = dictionaryBuffer)
fixed (nuint* lengthsAsNuintPtr = &MemoryMarshal.GetReference(lengthsAsNuint))
nuint dictSize;

unsafe
{
dictSize = Interop.Zstd.ZDICT_trainFromBuffer(
dictPtr, (nuint)maxDictionarySize,
samplesPtr, lengthsAsNuintPtr, (uint)sampleLengths.Length);
fixed (byte* samplesPtr = &MemoryMarshal.GetReference(samples))
fixed (byte* dictPtr = dictionaryBuffer)
fixed (nuint* lengthsAsNuintPtr = &MemoryMarshal.GetReference(lengthsAsNuint))
{
dictSize = Interop.Zstd.ZDICT_trainFromBuffer(
dictPtr, (nuint)maxDictionarySize,
samplesPtr, lengthsAsNuintPtr, (uint)sampleLengths.Length);
}

ZstandardUtils.ThrowIfError(dictSize);
return Create(dictionaryBuffer.AsSpan(0, (int)dictSize));
}

ZstandardUtils.ThrowIfError(dictSize);
return Create(dictionaryBuffer.AsSpan(0, (int)dictSize));
}
finally
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We already have a try/finally for the lengthsArray buffer, can we avoid even more nesting by reusing the existing blocks?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Another option is to not return the array to the pool on the exceptional path. I can't find a reference for this in learn.microsoft.com or this repo's docs, but I've seen Stephen Toub and others mentioned in a PRs that generally returning arrays to the pool in exceptions paths is more trouble than its worth. For example:

#71249 (comment)

{
// Clear before returning: the trained dictionary is derived from caller-supplied samples.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This clearing is expensive and unnecessary. We are not doing it anywhere else in similar situations

ArrayPool<byte>.Shared.Return(dictionaryBuffer, clearArray: true);
Comment on lines +152 to +153
Comment on lines +152 to +153
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
// Clear before returning: the trained dictionary is derived from caller-supplied samples.
ArrayPool<byte>.Shared.Return(dictionaryBuffer, clearArray: true);
ArrayPool<byte>.Shared.Return(dictionaryBuffer);

We practically never clear buffers outside of crypto, I don't see a reason to do it here

}
}
finally
Expand Down
Loading