Skip to content

Commit 7f3ebe0

Browse files
authored
Reduce memory usage for rune width cache. (#1756)
1 parent d77bfb6 commit 7f3ebe0

File tree

1 file changed

+28
-2
lines changed
  • src/Spectre.Console/Internal

1 file changed

+28
-2
lines changed

src/Spectre.Console/Internal/Cell.cs

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,26 @@ namespace Spectre.Console;
22

33
internal static class Cell
44
{
5-
private static readonly int?[] _runeWidthCache = new int?[char.MaxValue];
5+
private const sbyte Sentinel = -2;
6+
7+
/// <summary>
8+
/// UnicodeCalculator.GetWidth documents the width as (-1, 0, 1, 2). We only need space for these values and a sentinel for uninitialized values.
9+
/// 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.
10+
/// We should add one to char.MaxValue because the total number of characters includes \0 too so there are 65536 valid chars.
11+
/// </summary>
12+
private static readonly sbyte[] _runeWidthCache = new sbyte[char.MaxValue + 1];
13+
14+
static Cell()
15+
{
16+
#if !NETSTANDARD2_0
17+
Array.Fill(_runeWidthCache, Sentinel);
18+
#else
19+
for (var i = 0; i < _runeWidthCache.Length; i++)
20+
{
21+
_runeWidthCache[i] = Sentinel;
22+
}
23+
#endif
24+
}
625

726
public static int GetCellLength(string text)
827
{
@@ -29,6 +48,13 @@ public static int GetCellLength(char rune)
2948
return 1;
3049
}
3150

32-
return _runeWidthCache[rune] ??= UnicodeCalculator.GetWidth(rune);
51+
var width = _runeWidthCache[rune];
52+
if (width == Sentinel)
53+
{
54+
_runeWidthCache[rune] = (sbyte)UnicodeCalculator.GetWidth(rune);
55+
return _runeWidthCache[rune];
56+
}
57+
58+
return _runeWidthCache[rune];
3359
}
3460
}

0 commit comments

Comments
 (0)