public static bool TrySearch(IDataModel data, ModelDelta changeToken, string originalFormat, out ArrayRun self, Func <IFormattedRun, bool> runFilter = null) { self = null; var format = originalFormat; var allowPointersToEntries = format.StartsWith(AnchorStart.ToString()); if (allowPointersToEntries) { format = format.Substring(1); } var closeArray = format.LastIndexOf(ArrayEnd.ToString()); if (!format.StartsWith(ArrayStart.ToString()) || closeArray == -1) { throw new ArrayRunParseException($"Array Content must be wrapped in {ArrayStart}{ArrayEnd}"); } var segments = format.Substring(1, closeArray - 1); var length = format.Substring(closeArray + 1); var elementContent = ParseSegments(segments, data); if (elementContent.Count == 0) { return(false); } var elementLength = elementContent.Sum(e => e.Length); using (ModelCacheScope.CreateScope(data)) { if (string.IsNullOrEmpty(length)) { var bestAddress = StandardSearch(data, elementContent, elementLength, out int bestLength, runFilter); if (bestAddress == Pointer.NULL) { return(false); } self = new ArrayRun(data, originalFormat + bestLength, string.Empty, bestAddress, bestLength, elementContent, data.GetNextRun(bestAddress).PointerSources, null); } else { var bestAddress = KnownLengthSearch(data, elementContent, elementLength, length, out int bestLength, runFilter); if (bestAddress == Pointer.NULL) { return(false); } var lengthFromAnchor = int.TryParse(length, out var _) ? string.Empty : length; self = new ArrayRun(data, originalFormat, lengthFromAnchor, bestAddress, bestLength, elementContent, data.GetNextRun(bestAddress).PointerSources, null); } } if (allowPointersToEntries) { self = self.AddSourcesPointingWithinArray(changeToken); } return(true); }
private ArrayRun(IDataModel data, string format, int start, IReadOnlyList <int> pointerSources) : base(start, pointerSources) { owner = data; FormatString = format; SupportsPointersToElements = format.StartsWith(AnchorStart.ToString()); if (SupportsPointersToElements) { format = format.Substring(1); } var closeArray = format.LastIndexOf(ArrayEnd.ToString()); if (!format.StartsWith(ArrayStart.ToString()) || closeArray == -1) { throw new ArrayRunParseException($"Array Content must be wrapped in {ArrayStart}{ArrayEnd}."); } var segments = format.Substring(1, closeArray - 1); var length = format.Substring(closeArray + 1); ElementContent = ParseSegments(segments, data); if (ElementContent.Count == 0) { throw new ArrayRunParseException("Array Content must not be empty."); } ElementLength = ElementContent.Sum(e => e.Length); FormatMatchFlags flags = default; if (ElementContent.Count == 1) { flags |= FormatMatchFlags.IsSingleSegment; } if (length.Length == 0) { var nextRun = owner.GetNextRun(Start); while (nextRun is NoInfoRun && nextRun.Start < owner.Count) { nextRun = owner.GetNextRun(nextRun.Start + 1); } var byteLength = 0; var elementCount = 0; while (Start + byteLength + ElementLength <= nextRun.Start && DataMatchesElementFormat(owner, Start + byteLength, ElementContent, flags, nextRun)) { byteLength += ElementLength; elementCount++; } LengthFromAnchor = string.Empty; ElementCount = Math.Max(1, elementCount); // if the user said there's a format here, then there is, even if the format it wrong. } else if (int.TryParse(length, out int result)) { // fixed length is easy LengthFromAnchor = string.Empty; ElementCount = Math.Max(1, result); } else { LengthFromAnchor = length; ElementCount = Math.Max(1, ParseLengthFromAnchor()); } Length = ElementLength * ElementCount; }