Freeze non-mutable readonly static dictionaries.

Since these will live for the lifetime of the process, it is worth freezing them on creation to improve lookup performance. If the dictionaries were iterated, change to an immutable array instead to preserve the order.
This commit is contained in:
RoosterDragon
2025-12-04 20:45:21 +00:00
committed by Gustas Kažukauskas
parent f4eb5739d6
commit 9e548fd0f0
13 changed files with 92 additions and 82 deletions

View File

@@ -61,8 +61,8 @@ namespace OpenRA
static readonly ConcurrentCache<string, IntegerExpression> IntegerExpressionCache =
new(expression => new IntegerExpression(expression));
static readonly Dictionary<Type, Func<string, Type, string, object>> TypeParsers =
new()
static readonly FrozenDictionary<Type, Func<string, Type, string, object>> TypeParsers =
new Dictionary<Type, Func<string, Type, string, object>>
{
{ typeof(int), ParseInt },
{ typeof(ushort), ParseUShort },
@@ -93,10 +93,10 @@ namespace OpenRA
{ typeof(float3), ParseFloat3 },
{ typeof(Rectangle), ParseRectangle },
{ typeof(DateTime), ParseDateTime }
};
}.ToFrozenDictionary();
static readonly Dictionary<Type, Func<string, Type, string, MiniYaml, object>> GenericTypeParsers =
new()
static readonly FrozenDictionary<Type, Func<string, Type, string, MiniYaml, object>> GenericTypeParsers =
new Dictionary<Type, Func<string, Type, string, MiniYaml, object>>
{
{ typeof(HashSet<>), ParseHashSetOrList },
{ typeof(List<>), ParseHashSetOrList },
@@ -106,7 +106,7 @@ namespace OpenRA
{ typeof(FrozenDictionary<,>), ParseFrozenDictionary },
{ typeof(BitSet<>), ParseBitSet },
{ typeof(Nullable<>), ParseNullable },
};
}.ToFrozenDictionary();
static readonly object BoxedTrue = true;
static readonly object BoxedFalse = false;

View File

@@ -10,6 +10,7 @@
#endregion
using System;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
@@ -18,13 +19,13 @@ namespace OpenRA.Scripting
{
public static class ScriptMemberExts
{
static readonly Dictionary<string, string> LuaTypeNameReplacements = new()
static readonly FrozenDictionary<string, string> LuaTypeNameReplacements = new Dictionary<string, string>
{
{ "Void", "void" },
{ "Int32", "int" },
{ "String", "string" },
{ "Boolean", "bool" }
};
}.ToFrozenDictionary();
public static string LuaDocString(this Type t)
{

View File

@@ -10,6 +10,7 @@
#endregion
using System;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
@@ -40,7 +41,7 @@ namespace OpenRA
return GetHashFunction(sync)(sync);
}
static readonly Dictionary<Type, MethodInfo> CustomHashFunctions = new()
static readonly FrozenDictionary<Type, MethodInfo> CustomHashFunctions = new Dictionary<Type, MethodInfo>
{
{ typeof(int2), ((Func<int2, int>)HashInt2).Method },
{ typeof(CPos), ((Func<CPos, int>)HashCPos).Method },
@@ -53,7 +54,7 @@ namespace OpenRA
{ typeof(Actor), ((Func<Actor, int>)HashActor).Method },
{ typeof(Player), ((Func<Player, int>)HashPlayer).Method },
{ typeof(Target), ((Func<Target, int>)HashTarget).Method },
};
}.ToFrozenDictionary();
static void EmitSyncOpcodes(Type type, ILGenerator il)
{

View File

@@ -122,7 +122,7 @@ namespace OpenRA.Mods.Cnc.Traits
PlusY = 0x8,
}
static readonly Dictionary<Adjacency, int[]> BorderIndices = new()
static readonly FrozenDictionary<Adjacency, int[]> BorderIndices = new Dictionary<Adjacency, int[]>
{
{ Adjacency.MinusY, new[] { 3, 4, 5 } },
{ Adjacency.PlusX, new[] { 6, 7, 8 } },
@@ -139,7 +139,7 @@ namespace OpenRA.Mods.Cnc.Traits
{ Adjacency.MinusX | Adjacency.MinusY | Adjacency.PlusY, new[] { 39, 40, 41 } },
{ Adjacency.MinusX | Adjacency.PlusX | Adjacency.PlusY, new[] { 42, 43, 44 } },
{ Adjacency.MinusX | Adjacency.PlusX | Adjacency.MinusY | Adjacency.PlusY, new[] { 45, 46, 47 } },
};
}.ToFrozenDictionary();
static readonly int[] HeavyIndices = [48, 49, 50, 51];
static readonly int[] LightIndices = [52];

