/// <summary> The editor wants to do an exact match, not a best match, and receive the index if it hits (for preserving selections between frames) </summary> public bool TryGetExactValue(TagSet context, out T value, out int index) { // Use the sorted-by-rule-count property so that the first matched rule is the best one. for (int i = 0; i < count; i++) { var rule = rules[i]; if (context.IsEqual(rule)) { value = values[i]; index = i; return(true); } } value = default(T); index = -1; return(false); }
/// <summary>Editor support</summary> public bool TryGetBestValueWithIndex(TagSet context, out T value, out int index) { // Use the sorted-by-rule-count property so that the first matched rule is the best one. for (int i = 0; i < count; i++) { if (context.IsSupersetOf(rules[i])) { value = values[i]; index = i; return(true); } } Debug.Assert(!HasBaseFallback); // Should always match something if we have a base fallback. value = default(T); index = -1; return(false); }
/// <summary>Deserialize into new object instance</summary> public TagLookup(BinaryReader br, Func <T> deserializeValue) { count = br.ReadInt32(); rules = new TagSet[count]; for (int i = 0; i < rules.Length; i++) { rules[i] = new TagSet(br); if (rules[i].Count > 1) { countOfMultiTagRules++; } } values = new T[count]; for (int i = 0; i < values.Length; i++) { values[i] = deserializeValue(); } }
public bool IsEqual(TagSet other) { if (other == null) { return(false); } if (this.Count != other.Count) { return(false); } for (int i = 0; i < Count; i++) { if (this.tags[i] != other.tags[i]) { return(false); } } return(true); }
/// <summary> Editor use; wants to match "ru" with "run"</summary> public bool IsFuzzySupersetOf(TagSet subset) { // Take advantage of sorted property to search linearly int subsetIndex = 0, thisIndex = 0; while (subsetIndex < subset.Count) { while (thisIndex < Count) { var left = subset.tags[subsetIndex]; var right = tags[thisIndex]; // truncate to search length for "fuzzy" if (left.Length <= right.Length) { right = right.Substring(0, left.Length); } var comparison = String.Compare(left, right, StringComparison.InvariantCulture); // PERF: Should be Ordinal, but requires a complex data rewrite thisIndex++; if (comparison == 0) { goto nextSubsetItem; // This item matched, go to the next one } if (comparison > 0) { return(false); // This item comes after the given one (and all subsequent ones will as well - we'll never find a match) } } // If we get to here, we ran out of superset items to check against return(false); nextSubsetItem: subsetIndex++; } // If we get to here, all subset items were matched return(true); }
public void Add(TagSet rule, T value) { // Expand if necessary if (count == rules.Length) { int newSize = Math.Max(8, rules.Length * 2); // <- Required to handle 0-length rule sets (can come from deserialize) Array.Resize(ref rules, newSize); Array.Resize(ref values, newSize); } // Find the insertion point // Search backwards so that when we get pre-sorted data we are O(1) // Also maintain stability (items that sort the same are kept in insertion order) int i = count; while (i > 0) { if (rule.Count <= rules[i - 1].Count) { break; // Found insertion point } i--; } // Make space at the insertion point Array.Copy(rules, i, rules, i + 1, count - i); Array.Copy(values, i, values, i + 1, count - i); count++; // Insert the new data rules[i] = rule; values[i] = value; // Because we are sorted, we can treat this as a count: if (rule.Count > 1) { countOfMultiTagRules++; } }
/// <summary> /// Find one of the best matching values for a given context, selecting between multiple choices if they exist. /// A matching value is one who's tags (rules) are a subset of the given context. /// A best value is the one with the largest number of rules. /// In the case that there are multiple "best" matches, select the one at choiceIndex mod the number of options. /// </summary> public bool TryGetBestValueChoice(TagSet context, int choiceIndex, out T value) { // Try to find the first rule set that matches int first; for (first = 0; first < count; first++) { if (context.IsSupersetOf(rules[first])) { break; } } if (first == count) // Got to the end without finding anything { value = default(T); return(false); } int matchTagCount = rules[first].Count; // Optimisation: Oppertunistically avoid a re-walk if all matches are consecutive (common case) int lastMatch = first; // Find the last rule that could match and count the number of matches in between int matches = 1; int end; for (end = first + 1; end < count; end++) { if (rules[end].Count < matchTagCount) { break; // Don't match smaller rules } if (context.IsSupersetOf(rules[end])) { lastMatch = end; matches++; } } // At this point we know the number of valid matches, and we can select one choiceIndex = (choiceIndex % matches); // If all matching elements are consecutive, we can immediately return if ((lastMatch - first) + 1 == matches) { value = values[first + choiceIndex]; } else // Otherwise we must re-walk (will early-out once we hit our option) { // WARNING: Reusing local variable 'first' as indexer here! while (choiceIndex > 0) // NOTE: choiceIndex = 0 will skip this loop, immediately giving the 'first' element (its rules always match) { first++; if (context.IsSupersetOf(rules[first])) { choiceIndex--; // Decrement our way through options we're not choosing } } value = values[first]; } return(true); }
public static void Deserialize(DeserializeContext context, BinaryReader br, ref TagSet value) { throw new InvalidOperationException(); }
public static void Serialize(SerializeContext context, BinaryWriter bw, TagSet value) { }
public Enumerator(TagSet tagSet) { i = 0; current = null; this.tagSet = tagSet; }