From 7c0a7fd64e2ccd3589f98073b4b87be431c920e9 Mon Sep 17 00:00:00 2001 From: Kurt Date: Sat, 20 Aug 2022 12:11:43 -0700 Subject: [PATCH] Improve static class localization perf Every reflection GetValue / SetValue call would do a bunch of allocation; just let the runtime give us the full list it builds each time, then iterate off that. Improve start index searching; properties do not contain spaces, so we can find the start by just searching for the first character of the splitter (space). No longer need to trycatch as we're setting foreach property, instead of foreach string. --- .../Util/Localization/LocalizationUtil.cs | 66 +++++++++++-------- 1 file changed, 39 insertions(+), 27 deletions(-) diff --git a/PKHeX.Core/Util/Localization/LocalizationUtil.cs b/PKHeX.Core/Util/Localization/LocalizationUtil.cs index 9563e64df..af3c9397f 100644 --- a/PKHeX.Core/Util/Localization/LocalizationUtil.cs +++ b/PKHeX.Core/Util/Localization/LocalizationUtil.cs @@ -1,46 +1,59 @@ -using System; +using System; using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; +using System.Reflection; namespace PKHeX.Core; +/// +/// Localization utility for changing all properties of a static type. +/// public static class LocalizationUtil { private const string TranslationSplitter = " = "; + private const char TranslationFirst = ' '; // perf; no properties contain spaces. /// /// Gets the names of the properties defined in the given input /// /// Enumerable of translation definitions in the form "Property = Value". - private static string[] GetProperties(IEnumerable input) + private static string[] GetProperties(ReadOnlySpan input) { - static string AfterSplit(string l) + var result = new string[input.Length]; + for (int i = 0; i < input.Length; i++) { - var split = l.IndexOf(TranslationSplitter, StringComparison.Ordinal); - return l[..split]; + var l = input[i]; + var split = l.IndexOf(TranslationFirst); + result[i] = l[..split]; } - return input.Select(AfterSplit).ToArray(); + return result; } - private static IEnumerable DumpStrings(Type t) + private static string[] DumpStrings(Type t) { - var props = ReflectUtil.GetPropertiesStartWithPrefix(t, string.Empty); - return props.Select(p => $"{p}{TranslationSplitter}{ReflectUtil.GetValue(t, p)}"); + var ti = t.GetTypeInfo(); + var props = (PropertyInfo[])ti.DeclaredProperties; + var result = new string[props.Length]; + for (int i = 0; i < props.Length; i++) + { + var p = props[i]; + var value = p.GetValue(null); + result[i] = $"{p}{TranslationSplitter}{value}"; + } + return result; } /// /// Gets the current localization in a static class containing language-specific strings /// /// - public static string[] GetLocalization(Type t) => DumpStrings(t).ToArray(); + public static string[] GetLocalization(Type t) => DumpStrings(t); /// /// Gets the current localization in a static class containing language-specific strings /// /// /// Existing localization lines (if provided) - public static string[] GetLocalization(Type t, string[] existingLines) + public static string[] GetLocalization(Type t, ReadOnlySpan existingLines) { var currentLines = GetLocalization(t); var existing = GetProperties(existingLines); @@ -60,28 +73,27 @@ public static string[] GetLocalization(Type t, string[] existingLines) /// /// Type of the static class containing the desired strings. /// Lines containing the localized strings - private static void SetLocalization(Type t, IReadOnlyCollection lines) + private static void SetLocalization(Type t, ReadOnlySpan lines) { - if (lines.Count == 0) + if (lines.Length == 0) return; + + var translatable = new Dictionary(lines.Length); foreach (var line in lines) { - var index = line.IndexOf(TranslationSplitter, StringComparison.Ordinal); + var index = line.IndexOf(TranslationFirst); if (index < 0) continue; var prop = line[..index]; - var value = line[(index + TranslationSplitter.Length)..]; + translatable[prop] = line[(index + TranslationSplitter.Length)..]; + } - try - { - ReflectUtil.SetValue(t, prop, value); - } - // Malformed translation files, log - catch (Exception e) - { - Debug.WriteLine($"Property not present: {prop} || Value written: {value}"); - Debug.WriteLine(e.Message); - } + var ti = t.GetTypeInfo(); + var props = (PropertyInfo[])ti.DeclaredProperties; + foreach (var p in props) + { + if (translatable.TryGetValue(p.Name, out var value)) + p.SetValue(null, value); } }