diff --git a/src/CsvColumnizer/CsvColumnizer.cs b/src/CsvColumnizer/CsvColumnizer.cs index 2f2d4649..1453beda 100644 --- a/src/CsvColumnizer/CsvColumnizer.cs +++ b/src/CsvColumnizer/CsvColumnizer.cs @@ -177,7 +177,7 @@ public void Selected (ILogLineMemoryColumnizerCallback callback) { _columnList.Clear(); var line = _config.HasFieldNames - ? _firstLine + ? _firstLine ?? callback.GetLogLineMemory(0) : callback.GetLogLineMemory(0); if (line != null) @@ -205,6 +205,10 @@ public void Selected (ILogLineMemoryColumnizerCallback callback) } } } + else + { + _columnList.Add(new CsvColumn("Text")); + } } } @@ -290,28 +294,40 @@ public Priority GetPriority (string fileName, IEnumerable sample private ColumnizedLogLine SplitCsvLine (ILogLineMemory line) { + if (line.FullLine.IsEmpty) + { + return CreateColumnizedLogLine(line); + } + ColumnizedLogLine cLogLine = new() { LogLine = line }; - using CsvReader csv = new(new StringReader(line.FullLine.ToString()), _config.ReaderConfiguration); - _ = csv.Read(); - _ = csv.ReadHeader(); - - //we only read line by line and not the whole file so it is always the header - var records = csv.HeaderRecord; - - if (records != null) + try { - List columns = []; + using CsvReader csv = new(new StringReader(line.FullLine.ToString()), _config.ReaderConfiguration); + _ = csv.Read(); + _ = csv.ReadHeader(); + + //we only read line by line and not the whole file so it is always the header + var records = csv.HeaderRecord; - foreach (var record in records) + if (records != null) { - columns.Add(new Column { FullValue = record.AsMemory(), Parent = cLogLine }); - } + List columns = []; + + foreach (var record in records) + { + columns.Add(new Column { FullValue = record.AsMemory(), Parent = cLogLine }); + } - cLogLine.ColumnValues = [.. columns.Select(a => a as IColumnMemory)]; + cLogLine.ColumnValues = [.. columns.Select(a => a as IColumnMemory)]; + } + } + catch (CsvHelperException) + { + return CreateColumnizedLogLine(line); } return cLogLine; diff --git a/src/LogExpert.Benchmarks/CsvColumnizerBenchmarks.cs b/src/LogExpert.Benchmarks/CsvColumnizerBenchmarks.cs new file mode 100644 index 00000000..b8248f58 --- /dev/null +++ b/src/LogExpert.Benchmarks/CsvColumnizerBenchmarks.cs @@ -0,0 +1,113 @@ +using System.Text; + +using BenchmarkDotNet.Attributes; + +using ColumnizerLib; + +using CsvColumnizer; + +using Moq; + +namespace LogExpert.Benchmarks; + +/// +/// Benchmarks for CsvColumnizer covering PreProcessLine, Selected, and SplitLine operations +/// across varying line counts and column widths. +/// +[MemoryDiagnoser] +[RankColumn] +public class CsvColumnizerBenchmarks +{ + private ILogLineMemory[] _dataLines = null!; + private CsvColumnizer.CsvColumnizer _columnizer = null!; + + [Params(100, 1_000, 10_000)] + public int LineCount { get; set; } + + [Params(5, 15)] + public int ColumnCount { get; set; } + + [GlobalSetup] + public void Setup () + { + // Build header and data lines + var headerParts = new string[ColumnCount]; + for (var i = 0; i < ColumnCount; i++) + { + headerParts[i] = $"Column{i}"; + } + + var header = string.Join(";", headerParts); + + // Initialize columnizer with header + _columnizer = new CsvColumnizer.CsvColumnizer(); + _columnizer.PreProcessLine(header.AsMemory(), 0, 0); + + var mockCallback = new Mock(); + _columnizer.Selected(mockCallback.Object); + + // Generate data lines + _dataLines = new ILogLineMemory[LineCount]; + var random = new Random(42); + + for (var i = 0; i < LineCount; i++) + { + var parts = new string[ColumnCount]; + for (var j = 0; j < ColumnCount; j++) + { + parts[j] = GenerateFieldValue(random, j); + } + + _dataLines[i] = new CsvLogLine(string.Join(";", parts), i + 1); + } + } + + [Benchmark(Description = "SplitLine: parse all lines")] + public int SplitAllLines () + { + var totalColumns = 0; + for (var i = 0; i < _dataLines.Length; i++) + { + var result = _columnizer.SplitLine(null, _dataLines[i]); + totalColumns += result.ColumnValues.Length; + } + + return totalColumns; + } + + [Benchmark(Description = "PreProcessLine: preprocess all lines")] + public int PreProcessAllLines () + { + var processed = 0; + for (var i = 0; i < _dataLines.Length; i++) + { + var result = _columnizer.PreProcessLine(_dataLines[i].FullLine, i + 1, i + 1); + if (!result.IsEmpty) + { + processed++; + } + } + + return processed; + } + + [Benchmark(Description = "Selected: re-detect columns from header")] + public int RedetectColumns () + { + var mockCallback = new Mock(); + _columnizer.Selected(mockCallback.Object); + return _columnizer.GetColumnCount(); + } + + private static string GenerateFieldValue (Random random, int columnIndex) + { + // Mix of value types: numbers, short text, quoted text with commas + return (columnIndex % 4) switch + { + 0 => random.Next(1, 100000).ToString(), + 1 => $"text_{random.Next(1, 9999)}", + 2 => $"\"Value, with quotes {random.Next(1, 999)}\"", + _ => new string((char)('A' + random.Next(0, 26)), random.Next(5, 20)), + }; + } +} diff --git a/src/LogExpert.Benchmarks/LogExpert.Benchmarks.csproj b/src/LogExpert.Benchmarks/LogExpert.Benchmarks.csproj index 4bfb4225..56fe504a 100644 --- a/src/LogExpert.Benchmarks/LogExpert.Benchmarks.csproj +++ b/src/LogExpert.Benchmarks/LogExpert.Benchmarks.csproj @@ -2,7 +2,9 @@ Exe - net10.0 + net10.0-windows + true + true enable enable @@ -12,11 +14,14 @@ + + + diff --git a/src/LogExpert.Benchmarks/Program.cs b/src/LogExpert.Benchmarks/Program.cs index 01954f88..b29c9171 100644 --- a/src/LogExpert.Benchmarks/Program.cs +++ b/src/LogExpert.Benchmarks/Program.cs @@ -16,6 +16,7 @@ public static void Main (string[] args) _ = BenchmarkRunner.Run(); _ = BenchmarkRunner.Run(); _ = BenchmarkRunner.Run(); + _ = BenchmarkRunner.Run(); } else { @@ -28,6 +29,7 @@ public static void Main (string[] args) Console.WriteLine("ReadThroughputBenchmarks: Benchmarks for read throughput"); Console.WriteLine("BufferIndexBenchmarks: Benchmarks for buffer index"); Console.WriteLine("BufferIndexContentionBenchmarks: Benchmarks for buffer index contention"); + Console.WriteLine("CsvColumnizerBenchmarks: Benchmarks for CSV columnizer (SplitLine, PreProcess, Selected)"); Console.WriteLine("Dry run:"); Console.WriteLine("dotnet run -c Release -- --filter \"**\" --job Dry --noOverwrite"); Console.WriteLine("Short run:"); diff --git a/src/LogExpert.Core/Classes/Log/Streamreaders/PositionAwareStreamReaderLegacy.cs b/src/LogExpert.Core/Classes/Log/Streamreaders/PositionAwareStreamReaderLegacy.cs index 033de390..207ae31e 100644 --- a/src/LogExpert.Core/Classes/Log/Streamreaders/PositionAwareStreamReaderLegacy.cs +++ b/src/LogExpert.Core/Classes/Log/Streamreaders/PositionAwareStreamReaderLegacy.cs @@ -1,8 +1,10 @@ +using LogExpert.Core.Classes.Log.Buffers; using LogExpert.Core.Entities; +using LogExpert.Core.Interfaces; namespace LogExpert.Core.Classes.Log.Streamreaders; -public class PositionAwareStreamReaderLegacy (Stream stream, EncodingOptions encodingOptions, int maximumLineLength) : PositionAwareStreamReaderBase(stream, encodingOptions, maximumLineLength) +public class PositionAwareStreamReaderLegacy (Stream stream, EncodingOptions encodingOptions, int maximumLineLength) : PositionAwareStreamReaderBase(stream, encodingOptions, maximumLineLength), ILogStreamReaderMemory { #region Fields @@ -15,8 +17,39 @@ public class PositionAwareStreamReaderLegacy (Stream stream, EncodingOptions enc #endregion + #region Properties + + public CharBlockAllocator BlockAllocator + { + get => field ??= new CharBlockAllocator(); + private set; + } + + #endregion + #region Public methods + public bool TryReadLine (out ReadOnlyMemory lineMemory) + { + var line = ReadLine(); + + if (line is null) + { + lineMemory = default; + return false; + } + + var target = BlockAllocator.Rent(line.Length); + line.AsSpan().CopyTo(target.Span); + lineMemory = target; + return true; + } + + public void ReturnMemory (ReadOnlyMemory memory) + { + // Bulk return via BlockAllocator.DetachBlocks() when the LogBuffer is evicted. + } + public override string ReadLine () { int readInt; diff --git a/src/LogExpert.Tests/ColumnizerTests/CSVColumnizerTest.cs b/src/LogExpert.Tests/ColumnizerTests/CSVColumnizerTest.cs index 35445f9b..f1d44817 100644 --- a/src/LogExpert.Tests/ColumnizerTests/CSVColumnizerTest.cs +++ b/src/LogExpert.Tests/ColumnizerTests/CSVColumnizerTest.cs @@ -2,10 +2,14 @@ using ColumnizerLib; +using CsvColumnizer; + using LogExpert.Core.Classes.Log; using LogExpert.Core.Entities; using LogExpert.Core.Enums; +using Moq; + using NUnit.Framework; namespace LogExpert.Tests.ColumnizerTests; @@ -46,10 +50,120 @@ private static void ResetPluginRegistrySingleton () instanceField?.SetValue(null, null); } + /// + /// Sets a private field on the CsvColumnizer via reflection. + /// Used to reach edge-case states (e.g. _isValidCsv=true with _firstLine=null) + /// that are not reachable through the public API alone. + /// + private static void SetPrivateField (CsvColumnizer.CsvColumnizer columnizer, string fieldName, object? value) + { + var field = typeof(CsvColumnizer.CsvColumnizer).GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic); + Assert.That(field, Is.Not.Null, $"Field '{fieldName}' not found on CsvColumnizer"); + field!.SetValue(columnizer, value); + } + + [Test] + public void Selected_HasFieldNames_FirstLineNull_FallsBackToCallback () + { + // Arrange: _isValidCsv=true but _firstLine=null (edge case reachable via legacy adapter) + CsvColumnizer.CsvColumnizer columnizer = new(); + SetPrivateField(columnizer, "_isValidCsv", true); + + var callbackLine = new CsvLogLine("name;age;city", 0); + var mockCallback = new Mock(); + _ = mockCallback.Setup(c => c.GetLogLineMemory(0)).Returns(callbackLine); + + // Act + columnizer.Selected(mockCallback.Object); + + // Assert: columns detected from callback line + Assert.That(columnizer.GetColumnCount(), Is.EqualTo(3)); + Assert.That(columnizer.GetColumnNames(), Is.EqualTo(["name", "age", "city"])); + } + + [Test] + public void Selected_HasFieldNames_NoLineAvailable_FallsBackToTextColumn () + { + // Arrange: _isValidCsv=true, _firstLine=null, callback returns null + CsvColumnizer.CsvColumnizer columnizer = new(); + SetPrivateField(columnizer, "_isValidCsv", true); + + var mockCallback = new Mock(); + _ = mockCallback.Setup(c => c.GetLogLineMemory(0)).Returns((ILogLineMemory?)null); + + // Act + columnizer.Selected(mockCallback.Object); + + // Assert: graceful fallback to single "Text" column + Assert.That(columnizer.GetColumnCount(), Is.EqualTo(1)); + Assert.That(columnizer.GetColumnNames(), Is.EqualTo(["Text"])); + } + + [Test] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Globalization", "CA1303:Do not pass literals as localized parameters", Justification = "Unit Tests")] + public void SplitLine_EmptyFullLine_ReturnsSingleColumn () + { + // Arrange: columnizer in valid-CSV mode with columns established + CsvColumnizer.CsvColumnizer columnizer = new(); + _ = columnizer.PreProcessLine("a;b;c".AsMemory(), 0, 0); + + var mockCallback = new Mock(); + columnizer.Selected(mockCallback.Object); + + // Act: split a line with empty FullLine + var emptyLine = new CsvLogLine(ReadOnlyMemory.Empty, 1); + var result = columnizer.SplitLine(null, emptyLine); + + // Assert: single column with empty content, no crash + Assert.That(result.ColumnValues, Has.Length.EqualTo(1)); + } + + [Test] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Globalization", "CA1303:Do not pass literals as localized parameters", Justification = "Unit Tests")] + public void SplitLine_ValidCsvLine_ReturnsCorrectColumns () + { + // Arrange: columnizer in valid-CSV mode + CsvColumnizer.CsvColumnizer columnizer = new(); + _ = columnizer.PreProcessLine("name;age;city".AsMemory(), 0, 0); + + var mockCallback = new Mock(); + columnizer.Selected(mockCallback.Object); + + // Act: split a normal data line + var dataLine = new CsvLogLine("Alice;30;London", 1); + var result = columnizer.SplitLine(null, dataLine); + + // Assert: three columns with correct values + Assert.That(result.ColumnValues, Has.Length.EqualTo(3)); + Assert.That(result.ColumnValues[0].FullValue.ToString(), Is.EqualTo("Alice")); + Assert.That(result.ColumnValues[1].FullValue.ToString(), Is.EqualTo("30")); + Assert.That(result.ColumnValues[2].FullValue.ToString(), Is.EqualTo("London")); + } + + [Test] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Globalization", "CA1303:Do not pass literals as localized parameters", Justification = "Unit Tests")] + public void SplitLine_BadCsvData_ReturnsSingleColumnInsteadOfCrash () + { + // Arrange: columnizer with semicolon delimiter (default) but line uses commas with quoted fields + // This triggers CsvHelper.BadDataException because quotes appear mid-field in semicolon mode + CsvColumnizer.CsvColumnizer columnizer = new(); + _ = columnizer.PreProcessLine("header1;header2;header3".AsMemory(), 0, 0); + + var mockCallback = new Mock(); + columnizer.Selected(mockCallback.Object); + + // Act: line with comma-separated data containing quoted fields — bad data for semicolon delimiter + var badLine = new CsvLogLine("6,6774DC1dB00BD11,\"Farmer, Edwards and Andrade\",http://wolfe-boyd.com/,Norfolk Island,Virtual leadingedge benchmark,2003,Mental Health Care,3503", 1); + var result = columnizer.SplitLine(null, badLine); + + // Assert: returns single-column fallback, no crash + Assert.That(result.ColumnValues, Has.Length.EqualTo(1)); + Assert.That(result.ColumnValues[0].FullValue.ToString(), Does.Contain("6774DC1dB00BD11")); + } + [TestCase(@".\TestData\organizations-10000.csv", new[] { "Index", "Organization Id", "Name", "Website", "Country", "Description", "Founded", "Industry", "Number of employees" }, ReaderType.System)] [TestCase(@".\TestData\organizations-1000.csv", new[] { "Index", "Organization Id", "Name", "Website", "Country", "Description", "Founded", "Industry", "Number of employees" }, ReaderType.System)] [TestCase(@".\TestData\people-10000.csv", new[] { "Index", "User Id", "First Name", "Last Name", "Sex", "Email", "Phone", "Date of birth", "Job Title" }, ReaderType.System)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "Unit Test")] public void Instantiat_CSVFile_BuildCorrectColumnizer (string filename, string[] expectedHeaders, ReaderType readerType) { CsvColumnizer.CsvColumnizer csvColumnizer = new(); diff --git a/src/LogExpert.Tests/StreamReaderTests/LogStreamReaderTest.cs b/src/LogExpert.Tests/StreamReaderTests/LogStreamReaderTest.cs index d1203189..dcbd8a06 100644 --- a/src/LogExpert.Tests/StreamReaderTests/LogStreamReaderTest.cs +++ b/src/LogExpert.Tests/StreamReaderTests/LogStreamReaderTest.cs @@ -2,6 +2,7 @@ using LogExpert.Core.Classes.Log.Streamreaders; using LogExpert.Core.Entities; +using LogExpert.Core.Interfaces; using NUnit.Framework; @@ -419,4 +420,26 @@ public void TryReadLine_UTF8_MultiByteCharacters () Assert.That(reader.TryReadLine(out var line2), Is.True); Assert.That(line2.Span.ToString(), Is.EqualTo("Line 2")); } + + [Test] + [TestCase("Line 1\nLine 2\nLine 3", 3)] + [TestCase("Line 1\r\nLine 2\r\nLine 3", 3)] + [TestCase("Line 1\rLine 2\rLine 3", 3)] + public void TryReadLine_LegacyReader_ReadsAllLines (string text, int expectedLines) + { + using var stream = new MemoryStream(Encoding.ASCII.GetBytes(text)); + using var reader = new PositionAwareStreamReaderLegacy(stream, new EncodingOptions(), 500); + + var memoryReader = reader as ILogStreamReaderMemory; + Assert.That(memoryReader, Is.Not.Null, "Legacy reader must implement ILogStreamReaderMemory"); + + var lineCount = 0; + while (memoryReader!.TryReadLine(out var lineMemory)) + { + lineCount++; + Assert.That(lineMemory.Span.ToString(), Does.StartWith($"Line {lineCount}")); + } + + Assert.That(lineCount, Is.EqualTo(expectedLines)); + } } diff --git a/src/LogExpert.Tests/TestData/comma.csv b/src/LogExpert.Tests/TestData/comma.csv new file mode 100644 index 00000000..4a6aeafc --- /dev/null +++ b/src/LogExpert.Tests/TestData/comma.csv @@ -0,0 +1,2 @@ +"Date","Level","Message" +"2021-01-01","Error","comma file" \ No newline at end of file diff --git a/src/LogExpert.Tests/TestData/semicolon.csv b/src/LogExpert.Tests/TestData/semicolon.csv new file mode 100644 index 00000000..acf009c7 --- /dev/null +++ b/src/LogExpert.Tests/TestData/semicolon.csv @@ -0,0 +1,2 @@ +"Date";"Level";"Message" +"2021-12-12";"TRACE";"semicolon file " \ No newline at end of file diff --git a/src/LogExpert.UI/Controls/LogWindow/LogWindow.cs b/src/LogExpert.UI/Controls/LogWindow/LogWindow.cs index 18cb1030..dc35160e 100644 --- a/src/LogExpert.UI/Controls/LogWindow/LogWindow.cs +++ b/src/LogExpert.UI/Controls/LogWindow/LogWindow.cs @@ -3430,7 +3430,10 @@ private void SetColumnizerInternal (ILogLineMemoryColumnizer columnizer) _ = columnComboBox.Items.Add(columnName); } - columnComboBox.SelectedIndex = 0; + if (columnComboBox.Items.Count > 0) + { + columnComboBox.SelectedIndex = 0; + } OnColumnizerChanged(CurrentColumnizer); } diff --git a/src/PluginRegistry/PluginHashGenerator.Generated.cs b/src/PluginRegistry/PluginHashGenerator.Generated.cs index f128744a..7e995296 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-05-08 08:30:03 UTC + /// Generated: 2026-05-11 11:06:48 UTC /// Configuration: Release /// Plugin count: 22 /// @@ -18,28 +18,28 @@ public static Dictionary GetBuiltInPluginHashes() { return new Dictionary(StringComparer.OrdinalIgnoreCase) { - ["AutoColumnizer.dll"] = "9FA8AFCC6679D1CC3395C6C5C197725A174F80778A10C988F6F90F9B3E8CE649", + ["AutoColumnizer.dll"] = "18CCC14A0B0DF8EC3389BFEF8B6B83CDD0361C115506006B5B9D861A3D46DBF4", ["BouncyCastle.Cryptography.dll"] = "E5EEAF6D263C493619982FD3638E6135077311D08C961E1FE128F9107D29EBC6", ["BouncyCastle.Cryptography.dll (x86)"] = "E5EEAF6D263C493619982FD3638E6135077311D08C961E1FE128F9107D29EBC6", - ["CsvColumnizer.dll"] = "1494E79A0F48D2CDB400A71FFDBA87F223FEE93E3AD816739713168F3939759F", - ["CsvColumnizer.dll (x86)"] = "1494E79A0F48D2CDB400A71FFDBA87F223FEE93E3AD816739713168F3939759F", - ["DefaultPlugins.dll"] = "CB38E02AEABE3EA0795581A00CB13C0C1E4764E53E2F7A68A474EE082413AC3F", - ["FlashIconHighlighter.dll"] = "F30A3117FA542614C95F10C96795F2C9E2636D0BBDF6BB9603F17D055D8317DC", - ["GlassfishColumnizer.dll"] = "EA018635FA1CBD6DBD633AD1B1371B6221228236C4C39E8197297B184E8BB769", - ["JsonColumnizer.dll"] = "15F1B87051B1619A27AC604F67322B749C06AD58C51EA858B836499EB76FBC41", - ["JsonCompactColumnizer.dll"] = "A6B3296F31358A4BCBE678774570EC558E670C9A49F17253D61BED382451AA1F", - ["Log4jXmlColumnizer.dll"] = "F207F5C69E2B9306D140627BC182D3AE38EF21FA608D3E2A1D6A5E593472A745", - ["LogExpert.Core.dll"] = "605770F65650833D6BE95394358A36CC484C8C17236DDDFFA7B796A5AE7AB1B1", - ["LogExpert.Resources.dll"] = "C15873C7D384807D1A36E216E6EBF107BC85138E25ABF92EBA65E2523B0516D4", + ["CsvColumnizer.dll"] = "BF91C68897731C91F78C9BF5E12921541DA3830C5D0A51DE39B86CC54C547218", + ["CsvColumnizer.dll (x86)"] = "BF91C68897731C91F78C9BF5E12921541DA3830C5D0A51DE39B86CC54C547218", + ["DefaultPlugins.dll"] = "6CA00475DCC3CA3A305AD9F42E700D8FED57BAE3A97B15974F7DB34963E7074B", + ["FlashIconHighlighter.dll"] = "F973F35FF8374A2598F63EFB72821B387214EEB64094469B922D7FC23EAE2822", + ["GlassfishColumnizer.dll"] = "02228691B9C2C3595C45710B3870BEFFD60071A3653099F80514040D2F52FC2F", + ["JsonColumnizer.dll"] = "CF85A552AA2AA7B7EA483477E405ED36EAA7E8B0B654AB782804DFD30EF39356", + ["JsonCompactColumnizer.dll"] = "F83694079903B2A31989BEFE82F4AAE02A507E5C8F52A4BCE89AA3773A948844", + ["Log4jXmlColumnizer.dll"] = "8E4BE48F8C97747B231C031B3378F91C676B1C6350ADA4F3C457D5A9AB23E342", + ["LogExpert.Core.dll"] = "E90A83EF7032BAA2C2B28745F6DE34E098F900B4A35C47ED0C9C2B922456ADEA", + ["LogExpert.Resources.dll"] = "172F5D8D56262F5842E35B5C94B755F70C0AC0138A04992ECF8BB0A0BA079830", ["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"] = "FEE79755C0E0A9A9A1D11F48C43AA99E37708999D0BA6E339A8524BEB4FD90B5", - ["SftpFileSystem.dll"] = "D5297A77B0A5750216EB89B4C84B3F3C74B6A2DC414183E73159F208377C5542", - ["SftpFileSystem.dll (x86)"] = "03B8E6EF231DB0815C453DAEFBDF2165EE4B5450125C2CAD8AE56A6BC5460DFD", - ["SftpFileSystem.Resources.dll"] = "25DEAF1362B2186D0FCD76F129BEA6DBB2C36B0B542D46A206443061EA724378", - ["SftpFileSystem.Resources.dll (x86)"] = "25DEAF1362B2186D0FCD76F129BEA6DBB2C36B0B542D46A206443061EA724378", + ["RegexColumnizer.dll"] = "C038DAB2B98EEBC6C30A1C248E954727BF4D865D57CF7F5ACE071F8F1BD4F101", + ["SftpFileSystem.dll"] = "4268F380611E6B9A41237DE21134866BCD294453C7CBA222A684AF0391B32F10", + ["SftpFileSystem.dll (x86)"] = "28F9A111D923130B749C7180A79E86BE20488AB1566F4E366CD4F628B07F30DA", + ["SftpFileSystem.Resources.dll"] = "4764889B4A7F81D60B144704F95E04FE75CC3EA9FC2EA8C6DC1E8376D84B1501", + ["SftpFileSystem.Resources.dll (x86)"] = "4764889B4A7F81D60B144704F95E04FE75CC3EA9FC2EA8C6DC1E8376D84B1501", }; }