Skip to content

Commit

Permalink
Use span-based dictionary lookup in System.Console (#103742)
Browse files Browse the repository at this point in the history
* Use span-based dictionary lookup in System.Console

* Fix ctor
  • Loading branch information
stephentoub authored Jun 21, 2024
1 parent b1bb871 commit 78c82de
Show file tree
Hide file tree
Showing 2 changed files with 11 additions and 20 deletions.
9 changes: 5 additions & 4 deletions src/libraries/System.Console/src/System/IO/KeyParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,15 @@ private static bool TryParseTerminalInputSequence(char[] buffer, TerminalFormatS
return false;
}

Dictionary<ReadOnlyMemory<char>, ConsoleKeyInfo> terminfoDb = terminalFormatStrings.KeyFormatToConsoleKey; // the most important source of truth
Dictionary<string, ConsoleKeyInfo>.AlternateLookup<ReadOnlySpan<char>> terminfoDb = // the most important source of truth
terminalFormatStrings.KeyFormatToConsoleKey.GetAlternateLookup<string, ConsoleKeyInfo, ReadOnlySpan<char>>();
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.
Expand Down Expand Up @@ -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);
}
Expand All @@ -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)
Expand Down
22 changes: 6 additions & 16 deletions src/libraries/System.Console/src/System/TerminalFormatStrings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
/// </summary>
public readonly Dictionary<ReadOnlyMemory<char>, ConsoleKeyInfo> KeyFormatToConsoleKey =
new Dictionary<ReadOnlyMemory<char>, ConsoleKeyInfo>(new ReadOnlyMemoryContentComparer());
public readonly Dictionary<string, ConsoleKeyInfo> KeyFormatToConsoleKey = new(StringComparer.Ordinal);

/// <summary> Max key length </summary>
public readonly int MaxKeyFormatLength;
Expand Down Expand Up @@ -164,7 +163,7 @@ public TerminalFormatStrings(TermInfo.Database? db)
MaxKeyFormatLength = int.MinValue;
MinKeyFormatLength = int.MaxValue;

foreach (KeyValuePair<ReadOnlyMemory<char>, ConsoleKeyInfo> entry in KeyFormatToConsoleKey)
foreach (KeyValuePair<string, ConsoleKeyInfo> entry in KeyFormatToConsoleKey)
{
if (entry.Key.Length > MaxKeyFormatLength)
{
Expand Down Expand Up @@ -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<char> 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);
}

Expand All @@ -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<char> 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<ReadOnlyMemory<char>>
{
public bool Equals(ReadOnlyMemory<char> x, ReadOnlyMemory<char> y) =>
x.Span.SequenceEqual(y.Span);

public int GetHashCode(ReadOnlyMemory<char> obj) =>
string.GetHashCode(obj.Span);
}
}

0 comments on commit 78c82de

Please sign in to comment.