Как использовать Span в Convert.TryFromBase64String()?
Я пытался написать try catch
за Convert.FromBase64String()
и я узнал, что у него уже есть TryFromBase64String()
метод. Но для этого нужно 3 аргумента:
public static bool TryFromBase64String(string s, Span<byte> bytes, out int bytesWritten);
Так как я могу использовать Span<byte> bytes
там?
Я нашел это только в документах, но без надлежащего описания. Может быть, это слишком очевидно.
https://docs.microsoft.com/en-us/dotnet/api/system.convert.tryfrombase64string?view=netcore-2.1
Благодаря @Damien_The_Unbeliever и ЭТОЙ статье я узнал больше о Span
, Так...
Span используется для экономии памяти и не вызывает GC так часто. Он может хранить массивы или часть массива, но я все еще не могу понять, как использовать его в этом методе.
4 ответа
Как написано в связанных вопросах, System.Span<T>
это новая функция C# 7.2 (и Convert.TryFromBase64String
более новая функция.NET Core)
Использовать System.Span<>
Вы должны установить пакет nuget:
Install-Package System.Memory
Тогда использовать это:
byte[] buffer = new byte[((b64string.Length * 3) + 3) / 4 -
(b64string.Length > 0 && b64string[b64string.Length - 1] == '=' ?
b64string.Length > 1 && b64string[b64string.Length - 2] == '=' ?
2 : 1 : 0)];
int written;
bool success = Convert.TryFromBase64String(b64string, buffer, out written);
куда b64string
ваша строка base-64. Слишком сложный размер для buffer
должна быть точная длина буфера на основе длины b64string
,
Вот еще один подход с использованием ArrayPool, если вам нужен буфер только временно:
// Minimum length that is sure to fit all the data.
// We don't need to be 100% accurate here,
// because ArrayPool might return a larger buffer anyway.
var length = ((value.Length * 3) + 3) / 4;
var buffer = ArrayPool<byte>.Shared.Rent(length);
try
{
// (buffer is implicitly cast to Span<byte>)
if (Convert.TryFromBase64String(value, buffer, out var bytesWritten))
{
// do something with it...
return Encoding.UTF8.GetString(buffer, 0, bytesWritten);
}
throw new FormatException("Invalid base-64 sequence.");
}
finally
{
ArrayPool<byte>.Shared.Return(buffer);
}
Вы могли бы использовать это так, используя все TryFromBase64String
аргументы:
public string DecodeUtf8Base64(string input)
{
var bytes = new Span<byte>(new byte[256]); // 256 is arbitrary
if (!Convert.TryFromBase64String(input, bytes, out var bytesWritten))
{
throw new InvalidOperationException("The input is not a valid base64 string");
}
return Encoding.UTF8.GetString(bytes.Slice(0, bytesWritten));
}
Я использовал это так:
string base64String = 'somebase64';
Span<byte> bytesBuffer = stackalloc byte[base64String.Length];
if (!Convert.TryFromBase64String(base64String, bytesBuffer, out int bytesWritten))
{
return false;
}
ReadOnlySpan<byte> actualBytes = bytesBuffer[..bytesWritten];
ОБНОВЛЯТЬ:
более точный способ подсчета байтов
const int bitsEncodedPerChar = 6;
int bytesExpected = (base64String.Length * bitsEncodedPerChar) >> 3; // divide by 8 bits in a byte
см. https://en.wikipedia.org/wiki/Base64#Base64_table_from_RFC_4648