View File

@@ -10,6 +10,7 @@
#endregion
using System;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
@@ -364,7 +365,7 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
}
// TODO: fix this -- will have bitrotted pretty badly.
static readonly Dictionary<string, Color> NamedColorMapping = new()
static readonly FrozenDictionary<string, Color> NamedColorMapping = new Dictionary<string, Color>
{
{ "gold", Color.FromArgb(246, 214, 121) },
{ "blue", Color.FromArgb(226, 230, 246) },
@@ -376,7 +377,7 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
{ "green", Color.FromArgb(160, 240, 140) },
{ "white", Color.FromArgb(255, 255, 255) },
{ "black", Color.FromArgb(80, 80, 80) },
};
}.ToFrozenDictionary();
public static void SetMapPlayers(string section, string faction, string color, IniFile file, List<string> players, MapPlayers mapPlayers)
{

View File

@@ -10,6 +10,7 @@
#endregion
using System;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -47,7 +48,7 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
"fpls", "wcrate", "scrate", "fenc", "sbag",
];
static readonly Dictionary<string, (byte Type, byte Index)> OverlayResourceMapping = new()
static readonly FrozenDictionary<string, (byte Type, byte Index)> OverlayResourceMapping = new Dictionary<string, (byte Type, byte Index)>
{
// RA ore & crystals
{ "gold01", (1, 0) },
@@ -58,7 +59,7 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
{ "gem02", (2, 1) },
{ "gem03", (2, 2) },
{ "gem04", (2, 3) },
};
}.ToFrozenDictionary();
void UnpackTileData(MemoryStream ms)
{

View File

@@ -10,6 +10,7 @@
#endregion
using System;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -35,7 +36,7 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
Console.WriteLine($"ERROR: Detected NewINIFormat {format}. Are you trying to import a Red Alert map?");
}
static readonly Dictionary<string, (byte Type, byte Index)> OverlayResourceMapping = new()
static readonly FrozenDictionary<string, (byte Type, byte Index)> OverlayResourceMapping = new Dictionary<string, (byte Type, byte Index)>
{
// Tiberium
{ "ti1", (1, 0) },
@@ -50,7 +51,7 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
{ "ti10", (1, 9) },
{ "ti11", (1, 10) },
{ "ti12", (1, 11) },
};
}.ToFrozenDictionary();
void UnpackTileData(Stream ms)
{

View File

@@ -9,7 +9,7 @@
*/
#endregion
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Primitives;
@@ -22,13 +22,13 @@ namespace OpenRA.Mods.Common.Graphics
// Maps a cell offset to the index of the corner (in the 'Corner' arrays in the MapGrid.CellRamp structs)
// from which a border should be drawn. The index of the end corner will be (cornerIndex + 1) % 4.
static readonly Dictionary<CVec, int> Offset2CornerIndex = new()
{
{ new CVec(0, -1), (int)Corner.TopLeft },
{ new CVec(1, 0), (int)Corner.TopRight },
{ new CVec(0, 1), (int)Corner.BottomRight },
{ new CVec(-1, 0), (int)Corner.BottomLeft },
};
static readonly ImmutableArray<(CVec Offset, int CornerIndex)> Offset2CornerIndex =
[
(new CVec(0, -1), (int)Corner.TopLeft),
(new CVec(1, 0), (int)Corner.TopRight),
(new CVec(0, 1), (int)Corner.BottomRight),
(new CVec(-1, 0), (int)Corner.BottomLeft),
];
readonly CPos[] region;
readonly Color color, contrastColor;
@@ -77,14 +77,14 @@ namespace OpenRA.Mods.Common.Graphics
var corners = map.Grid.Ramps[ramp].Corners;
var pos = map.CenterOfCell(c) - new WVec(0, 0, map.Grid.Ramps[ramp].CenterHeightOffset);
foreach (var o in Offset2CornerIndex)
foreach (var (offset, cornerIndex) in Offset2CornerIndex)
{
// If the neighboring cell is part of the region, don't draw a border between the cells.
if (region.Contains(c + o.Key))
if (region.Contains(c + offset))
continue;
var start = wr.Viewport.WorldToViewPx(wr.Screen3DPosition(pos + corners[o.Value]));
var end = wr.Viewport.WorldToViewPx(wr.Screen3DPosition(pos + corners[(o.Value + 1) % 4]));
var start = wr.Viewport.WorldToViewPx(wr.Screen3DPosition(pos + corners[cornerIndex]));
var end = wr.Viewport.WorldToViewPx(wr.Screen3DPosition(pos + corners[(cornerIndex + 1) % 4]));
if (constrastWidth > 0)
cr.DrawLine(start, end, constrastWidth, constrastColor);

View File

@@ -10,6 +10,7 @@
#endregion
using System;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Net.Http;
using System.Text.RegularExpressions;
@@ -49,11 +50,11 @@ namespace OpenRA.Mods.Common.Server
const string GameOffline = "notification-game-offline";
static readonly ushort LanAdvertisePort = (ushort)new Random(DateTime.Now.Millisecond).Next(2048, 60000);
static readonly Dictionary<int, string> MasterServerErrors = new()
static readonly FrozenDictionary<int, string> MasterServerErrors = new Dictionary<int, string>
{
{ 1, NoPortForward },
{ 2, BlacklistedTitle }
};
}.ToFrozenDictionary();
Beacon lanGameBeacon;
long lastPing = 0;

View File

@@ -10,6 +10,7 @@
#endregion
using System;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
@@ -386,7 +387,7 @@ namespace OpenRA.Mods.Common.UtilityCommands.Documentation
public static class EmmyLuaExts
{
static readonly Dictionary<string, string> LuaTypeNameReplacements = new()
static readonly FrozenDictionary<string, string> LuaTypeNameReplacements = new Dictionary<string, string>
{
// These are weak type mappings, don't add these.
// Instead, use ScriptEmmyTypeOverrideAttribute to provide a specific type.
@@ -409,7 +410,7 @@ namespace OpenRA.Mods.Common.UtilityCommands.Documentation
{ "Color", "color" },
{ "Actor", "actor" },
{ "Player", "player" },
};
}.ToFrozenDictionary();
public static string EmmyLuaString(this Type type, string notSupportedExceptionContext)
{

View File

@@ -11,6 +11,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Mods.Common.Lint;
@@ -54,37 +55,37 @@ namespace OpenRA.Mods.Common.Widgets
public FrozenActor FrozenActorTooltip { get; private set; }
public string ResourceTooltip { get; private set; }
static readonly Dictionary<ScrollDirection, string> ScrollCursors = new()
{
{ ScrollDirection.Up | ScrollDirection.Left, "scroll-tl" },
{ ScrollDirection.Up | ScrollDirection.Right, "scroll-tr" },
{ ScrollDirection.Down | ScrollDirection.Left, "scroll-bl" },
{ ScrollDirection.Down | ScrollDirection.Right, "scroll-br" },
{ ScrollDirection.Up, "scroll-t" },
{ ScrollDirection.Down, "scroll-b" },
{ ScrollDirection.Left, "scroll-l" },
{ ScrollDirection.Right, "scroll-r" },
};
static readonly ImmutableArray<(ScrollDirection Direction, string Cursor)> ScrollCursors =
[
(ScrollDirection.Up | ScrollDirection.Left, "scroll-tl"),
(ScrollDirection.Up | ScrollDirection.Right, "scroll-tr"),
(ScrollDirection.Down | ScrollDirection.Left, "scroll-bl"),
(ScrollDirection.Down | ScrollDirection.Right, "scroll-br"),
(ScrollDirection.Up, "scroll-t"),
(ScrollDirection.Down, "scroll-b"),
(ScrollDirection.Left, "scroll-l"),
(ScrollDirection.Right, "scroll-r"),
];
static readonly Dictionary<ScrollDirection, string> JoystickCursors = new()
{
{ ScrollDirection.Up | ScrollDirection.Left, "joystick-tl-blocked" },
{ ScrollDirection.Up | ScrollDirection.Right, "joystick-tr-blocked" },
{ ScrollDirection.Down | ScrollDirection.Left, "joystick-bl-blocked" },
{ ScrollDirection.Down | ScrollDirection.Right, "joystick-br-blocked" },
{ ScrollDirection.Up, "joystick-t-blocked" },
{ ScrollDirection.Down, "joystick-b-blocked" },
{ ScrollDirection.Left, "joystick-l-blocked" },
{ ScrollDirection.Right, "joystick-r-blocked" },
};
static readonly ImmutableArray<(ScrollDirection Direction, string Cursor)> JoystickCursors =
[
(ScrollDirection.Up | ScrollDirection.Left, "joystick-tl-blocked"),
(ScrollDirection.Up | ScrollDirection.Right, "joystick-tr-blocked"),
(ScrollDirection.Down | ScrollDirection.Left, "joystick-bl-blocked"),
(ScrollDirection.Down | ScrollDirection.Right, "joystick-br-blocked"),
(ScrollDirection.Up, "joystick-t-blocked"),
(ScrollDirection.Down, "joystick-b-blocked"),
(ScrollDirection.Left, "joystick-l-blocked"),
(ScrollDirection.Right, "joystick-r-blocked"),
];
static readonly Dictionary<ScrollDirection, float2> ScrollOffsets = new()
{
{ ScrollDirection.Up, new float2(0, -1) },
{ ScrollDirection.Down, new float2(0, 1) },
{ ScrollDirection.Left, new float2(-1, 0) },
{ ScrollDirection.Right, new float2(1, 0) },
};
static readonly ImmutableArray<(ScrollDirection Direction, float2 Offset)> ScrollOffsets =
[
(ScrollDirection.Up, new float2(0, -1)),
(ScrollDirection.Down, new float2(0, 1)),
(ScrollDirection.Left, new float2(-1, 0)),
(ScrollDirection.Right, new float2(1, 0)),
];
readonly Lazy<TooltipContainerWidget> tooltipContainer;
readonly World world;
@@ -202,9 +203,9 @@ namespace OpenRA.Mods.Common.Widgets
{
var scroll = float2.Zero;
foreach (var kv in ScrollOffsets)
if (keyboardDirections.Includes(kv.Key) || edgeDirections.Includes(kv.Key))
scroll += kv.Value;
foreach (var (direction, offset) in ScrollOffsets)
if (keyboardDirections.Includes(direction) || edgeDirections.Includes(direction))
scroll += offset;
// Scroll rate is defined for a 40ms interval
var deltaScale = Math.Min(Game.RunTime - lastScrollTime, 25f);
@@ -292,15 +293,15 @@ namespace OpenRA.Mods.Common.Widgets
if (IsJoystickScrolling || isStandardScrolling)
{
foreach (var dir in JoystickCursors)
if (blockedDirections.Includes(dir.Key))
return dir.Value;
foreach (var (direction, cursor) in JoystickCursors)
if (blockedDirections.Includes(direction))
return cursor;
return "joystick-all";
}
foreach (var dir in ScrollCursors)
if (edgeDirections.Includes(dir.Key))
return dir.Value + (blockedDirections.Includes(dir.Key) ? "-blocked" : "");
foreach (var (direction, cursor) in ScrollCursors)
if (edgeDirections.Includes(direction))
return cursor + (blockedDirections.Includes(direction) ? "-blocked" : "");
return null;
}

View File

@@ -10,6 +10,7 @@
#endregion
using System;
using System.Collections.Frozen;
using System.Collections.Generic;
using OpenRA.Mods.Common.Traits;
using OpenRA.Traits;
@@ -42,7 +43,7 @@ namespace OpenRA.Mods.D2k.Traits
All = Left | Top | Right | Bottom | TopLeft | TopRight | BottomLeft | BottomRight
}
public static readonly Dictionary<ClearSides, int> SpriteMap = new()
public static readonly FrozenDictionary<ClearSides, int> SpriteMap = new Dictionary<ClearSides, int>
{
{ ClearSides.Left | ClearSides.Top | ClearSides.TopLeft | ClearSides.TopRight | ClearSides.BottomLeft | ClearSides.BottomRight, 2 },
{ ClearSides.Top | ClearSides.Right | ClearSides.TopLeft | ClearSides.TopRight | ClearSides.BottomLeft | ClearSides.BottomRight, 3 },
@@ -90,7 +91,7 @@ namespace OpenRA.Mods.D2k.Traits
{ ClearSides.Right | ClearSides.TopLeft | ClearSides.TopRight | ClearSides.BottomRight, 47 },
{ ClearSides.Bottom | ClearSides.TopRight | ClearSides.BottomLeft | ClearSides.BottomRight, 48 },
{ ClearSides.Bottom | ClearSides.TopLeft | ClearSides.BottomLeft | ClearSides.BottomRight, 49 },
};
}.ToFrozenDictionary();
public D2kResourceRenderer(Actor self, D2kResourceRendererInfo info)
: base(self, info) { }

