Cache regex instances in most cases (#27699)

Using static Regex functions that take in a pattern is bad because the pattern constantly needs to be re-parsed. With https://github.com/space-wizards/RobustToolbox/pull/5107, the engine has an analyzer to warn for this practice now.

This commit brings most of content up to snuff already, though some of the tricker code I left for somebody else.
This commit is contained in:
Pieter-Jan Briers
2024-05-06 00:57:32 +02:00
committed by GitHub
parent 70d3cf7ba4
commit 4a2a63a86b
10 changed files with 74 additions and 60 deletions

View File

@@ -10,6 +10,10 @@ public sealed class FrenchAccentSystem : EntitySystem
{ {
[Dependency] private readonly ReplacementAccentSystem _replacement = default!; [Dependency] private readonly ReplacementAccentSystem _replacement = default!;
private static readonly Regex RegexTh = new(@"th", RegexOptions.IgnoreCase);
private static readonly Regex RegexStartH = new(@"(?<!\w)h", RegexOptions.IgnoreCase);
private static readonly Regex RegexSpacePunctuation = new(@"(?<=\w\w)[!?;:](?!\w)", RegexOptions.IgnoreCase);
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
@@ -24,16 +28,13 @@ public sealed class FrenchAccentSystem : EntitySystem
msg = _replacement.ApplyReplacements(msg, "french"); msg = _replacement.ApplyReplacements(msg, "french");
// replaces th with dz // replaces th with dz
msg = Regex.Replace(msg, @"th", "'z", RegexOptions.IgnoreCase); msg = RegexTh.Replace(msg, "'z");
// removes the letter h from the start of words. // removes the letter h from the start of words.
msg = Regex.Replace(msg, @"(?<!\w)[h]", "'", RegexOptions.IgnoreCase); msg = RegexStartH.Replace(msg, "'");
// spaces out ! ? : and ;. // spaces out ! ? : and ;.
msg = Regex.Replace(msg, @"(?<=\w\w)!(?!\w)", " !", RegexOptions.IgnoreCase); msg = RegexSpacePunctuation.Replace(msg, " $&");
msg = Regex.Replace(msg, @"(?<=\w\w)[?](?!\w)", " ?", RegexOptions.IgnoreCase);
msg = Regex.Replace(msg, @"(?<=\w\w)[;](?!\w)", " ;", RegexOptions.IgnoreCase);
msg = Regex.Replace(msg, @"(?<=\w\w)[:](?!\w)", " :", RegexOptions.IgnoreCase);
return msg; return msg;
} }

View File

@@ -5,6 +5,13 @@ namespace Content.Server.Speech.EntitySystems;
public sealed class FrontalLispSystem : EntitySystem public sealed class FrontalLispSystem : EntitySystem
{ {
// @formatter:off
private static readonly Regex RegexUpperTh = new(@"[T]+[Ss]+|[S]+[Cc]+(?=[IiEeYy]+)|[C]+(?=[IiEeYy]+)|[P][Ss]+|([S]+[Tt]+|[T]+)(?=[Ii]+[Oo]+[Uu]*[Nn]*)|[C]+[Hh]+(?=[Ii]*[Ee]*)|[Z]+|[S]+|[X]+(?=[Ee]+)");
private static readonly Regex RegexLowerTh = new(@"[t]+[s]+|[s]+[c]+(?=[iey]+)|[c]+(?=[iey]+)|[p][s]+|([s]+[t]+|[t]+)(?=[i]+[o]+[u]*[n]*)|[c]+[h]+(?=[i]*[e]*)|[z]+|[s]+|[x]+(?=[e]+)");
private static readonly Regex RegexUpperEcks = new(@"[E]+[Xx]+[Cc]*|[X]+");
private static readonly Regex RegexLowerEcks = new(@"[e]+[x]+[c]*|[x]+");
// @formatter:on
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
@@ -16,11 +23,11 @@ public sealed class FrontalLispSystem : EntitySystem
var message = args.Message; var message = args.Message;
// handles ts, sc(i|e|y), c(i|e|y), ps, st(io(u|n)), ch(i|e), z, s // handles ts, sc(i|e|y), c(i|e|y), ps, st(io(u|n)), ch(i|e), z, s
message = Regex.Replace(message, @"[T]+[Ss]+|[S]+[Cc]+(?=[IiEeYy]+)|[C]+(?=[IiEeYy]+)|[P][Ss]+|([S]+[Tt]+|[T]+)(?=[Ii]+[Oo]+[Uu]*[Nn]*)|[C]+[Hh]+(?=[Ii]*[Ee]*)|[Z]+|[S]+|[X]+(?=[Ee]+)", "TH"); message = RegexUpperTh.Replace(message, "TH");
message = Regex.Replace(message, @"[t]+[s]+|[s]+[c]+(?=[iey]+)|[c]+(?=[iey]+)|[p][s]+|([s]+[t]+|[t]+)(?=[i]+[o]+[u]*[n]*)|[c]+[h]+(?=[i]*[e]*)|[z]+|[s]+|[x]+(?=[e]+)", "th"); message = RegexLowerTh.Replace(message, "th");
// handles ex(c), x // handles ex(c), x
message = Regex.Replace(message, @"[E]+[Xx]+[Cc]*|[X]+", "EKTH"); message = RegexUpperEcks.Replace(message, "EKTH");
message = Regex.Replace(message, @"[e]+[x]+[c]*|[x]+", "ekth"); message = RegexLowerEcks.Replace(message, "ekth");
args.Message = message; args.Message = message;
} }

View File

@@ -5,6 +5,12 @@ namespace Content.Server.Speech.EntitySystems;
public sealed class LizardAccentSystem : EntitySystem public sealed class LizardAccentSystem : EntitySystem
{ {
private static readonly Regex RegexLowerS = new("s+");
private static readonly Regex RegexUpperS = new("S+");
private static readonly Regex RegexInternalX = new(@"(\w)x");
private static readonly Regex RegexLowerEndX = new(@"\bx([\-|r|R]|\b)");
private static readonly Regex RegexUpperEndX = new(@"\bX([\-|r|R]|\b)");
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
@@ -16,15 +22,15 @@ public sealed class LizardAccentSystem : EntitySystem
var message = args.Message; var message = args.Message;
// hissss // hissss
message = Regex.Replace(message, "s+", "sss"); message = RegexLowerS.Replace(message, "sss");
// hiSSS // hiSSS
message = Regex.Replace(message, "S+", "SSS"); message = RegexUpperS.Replace(message, "SSS");
// ekssit // ekssit
message = Regex.Replace(message, @"(\w)x", "$1kss"); message = RegexInternalX.Replace(message, "$1kss");
// ecks // ecks
message = Regex.Replace(message, @"\bx([\-|r|R]|\b)", "ecks$1"); message = RegexLowerEndX.Replace(message, "ecks$1");
// eckS // eckS
message = Regex.Replace(message, @"\bX([\-|r|R]|\b)", "ECKS$1"); message = RegexUpperEndX.Replace(message, "ECKS$1");
args.Message = message; args.Message = message;
} }

View File

@@ -1,4 +1,3 @@
using System.Globalization;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Content.Server.Speech.Components; using Content.Server.Speech.Components;
@@ -8,30 +7,17 @@ namespace Content.Server.Speech.EntitySystems;
public sealed class MobsterAccentSystem : EntitySystem public sealed class MobsterAccentSystem : EntitySystem
{ {
private static readonly Regex RegexIng = new(@"(?<=\w\w)(in)g(?!\w)", RegexOptions.IgnoreCase);
private static readonly Regex RegexLowerOr = new(@"(?<=\w)o[Rr](?=\w)");
private static readonly Regex RegexUpperOr = new(@"(?<=\w)O[Rr](?=\w)");
private static readonly Regex RegexLowerAr = new(@"(?<=\w)a[Rr](?=\w)");
private static readonly Regex RegexUpperAr = new(@"(?<=\w)A[Rr](?=\w)");
private static readonly Regex RegexFirstWord = new(@"^(\S+)");
private static readonly Regex RegexLastWord = new(@"(\S+)$");
[Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly ReplacementAccentSystem _replacement = default!; [Dependency] private readonly ReplacementAccentSystem _replacement = default!;
private static readonly Dictionary<string, string> DirectReplacements = new()
{
{ "let me", "lemme" },
{ "should", "oughta" },
{ "the", "da" },
{ "them", "dem" },
{ "attack", "whack" },
{ "kill", "whack" },
{ "murder", "whack" },
{ "dead", "sleepin' with da fishies"},
{ "hey", "ey'o" },
{ "hi", "ey'o"},
{ "hello", "ey'o"},
{ "rules", "roolz" },
{ "you", "yous" },
{ "have to", "gotta" },
{ "going to", "boutta" },
{ "about to", "boutta" },
{ "here", "'ere" }
};
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
@@ -51,20 +37,20 @@ public sealed class MobsterAccentSystem : EntitySystem
// thinking -> thinkin' // thinking -> thinkin'
// king -> king // king -> king
//Uses captures groups to make sure the captialization of IN is kept //Uses captures groups to make sure the captialization of IN is kept
msg = Regex.Replace(msg, @"(?<=\w\w)(in)g(?!\w)", "$1'", RegexOptions.IgnoreCase); msg = RegexIng.Replace(msg, "$1'");
// or -> uh and ar -> ah in the middle of words (fuhget, tahget) // or -> uh and ar -> ah in the middle of words (fuhget, tahget)
msg = Regex.Replace(msg, @"(?<=\w)o[Rr](?=\w)", "uh"); msg = RegexLowerOr.Replace(msg, "uh");
msg = Regex.Replace(msg, @"(?<=\w)O[Rr](?=\w)", "UH"); msg = RegexUpperOr.Replace(msg, "UH");
msg = Regex.Replace(msg, @"(?<=\w)a[Rr](?=\w)", "ah"); msg = RegexLowerAr.Replace(msg, "ah");
msg = Regex.Replace(msg, @"(?<=\w)A[Rr](?=\w)", "AH"); msg = RegexUpperAr.Replace(msg, "AH");
// Prefix // Prefix
if (_random.Prob(0.15f)) if (_random.Prob(0.15f))
{ {
//Checks if the first word of the sentence is all caps //Checks if the first word of the sentence is all caps
//So the prefix can be allcapped and to not resanitize the captial //So the prefix can be allcapped and to not resanitize the captial
var firstWordAllCaps = !Regex.Match(msg, @"^(\S+)").Value.Any(char.IsLower); var firstWordAllCaps = !RegexFirstWord.Match(msg).Value.Any(char.IsLower);
var pick = _random.Next(1, 2); var pick = _random.Next(1, 2);
// Reverse sanitize capital // Reverse sanitize capital
@@ -84,7 +70,7 @@ public sealed class MobsterAccentSystem : EntitySystem
{ {
//Checks if the last word of the sentence is all caps //Checks if the last word of the sentence is all caps
//So the suffix can be allcapped //So the suffix can be allcapped
var lastWordAllCaps = !Regex.Match(msg, @"(\S+)$").Value.Any(char.IsLower); var lastWordAllCaps = !RegexLastWord.Match(msg).Value.Any(char.IsLower);
var suffix = ""; var suffix = "";
if (component.IsBoss) if (component.IsBoss)
{ {

View File

@@ -5,6 +5,9 @@ namespace Content.Server.Speech.EntitySystems;
public sealed class MothAccentSystem : EntitySystem public sealed class MothAccentSystem : EntitySystem
{ {
private static readonly Regex RegexLowerBuzz = new Regex("z{1,3}");
private static readonly Regex RegexUpperBuzz = new Regex("Z{1,3}");
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
@@ -16,9 +19,9 @@ public sealed class MothAccentSystem : EntitySystem
var message = args.Message; var message = args.Message;
// buzzz // buzzz
message = Regex.Replace(message, "z{1,3}", "zzz"); message = RegexLowerBuzz.Replace(message, "zzz");
// buZZZ // buZZZ
message = Regex.Replace(message, "Z{1,3}", "ZZZ"); message = RegexUpperBuzz.Replace(message, "ZZZ");
args.Message = message; args.Message = message;
} }

View File

@@ -7,6 +7,8 @@ namespace Content.Server.Speech.EntitySystems;
public sealed partial class ParrotAccentSystem : EntitySystem public sealed partial class ParrotAccentSystem : EntitySystem
{ {
private static readonly Regex WordCleanupRegex = new Regex("[^A-Za-z0-9 -]");
[Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IRobustRandom _random = default!;
public override void Initialize() public override void Initialize()
@@ -27,7 +29,7 @@ public sealed partial class ParrotAccentSystem : EntitySystem
if (_random.Prob(entity.Comp.LongestWordRepeatChance)) if (_random.Prob(entity.Comp.LongestWordRepeatChance))
{ {
// Don't count non-alphanumeric characters as parts of words // Don't count non-alphanumeric characters as parts of words
var cleaned = Regex.Replace(message, "[^A-Za-z0-9 -]", string.Empty); var cleaned = WordCleanupRegex.Replace(message, string.Empty);
// Split on whitespace and favor words towards the end of the message // Split on whitespace and favor words towards the end of the message
var words = cleaned.Split(null).Reverse(); var words = cleaned.Split(null).Reverse();
// Find longest word // Find longest word

View File

@@ -7,6 +7,8 @@ namespace Content.Server.Speech.EntitySystems;
public sealed class PirateAccentSystem : EntitySystem public sealed class PirateAccentSystem : EntitySystem
{ {
private static readonly Regex FirstWordAllCapsRegex = new(@"^(\S+)");
[Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly ReplacementAccentSystem _replacement = default!; [Dependency] private readonly ReplacementAccentSystem _replacement = default!;
@@ -26,7 +28,7 @@ public sealed class PirateAccentSystem : EntitySystem
return msg; return msg;
//Checks if the first word of the sentence is all caps //Checks if the first word of the sentence is all caps
//So the prefix can be allcapped and to not resanitize the captial //So the prefix can be allcapped and to not resanitize the captial
var firstWordAllCaps = !Regex.Match(msg, @"^(\S+)").Value.Any(char.IsLower); var firstWordAllCaps = !FirstWordAllCapsRegex.Match(msg).Value.Any(char.IsLower);
var pick = _random.Pick(component.PirateWords); var pick = _random.Pick(component.PirateWords);
var pirateWord = Loc.GetString(pick); var pirateWord = Loc.GetString(pick);

View File

@@ -7,6 +7,8 @@ namespace Content.Server.Speech.EntitySystems
{ {
public sealed class ScrambledAccentSystem : EntitySystem public sealed class ScrambledAccentSystem : EntitySystem
{ {
private static readonly Regex RegexLoneI = new(@"(?<=\ )i(?=[\ \.\?]|$)");
[Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IRobustRandom _random = default!;
public override void Initialize() public override void Initialize()
@@ -34,7 +36,7 @@ namespace Content.Server.Speech.EntitySystems
msg = msg[0].ToString().ToUpper() + msg.Remove(0, 1); msg = msg[0].ToString().ToUpper() + msg.Remove(0, 1);
// Capitalize lone i's // Capitalize lone i's
msg = Regex.Replace(msg, @"(?<=\ )i(?=[\ \.\?]|$)", "I"); msg = RegexLoneI.Replace(msg, "I");
return msg; return msg;
} }

View File

@@ -5,6 +5,10 @@ namespace Content.Server.Speech.EntitySystems;
public sealed class SouthernAccentSystem : EntitySystem public sealed class SouthernAccentSystem : EntitySystem
{ {
private static readonly Regex RegexIng = new(@"ing\b");
private static readonly Regex RegexAnd = new(@"\band\b");
private static readonly Regex RegexDve = new("d've");
[Dependency] private readonly ReplacementAccentSystem _replacement = default!; [Dependency] private readonly ReplacementAccentSystem _replacement = default!;
public override void Initialize() public override void Initialize()
@@ -20,9 +24,9 @@ public sealed class SouthernAccentSystem : EntitySystem
message = _replacement.ApplyReplacements(message, "southern"); message = _replacement.ApplyReplacements(message, "southern");
//They shoulda started runnin' an' hidin' from me! //They shoulda started runnin' an' hidin' from me!
message = Regex.Replace(message, @"ing\b", "in'"); message = RegexIng.Replace(message, "in'");
message = Regex.Replace(message, @"\band\b", "an'"); message = RegexAnd.Replace(message, "an'");
message = Regex.Replace(message, "d've", "da"); message = RegexDve.Replace(message, "da");
args.Message = message; args.Message = message;
} }
}; };

View File

@@ -26,6 +26,9 @@ namespace Content.Shared.Preferences
[Serializable, NetSerializable] [Serializable, NetSerializable]
public sealed partial class HumanoidCharacterProfile : ICharacterProfile public sealed partial class HumanoidCharacterProfile : ICharacterProfile
{ {
private static readonly Regex RestrictedNameRegex = new("[^A-Z,a-z,0-9, -]");
private static readonly Regex ICNameCaseRegex = new(@"^(?<word>\w)|\b(?<word>\w)(?=\w*$)");
public const int MaxNameLength = 32; public const int MaxNameLength = 32;
public const int MaxDescLength = 512; public const int MaxDescLength = 512;
@@ -418,15 +421,13 @@ namespace Content.Shared.Preferences
if (configManager.GetCVar(CCVars.RestrictedNames)) if (configManager.GetCVar(CCVars.RestrictedNames))
{ {
name = Regex.Replace(name, @"[^A-Z,a-z,0-9, -]", string.Empty); name = RestrictedNameRegex.Replace(name, string.Empty);
} }
if (configManager.GetCVar(CCVars.ICNameCase)) if (configManager.GetCVar(CCVars.ICNameCase))
{ {
// This regex replaces the first character of the first and last words of the name with their uppercase version // This regex replaces the first character of the first and last words of the name with their uppercase version
name = Regex.Replace(name, name = ICNameCaseRegex.Replace(name, m => m.Groups["word"].Value.ToUpper());
@"^(?<word>\w)|\b(?<word>\w)(?=\w*$)",
m => m.Groups["word"].Value.ToUpper());
} }
if (string.IsNullOrEmpty(name)) if (string.IsNullOrEmpty(name))