Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 28 additions & 2 deletions src/Spectre.Console/Internal/Cell.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,26 @@ namespace Spectre.Console;

internal static class Cell
{
private static readonly int?[] _runeWidthCache = new int?[char.MaxValue];
private const sbyte Sentinel = -2;

/// <summary>
/// UnicodeCalculator.GetWidth documents the width as (-1, 0, 1, 2). We only need space for these values and a sentinel for uninitialized values.
/// This is only five values in total so we are storing one byte per value. We could store 2 per byte but that would add more logic to the retrieval.
/// We should add one to char.MaxValue because the total number of characters includes \0 too so there are 65536 valid chars.
/// </summary>
private static readonly sbyte[] _runeWidthCache = new sbyte[char.MaxValue + 1];

static Cell()
{
#if !NETSTANDARD2_0
Array.Fill(_runeWidthCache, Sentinel);
#else
for (var i = 0; i < _runeWidthCache.Length; i++)
{
_runeWidthCache[i] = Sentinel;
}
#endif
}

public static int GetCellLength(string text)
{
Expand All @@ -29,6 +48,13 @@ public static int GetCellLength(char rune)
return 1;
}

return _runeWidthCache[rune] ??= UnicodeCalculator.GetWidth(rune);
var width = _runeWidthCache[rune];
if (width == Sentinel)
{
_runeWidthCache[rune] = (sbyte)UnicodeCalculator.GetWidth(rune);
return _runeWidthCache[rune];
}

return _runeWidthCache[rune];
}
}