Skip to content
Closed
Changes from 1 commit
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
8c07524
Avoid unnecessary byte[] allocations
stephentoub Aug 19, 2022
53d24d0
Remove unnecessary use of FileStreamOptions
stephentoub Aug 19, 2022
c9dc0be
Clean up Dispose{Async} implementations
stephentoub Aug 19, 2022
4e1af20
Clean up unnecessary consts
stephentoub Aug 19, 2022
13f011b
Remove MemoryStream/Encoding.UTF8.GetBytes allocations, unnecessary a…
stephentoub Aug 19, 2022
8735a67
Avoid string allocations in ReadMagicAttribute
stephentoub Aug 19, 2022
f50edcf
Avoid allocation in WriteAsOctal
stephentoub Aug 19, 2022
29a90d3
Improve handling of octal
stephentoub Aug 19, 2022
e211457
Avoid allocation for version string
stephentoub Aug 19, 2022
868a20c
Removing boxing and char string allocation in GenerateExtendedAttribu…
stephentoub Aug 19, 2022
c83e97c
Fix a couple unnecessary dictionary lookups
stephentoub Aug 19, 2022
82b8909
Replace Enum.HasFlag usage
stephentoub Aug 19, 2022
60145e9
Remove allocations from Write{Posix}Name
stephentoub Aug 19, 2022
e9a32ec
Replace ArrayPool use with string.Create
stephentoub Aug 19, 2022
d632a95
Replace more superfluous ArrayPool usage
stephentoub Aug 19, 2022
fbf22b3
Remove ArrayPool use from System.IO.Compression.ZipFile
stephentoub Aug 20, 2022
60d9352
Fix inverted condition
stephentoub Aug 20, 2022
24aa3a2
Use generic math to parse octal
stephentoub Aug 20, 2022
25b32ff
Remove allocations from StringReader and string.Split
stephentoub Aug 20, 2022
9649015
Remove magic string allocation for Ustar when not V7
stephentoub Aug 20, 2022
c5a8bfe
Remove file name and directory name allocation in GenerateExtendedAtt…
stephentoub Aug 20, 2022
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
Prev Previous commit
Next Next commit
Remove allocations from Write{Posix}Name
  • Loading branch information
stephentoub authored and github-actions committed Aug 21, 2022
commit 60145e92eeb1d2e806cac3123590c35068467d6f
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ private long WriteV7FieldsToBuffer(Span<byte> buffer)
long actualLength = GetTotalDataBytesToWrite();
TarEntryType actualEntryType = TarHelpers.GetCorrectTypeFlagForFormat(TarEntryFormat.V7, _typeFlag);

int tmpChecksum = WriteName(buffer, out _);
int tmpChecksum = WriteName(buffer);
tmpChecksum += WriteCommonFields(buffer, actualLength, actualEntryType);
_checksum = WriteChecksum(tmpChecksum, buffer);

Expand Down Expand Up @@ -275,7 +275,7 @@ private void WriteAsGnuSharedInternal(Span<byte> buffer, out long actualLength)
{
actualLength = GetTotalDataBytesToWrite();

int tmpChecksum = WriteName(buffer, out _);
int tmpChecksum = WriteName(buffer);
tmpChecksum += WriteCommonFields(buffer, actualLength, TarHelpers.GetCorrectTypeFlagForFormat(TarEntryFormat.Gnu, _typeFlag));
tmpChecksum += WriteGnuMagicAndVersion(buffer);
tmpChecksum += WritePosixAndGnuSharedFields(buffer);
Expand Down Expand Up @@ -358,24 +358,33 @@ private void WriteAsPaxSharedInternal(Span<byte> buffer, out long actualLength)
_checksum = WriteChecksum(tmpChecksum, buffer);
}

// All formats save in the name byte array only the ASCII bytes that fit. The full string is returned in the out byte array.
private int WriteName(Span<byte> buffer, out byte[] fullNameBytes)
// All formats save in the name byte array only the ASCII bytes that fit.
private int WriteName(Span<byte> buffer)
{
fullNameBytes = Encoding.ASCII.GetBytes(_name);
int nameBytesLength = Math.Min(fullNameBytes.Length, FieldLengths.Name);
int checksum = WriteLeftAlignedBytesAndGetChecksum(fullNameBytes.AsSpan(0, nameBytesLength), buffer.Slice(FieldLocations.Name, FieldLengths.Name));
return checksum;
ReadOnlySpan<char> src = _name.AsSpan(0, Math.Min(_name.Length, FieldLengths.Name));
Span<byte> dest = buffer.Slice(FieldLocations.Name, FieldLengths.Name);
int encoded = Encoding.ASCII.GetBytes(src, dest);
return Checksum(dest.Slice(0, encoded));
}

