using System.Collections.Generic; using System.IO; namespace SabreTools.Matching { /// /// A set of content matches that work together /// public class ContentMatchSet : IMatchSet { /// public List Matchers { get; } /// public string SetName { get; } /// /// Function to get a content version /// /// /// A content version method takes the file path, the file contents, /// and a list of found positions and returns a single string. That /// string is either a version string, in which case it will be appended /// to the match name, or `null`, in which case it will cause /// the match name to be omitted. /// public GetArrayVersion? GetArrayVersion { get; } /// /// Function to get a content version /// /// /// A content version method takes the file path, the file contents, /// and a list of found positions and returns a single string. That /// string is either a version string, in which case it will be appended /// to the match name, or `null`, in which case it will cause /// the match name to be omitted. /// public GetStreamVersion? GetStreamVersion { get; } #region Generic Constructors /// /// Constructor /// /// ContentMatch representing the comparisons /// Unique name for the set public ContentMatchSet(ContentMatch needle, string setName) : this([needle], setName) { } /// /// Constructor /// /// List of ContentMatch objects representing the comparisons /// Unique name for the set /// /// Thrown if is empty. /// public ContentMatchSet(List needles, string setName) { // Validate the inputs if (needles.Count == 0) throw new InvalidDataException(nameof(needles)); Matchers = needles; SetName = setName; GetArrayVersion = null; GetStreamVersion = null; } #endregion #region Array Constructors /// /// Constructor /// /// ContentMatch representing the comparisons /// Delegate for deriving a version on match of an array /// Unique name for the set public ContentMatchSet(ContentMatch needle, GetArrayVersion getVersion, string setName) : this([needle], getVersion, setName) { } /// /// Constructor /// /// List of ContentMatch objects representing the comparisons /// Delegate for deriving a version on match of an array /// Unique name for the set /// /// Thrown if is empty. /// public ContentMatchSet(List needles, GetArrayVersion getVersion, string setName) { // Validate the inputs if (needles.Count == 0) throw new InvalidDataException(nameof(needles)); Matchers = needles; SetName = setName; GetArrayVersion = getVersion; GetStreamVersion = null; } #endregion #region Stream Constructors /// /// Constructor /// /// ContentMatch representing the comparisons /// Delegate for deriving a version on match of a Stream /// Unique name for the set public ContentMatchSet(ContentMatch needle, GetStreamVersion getVersion, string setName) : this([needle], getVersion, setName) { } /// /// Constructor /// /// List of ContentMatch objects representing the comparisons /// Delegate for deriving a version on match of a Stream /// Unique name for the set /// /// Thrown if is empty. /// public ContentMatchSet(List needles, GetStreamVersion getVersion, string setName) { // Validate the inputs if (needles.Count == 0) throw new InvalidDataException(nameof(needles)); Matchers = needles; SetName = setName; GetArrayVersion = null; GetStreamVersion = getVersion; } #endregion #region Array Matching /// /// Determine whether all content matches pass /// /// Array to search /// List of matching positions, if any public List MatchesAll(byte[]? stack) { // If either set is null or empty if (stack is null || stack.Length == 0 || Matchers.Count == 0) return []; // Initialize the position list var positions = new List(); // Loop through all content matches and make sure all pass foreach (var contentMatch in Matchers) { int position = contentMatch.Match(stack); if (position < 0) return []; positions.Add(position); } return positions; } /// /// Determine whether any content matches pass /// /// Array to search /// First matching position on success, -1 on error public int MatchesAny(byte[]? stack) { // If either set is null or empty if (stack is null || stack.Length == 0 || Matchers.Count == 0) return -1; // Loop through all content matches and make sure all pass foreach (var contentMatch in Matchers) { int position = contentMatch.Match(stack); if (position >= 0) return position; } return -1; } #endregion #region Stream Matching /// /// Determine whether all content matches pass /// /// Stream to search /// List of matching positions, if any public List MatchesAll(Stream? stack) { // If either set is null or empty if (stack is null || stack.Length == 0 || Matchers.Count == 0) return []; // Initialize the position list var positions = new List(); // Loop through all content matches and make sure all pass foreach (var contentMatch in Matchers) { int position = contentMatch.Match(stack); if (position < 0) return []; positions.Add(position); } return positions; } /// /// Determine whether any content matches pass /// /// Stream to search /// First matching position on success, -1 on error public int MatchesAny(Stream? stack) { // If either set is null or empty if (stack is null || stack.Length == 0 || Matchers.Count == 0) return -1; // Loop through all content matches and make sure all pass foreach (var contentMatch in Matchers) { int position = contentMatch.Match(stack); if (position >= 0) return position; } return -1; } #endregion } }