From 78c82de986583d5455d58f1b0a669888b7262392 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Fri, 21 Jun 2024 06:57:45 -0700 Subject: [PATCH] Use span-based dictionary lookup in System.Console (#103742) * Use span-based dictionary lookup in System.Console * Fix ctor --- .../System.Console/src/System/IO/KeyParser.cs | 9 ++++---- .../src/System/TerminalFormatStrings.cs | 22 +++++-------------- 2 files changed, 11 insertions(+), 20 deletions(-) diff --git a/src/libraries/System.Console/src/System/IO/KeyParser.cs b/src/libraries/System.Console/src/System/IO/KeyParser.cs index 23326d485d497..2b4cff2502c2c 100644 --- a/src/libraries/System.Console/src/System/IO/KeyParser.cs +++ b/src/libraries/System.Console/src/System/IO/KeyParser.cs @@ -62,14 +62,15 @@ private static bool TryParseTerminalInputSequence(char[] buffer, TerminalFormatS return false; } - Dictionary, ConsoleKeyInfo> terminfoDb = terminalFormatStrings.KeyFormatToConsoleKey; // the most important source of truth + Dictionary.AlternateLookup> terminfoDb = // the most important source of truth + terminalFormatStrings.KeyFormatToConsoleKey.GetAlternateLookup>(); ConsoleModifiers modifiers = ConsoleModifiers.None; ConsoleKey key; // Is it a three character sequence? (examples: '^[[H' (Home), '^[OP' (F1)) if (input[1] == 'O' || char.IsAsciiLetter(input[2]) || input.Length == MinimalSequenceLength) { - if (!terminfoDb.TryGetValue(buffer.AsMemory(startIndex, MinimalSequenceLength), out parsed)) + if (!terminfoDb.TryGetValue(buffer.AsSpan(startIndex, MinimalSequenceLength), out parsed)) { // All terminals which use "^[O{letter}" escape sequences don't define conflicting mappings. // Example: ^[OH either means Home or simply is not used by given terminal. @@ -103,7 +104,7 @@ private static bool TryParseTerminalInputSequence(char[] buffer, TerminalFormatS // Is it a four character sequence used by Linux Console or PuTTy configured to emulate it? (examples: '^[[[A' (F1), '^[[[B' (F2)) if (input[1] == '[' && input[2] == '[' && char.IsBetween(input[3], 'A', 'E')) { - if (!terminfoDb.TryGetValue(buffer.AsMemory(startIndex, 4), out parsed)) + if (!terminfoDb.TryGetValue(buffer.AsSpan(startIndex, 4), out parsed)) { parsed = new ConsoleKeyInfo(default, ConsoleKey.F1 + input[3] - 'A', false, false, false); } @@ -128,7 +129,7 @@ private static bool TryParseTerminalInputSequence(char[] buffer, TerminalFormatS { // it's a VT Sequence like ^[[11~ or rxvt like ^[[11^ int sequenceLength = SequencePrefixLength + digitCount + 1; - if (!terminfoDb.TryGetValue(buffer.AsMemory(startIndex, sequenceLength), out parsed)) + if (!terminfoDb.TryGetValue(buffer.AsSpan(startIndex, sequenceLength), out parsed)) { key = MapEscapeSequenceNumber(byte.Parse(input.Slice(SequencePrefixLength, digitCount))); if (key == default) diff --git a/src/libraries/System.Console/src/System/TerminalFormatStrings.cs b/src/libraries/System.Console/src/System/TerminalFormatStrings.cs index a1f194184a09e..7e0000dae2a31 100644 --- a/src/libraries/System.Console/src/System/TerminalFormatStrings.cs +++ b/src/libraries/System.Console/src/System/TerminalFormatStrings.cs @@ -51,8 +51,7 @@ internal sealed class TerminalFormatStrings /// The dictionary of keystring to ConsoleKeyInfo. /// Only some members of the ConsoleKeyInfo are used; in particular, the actual char is ignored. /// - public readonly Dictionary, ConsoleKeyInfo> KeyFormatToConsoleKey = - new Dictionary, ConsoleKeyInfo>(new ReadOnlyMemoryContentComparer()); + public readonly Dictionary KeyFormatToConsoleKey = new(StringComparer.Ordinal); /// Max key length public readonly int MaxKeyFormatLength; @@ -164,7 +163,7 @@ public TerminalFormatStrings(TermInfo.Database? db) MaxKeyFormatLength = int.MinValue; MinKeyFormatLength = int.MaxValue; - foreach (KeyValuePair, ConsoleKeyInfo> entry in KeyFormatToConsoleKey) + foreach (KeyValuePair entry in KeyFormatToConsoleKey) { if (entry.Key.Length > MaxKeyFormatLength) { @@ -229,8 +228,8 @@ private void AddKey(TermInfo.Database db, TermInfo.WellKnownStrings keyId, Conso private void AddKey(TermInfo.Database db, TermInfo.WellKnownStrings keyId, ConsoleKey key, bool shift, bool alt, bool control) { - ReadOnlyMemory keyFormat = db.GetString(keyId).AsMemory(); - if (!keyFormat.IsEmpty) + string? keyFormat = db.GetString(keyId); + if (!string.IsNullOrEmpty(keyFormat)) KeyFormatToConsoleKey[keyFormat] = new ConsoleKeyInfo(key == ConsoleKey.Enter ? '\r' : '\0', key, shift, alt, control); } @@ -248,17 +247,8 @@ private void AddPrefixKey(TermInfo.Database db, string extendedNamePrefix, Conso private void AddKey(TermInfo.Database db, string extendedName, ConsoleKey key, bool shift, bool alt, bool control) { - ReadOnlyMemory keyFormat = db.GetExtendedString(extendedName).AsMemory(); - if (!keyFormat.IsEmpty) + string? keyFormat = db.GetExtendedString(extendedName); + if (!string.IsNullOrEmpty(keyFormat)) KeyFormatToConsoleKey[keyFormat] = new ConsoleKeyInfo('\0', key, shift, alt, control); } - - private sealed class ReadOnlyMemoryContentComparer : IEqualityComparer> - { - public bool Equals(ReadOnlyMemory x, ReadOnlyMemory y) => - x.Span.SequenceEqual(y.Span); - - public int GetHashCode(ReadOnlyMemory obj) => - string.GetHashCode(obj.Span); - } }