// Ustar and PAX save in the name byte array only the ASCII bytes that fit, and the rest of that string is saved in the prefix field.
private int WritePosixName(Span<byte> buffer)
{
int checksum = WriteName(buffer, out byte[] fullNameBytes);
if (fullNameBytes.Length > FieldLengths.Name)
int checksum = WriteName(buffer);

if (_name.Length > FieldLengths.Name)
{
int prefixBytesLength = Math.Min(fullNameBytes.Length - FieldLengths.Name, FieldLengths.Name);
checksum += WriteLeftAlignedBytesAndGetChecksum(fullNameBytes.AsSpan(FieldLengths.Name, prefixBytesLength), buffer.Slice(FieldLocations.Prefix, FieldLengths.Prefix));
int prefixBytesLength = Math.Min(_name.Length - FieldLengths.Name, FieldLengths.Name);
Span<byte> remaining = prefixBytesLength <= 256 ?
stackalloc byte[prefixBytesLength] :
new byte[prefixBytesLength];

int encoded = Encoding.ASCII.GetBytes(_name.AsSpan(FieldLengths.Name), remaining);
Debug.Assert(encoded == remaining.Length);

checksum += WriteLeftAlignedBytesAndGetChecksum(remaining, buffer.Slice(FieldLocations.Prefix, FieldLengths.Prefix));
}

return checksum;
}

Expand Down Expand Up @@ -642,7 +651,7 @@ static void TryAddStringField(Dictionary<string, string> extendedAttributes, str
// The checksum accumulator first adds up the byte values of eight space chars, then the final number
// is written on top of those spaces on the specified span as ascii.
// At the end, it's saved in the header field and the final value returned.
internal int WriteChecksum(int checksum, Span<byte> buffer)
internal static int WriteChecksum(int checksum, Span<byte> buffer)
{
// The checksum field is also counted towards the total sum
// but as an array filled with spaces
Expand Down Expand Up @@ -683,44 +692,42 @@ private static int WriteLeftAlignedBytesAndGetChecksum(ReadOnlySpan<byte> bytesT
{
Debug.Assert(destination.Length > 1);

int checksum = 0;

for (int i = 0, j = 0; i < destination.Length && j < bytesToWrite.Length; i++, j++)
{
destination[i] = bytesToWrite[j];
checksum += destination[i];
}
// Copy as many bytes as will fit
int numToCopy = Math.Min(bytesToWrite.Length, destination.Length);
bytesToWrite = bytesToWrite.Slice(0, numToCopy);
bytesToWrite.CopyTo(destination);

return checksum;
return Checksum(bytesToWrite);
}

// Writes the specified bytes aligned to the right, filling all the leading bytes with the zero char 0x30,
// ensuring a null terminator is included at the end of the specified span.
private static int WriteRightAlignedBytesAndGetChecksum(ReadOnlySpan<byte> bytesToWrite, Span<byte> destination)
{
int checksum = 0;
int i = destination.Length - 1;
int j = bytesToWrite.Length - 1;
Debug.Assert(destination.Length > 1);

while (i >= 0)
// Null terminated
destination[^1] = (byte)'\0';

// Copy as many input bytes as will fit
int numToCopy = Math.Min(bytesToWrite.Length, destination.Length - 1);
bytesToWrite = bytesToWrite.Slice(0, numToCopy);
int copyPos = destination.Length - 1 - bytesToWrite.Length;
bytesToWrite.CopyTo(destination.Slice(copyPos));

// Fill all leading bytes with zeros
destination.Slice(0, copyPos).Fill((byte)'0');

return Checksum(destination);
}

private static int Checksum(ReadOnlySpan<byte> bytes)
{
int checksum = 0;
foreach (byte b in bytes)
{
if (i == destination.Length - 1)
{
destination[i] = 0; // null terminated
}
else if (j >= 0)
{
destination[i] = bytesToWrite[j];
j--;
}
else
{
destination[i] = (byte)'0'; // leading zeros
}
checksum += destination[i];
i--;
checksum += b;
}

return checksum;
}

Expand Down