using System; using System.IO; using System.Text; using System.Text.Encodings.Web; using System.Text.Json; using System.Text.Json.Serialization; using ParadoxSaveParser.Lib; namespace ParadoxSaveParser.Benchmarks; /// /// Locates and lazily loads the benchmark corpus: /// a real EU4 gamestate file and its compact JSON twin (for jq). /// Override the directory with the PSP_BENCH_DATA environment variable. /// public static class BenchData { public const string PdxQuery = "countries.*.technology"; /// jq program extracting the same data as . public const string JqQuery = ".countries | map_values(.technology)"; public static string Dir { get; } = Environment.GetEnvironmentVariable("PSP_BENCH_DATA") ?? Path.Combine(FindRepoRoot(), "ParadoxSaveParser.CLI", "bin", "Debug", "net10.0"); public static string PdxPath => Path.Combine(Dir, "gamestate"); public static string JsonPath => Path.Combine(Dir, "gamestate.min.json"); private static byte[]? _pdxBytes; private static string? _pdxText; /// Raw bytes of the save, kept in memory so benchmarks don't measure disk I/O. public static byte[] PdxBytes => _pdxBytes ??= File.ReadAllBytes(PdxPath); /// /// The save decoded as text for the regex engines. /// Latin1 is used because EU4 saves contain non-UTF8 bytes inside dynasty names, /// and it maps every byte to exactly one char. /// public static string PdxText => _pdxText ??= Encoding.Latin1.GetString(PdxBytes); private static string FindRepoRoot() { var dir = new DirectoryInfo(AppContext.BaseDirectory); while (dir != null && !File.Exists(Path.Combine(dir.FullName, "ParadoxSaveParser.sln"))) dir = dir.Parent; return dir?.FullName ?? Directory.GetCurrentDirectory(); } /// /// Produces the compact JSON twin of the save file, which is what jq is given. /// Compact (not indented) so jq isn't penalized by parsing whitespace. /// public static void PrepareJson() { if (File.Exists(JsonPath)) { Console.WriteLine($"{JsonPath} already exists, skipping"); return; } Console.WriteLine($"parsing {PdxPath} ..."); using var input = File.OpenRead(PdxPath); var root = new SaveParserEU4(input, null).Parse(); Console.WriteLine($"serializing to {JsonPath} ..."); using var output = File.Create(JsonPath); JsonSerializer.Serialize(output, root, new JsonSerializerOptions { WriteIndented = false, Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, MaxDepth = 1024, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, }); Console.WriteLine($"done: {new FileInfo(JsonPath).Length / 1024 / 1024} MB"); } }