diff --git a/CONTEXT.md b/CONTEXT.md index f872e975..adbf054b 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -86,6 +86,24 @@ bare "session file" when you mean the workspace (that's a **Session**). *Avoid*: "control char rendering" (use **Substitution**), "escape" alone (ambiguous between the **C escape** style and the general concept). +## Filtering + +- **Filter Spread** — Context expansion around a filter hit: **Back Spread** + (`FilterParams.SpreadBefore`) lines before and **Fore Spread** + (`FilterParams.SpreadBehind`) lines after the hit are included in the + filter result, deduplicated against recently emitted lines and clamped + to the file's line range (line 0 is a valid context line). Owned by + `LogExpert.Core.Classes.Filter.FilterSpread` — the single home of the + expansion, history-trim, and rollover-shift rules; the serial filter, + the parallel filter, and Filter Pipes all delegate to it. The maximum + UI-configurable spread (99) and the duplicate-suppression window + (2 × 99) derive from `FilterSpread.SPREAD_MAX`, which bounds the UI + spread knobs (`FilterParams` itself does not clamp its values). + +*Avoid*: "spread" alone when ambiguous (say **Back Spread** / **Fore +Spread**), "additional filter results" (the old internal name — use +**Filter Spread**). + ## Columnizer selection - **Columnizer** (`ILogLineMemoryColumnizer`) — A plugin that parses a log diff --git a/src/LogExpert.Core/Classes/Filter/Filter.cs b/src/LogExpert.Core/Classes/Filter/Filter.cs index 65720a04..f2b611bf 100644 --- a/src/LogExpert.Core/Classes/Filter/Filter.cs +++ b/src/LogExpert.Core/Classes/Filter/Filter.cs @@ -11,7 +11,6 @@ internal class Filter #region Fields private const int PROGRESS_BAR_MODULO = 1000; - private const int SPREAD_MAX = 50; private static readonly Logger _logger = LogManager.GetCurrentClassLogger(); private readonly ColumnizerCallback _callback; @@ -108,66 +107,13 @@ private int DoFilter (FilterParams filterParams, int startLine, int maxCount, Li private void AddFilterLine (int lineNum, FilterParams filterParams, List filterResultLines, List lastFilterLinesList, List filterHitList) { filterHitList.Add(lineNum); - var filterResult = GetAdditionalFilterResults(filterParams, lineNum, lastFilterLinesList); + var filterResult = FilterSpread.Expand(lineNum, filterParams.SpreadBefore, filterParams.SpreadBehind, _callback.GetLineCount(), lastFilterLinesList); filterResultLines.AddRange(filterResult); lastFilterLinesList.AddRange(filterResult); - if (lastFilterLinesList.Count > SPREAD_MAX * 2) - { - lastFilterLinesList.RemoveRange(0, lastFilterLinesList.Count - SPREAD_MAX * 2); - } - } - - /// - /// Returns a list with 'additional filter results'. This is the given line number - /// and (if back spread and/or fore spread is enabled) some additional lines. - /// This function doesn't check the filter condition! - /// - /// - /// - /// - /// - private IList GetAdditionalFilterResults (FilterParams filterParams, int lineNum, IList checkList) - { - IList resultList = []; - - if (filterParams.SpreadBefore == 0 && filterParams.SpreadBehind == 0) - { - resultList.Add(lineNum); - return resultList; - } - - // back spread - for (var i = filterParams.SpreadBefore; i > 0; --i) - { - if (lineNum - i > 0) - { - if (!resultList.Contains(lineNum - i) && !checkList.Contains(lineNum - i)) - { - resultList.Add(lineNum - i); - } - } - } - // direct filter hit - if (!resultList.Contains(lineNum) && !checkList.Contains(lineNum)) - { - resultList.Add(lineNum); - } - // after spread - for (var i = 1; i <= filterParams.SpreadBehind; ++i) - { - if (lineNum + i < _callback.GetLineCount()) - { - if (!resultList.Contains(lineNum + i) && !checkList.Contains(lineNum + i)) - { - resultList.Add(lineNum + i); - } - } - } - - return resultList; + FilterSpread.TrimHistory(lastFilterLinesList); } #endregion diff --git a/src/LogExpert.Core/Classes/Filter/FilterSpread.cs b/src/LogExpert.Core/Classes/Filter/FilterSpread.cs new file mode 100644 index 00000000..8c7d97b1 --- /dev/null +++ b/src/LogExpert.Core/Classes/Filter/FilterSpread.cs @@ -0,0 +1,102 @@ +namespace LogExpert.Core.Classes.Filter; + +/// +/// Owner of the Filter Spread rules: expanding a filter hit into its back/fore spread +/// context lines and trimming the duplicate-suppression history. +/// Pure — no dependency on controls, callbacks, readers or locks. +/// +public static class FilterSpread +{ + /// + /// Maximum configurable spread. The duplicate-suppression history window derives from + /// this value so it always covers the largest configurable spread. + /// + public const int SPREAD_MAX = 99; + + /// + /// Trims the duplicate-suppression history to the last 2 × entries. + /// + public static void TrimHistory (IList history) + { + ArgumentNullException.ThrowIfNull(history); + + while (history.Count > SPREAD_MAX * 2) + { + history.RemoveAt(0); + } + } + + /// + /// Shifts collected line numbers by a multi-file rollover offset. Lines that roll off + /// the start of the virtual file are dropped. + /// + public static List ShiftLines (IEnumerable lines, int offset) + { + ArgumentNullException.ThrowIfNull(lines); + + return [.. lines + .Select(lineNum => lineNum - offset) + .Where(line => line >= 0)]; + } + + /// + /// Rebuilds the duplicate-suppression history after a rollover shift: the last + /// entries of the shifted result lines (all of them when + /// there are fewer). Capping at SPREAD_MAX — not the 2× trim window — preserves the + /// original rollover behavior. + /// + public static List RebuildHistory (List shiftedResultLines) + { + ArgumentNullException.ThrowIfNull(shiftedResultLines); + + var count = Math.Min(SPREAD_MAX, shiftedResultLines.Count); + return shiftedResultLines.GetRange(shiftedResultLines.Count - count, count); + } + + /// + /// Returns the lines a filter hit contributes to the filter result: the hit itself, + /// plus (if configured) back-spread and fore-spread context lines. Lines already present + /// in are suppressed. This function does not evaluate + /// the filter condition. + /// + public static IList Expand (int lineNum, int spreadBefore, int spreadBehind, int lineCount, IList alreadyTaken) + { + ArgumentNullException.ThrowIfNull(alreadyTaken); + + IList resultList = []; + + if (spreadBefore == 0 && spreadBehind == 0) + { + resultList.Add(lineNum); + return resultList; + } + + // back spread (ascending, clamped at line 0 — line 0 is a valid context line) + for (var i = spreadBefore; i > 0; --i) + { + var line = lineNum - i; + if (line >= 0 && !resultList.Contains(line) && !alreadyTaken.Contains(line)) + { + resultList.Add(line); + } + } + + // the hit itself + if (!resultList.Contains(lineNum) && !alreadyTaken.Contains(lineNum)) + { + resultList.Add(lineNum); + } + + // fore spread (ascending, clamped at the last line) + for (var i = 1; i <= spreadBehind; ++i) + { + var line = lineNum + i; + if (line < lineCount && !resultList.Contains(line) && !alreadyTaken.Contains(line)) + { + resultList.Add(line); + } + } + + return resultList; + } +} diff --git a/src/LogExpert.Tests/Filter/FilterSpreadTests.cs b/src/LogExpert.Tests/Filter/FilterSpreadTests.cs new file mode 100644 index 00000000..b2bee626 --- /dev/null +++ b/src/LogExpert.Tests/Filter/FilterSpreadTests.cs @@ -0,0 +1,167 @@ +using System.Collections.ObjectModel; + +using LogExpert.Core.Classes.Filter; + +using NUnit.Framework; + +namespace LogExpert.Tests.Filter; + +[TestFixture] +public class FilterSpreadTests +{ + // Row format: hit line, spread before, spread behind, line count, already-taken history, expected lines. + // Expected values are worked examples from the spec (docs/specs/filter-spread-extraction.md). + [TestCase(5, 0, 0, 100, new int[0], new[] { 5 }, + TestName = "Expand_NoSpreadConfigured_ReturnsOnlyTheHitLine")] + [TestCase(5, 0, 0, 100, new[] { 5 }, new[] { 5 }, + TestName = "Expand_NoSpreadConfigured_IgnoresHistory_HistoricalQuirkPreserved")] + [TestCase(0, 3, 2, 100, new int[0], new[] { 0, 1, 2 }, + TestName = "Expand_HitAtLineZero_EmitsNoNegativeLines")] + [TestCase(3, 5, 0, 100, new int[0], new[] { 0, 1, 2, 3 }, + TestName = "Expand_BackSpreadReachingPastTopOfFile_ClampsAtLineZeroAndIncludesIt")] + [TestCase(97, 0, 5, 100, new int[0], new[] { 97, 98, 99 }, + TestName = "Expand_ForeSpreadReachingPastEndOfFile_ClampsAtLastLine")] + [TestCase(99, 0, 3, 100, new int[0], new[] { 99 }, + TestName = "Expand_HitAtLastLineWithForeSpread_ReturnsOnlyTheHit")] + [TestCase(10, 1, 3, 100, new int[0], new[] { 9, 10, 11, 12, 13 }, + TestName = "Expand_AsymmetricSpread_ReturnsBackLinesHitThenForeLines")] + [TestCase(12, 2, 2, 100, new[] { 8, 9, 10, 11, 12 }, new[] { 13, 14 }, + TestName = "Expand_OverlappingWithEarlierHit_SuppressesLinesAlreadyTaken")] + [TestCase(5, 1, 1, 100, new[] { 5 }, new[] { 4, 6 }, + TestName = "Expand_HitItselfAlreadyTaken_IsNotEmittedAgain")] + public void Expand_Table (int lineNum, int spreadBefore, int spreadBehind, int lineCount, int[] alreadyTaken, int[] expected) + { + var result = FilterSpread.Expand(lineNum, spreadBefore, spreadBehind, lineCount, alreadyTaken); + + Assert.That(result, Is.EqualTo(expected)); + } + + [Test] + public void TrimHistory_LongerThanWindow_DropsOldestEntriesDownToWindowSize () + { + // Window is 2 × 99 (the maximum configurable spread) — unifies the divergent 50/99 constants. + var history = new List(Enumerable.Range(0, 250)); + + FilterSpread.TrimHistory(history); + + Assert.Multiple(() => + { + Assert.That(history, Has.Count.EqualTo(198)); + Assert.That(history[0], Is.EqualTo(52)); + Assert.That(history[^1], Is.EqualTo(249)); + }); + } + + [Test] + public void TrimHistory_WithinWindow_IsUnchanged () + { + var history = new List(Enumerable.Range(0, 198)); + + FilterSpread.TrimHistory(history); + + Assert.That(history, Has.Count.EqualTo(198)); + } + + [Test] + public void TrimHistory_WorksOnAnyIListImplementation () + { + // The Filter Pipe history is an IList, not a List — the trim rule must not care. + IList history = new Collection(); + foreach (var i in Enumerable.Range(0, 200)) + { + history.Add(i); + } + + FilterSpread.TrimHistory(history); + + Assert.Multiple(() => + { + Assert.That(history, Has.Count.EqualTo(198)); + Assert.That(history[0], Is.EqualTo(2)); + }); + } + + [Test] + public void ShiftLines_OnRollover_SubtractsOffsetFromEveryLine () + { + var shifted = FilterSpread.ShiftLines([10, 20, 30], offset: 5); + + Assert.That(shifted, Is.EqualTo(new[] { 5, 15, 25 })); + } + + [Test] + public void ShiftLines_LinesRolledOffTheStart_AreDropped () + { + // Offset 15 rolls lines 10 and 14 off the virtual file; 15 shifts to 0 and survives. + var shifted = FilterSpread.ShiftLines([10, 14, 15, 40], offset: 15); + + Assert.That(shifted, Is.EqualTo(new[] { 0, 25 })); + } + + [Test] + public void RebuildHistory_ResultsLongerThanSpreadMax_TakesTheLastSpreadMaxLines () + { + // The rollover rebuild caps the history at SPREAD_MAX (99), not the 2×99 trim + // window — a preserved quirk of the original rollover code. + var results = new List(Enumerable.Range(0, 150)); + + var history = FilterSpread.RebuildHistory(results); + + Assert.Multiple(() => + { + Assert.That(history, Has.Count.EqualTo(99)); + Assert.That(history[0], Is.EqualTo(51)); + Assert.That(history[^1], Is.EqualTo(149)); + }); + } + + [Test] + public void RebuildHistory_ResultsShorterThanSpreadMax_TakesAllOfThem () + { + var history = FilterSpread.RebuildHistory([3, 4, 5]); + + Assert.That(history, Is.EqualTo(new[] { 3, 4, 5 })); + } + + [Test] + public void RebuildHistory_EmptyResults_ReturnsEmptyHistory () + { + var history = FilterSpread.RebuildHistory([]); + + Assert.That(history, Is.Empty); + } + + /// + /// Equivalence guard: the single-threaded Log Window filter and the multi-threaded + /// Core filter accumulate per-hit results through the same recipe + /// (hit → Expand → append to results/history → TrimHistory). This pins that shared + /// recipe's output for a hit sequence with overlapping spreads; a call site that + /// deviates from the recipe no longer matches this specification. Expected values are + /// a worked example: hits 5, 7 and 50 with spread 2/2 in a 100-line file. + /// + [Test] + public void AccumulationRecipe_OverlappingHitSequence_ProducesPinnedResultHitAndHistoryLists () + { + int[] hits = [5, 7, 50]; + List resultLines = []; + List history = []; + List hitList = []; + + foreach (var hit in hits) + { + hitList.Add(hit); + var expanded = FilterSpread.Expand(hit, spreadBefore: 2, spreadBehind: 2, lineCount: 100, alreadyTaken: history); + resultLines.AddRange(expanded); + history.AddRange(expanded); + FilterSpread.TrimHistory(history); + } + + Assert.Multiple(() => + { + // Hit 5 contributes 3..7; hit 7 contributes only 8,9 (5..7 already taken); hit 50 contributes 48..52. + Assert.That(resultLines, Is.EqualTo(new[] { 3, 4, 5, 6, 7, 8, 9, 48, 49, 50, 51, 52 })); + Assert.That(hitList, Is.EqualTo(new[] { 5, 7, 50 })); + Assert.That(history, Is.EqualTo(resultLines), "history below the window size mirrors the result lines"); + }); + } +} diff --git a/src/LogExpert.UI/Controls/LogWindow/LogWindow.cs b/src/LogExpert.UI/Controls/LogWindow/LogWindow.cs index bead496e..a6dbbf71 100644 --- a/src/LogExpert.UI/Controls/LogWindow/LogWindow.cs +++ b/src/LogExpert.UI/Controls/LogWindow/LogWindow.cs @@ -44,7 +44,6 @@ internal partial class LogWindow : DockContent, ILogPaintContextUI, ILogView, IL { #region Fields - private const int SPREAD_MAX = 99; private const int PROGRESS_BAR_MODULO = 1000; private const int FILTER_ADVANCED_SPLITTER_DISTANCE = 110; private const int FILTER_PANEL2_CONTROL_GAP = 6; @@ -273,10 +272,10 @@ public LogWindow (ILogWindowCoordinator logWindowCoordinator, string fileName, b dataGridView.ColumnDividerDoubleClick += OnDataGridViewColumnDividerDoubleClick; ShowAdvancedFilterPanel(false); knobControlFilterBackSpread.MinValue = 0; - knobControlFilterBackSpread.MaxValue = SPREAD_MAX; + knobControlFilterBackSpread.MaxValue = FilterSpread.SPREAD_MAX; knobControlFilterBackSpread.ValueChanged += OnFilterKnobControlValueChanged; knobControlFilterForeSpread.MinValue = 0; - knobControlFilterForeSpread.MaxValue = SPREAD_MAX; + knobControlFilterForeSpread.MaxValue = FilterSpread.SPREAD_MAX; knobControlFilterForeSpread.ValueChanged += OnFilterKnobControlValueChanged; knobControlFuzzy.MinValue = 0; knobControlFuzzy.MaxValue = 10; @@ -4624,60 +4623,6 @@ private void Filter (FilterParams filterParams, List filterResultLines, Lis StatusLineText(string.Format(CultureInfo.InvariantCulture, Resources.LogWindow_UI_StatusLineText_Filter_FilterDurationMs, endTime - startTime)); } - /// - /// Returns a list with 'additional filter results'. This is the given line number and (if back spread and/or fore - /// spread is enabled) some additional lines. This function doesn't check the filter condition! - /// - /// - /// - /// - /// - private IList GetAdditionalFilterResults (FilterParams filterParams, int lineNum, IList checkList) - { - IList resultList = []; - //string textLine = this.logFileReader.GetLogLine(lineNum); - //ColumnizerCallback callback = new ColumnizerCallback(this); - //callback.LineNum = lineNum; - - if (filterParams.SpreadBefore == 0 && filterParams.SpreadBehind == 0) - { - resultList.Add(lineNum); - return resultList; - } - - // back spread - for (var i = filterParams.SpreadBefore; i > 0; --i) - { - if (lineNum - i > 0) - { - if (!resultList.Contains(lineNum - i) && !checkList.Contains(lineNum - i)) - { - resultList.Add(lineNum - i); - } - } - } - - // direct filter hit - if (!resultList.Contains(lineNum) && !checkList.Contains(lineNum)) - { - resultList.Add(lineNum); - } - - // after spread - for (var i = 1; i <= filterParams.SpreadBehind; ++i) - { - if (lineNum + i < _logFileReader.LineCount) - { - if (!resultList.Contains(lineNum + i) && !checkList.Contains(lineNum + i)) - { - resultList.Add(lineNum + i); - } - } - } - - return resultList; - } - [SupportedOSPlatform("windows")] private void AddFilterLine (int lineNum, bool immediate, FilterParams filterParams, List filterResultLines, List lastFilterLinesList, List filterHitList) { @@ -4685,14 +4630,11 @@ private void AddFilterLine (int lineNum, bool immediate, FilterParams filterPara lock (_filterResultList) { filterHitList.Add(lineNum); - var filterResult = GetAdditionalFilterResults(filterParams, lineNum, lastFilterLinesList); + var filterResult = FilterSpread.Expand(lineNum, filterParams.SpreadBefore, filterParams.SpreadBehind, _logFileReader.LineCount, lastFilterLinesList); filterResultLines.AddRange(filterResult); count = filterResultLines.Count; lastFilterLinesList.AddRange(filterResult); - if (lastFilterLinesList.Count > SPREAD_MAX * 2) - { - lastFilterLinesList.RemoveRange(0, lastFilterLinesList.Count - SPREAD_MAX * 2); - } + FilterSpread.TrimHistory(lastFilterLinesList); } if (immediate) @@ -4890,45 +4832,14 @@ private void ClearBookmarkList () [SupportedOSPlatform("windows")] private void ShiftFilterLines (int offset) { - List newFilterList = []; lock (_filterResultList) { - foreach (var lineNum in _filterResultList) - { - var line = lineNum - offset; - if (line >= 0) - { - newFilterList.Add(line); - } - } - - _filterResultList = newFilterList; - } - - newFilterList = []; - foreach (var lineNum in _filterHitList) - { - var line = lineNum - offset; - if (line >= 0) - { - newFilterList.Add(line); - } - } - - _filterHitList = newFilterList; - - var count = SPREAD_MAX; - if (_filterResultList.Count < SPREAD_MAX) - { - count = _filterResultList.Count; + _filterResultList = FilterSpread.ShiftLines(_filterResultList, offset); } - _lastFilterLinesList = _filterResultList.GetRange(_filterResultList.Count - count, count); + _filterHitList = FilterSpread.ShiftLines(_filterHitList, offset); + _lastFilterLinesList = FilterSpread.RebuildHistory(_filterResultList); - //this.filterGridView.RowCount = this.filterResultList.Count; - //this.filterCountLabel.Text = "" + this.filterResultList.Count; - //this.BeginInvoke(new MethodInvoker(this.filterGridView.Refresh)); - //this.BeginInvoke(new MethodInvoker(AddFilterLineGuiUpdate)); TriggerFilterLineGuiUpdate(); } @@ -5320,16 +5231,13 @@ private void ProcessFilterPipes (int lineNum) //long startTime = Environment.TickCount; if (Util.TestFilterCondition(pipe.FilterParams, searchLine, callback)) { - var filterResult = GetAdditionalFilterResults(pipe.FilterParams, lineNum, pipe.LastLinesHistoryList); + var filterResult = FilterSpread.Expand(lineNum, pipe.FilterParams.SpreadBefore, pipe.FilterParams.SpreadBehind, _logFileReader.LineCount, pipe.LastLinesHistoryList); pipe.OpenFile(); foreach (var line in filterResult) { pipe.LastLinesHistoryList.Add(line); - if (pipe.LastLinesHistoryList.Count > SPREAD_MAX * 2) - { - pipe.LastLinesHistoryList.RemoveAt(0); - } + FilterSpread.TrimHistory(pipe.LastLinesHistoryList); var textLine = _logFileReader.GetLogLineMemory(line); var fileOk = pipe.WriteToPipe(textLine, line); diff --git a/src/PluginRegistry/PluginHashGenerator.Generated.cs b/src/PluginRegistry/PluginHashGenerator.Generated.cs index 5f1cb401..f5d185bb 100644 --- a/src/PluginRegistry/PluginHashGenerator.Generated.cs +++ b/src/PluginRegistry/PluginHashGenerator.Generated.cs @@ -10,7 +10,7 @@ public static partial class PluginValidator { /// /// Gets pre-calculated SHA256 hashes for built-in plugins. - /// Generated: 2026-07-09 13:31:31 UTC + /// Generated: 2026-07-09 17:38:54 UTC /// Configuration: Release /// Plugin count: 21 /// @@ -18,27 +18,27 @@ public static Dictionary GetBuiltInPluginHashes() { return new Dictionary(StringComparer.OrdinalIgnoreCase) { - ["AutoColumnizer.dll"] = "F1F2DC47E77D7D53F36BDF7C49A4B011C1D9C9445D55F8F9076FF0049F800758", + ["AutoColumnizer.dll"] = "CDEFDC033AD707622C49BD4EC937EB9EDE54F8C1EAB19678E29CDC938A91365A", ["BouncyCastle.Cryptography.dll"] = "E5EEAF6D263C493619982FD3638E6135077311D08C961E1FE128F9107D29EBC6", ["BouncyCastle.Cryptography.dll (x86)"] = "E5EEAF6D263C493619982FD3638E6135077311D08C961E1FE128F9107D29EBC6", - ["CsvColumnizer.dll"] = "AE76D824E4FA7C22430D44E2908F6280DCF6EEF60421AC941F0629CD3C2EFA1B", - ["CsvColumnizer.dll (x86)"] = "AE76D824E4FA7C22430D44E2908F6280DCF6EEF60421AC941F0629CD3C2EFA1B", - ["DefaultPlugins.dll"] = "13A1110029941B0AD680C9A612E8915E0B5C41867E46271E6B27E4FEA4A99567", - ["FlashIconHighlighter.dll"] = "F572E5ECD91CD0C06801C14B1B80BE1084BA227EFCC75498E5DC2E7D85A070B6", - ["GlassfishColumnizer.dll"] = "78ED9C9C15FC2B882BE0090C9925206D3F5E421CE3E81902EF8BA3188466E94F", - ["JsonColumnizer.dll"] = "ED065D921ACE1C5E46933B5BD0B7CBFEF70D3D06F06A760C985CF9D6748B547B", - ["JsonCompactColumnizer.dll"] = "246CB3F50CCE4A0812050C905D773F5FFA98C853448FDEC2EF9238F81DEB14B1", - ["Log4jXmlColumnizer.dll"] = "B1FA5347C1CD897B579FE05C6ADD41CCEE580328063CB32F99D2184F94D36D48", - ["LogExpert.Resources.dll"] = "6D842E513DC8616900AF1458F8799D5E749B251CD281FB3E14C0C225EE48D040", + ["CsvColumnizer.dll"] = "4EBBA85D9A259D82342E605E4381CD242A9212186711036E93AFD8DADDA23486", + ["CsvColumnizer.dll (x86)"] = "4EBBA85D9A259D82342E605E4381CD242A9212186711036E93AFD8DADDA23486", + ["DefaultPlugins.dll"] = "CB04A2942034D9FF2506379A81D04A438E9C3F55225C28CE4249CB2639724F83", + ["FlashIconHighlighter.dll"] = "A3DC19BF3D3E660325E54B6ABBEB38A81351DC269CD2C292C7038A0181250B39", + ["GlassfishColumnizer.dll"] = "55FF72EC8D78E1E33EECE105C625BEEE633A1AB114A9A8F6EF3D7F489770F2BE", + ["JsonColumnizer.dll"] = "EAF4FE2D20542C7747807083088B222CCE201AF8E9B2287255D0CF76EB52C114", + ["JsonCompactColumnizer.dll"] = "0F8A8BF3FF85F063D2B219A27F11598954A4A4679C2A972D303836D12E8D7C8E", + ["Log4jXmlColumnizer.dll"] = "113BD3CACB5D0376BD74DBE98A51E0A4F60405CC0BF10987097234162A3275F0", + ["LogExpert.Resources.dll"] = "4F151AC6D75E0844E5B4EFEDE591252EE9C998BF61CB9B715E2A6DB7CEA0DBB3", ["Microsoft.Extensions.DependencyInjection.Abstractions.dll"] = "67FA4325000DB017DC0C35829B416F024F042D24EFB868BCF17A895EE6500A93", ["Microsoft.Extensions.DependencyInjection.Abstractions.dll (x86)"] = "67FA4325000DB017DC0C35829B416F024F042D24EFB868BCF17A895EE6500A93", ["Microsoft.Extensions.Logging.Abstractions.dll"] = "BB853130F5AFAF335BE7858D661F8212EC653835100F5A4E3AA2C66A4D4F685D", ["Microsoft.Extensions.Logging.Abstractions.dll (x86)"] = "BB853130F5AFAF335BE7858D661F8212EC653835100F5A4E3AA2C66A4D4F685D", - ["RegexColumnizer.dll"] = "4DC5B66DCD8B722C35DF01AB07388D5723D0C4EC2A8D8860C28ACCE127588981", - ["SftpFileSystem.dll"] = "10A371D93A95C985270DDC912BC01AF2BE8D7AAA7705C6622EB2AF96FD3AB17B", - ["SftpFileSystem.dll (x86)"] = "70F6B8AA1FD98F3BB62D3B8A5D3000E2840F6F783693215083D6E014B0ED8D33", - ["SftpFileSystem.Resources.dll"] = "45A3D1C54734819BAD2CFE2245CE2CEE664B9E6401E9F1BEF4B2955BEF0C307F", - ["SftpFileSystem.Resources.dll (x86)"] = "45A3D1C54734819BAD2CFE2245CE2CEE664B9E6401E9F1BEF4B2955BEF0C307F", + ["RegexColumnizer.dll"] = "ADAF0EA008A488EB108C6B4C8562EBA96E65A6EAD58ED351DA696C8F7DA02B86", + ["SftpFileSystem.dll"] = "712F494AA8627BC4D6FA5EE729F5E225BCB61DAE3483D57C8BFD71CBC54841B4", + ["SftpFileSystem.dll (x86)"] = "F37E477D24B41C506CEE321423E1D5E1EEC731E04E9FD7378ADD695736CE62CC", + ["SftpFileSystem.Resources.dll"] = "3BD8924373C41D64FCB8AD7A6EE9B4E8FCF5BE311A7E4222A75FECF6567D6BEE", + ["SftpFileSystem.Resources.dll (x86)"] = "3BD8924373C41D64FCB8AD7A6EE9B4E8FCF5BE311A7E4222A75FECF6567D6BEE", }; }