| File: System\Formats\Asn1\AsnCharacterStringEncodings.net.cs | Web Access |
| Project: src\runtime\src\libraries\System.Formats.Asn1\src\System.Formats.Asn1.csproj (System.Formats.Asn1) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; using System.Numerics; using System.Runtime.InteropServices; namespace System.Formats.Asn1 { internal abstract class RestrictedAsciiRangeEncoding : SpanBasedEncoding { private readonly byte _minCharAllowed; private readonly byte _range; protected RestrictedAsciiRangeEncoding(byte minCharAllowed, byte maxCharAllowed) { Debug.Assert(minCharAllowed <= maxCharAllowed); Debug.Assert(maxCharAllowed <= 0x7F); _minCharAllowed = minCharAllowed; _range = (byte)(maxCharAllowed - minCharAllowed); } public override int GetMaxByteCount(int charCount) { return charCount; } public override int GetMaxCharCount(int byteCount) { return byteCount; } protected override int GetBytes(ReadOnlySpan<char> chars, Span<byte> bytes, bool write) { int position = 0; if (chars.Length >= Vector<byte>.Count && Vector.IsHardwareAccelerated) { position = GetBytesVectorized(chars, bytes, write); } for (; position < chars.Length; position++) { char c = chars[position]; if (!IsAllowed(c)) { EncoderFallback.CreateFallbackBuffer().Fallback(c, position); Debug.Fail("Fallback should have thrown"); throw new InvalidOperationException(); } if (write) { bytes[position] = (byte)c; } } return chars.Length; } protected override int GetChars(ReadOnlySpan<byte> bytes, Span<char> chars, bool write) { int position = 0; if (bytes.Length >= Vector<byte>.Count && Vector.IsHardwareAccelerated) { position = GetCharsVectorized(bytes, chars, write); } for (; position < bytes.Length; position++) { byte b = bytes[position]; if (!IsAllowed(b)) { DecoderFallback.CreateFallbackBuffer().Fallback( new[] { b }, position); Debug.Fail("Fallback should have thrown"); throw new InvalidOperationException(); } if (write) { chars[position] = (char)b; } } return bytes.Length; } // The vectorization is left out of the GetChars and GetBytes directly to not regress the code size // and register allocation for small inputs. Instead they are extracted methods. private int GetBytesVectorized(ReadOnlySpan<char> chars, Span<byte> bytes, bool write) { int available = write ? Math.Min(chars.Length, bytes.Length) : chars.Length; int vectorizedLength = available - (available % Vector<byte>.Count); int position = 0; Debug.Assert(Vector<byte>.Count == 2 * Vector<ushort>.Count); // Revisit this cast when Vector<char> is supported: https://github.com/dotnet/runtime/issues/127611 ReadOnlySpan<ushort> source = MemoryMarshal.Cast<char, ushort>(chars); Vector<ushort> minCharAllowed = new Vector<ushort>(_minCharAllowed); Vector<ushort> range = new Vector<ushort>(_range); for (; position < vectorizedLength; position += Vector<byte>.Count) { Vector<ushort> lower = new Vector<ushort>(source.Slice(position)); Vector<ushort> upper = new Vector<ushort>(source.Slice(position + Vector<ushort>.Count)); if (!IsAllowed(lower, minCharAllowed, range) || !IsAllowed(upper, minCharAllowed, range)) { // If any element in the vector is not allowed, we break out and return the position before the // current vector's width so that it goes down the scalar path. The scalar path will determine the // precise location of the invalid element. break; } if (write) { Vector.Narrow(lower, upper).CopyTo(bytes.Slice(position)); } } return position; } private int GetCharsVectorized(ReadOnlySpan<byte> bytes, Span<char> chars, bool write) { int available = write ? Math.Min(bytes.Length, chars.Length) : bytes.Length; int vectorizedLength = available - (available % Vector<byte>.Count); int position = 0; Debug.Assert(Vector<byte>.Count == 2 * Vector<ushort>.Count); // Revisit this cast when Vector<char> is supported: https://github.com/dotnet/runtime/issues/127611 Span<ushort> destination = write ? MemoryMarshal.Cast<char, ushort>(chars) : Span<ushort>.Empty; Vector<byte> minCharAllowed = new Vector<byte>(_minCharAllowed); Vector<byte> range = new Vector<byte>(_range); for (; position < vectorizedLength; position += Vector<byte>.Count) { Vector<byte> source = new Vector<byte>(bytes.Slice(position)); if (!IsAllowed(source, minCharAllowed, range)) { // If any element in the vector is not allowed, we break out and return the position before the // current vector's width so that it goes down the scalar path. The scalar path will determine the // precise location of the invalid element. break; } if (write) { Vector.Widen(source, out Vector<ushort> lower, out Vector<ushort> upper); lower.CopyTo(destination.Slice(position)); upper.CopyTo(destination.Slice(position + Vector<ushort>.Count)); } } return position; } private bool IsAllowed(byte value) { return (byte)(value - _minCharAllowed) <= _range; } private bool IsAllowed(char value) { return (uint)(value - _minCharAllowed) <= _range; } private static bool IsAllowed(Vector<byte> value, Vector<byte> minCharAllowed, Vector<byte> range) { Vector<byte> offset = value - minCharAllowed; return Vector.LessThanOrEqualAll(offset, range); } private static bool IsAllowed(Vector<ushort> value, Vector<ushort> minCharAllowed, Vector<ushort> range) { Vector<ushort> offset = value - minCharAllowed; return Vector.LessThanOrEqualAll(offset, range); } } }