Skip to content

Commit

Permalink
Merge pull request #415 from iamcarbon/scoped-buffer
Browse files Browse the repository at this point in the history
Introduce ScopedBuffer
  • Loading branch information
iamcarbon authored Feb 23, 2024
2 parents 3ee6d56 + 50d44e8 commit ec5fe3c
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 76 deletions.
11 changes: 2 additions & 9 deletions MetadataExtractor/Formats/Apple/BplistReader.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
// Copyright (c) Drew Noakes and contributors. All Rights Reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.

using System.Buffers;

namespace MetadataExtractor.Formats.Apple;

/// <summary>
Expand Down Expand Up @@ -123,12 +121,9 @@ static object HandleInt(ref BufferReader reader, byte marker)

static Dictionary<byte, byte> HandleDict(ref BufferReader reader, byte count)
{
var keyRefs = ArrayPool<byte>.Shared.Rent(count);
Span<byte> keyRefs = stackalloc byte[count]; // count is a byte (0-255)

for (int j = 0; j < count; j++)
{
keyRefs[j] = reader.GetByte();
}
reader.GetBytes(keyRefs);

Dictionary<byte, byte> map = [];

Expand All @@ -137,8 +132,6 @@ static Dictionary<byte, byte> HandleDict(ref BufferReader reader, byte count)
map.Add(keyRefs[j], reader.GetByte());
}

ArrayPool<byte>.Shared.Return(keyRefs);

return map;
}

Expand Down
28 changes: 8 additions & 20 deletions MetadataExtractor/IO/BufferReader.Indexed.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// Copyright (c) Drew Noakes and contributors. All Rights Reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.

using System.Buffers;
using System.Buffers.Binary;

namespace MetadataExtractor.IO;
Expand Down Expand Up @@ -178,33 +177,22 @@ public readonly double GetDouble64(int index)

public readonly string GetString(int index, int bytesRequested, Encoding encoding)
{
if (bytesRequested < 0)
throw new ArgumentOutOfRangeException(nameof(bytesRequested), "Must be zero or greater.");

// This check is important on .NET Framework
if (bytesRequested is 0)
{
return "";
}
else if (bytesRequested < 256)
{
Span<byte> bytes = stackalloc byte[bytesRequested];

GetBytes(index, bytes);

return encoding.GetString(bytes);
}
else
{
byte[] bytes = ArrayPool<byte>.Shared.Rent(bytesRequested);

Span<byte> span = bytes.AsSpan(0, bytesRequested);

GetBytes(index, span);
using var buffer = bytesRequested <= ScopedBuffer.MaxStackBufferSize
? new ScopedBuffer(stackalloc byte[bytesRequested])
: new ScopedBuffer(bytesRequested);

var s = encoding.GetString(span);
GetBytes(index, buffer);

ArrayPool<byte>.Shared.Return(bytes);

return s;
}
return encoding.GetString(buffer);
}

private readonly void ValidateIndex(int index, int bytesRequested)
Expand Down
13 changes: 8 additions & 5 deletions MetadataExtractor/IO/BufferReader.Sequential.cs
Original file line number Diff line number Diff line change
Expand Up @@ -114,17 +114,20 @@ public ulong GetUInt64()

public string GetString(int bytesRequested, Encoding encoding)
{
if (bytesRequested < 0)
throw new ArgumentOutOfRangeException(nameof(bytesRequested), "Must be zero or greater.");

// This check is important on .NET Framework
if (bytesRequested is 0)
return "";

Span<byte> bytes = bytesRequested <= 256
? stackalloc byte[bytesRequested]
: new byte[bytesRequested];
using var buffer = bytesRequested <= ScopedBuffer.MaxStackBufferSize
? new ScopedBuffer(stackalloc byte[bytesRequested])
: new ScopedBuffer(bytesRequested);

GetBytes(bytes);
GetBytes(buffer);

return encoding.GetString(bytes);
return encoding.GetString(buffer);
}

public StringValue GetNullTerminatedStringValue(int maxLengthBytes, Encoding? encoding = null, bool moveToMaxLength = false)
Expand Down
29 changes: 8 additions & 21 deletions MetadataExtractor/IO/IndexedReader.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
// Copyright (c) Drew Noakes and contributors. All Rights Reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.


using System.Buffers;
using System.Buffers.Binary;