View File

@@ -10,6 +10,7 @@
#endregion
using System;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
@@ -65,7 +66,7 @@ namespace OpenRA.Platforms.Default
public const int GL_CONTEXT_LOST = 0x0507;
public const int GL_TABLE_TOO_LARGE = 0x8031;
static readonly Dictionary<int, string> ErrorToText = new()
static readonly FrozenDictionary<int, string> ErrorToText = new Dictionary<int, string>
{
{ GL_NO_ERROR, "No Error" },
{ GL_INVALID_ENUM, "Invalid Enum" },
@@ -77,7 +78,7 @@ namespace OpenRA.Platforms.Default
{ GL_INVALID_FRAMEBUFFER_OPERATION, "Invalid Framebuffer Operation" },
{ GL_CONTEXT_LOST, "Context Lost" },
{ GL_TABLE_TOO_LARGE, "Table Too Large" },
};
}.ToFrozenDictionary();
// BeginMode
public const int GL_POINTS = 0;
@@ -161,7 +162,7 @@ namespace OpenRA.Platforms.Default
public const int GL_DEBUG_SOURCE_APPLICATION = 0x824A;
public const int GL_DEBUG_SOURCE_OTHER = 0x824B;
static readonly Dictionary<int, string> DebugSourceToText = new()
static readonly FrozenDictionary<int, string> DebugSourceToText = new Dictionary<int, string>
{
{ GL_DEBUG_SOURCE_API, "API" },
{ GL_DEBUG_SOURCE_WINDOW_SYSTEM, "Window System" },
@@ -169,7 +170,7 @@ namespace OpenRA.Platforms.Default
{ GL_DEBUG_SOURCE_THIRD_PARTY, "Third Party" },
{ GL_DEBUG_SOURCE_APPLICATION, "Application" },
{ GL_DEBUG_SOURCE_OTHER, "Other" }
};
}.ToFrozenDictionary();
public const int GL_DEBUG_TYPE_ERROR = 0x824C;
public const int GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR = 0x824D;
@@ -181,7 +182,7 @@ namespace OpenRA.Platforms.Default
public const int GL_DEBUG_TYPE_POP_GROUP = 0x826A;
public const int GL_DEBUG_TYPE_OTHER = 0x8251;
static readonly Dictionary<int, string> DebugTypeToText = new()
static readonly FrozenDictionary<int, string> DebugTypeToText = new Dictionary<int, string>
{
{ GL_DEBUG_TYPE_ERROR, "Error" },
{ GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR, "Deprecated Behaviour" },
@@ -192,20 +193,20 @@ namespace OpenRA.Platforms.Default
{ GL_DEBUG_TYPE_PUSH_GROUP, "Push Group" },
{ GL_DEBUG_TYPE_POP_GROUP, "Pop Group" },
{ GL_DEBUG_TYPE_OTHER, "Other" }
};
}.ToFrozenDictionary();
public const int GL_DEBUG_SEVERITY_HIGH = 0x9146;
public const int GL_DEBUG_SEVERITY_MEDIUM = 0x9147;
public const int GL_DEBUG_SEVERITY_LOW = 0x9148;
public const int GL_DEBUG_SEVERITY_NOTIFICATION = 0x826B;
static readonly Dictionary<int, string> DebugSeverityToText = new()
static readonly FrozenDictionary<int, string> DebugSeverityToText = new Dictionary<int, string>
{
{ GL_DEBUG_SEVERITY_HIGH, "High" },
{ GL_DEBUG_SEVERITY_MEDIUM, "Medium" },
{ GL_DEBUG_SEVERITY_LOW, "Low" },
{ GL_DEBUG_SEVERITY_NOTIFICATION, "Notification" }
};
}.ToFrozenDictionary();
// Pixel Mode / Transfer
public const int GL_PACK_ROW_LENGTH = 0x0D02;