Skip to content

Commit

Permalink
Polyfill non-allocating encoding span->string without byte[]
Browse files Browse the repository at this point in the history
Originally proposed by @iamcarbon in #380 (comment)

This requires allowing unsafe blocks for `net462` and `netstandard1.3`.

Co-authored-by: Jason Nelson <[email protected]>
  • Loading branch information
drewnoakes and iamcarbon committed Jan 29, 2024
1 parent 4a9cf7d commit ae0bcdb
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 11 deletions.
8 changes: 3 additions & 5 deletions MetadataExtractor/IO/IndexedReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -319,17 +319,15 @@ public double GetDouble64(int index)
/// <exception cref="IOException"/>
public string GetString(int index, int bytesRequested, Encoding encoding)
{
#if NET462 || NETSTANDARD1_3
var bytes = GetBytes(index, bytesRequested);
// This check is important on .NET Framework
if (bytesRequested is 0)
return "";

return encoding.GetString(bytes, 0, bytes.Length);
#else
Span<byte> bytes = bytesRequested < 256 ? stackalloc byte[bytesRequested] : new byte[bytesRequested];

GetBytes(index, bytes);

return encoding.GetString(bytes);
#endif
}

/// <summary>
Expand Down
13 changes: 7 additions & 6 deletions MetadataExtractor/IO/SequentialReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -220,14 +220,15 @@ public float GetS15Fixed16()
/// <exception cref="IOException"/>
public string GetString(int bytesRequested, Encoding encoding)
{
#if NETSTANDARD2_1
Span<byte> bytes = bytesRequested > 2048 ? new byte[bytesRequested] : stackalloc byte[bytesRequested];
// This check is important on .NET Framework
if (bytesRequested is 0)
return "";

Span<byte> bytes = bytesRequested < 256 ? stackalloc byte[bytesRequested] : new byte[bytesRequested];

GetBytes(bytes);

return encoding.GetString(bytes);
#else
var bytes = GetBytes(bytesRequested);
return encoding.GetString(bytes, 0, bytes.Length);
#endif
}

public StringValue GetStringValue(int bytesRequested, Encoding? encoding = null)
Expand Down
2 changes: 2 additions & 0 deletions MetadataExtractor/MetadataExtractor.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ Camera manufacturer specific support exists for Agfa, Canon, Casio, DJI, Epson,
<SignAssembly>true</SignAssembly>
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
<PackageReadmeFile>README.md</PackageReadmeFile>

<AllowUnsafeBlocks Condition=" '$(TargetFramework)' == 'net462' OR '$(TargetFramework)' == 'netstandard1.3' ">true</AllowUnsafeBlocks>
</PropertyGroup>

<ItemGroup>
Expand Down
24 changes: 24 additions & 0 deletions MetadataExtractor/Util/EncodingExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// 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.

#if NETFRAMEWORK || NETSTANDARD1_3

internal static class EncodingExtensions
{
/// <summary>
/// Converts a span of bytes into a string, following the specified encoding's rules.
/// </summary>
/// <param name="encoding">The encoding to follow.</param>
/// <param name="bytes">The bytes to convert.</param>
/// <returns>The decoded string.</returns>
public unsafe static string GetString(this Encoding encoding, ReadOnlySpan<byte> bytes)
{
// Poly-fill for method available in newer versions of .NET

fixed (byte* pBytes = bytes)
{
return encoding.GetString(pBytes, bytes.Length);
}
}
}

#endif

0 comments on commit ae0bcdb

Please sign in to comment.