namespace MetadataExtractor.IO
Expand Down Expand Up @@ -320,33 +318,22 @@ public double GetDouble64(int index)
/// <exception cref="IOException"/>
public string GetString(int index, int bytesRequested, Encoding encoding)
{
if (bytesRequested < 0)
throw new ArgumentOutOfRangeException(nameof(bytesRequested), "Must be zero or greater.");

// This check is important on .NET Framework
if (bytesRequested is 0)
{
return "";
}
else if (bytesRequested < 256)
{
Span<byte> bytes = stackalloc byte[bytesRequested];

GetBytes(index, bytes);
using var buffer = bytesRequested <= ScopedBuffer.MaxStackBufferSize
? new ScopedBuffer(stackalloc byte[bytesRequested])
: new ScopedBuffer(bytesRequested);

return encoding.GetString(bytes);
}
else
{
byte[] bytes = ArrayPool<byte>.Shared.Rent(bytesRequested);
GetBytes(index, buffer);

Span<byte> span = bytes.AsSpan(0, bytesRequested);

GetBytes(index, span);

var s = encoding.GetString(span);

ArrayPool<byte>.Shared.Return(bytes);

return s;
}
return encoding.GetString(buffer);
}

/// <summary>
Expand Down
43 changes: 43 additions & 0 deletions MetadataExtractor/IO/ScopedBuffer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright (c) Drew Noakes and contributors. All Rights Reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.

using System.Buffers;

namespace MetadataExtractor.IO;

internal ref struct ScopedBuffer
{
public const int MaxStackBufferSize = 256;

private byte[]? _rentedBuffer;
private readonly Span<byte> _span;

public ScopedBuffer(int size)
{
_rentedBuffer = ArrayPool<byte>.Shared.Rent(size);
_span = _rentedBuffer.AsSpan(0, size);
}

public ScopedBuffer(Span<byte> span)
{
_span = span;
}

public readonly Span<byte> Span => _span;

public static implicit operator Span<byte>(ScopedBuffer bufferScope)
{
return bufferScope._span;
}

public static implicit operator ReadOnlySpan<byte>(ScopedBuffer bufferScope)
{
return bufferScope._span;
}

public void Dispose()
{
if (_rentedBuffer is null) return;

ArrayPool<byte>.Shared.Return(_rentedBuffer);
}
}
28 changes: 8 additions & 20 deletions MetadataExtractor/IO/SequentialReader.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// Copyright (c) Drew Noakes and contributors. All Rights Reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.

using System.Buffers;
using System.Buffers.Binary;

namespace MetadataExtractor.IO
Expand Down Expand Up @@ -221,33 +220,22 @@ public float GetS15Fixed16()
/// <exception cref="IOException"/>
public string GetString(int bytesRequested, Encoding encoding)
{
if (bytesRequested < 0)
throw new ArgumentOutOfRangeException(nameof(bytesRequested), "Must be zero or greater.");

// This check is important on .NET Framework
if (bytesRequested is 0)
{
return "";
}
else if (bytesRequested < 256)
{
Span<byte> bytes = stackalloc byte[bytesRequested];

GetBytes(bytes);

return encoding.GetString(bytes);
}
else
{
byte[] bytes = ArrayPool<byte>.Shared.Rent(bytesRequested);

Span<byte> span = bytes.AsSpan(0, bytesRequested);

GetBytes(span);
using var buffer = bytesRequested <= ScopedBuffer.MaxStackBufferSize
? new ScopedBuffer(stackalloc byte[bytesRequested])
: new ScopedBuffer(bytesRequested);

var s = encoding.GetString(span);
GetBytes(buffer);

ArrayPool<byte>.Shared.Return(bytes);

return s;
}
return encoding.GetString(buffer);
}

public StringValue GetStringValue(int bytesRequested, Encoding? encoding = null)
Expand Down
2 changes: 1 addition & 1 deletion MetadataExtractor/Util/DateUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public static bool IsValidTime(int hours, int minutes, int seconds)
minutes is >= 0 and < 60
&& seconds is >= 0 and < 60;

#if NET8_0 || NETSTANDARD2_1
#if NET8_0_OR_GREATER || NETSTANDARD2_1
private static readonly DateTime _unixEpoch = DateTime.UnixEpoch;
#else
private static readonly DateTime _unixEpoch = new(1970, 1, 1, 0, 0, 0);
Expand Down

0 comments on commit ec5fe3c

Please sign in to comment.