|
|
@ -1032,7 +1032,7 @@ namespace SixLabors.ImageSharp.Formats.Png |
|
|
tempExifBuf = new byte[exifHeader.Length]; |
|
|
tempExifBuf = new byte[exifHeader.Length]; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
HexStringToBytes(dataSpan.Slice(0, exifHeader.Length * 2), tempExifBuf.AsSpan()); |
|
|
HexStringToBytes(dataSpan.Slice(0, exifHeader.Length * 2), tempExifBuf); |
|
|
if (!tempExifBuf.AsSpan().Slice(0, exifHeader.Length).SequenceEqual(exifHeader)) |
|
|
if (!tempExifBuf.AsSpan().Slice(0, exifHeader.Length).SequenceEqual(exifHeader)) |
|
|
{ |
|
|
{ |
|
|
// Exif header in the hex data is not valid
|
|
|
// Exif header in the hex data is not valid
|
|
|
@ -1103,51 +1103,90 @@ namespace SixLabors.ImageSharp.Formats.Png |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// <summary>
|
|
|
/// Parses a hexadecimal string into a byte array without allocations.
|
|
|
/// Parses a hexadecimal string into a byte array without allocations. Throws on non-hexadecimal character.
|
|
|
/// Adapted from https://stackoverflow.com/a/9995303/871842
|
|
|
/// Adapted from https://source.dot.net/#System.Private.CoreLib/Convert.cs,c9e4fbeaca708991.
|
|
|
/// </summary>
|
|
|
/// </summary>
|
|
|
/// <param name="hexString">The hexadecimal string to parse.</param>
|
|
|
/// <param name="chars">The hexadecimal string to parse.</param>
|
|
|
/// <param name="outputBytes">The destination for the parsed bytes. Must be at least <paramref name="hexString"/>.Length / 2 bytes long.</param>
|
|
|
/// <param name="bytes">The destination for the parsed bytes. Must be at least <paramref name="chars"/>.Length / 2 bytes long.</param>
|
|
|
/// <returns>The number of bytes written to <paramref name="outputBytes"/>.</returns>
|
|
|
/// <returns>The number of bytes written to <paramref name="bytes"/>.</returns>
|
|
|
private static int HexStringToBytes(ReadOnlySpan<char> hexString, Span<byte> outputBytes) |
|
|
private static int HexStringToBytes(ReadOnlySpan<char> chars, Span<byte> bytes) |
|
|
{ |
|
|
{ |
|
|
if ((hexString.Length % 2) != 0) |
|
|
if ((chars.Length % 2) != 0) |
|
|
{ |
|
|
{ |
|
|
throw new ArgumentException("Input string length must be a multiple of 2", nameof(hexString)); |
|
|
throw new ArgumentException("Input string length must be a multiple of 2", nameof(chars)); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if ((outputBytes.Length * 2) < hexString.Length) |
|
|
if ((bytes.Length * 2) < chars.Length) |
|
|
{ |
|
|
{ |
|
|
throw new ArgumentException("Output span must be at least half the length of the input string"); |
|
|
throw new ArgumentException("Output span must be at least half the length of the input string"); |
|
|
} |
|
|
} |
|
|
|
|
|
else |
|
|
|
|
|
{ |
|
|
|
|
|
// Slightly better performance in the loop below, allows us to skip a bounds check
|
|
|
|
|
|
// while still supporting output buffers that are larger than necessary
|
|
|
|
|
|
bytes = bytes.Slice(0, chars.Length / 2); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
static int GetHexVal(char hexChar) |
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
|
|
|
|
static int FromChar(int c) |
|
|
{ |
|
|
{ |
|
|
if (hexChar >= '0' && hexChar <= '9') |
|
|
// Map from an ASCII char to its hex value, e.g. arr['b'] == 11. 0xFF means it's not a hex digit.
|
|
|
{ |
|
|
// This doesn't actually allocate.
|
|
|
return hexChar - '0'; |
|
|
ReadOnlySpan<byte> charToHexLookup = new byte[] |
|
|
} |
|
|
|
|
|
else if (hexChar >= 'A' && hexChar <= 'F') |
|
|
|
|
|
{ |
|
|
{ |
|
|
return 10 + (hexChar - 'A'); |
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 15
|
|
|
} |
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 31
|
|
|
else if (hexChar >= 'a' && hexChar <= 'f') |
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 47
|
|
|
{ |
|
|
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 63
|
|
|
return 10 + (hexChar - 'a'); |
|
|
0xFF, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 79
|
|
|
} |
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 95
|
|
|
else |
|
|
0xFF, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 111
|
|
|
|
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 127
|
|
|
|
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 143
|
|
|
|
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 159
|
|
|
|
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 175
|
|
|
|
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 191
|
|
|
|
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 207
|
|
|
|
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 223
|
|
|
|
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 239
|
|
|
|
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 255
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
return c >= charToHexLookup.Length ? 0xFF : charToHexLookup[c]; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// See https://source.dot.net/#System.Private.CoreLib/HexConverter.cs,4681d45a0aa0b361
|
|
|
|
|
|
int i = 0; |
|
|
|
|
|
int j = 0; |
|
|
|
|
|
int byteLo = 0; |
|
|
|
|
|
int byteHi = 0; |
|
|
|
|
|
while (j < bytes.Length) |
|
|
|
|
|
{ |
|
|
|
|
|
byteLo = FromChar(chars[i + 1]); |
|
|
|
|
|
byteHi = FromChar(chars[i]); |
|
|
|
|
|
|
|
|
|
|
|
// byteHi hasn't been shifted to the high half yet, so the only way the bitwise or produces this pattern
|
|
|
|
|
|
// is if either byteHi or byteLo was not a hex character.
|
|
|
|
|
|
if ((byteLo | byteHi) == 0xFF) |
|
|
{ |
|
|
{ |
|
|
throw new ArgumentException($"Invalid hexadecimal value {hexChar}"); |
|
|
break; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
bytes[j++] = (byte)((byteHi << 4) | byteLo); |
|
|
|
|
|
i += 2; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (byteLo == 0xFF) |
|
|
|
|
|
{ |
|
|
|
|
|
i++; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
int inputByteCount = hexString.Length / 2; |
|
|
if ((byteLo | byteHi) == 0xFF) |
|
|
for (int i = 0; i < inputByteCount; i++) |
|
|
|
|
|
{ |
|
|
{ |
|
|
outputBytes[i] = (byte)((GetHexVal(hexString[i * 2]) << 4) + GetHexVal(hexString[(i * 2) + 1])); |
|
|
throw new ArgumentException("Input string contained non-hexadecimal characters", nameof(chars)); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
return inputByteCount; |
|
|
return j; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// <summary>
|
|
|
|