From 8659300bdd87e0d6c029f0e7fc23740ff009d7cb Mon Sep 17 00:00:00 2001 From: BRUNER Patrick Date: Thu, 25 Jun 2026 15:23:01 +0200 Subject: [PATCH 1/5] Lift char-block detachment onto the reader interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The LogfileReader read loop type-checked `reader is …System/…Direct` at three sites (six branches) to call DetachBlocks(), reaching through the ILogStreamReaderMemory seam to the concrete readers. Adding a reader meant editing every site, and the Legacy reader was silently skipped — its pooling allocator was never detached and never disposed, orphaning its rented blocks (a latent ArrayPool leak). Add DetachCharBlocks() to ILogStreamReaderMemory so the loop calls it unconditionally: - Direct: rename DetachBlocks() -> DetachCharBlocks(); drop the dead `BlockAllocator => null` shim. - System/Legacy: implement DetachCharBlocks() over their allocator and make BlockAllocator private — detachment is the only seam-crossing surface now. - Legacy: add the Dispose override System already had, closing the leak. - LogfileReader: six `is`-branches collapse to one DetachCharBlocks() call per site. Legacy now participates uniformly. Tests migrated off reader.BlockAllocator onto the public DetachCharBlocks() seam. --- .../Classes/Log/LogfileReader.cs | 29 +++---------------- .../PositionAwareStreamReaderDirect.cs | 9 +----- .../PositionAwareStreamReaderLegacy.cs | 19 ++++++++++-- .../PositionAwareStreamReaderSystem.cs | 12 ++++---- .../Interfaces/ILogStreamReaderMemory.cs | 15 ++++++++++ .../StreamReaderTests/LogStreamReaderTest.cs | 13 ++++----- .../PositionAwareStreamReaderDirectTests.cs | 20 ++++++------- 7 files changed, 58 insertions(+), 59 deletions(-) diff --git a/src/LogExpert.Core/Classes/Log/LogfileReader.cs b/src/LogExpert.Core/Classes/Log/LogfileReader.cs index 98f5491e..cd0746ef 100644 --- a/src/LogExpert.Core/Classes/Log/LogfileReader.cs +++ b/src/LogExpert.Core/Classes/Log/LogfileReader.cs @@ -1256,16 +1256,9 @@ private void ReadToBufferList (ILogFileInfo logFileInfo, long filePos, int start logBuffer.Size = filePos - logBuffer.StartPos; - // Detach char blocks from the reader's allocator and attach to the completed buffer. + // Detach char blocks from the reader and attach to the completed buffer. // Must happen before Monitor.Exit so the buffer is still exclusively owned. - if (reader is PositionAwareStreamReaderSystem systemDetachBlockReader) - { - logBuffer.AttachCharBlocks(systemDetachBlockReader.BlockAllocator.DetachBlocks()); - } - else if (reader is PositionAwareStreamReaderDirect directReader) - { - logBuffer.AttachCharBlocks(directReader.DetachBlocks()); - } + logBuffer.AttachCharBlocks(reader.DetachCharBlocks()); Monitor.Exit(logBuffer); try @@ -1297,14 +1290,7 @@ private void ReadToBufferList (ILogFileInfo logFileInfo, long filePos, int start logBuffer.Size = filePos - logBuffer.StartPos; // Attach remaining blocks to the final buffer - if (reader is PositionAwareStreamReaderSystem systemDetachBlockReader2) - { - logBuffer.AttachCharBlocks(systemDetachBlockReader2.BlockAllocator.DetachBlocks()); - } - else if (reader is PositionAwareStreamReaderDirect directReader) - { - logBuffer.AttachCharBlocks(directReader.DetachBlocks()); - } + logBuffer.AttachCharBlocks(reader.DetachCharBlocks()); } finally { @@ -1427,14 +1413,7 @@ private void ReReadBuffer (LogBuffer logBuffer) } // Attach char blocks from the reader to the re-read buffer - if (reader is PositionAwareStreamReaderSystem systemReader) - { - logBuffer.AttachCharBlocks(systemReader.BlockAllocator.DetachBlocks()); - } - else if (reader is PositionAwareStreamReaderDirect directReader) - { - logBuffer.AttachCharBlocks(directReader.DetachBlocks()); - } + logBuffer.AttachCharBlocks(reader.DetachCharBlocks()); if (maxLinesCount != logBuffer.LineCount) { diff --git a/src/LogExpert.Core/Classes/Log/Streamreaders/PositionAwareStreamReaderDirect.cs b/src/LogExpert.Core/Classes/Log/Streamreaders/PositionAwareStreamReaderDirect.cs index e3282c88..4dde4b76 100644 --- a/src/LogExpert.Core/Classes/Log/Streamreaders/PositionAwareStreamReaderDirect.cs +++ b/src/LogExpert.Core/Classes/Log/Streamreaders/PositionAwareStreamReaderDirect.cs @@ -1,7 +1,6 @@ using System.Buffers; using System.Text; -using LogExpert.Core.Classes.Log.Buffers; using LogExpert.Core.Entities; using LogExpert.Core.Interfaces; @@ -135,17 +134,11 @@ public void ReturnMemory (ReadOnlyMemory memory) // Bulk return via DetachBlocks()/Dispose(). Individual return not needed. } - /// - /// Gets the block allocator for compatibility with the DetachBlocks pattern. - /// This reader manages its own blocks directly rather than through CharBlockAllocator. - /// - public CharBlockAllocator? BlockAllocator => null; - /// /// Detaches completed blocks (fully scanned) for transfer to the LogBuffer. /// The current _readBlock (partially scanned) stays with the reader. /// - public List DetachBlocks () + public List DetachCharBlocks () { // Nothing to detach: no completed blocks and no lines were scanned from the current block. if (_completedBlocks.Count == 0 && _scanOffset == 0) diff --git a/src/LogExpert.Core/Classes/Log/Streamreaders/PositionAwareStreamReaderLegacy.cs b/src/LogExpert.Core/Classes/Log/Streamreaders/PositionAwareStreamReaderLegacy.cs index 207ae31e..2a2880be 100644 --- a/src/LogExpert.Core/Classes/Log/Streamreaders/PositionAwareStreamReaderLegacy.cs +++ b/src/LogExpert.Core/Classes/Log/Streamreaders/PositionAwareStreamReaderLegacy.cs @@ -19,10 +19,10 @@ public class PositionAwareStreamReaderLegacy (Stream stream, EncodingOptions enc #region Properties - public CharBlockAllocator BlockAllocator + private CharBlockAllocator BlockAllocator { get => field ??= new CharBlockAllocator(); - private set; + set; } #endregion @@ -47,7 +47,20 @@ public bool TryReadLine (out ReadOnlyMemory lineMemory) public void ReturnMemory (ReadOnlyMemory memory) { - // Bulk return via BlockAllocator.DetachBlocks() when the LogBuffer is evicted. + // Bulk return via DetachCharBlocks() when the LogBuffer is evicted, or Dispose(). + } + + public List DetachCharBlocks () => BlockAllocator.DetachBlocks(); + + protected override void Dispose (bool disposing) + { + if (disposing) + { + BlockAllocator?.Dispose(); + BlockAllocator = null; + } + + base.Dispose(disposing); } public override string ReadLine () diff --git a/src/LogExpert.Core/Classes/Log/Streamreaders/PositionAwareStreamReaderSystem.cs b/src/LogExpert.Core/Classes/Log/Streamreaders/PositionAwareStreamReaderSystem.cs index fce09075..55090406 100644 --- a/src/LogExpert.Core/Classes/Log/Streamreaders/PositionAwareStreamReaderSystem.cs +++ b/src/LogExpert.Core/Classes/Log/Streamreaders/PositionAwareStreamReaderSystem.cs @@ -36,13 +36,13 @@ public PositionAwareStreamReaderSystem (Stream stream, EncodingOptions encodingO #region Properties /// - /// Gets or creates the block allocator used by this reader instance. - /// The caller can detach the blocks after reading a buffer's worth of lines. + /// The block allocator used by this reader instance. Blocks are handed to the owning buffer via + /// ; ownership of the allocator does not cross the reader seam. /// - public CharBlockAllocator BlockAllocator + private CharBlockAllocator BlockAllocator { get => field ??= new CharBlockAllocator(); - private set; + set; } #endregion @@ -112,10 +112,12 @@ public bool TryReadLine (out ReadOnlyMemory lineMemory) /// public void ReturnMemory (ReadOnlyMemory memory) { - // Bulk return via BlockAllocator.DetachBlocks() or Dispose(). + // Bulk return via DetachCharBlocks() or Dispose(). // Individual per-line return is not needed with block-based allocation. } + public List DetachCharBlocks () => BlockAllocator.DetachBlocks(); + #endregion #region Private Methods diff --git a/src/LogExpert.Core/Interfaces/ILogStreamReaderMemory.cs b/src/LogExpert.Core/Interfaces/ILogStreamReaderMemory.cs index 31a07649..31af8db6 100644 --- a/src/LogExpert.Core/Interfaces/ILogStreamReaderMemory.cs +++ b/src/LogExpert.Core/Interfaces/ILogStreamReaderMemory.cs @@ -36,4 +36,19 @@ public interface ILogStreamReaderMemory : ILogStreamReader /// call this method multiple times for the same memory, but only the first call will have an effect. /// void ReturnMemory (ReadOnlyMemory memory); + + /// + /// Detaches the completed character blocks that back the slices handed out by + /// , transferring their ownership to the caller (the LogBuffer that owns those + /// lines). + /// + /// + /// The list of blocks the reader has filled. The caller owns them until every slice into them is released, at + /// which point they are returned to the shared pool. Readers that have nothing to hand over return an empty list. + /// + /// + /// Call this after a buffer's worth of lines has been read and before the reader continues filling the next + /// buffer. The reader retains any partially-scanned block so reading can continue seamlessly. + /// + List DetachCharBlocks (); } diff --git a/src/LogExpert.Tests/StreamReaderTests/LogStreamReaderTest.cs b/src/LogExpert.Tests/StreamReaderTests/LogStreamReaderTest.cs index b2788ee3..89b01592 100644 --- a/src/LogExpert.Tests/StreamReaderTests/LogStreamReaderTest.cs +++ b/src/LogExpert.Tests/StreamReaderTests/LogStreamReaderTest.cs @@ -178,7 +178,7 @@ public void TryReadLine_ReturnsBlockBackedMemory_NotStringBacked () } [Test] - public void TryReadLine_BlockAllocatorHasBlocks_AfterReading () + public void TryReadLine_DetachCharBlocks_ReturnsFilledBlocks_AfterReading () { var text = "Line 1\nLine 2\nLine 3\n"; using var stream = new MemoryStream(Encoding.UTF8.GetBytes(text)); @@ -189,12 +189,12 @@ public void TryReadLine_BlockAllocatorHasBlocks_AfterReading () // Intentionally empty: consume all lines to advance reader state. } - // The allocator should have at least 1 block - Assert.That(reader.BlockAllocator.BlockCount, Is.GreaterThanOrEqualTo(1)); + // Reading rented at least one block, exposed only through the seam. + Assert.That(reader.DetachCharBlocks(), Has.Count.GreaterThanOrEqualTo(1)); } [Test] - public void TryReadLine_DetachBlocks_TransfersOwnership () + public void TryReadLine_DetachCharBlocks_TransfersOwnership () { var text = "Line 1\nLine 2\nLine 3\n"; using var stream = new MemoryStream(Encoding.UTF8.GetBytes(text)); @@ -205,11 +205,8 @@ public void TryReadLine_DetachBlocks_TransfersOwnership () // Intentionally empty: consume all lines to advance reader state. } - var blocks = reader.BlockAllocator.DetachBlocks(); + var blocks = reader.DetachCharBlocks(); Assert.That(blocks, Has.Count.GreaterThanOrEqualTo(1)); - - // After detach, allocator should have a fresh block - Assert.That(reader.BlockAllocator.BlockCount, Is.EqualTo(1)); } [Test] diff --git a/src/LogExpert.Tests/StreamReaderTests/PositionAwareStreamReaderDirectTests.cs b/src/LogExpert.Tests/StreamReaderTests/PositionAwareStreamReaderDirectTests.cs index a3390aa3..feb83503 100644 --- a/src/LogExpert.Tests/StreamReaderTests/PositionAwareStreamReaderDirectTests.cs +++ b/src/LogExpert.Tests/StreamReaderTests/PositionAwareStreamReaderDirectTests.cs @@ -258,10 +258,10 @@ public void ContentParity_LargeFile () #endregion - #region DetachBlocks + #region DetachCharBlocks [Test] - public void DetachBlocks_ReturnsCompletedBlocks () + public void DetachCharBlocks_ReturnsCompletedBlocks () { // Create enough content to fill multiple blocks var sb = new StringBuilder(); @@ -279,11 +279,11 @@ public void DetachBlocks_ReturnsCompletedBlocks () // Intentionally empty: consume all lines to advance reader state. } - var blocks = reader.DetachBlocks(); + var blocks = reader.DetachCharBlocks(); Assert.That(blocks.Count, Is.GreaterThan(0), "Should have completed blocks to detach"); // Second detach should be empty - var blocks2 = reader.DetachBlocks(); + var blocks2 = reader.DetachCharBlocks(); Assert.That(blocks2.Count, Is.EqualTo(0)); } @@ -508,7 +508,7 @@ public void TryReadLine_LineLongerThanBlockSize_WithMaxLineLengthTruncation () } [Test] - public void TryReadLine_LineLongerThanBlockSize_DetachBlocksAfterEachLine () + public void TryReadLine_LineLongerThanBlockSize_DetachCharBlocksAfterEachLine () { RunWithTimeout(() => { @@ -523,12 +523,12 @@ public void TryReadLine_LineLongerThanBlockSize_DetachBlocksAfterEachLine () Assert.That(reader.TryReadLine(out var line1), Is.True); Assert.That(line1.Span.ToString(), Is.EqualTo("line1")); - var blocks1 = reader.DetachBlocks(); + var blocks1 = reader.DetachCharBlocks(); Assert.That(blocks1.Count, Is.GreaterThan(0)); Assert.That(reader.TryReadLine(out var line2), Is.True); Assert.That(line2.Length, Is.EqualTo(lineLength)); - var blocks2 = reader.DetachBlocks(); + var blocks2 = reader.DetachCharBlocks(); Assert.That(blocks2.Count, Is.GreaterThan(0)); Assert.That(reader.TryReadLine(out var line3), Is.True); @@ -539,7 +539,7 @@ public void TryReadLine_LineLongerThanBlockSize_DetachBlocksAfterEachLine () } [Test] - public void TryReadLine_LineLongerThanBlockSize_DetachBlocksWithLargeTail () + public void TryReadLine_LineLongerThanBlockSize_DetachCharBlocksWithLargeTail () { RunWithTimeout(() => { @@ -575,14 +575,14 @@ public void TryReadLine_LineLongerThanBlockSize_DetachBlocksWithLargeTail () // Read and detach the prefix (resets reader to fresh BLOCK_SIZE buffer) Assert.That(reader.TryReadLine(out var line1), Is.True); Assert.That(line1.Span.ToString(), Is.EqualTo("prefix")); - _ = reader.DetachBlocks(); + _ = reader.DetachCharBlocks(); // Read the long line — this grows the buffer to 131072 Assert.That(reader.TryReadLine(out var line2), Is.True); Assert.That(line2.Length, Is.EqualTo(longLineLength)); // THIS IS THE CRITICAL CALL: DetachBlocks must handle tail > BLOCK_SIZE - var blocks = reader.DetachBlocks(); + var blocks = reader.DetachCharBlocks(); Assert.That(blocks.Count, Is.GreaterThan(0)); // Verify we can still read subsequent lines correctly From fb1bd8fedcab26f8e9768b4f8d2d0096a74385d3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 25 Jun 2026 13:27:23 +0000 Subject: [PATCH 2/5] chore: update plugin hashes [skip ci] --- .../PluginHashGenerator.Generated.cs | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/PluginRegistry/PluginHashGenerator.Generated.cs b/src/PluginRegistry/PluginHashGenerator.Generated.cs index 633650c5..88fc7d51 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-06-24 08:06:11 UTC + /// Generated: 2026-06-25 13:27:22 UTC /// Configuration: Release /// Plugin count: 21 /// @@ -18,27 +18,27 @@ public static Dictionary GetBuiltInPluginHashes() { return new Dictionary(StringComparer.OrdinalIgnoreCase) { - ["AutoColumnizer.dll"] = "36D3E82056BBE33D1F6A32ED28107475A6E466DA11366D92BF2FC75468659BAB", + ["AutoColumnizer.dll"] = "1CDDD36EF9AC1C16C8386D9EE16A5F3B1BDB1C424FEACC4BC3C9D7CCE8041476", ["BouncyCastle.Cryptography.dll"] = "E5EEAF6D263C493619982FD3638E6135077311D08C961E1FE128F9107D29EBC6", ["BouncyCastle.Cryptography.dll (x86)"] = "E5EEAF6D263C493619982FD3638E6135077311D08C961E1FE128F9107D29EBC6", - ["CsvColumnizer.dll"] = "6AB202B1E664F587A0655C2426831FB3E5640AECC6205C4C6C147A8DC9DBEFC9", - ["CsvColumnizer.dll (x86)"] = "6AB202B1E664F587A0655C2426831FB3E5640AECC6205C4C6C147A8DC9DBEFC9", - ["DefaultPlugins.dll"] = "05783A26E389565F7D262C005F7D5CD7938738CD28A6BB5C7E7E275291616E73", - ["FlashIconHighlighter.dll"] = "C68A44AC9542B23F852B681909C1D6810246474081092432FBC507CF1E9D4038", - ["GlassfishColumnizer.dll"] = "A30C04E7C61309977731327B26AB88B01386EC96CF8D84266ACF8434E4257575", - ["JsonColumnizer.dll"] = "8600F78EF0930211D8986737D7AAC968F4EB7213CE691AFF0691A8682DF82D52", - ["JsonCompactColumnizer.dll"] = "0F77645DA79291D3CC20FD254798C0D6AB3E27DB9BA96BB04F73D566F49FDCDD", - ["Log4jXmlColumnizer.dll"] = "B502ED950944FC439634AC3F3FB45E7B2308E945CBFBEA5A3CAF1257ACF49457", - ["LogExpert.Resources.dll"] = "D44708FD8FC9F9485D8D36068A131903954E385786F0FAE78AC6EF9BDC0ED576", + ["CsvColumnizer.dll"] = "2085E0E26F10B24A52CDE8D924F95907D47DBEFC6ED2A4AEAFF5E9EADCDD3B5B", + ["CsvColumnizer.dll (x86)"] = "2085E0E26F10B24A52CDE8D924F95907D47DBEFC6ED2A4AEAFF5E9EADCDD3B5B", + ["DefaultPlugins.dll"] = "9EA1C05844007DEAF43BFCAB69020FA3D6A5FC835EFDF77DA0CA1D3C22E94E22", + ["FlashIconHighlighter.dll"] = "EBB50412CA52CD493E85129B99BB9CACE644BCF0C3D365DFC99109CA96562039", + ["GlassfishColumnizer.dll"] = "9D4376F223E64824713105206B3E469BB0729B02E772BC55C4C6C7F32D801E40", + ["JsonColumnizer.dll"] = "A195D76C08EDDE959D0B9AE3A6E0EB83EB7C2A10AE3CD693B6827EE2311F8B4B", + ["JsonCompactColumnizer.dll"] = "6F1871809F1281D718B6CB5F7854555BE90B3B7582E0BDF7B8352E0BF3125450", + ["Log4jXmlColumnizer.dll"] = "F0FA3B0CAA0F5CAB00F30C377EDB7DFDC6498620A3D54D7279B4918A03E56C68", + ["LogExpert.Resources.dll"] = "CA3952C3C5F8BD98416C0CAD9C9EBD6074187AD8D390C16A411235BC7B257EBC", ["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"] = "9F18D9269DF2B8C482F1C82A1FDA93D1D445F0C2CEEEFA72124A9B6C4B5C1E9C", - ["SftpFileSystem.dll"] = "301D74FC8683B8D8B71A286711152ACA5581CF78D32CF9DCF218802F81D7D02B", - ["SftpFileSystem.dll (x86)"] = "2FE8EE1C55CC85AA82F4CE3B19B5E2734BC00E928D65C94E8B88695D9434AEF9", - ["SftpFileSystem.Resources.dll"] = "C39F867FFA577867BE6A7101F66D0EBA3207E2060FDE03646AC1F8D8ECCC140E", - ["SftpFileSystem.Resources.dll (x86)"] = "C39F867FFA577867BE6A7101F66D0EBA3207E2060FDE03646AC1F8D8ECCC140E", + ["RegexColumnizer.dll"] = "B5714C5EE1ED8612F269703CD35F631B62C93488BFB6304500362414968C141D", + ["SftpFileSystem.dll"] = "854080A8593DEC3D5B1E8B01941DF05C046D9698322B557FAC67BE07E91F0EE9", + ["SftpFileSystem.dll (x86)"] = "FFA9C3BA00C2836093BD08F559415CEEB6C39359BD0FE692E0CBDC2CF739E666", + ["SftpFileSystem.Resources.dll"] = "3DC725E3380AB77E51D37682507754D78F351BC66DCCC4F29B93E9002040D792", + ["SftpFileSystem.Resources.dll (x86)"] = "3DC725E3380AB77E51D37682507754D78F351BC66DCCC4F29B93E9002040D792", }; } From 286ff9485ffef8bc1695dc6c200899ba91031cdf Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 25 Jun 2026 13:33:37 +0000 Subject: [PATCH 3/5] chore: update plugin hashes [skip ci] --- .../PluginHashGenerator.Generated.cs | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/PluginRegistry/PluginHashGenerator.Generated.cs b/src/PluginRegistry/PluginHashGenerator.Generated.cs index d5472889..0f7573cf 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-06-25 12:45:02 UTC + /// Generated: 2026-06-25 13:33:35 UTC /// Configuration: Release /// Plugin count: 21 /// @@ -18,27 +18,27 @@ public static Dictionary GetBuiltInPluginHashes() { return new Dictionary(StringComparer.OrdinalIgnoreCase) { - ["AutoColumnizer.dll"] = "97CB14C8E6F7A714A74E92152CD947B2DF26742AC4C6F1550366B5C2586FB7F8", + ["AutoColumnizer.dll"] = "464EF93AA82C933ED1C7A334FBFC1D1C692EB3371860711065C41BC2E0A7BEB9", ["BouncyCastle.Cryptography.dll"] = "E5EEAF6D263C493619982FD3638E6135077311D08C961E1FE128F9107D29EBC6", ["BouncyCastle.Cryptography.dll (x86)"] = "E5EEAF6D263C493619982FD3638E6135077311D08C961E1FE128F9107D29EBC6", - ["CsvColumnizer.dll"] = "9C9F3914A4ECD1E9A45A0B87845E942162DB8203BCEF05FCA8E8E4A6C10D6AC2", - ["CsvColumnizer.dll (x86)"] = "9C9F3914A4ECD1E9A45A0B87845E942162DB8203BCEF05FCA8E8E4A6C10D6AC2", - ["DefaultPlugins.dll"] = "8EA6195A67840581DA4987B3B2893C68D8DC6DCA919B180896258386B9DB2713", - ["FlashIconHighlighter.dll"] = "5095E95DD6AEED1747BB6CF259D550B2B3D530B6F925C47A7458ECEDBF2EBC60", - ["GlassfishColumnizer.dll"] = "66730D4646D90568BFDCE500363CF2215B52F538B1E9172A78ECC6CAE0DD1C27", - ["JsonColumnizer.dll"] = "7C064A5DE3A970C79518280D43E8C041322B4FBC27D0D8A77BC387DFD738936F", - ["JsonCompactColumnizer.dll"] = "5EEAFD0882DD1DBE5F0FFF92A65580737B775E43CA7F3A0DC4EFDE6A28B0DE5E", - ["Log4jXmlColumnizer.dll"] = "C8458E855FE59F799E2394500BF4991A7E5E7F8AA6B030E2B7A733322E2AFAEF", - ["LogExpert.Resources.dll"] = "F765E294A0EEEC525FA8992D22B1A666880CE2D0F720EFAF154D01EBB7A8AEED", + ["CsvColumnizer.dll"] = "AA35A40DA678913C9A5187D1D28E26D550EEBEBE8D22DC7925A6E02ECDD1C916", + ["CsvColumnizer.dll (x86)"] = "AA35A40DA678913C9A5187D1D28E26D550EEBEBE8D22DC7925A6E02ECDD1C916", + ["DefaultPlugins.dll"] = "7B8100FD3C81CE14CB3A138E4B117AA3B4BAA17B3EF4A68E13CDB7096C8159D0", + ["FlashIconHighlighter.dll"] = "2FF825E92244826562E8BA2429176494C75C2D34D709D180BA2B387536B2C0A6", + ["GlassfishColumnizer.dll"] = "703C18C5175A9C973C73A8504A0D9444DEFEF8CE8A3D1106F318B77952F964EC", + ["JsonColumnizer.dll"] = "936A9BA44B758BDEFEA54790609A477E8053744059B449E1865F349818523561", + ["JsonCompactColumnizer.dll"] = "A52E65EC36F809AC0F2BEA0D56100BF32397622D49F59D6C0BC2F8E3DCAE53F3", + ["Log4jXmlColumnizer.dll"] = "FB1387B89E806EB36BE9064BAF1F3E7A2016AC1CCFDEB3D094CAE73E40DE4DC1", + ["LogExpert.Resources.dll"] = "722978EC212D0DBD8FF7E92A5EACBB256B2F15FB74739652623536E9C91F2E11", ["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"] = "009C8C7E56BB89C9448091A294894FE69C661DF35F9082CD2A092EEAF85E23A7", - ["SftpFileSystem.dll"] = "74624F9B6659F83B53B7FEC45267345B84BE3DB13C811E04926884E65C32D56E", - ["SftpFileSystem.dll (x86)"] = "03A1F8E1A3DF8915404A2653FE55B39B196EFD19C6DE42586D0B9822EFFBC617", - ["SftpFileSystem.Resources.dll"] = "09F468603D3EAF56B13058BCE104CF9EA3F7B757352E9199D54486C8734AC47B", - ["SftpFileSystem.Resources.dll (x86)"] = "09F468603D3EAF56B13058BCE104CF9EA3F7B757352E9199D54486C8734AC47B", + ["RegexColumnizer.dll"] = "F8FA23550508FBC6B7271D0D840BBA4A4AB173FC2140091C6614A95F239FD2A2", + ["SftpFileSystem.dll"] = "2347502790B007FF3C0ACD2BA56255C683660D3E12A20562DA8A684722804B01", + ["SftpFileSystem.dll (x86)"] = "DA261F052DE91A037CD30011B482C7E5B0D37F8797529890EA501999D8143FFC", + ["SftpFileSystem.Resources.dll"] = "B0642BBC46509EA558221FAC5D03C35DBBEDCE243EF32C29A2DB86A21490A7C9", + ["SftpFileSystem.Resources.dll (x86)"] = "B0642BBC46509EA558221FAC5D03C35DBBEDCE243EF32C29A2DB86A21490A7C9", }; } From 1caf69dcbbfb446b1322268ac8f1c050af06ed5a Mon Sep 17 00:00:00 2001 From: BRUNER Patrick Date: Thu, 25 Jun 2026 16:04:35 +0200 Subject: [PATCH 4/5] small guard --- src/LogExpert.Core/Classes/Log/LogfileReader.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/LogExpert.Core/Classes/Log/LogfileReader.cs b/src/LogExpert.Core/Classes/Log/LogfileReader.cs index cd0746ef..a0c1fab7 100644 --- a/src/LogExpert.Core/Classes/Log/LogfileReader.cs +++ b/src/LogExpert.Core/Classes/Log/LogfileReader.cs @@ -909,6 +909,11 @@ public ILogLineMemory[] GetLogLineMemories (int startLine, int count) var availableInBuffer = logBufferEntry.Buffer.LineCount - bufferOffset; var toCopy = Math.Min(count - filled, availableInBuffer); + if (bufferOffset < 0 || toCopy <= 0) + { + break; + } + for (var i = 0; i < toCopy; i++) { result[filled + i] = logBufferEntry.Buffer.GetLineMemoryOfBlock(bufferOffset + i); From 095c8181f700d0acd9390760395b97eedbd38687 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 25 Jun 2026 14:06:30 +0000 Subject: [PATCH 5/5] chore: update plugin hashes [skip ci] --- .../PluginHashGenerator.Generated.cs | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/PluginRegistry/PluginHashGenerator.Generated.cs b/src/PluginRegistry/PluginHashGenerator.Generated.cs index 0f7573cf..c47db8ff 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-06-25 13:33:35 UTC + /// Generated: 2026-06-25 14:06:29 UTC /// Configuration: Release /// Plugin count: 21 /// @@ -18,27 +18,27 @@ public static Dictionary GetBuiltInPluginHashes() { return new Dictionary(StringComparer.OrdinalIgnoreCase) { - ["AutoColumnizer.dll"] = "464EF93AA82C933ED1C7A334FBFC1D1C692EB3371860711065C41BC2E0A7BEB9", + ["AutoColumnizer.dll"] = "E6BBCB994A140BC595E67D19A7E6E9975469718628C5BA8E423DCF13E076FA81", ["BouncyCastle.Cryptography.dll"] = "E5EEAF6D263C493619982FD3638E6135077311D08C961E1FE128F9107D29EBC6", ["BouncyCastle.Cryptography.dll (x86)"] = "E5EEAF6D263C493619982FD3638E6135077311D08C961E1FE128F9107D29EBC6", - ["CsvColumnizer.dll"] = "AA35A40DA678913C9A5187D1D28E26D550EEBEBE8D22DC7925A6E02ECDD1C916", - ["CsvColumnizer.dll (x86)"] = "AA35A40DA678913C9A5187D1D28E26D550EEBEBE8D22DC7925A6E02ECDD1C916", - ["DefaultPlugins.dll"] = "7B8100FD3C81CE14CB3A138E4B117AA3B4BAA17B3EF4A68E13CDB7096C8159D0", - ["FlashIconHighlighter.dll"] = "2FF825E92244826562E8BA2429176494C75C2D34D709D180BA2B387536B2C0A6", - ["GlassfishColumnizer.dll"] = "703C18C5175A9C973C73A8504A0D9444DEFEF8CE8A3D1106F318B77952F964EC", - ["JsonColumnizer.dll"] = "936A9BA44B758BDEFEA54790609A477E8053744059B449E1865F349818523561", - ["JsonCompactColumnizer.dll"] = "A52E65EC36F809AC0F2BEA0D56100BF32397622D49F59D6C0BC2F8E3DCAE53F3", - ["Log4jXmlColumnizer.dll"] = "FB1387B89E806EB36BE9064BAF1F3E7A2016AC1CCFDEB3D094CAE73E40DE4DC1", - ["LogExpert.Resources.dll"] = "722978EC212D0DBD8FF7E92A5EACBB256B2F15FB74739652623536E9C91F2E11", + ["CsvColumnizer.dll"] = "024575D2CBD09E58AE7CFF058B92C3785006AD2280301B0DE0724409934F4BCA", + ["CsvColumnizer.dll (x86)"] = "024575D2CBD09E58AE7CFF058B92C3785006AD2280301B0DE0724409934F4BCA", + ["DefaultPlugins.dll"] = "19B0A154B48F99A616A8468E8AC45ACCF86B4402F9F4E7DC860C6FE1D7D92B13", + ["FlashIconHighlighter.dll"] = "4A488895316B01053DD3287604DC4344CCEA918EB09EB664009BC419F29FC021", + ["GlassfishColumnizer.dll"] = "93DF4EEDC1A695F06E7E09AB483782FA0FDC867BED61FB63BD25458389FA8E0E", + ["JsonColumnizer.dll"] = "C9538CA60C8E1ADFEAC74C75CAE7F1627311FF500804C6FD8B2EFEF068C6FFFE", + ["JsonCompactColumnizer.dll"] = "1F30ABDC119D4459A2272D96080F4BF0683D7420A624CC256C2F07EEE02B192A", + ["Log4jXmlColumnizer.dll"] = "D99A998A0E0C3969EB8B49CB6E8BD9CCC934641EEE18852B202244C837ADC7F5", + ["LogExpert.Resources.dll"] = "43FB2C2C6D53D1E2CDB0D8FD663AEE5CF3E4FC51A05D69DBFBBF2123E8F67935", ["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"] = "F8FA23550508FBC6B7271D0D840BBA4A4AB173FC2140091C6614A95F239FD2A2", - ["SftpFileSystem.dll"] = "2347502790B007FF3C0ACD2BA56255C683660D3E12A20562DA8A684722804B01", - ["SftpFileSystem.dll (x86)"] = "DA261F052DE91A037CD30011B482C7E5B0D37F8797529890EA501999D8143FFC", - ["SftpFileSystem.Resources.dll"] = "B0642BBC46509EA558221FAC5D03C35DBBEDCE243EF32C29A2DB86A21490A7C9", - ["SftpFileSystem.Resources.dll (x86)"] = "B0642BBC46509EA558221FAC5D03C35DBBEDCE243EF32C29A2DB86A21490A7C9", + ["RegexColumnizer.dll"] = "926229B7C704E367BA768E44981D133E6B26356CCEC4BCF0108EC77B2521C33A", + ["SftpFileSystem.dll"] = "066BDA9245EDCECCD49E3C36B6ACC05E52C8C34D25A1A9406E403987E603FC57", + ["SftpFileSystem.dll (x86)"] = "A30EB5C2D2E8A403C0ADF179AB626278689198E64935DA8BA108B33FB164AECA", + ["SftpFileSystem.Resources.dll"] = "E68455B141D714A19779E717558DEEA6C9A9829ECF5A3C5EDCD65F18C06E6D33", + ["SftpFileSystem.Resources.dll (x86)"] = "E68455B141D714A19779E717558DEEA6C9A9829ECF5A3C5EDCD65F18C06E6D33", }; }