Improve lookups of nodes by key in MiniYaml.
When handling the Nodes collection in MiniYaml, individual nodes are located via one of two methods:
// Lookup a single key with linear search.
var node = yaml.Nodes.FirstOrDefault(n => n.Key == "SomeKey");
// Convert to dictionary, expecting many key lookups.
var dict = nodes.ToDictionary();
// Lookup a single key in the dictionary.
var node = dict["SomeKey"];
To simplify lookup of individual keys via linear search, provide helper methods NodeWithKeyOrDefault and NodeWithKey. These helpers do the equivalent of Single{OrDefault} searches. Whilst this requires checking the whole list, it provides a useful correctness check. Two duplicated keys in TS yaml are fixed as a result. We can also optimize the helpers to not use LINQ, avoiding allocation of the delegate to search for a key.
Adjust existing code to use either lnear searches or dictionary lookups based on whether it will be resolving many keys. Resolving few keys can be done with linear searches to avoid building a dictionary. Resolving many keys should be done with a dictionary to avoid quaradtic runtime from repeated linear searches.
This commit is contained in:
committed by
Matthias Mailänder
parent
0ab7caedd9
commit
b7e0ed9b87
@@ -141,6 +141,34 @@ namespace OpenRA
|
||||
return new MiniYaml(Value, newNodes);
|
||||
}
|
||||
|
||||
public MiniYamlNode NodeWithKey(string key)
|
||||
{
|
||||
var result = NodeWithKeyOrDefault(key);
|
||||
if (result == null)
|
||||
throw new InvalidDataException($"No node with key '{key}'");
|
||||
return result;
|
||||
}
|
||||
|
||||
public MiniYamlNode NodeWithKeyOrDefault(string key)
|
||||
{
|
||||
// PERF: Avoid LINQ.
|
||||
var first = true;
|
||||
MiniYamlNode result = null;
|
||||
foreach (var node in Nodes)
|
||||
{
|
||||
if (node.Key != key)
|
||||
continue;
|
||||
|
||||
if (!first)
|
||||
throw new InvalidDataException($"Duplicate key '{node.Key}' in {node.Location}");
|
||||
|
||||
first = false;
|
||||
result = node;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public Dictionary<string, MiniYaml> ToDictionary()
|
||||
{
|
||||
return ToDictionary(MiniYamlIdentity);
|
||||
@@ -175,11 +203,6 @@ namespace OpenRA
|
||||
Nodes = ImmutableArray.CreateRange(nodes);
|
||||
}
|
||||
|
||||
public static ImmutableArray<MiniYamlNode> NodesOrEmpty(MiniYaml y, string s)
|
||||
{
|
||||
return y.Nodes.FirstOrDefault(n => n.Key == s)?.Value.Nodes ?? ImmutableArray<MiniYamlNode>.Empty;
|
||||
}
|
||||
|
||||
static List<MiniYamlNode> FromLines(IEnumerable<ReadOnlyMemory<char>> lines, string filename, bool discardCommentsAndWhitespace, Dictionary<string, string> stringPool)
|
||||
{
|
||||
stringPool ??= new Dictionary<string, string>();
|
||||
@@ -668,6 +691,11 @@ namespace OpenRA
|
||||
foreach (var line in Nodes.ToLines())
|
||||
yield return "\t" + line;
|
||||
}
|
||||
|
||||
public MiniYamlNodeBuilder NodeWithKeyOrDefault(string key)
|
||||
{
|
||||
return Nodes.SingleOrDefault(n => n.Key == key);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
|
||||
Reference in New Issue
Block a user