private void FlushLogicalLine(LogicalLinesHistoryQueue logicalLinesHistoryQueue = null) { if (_logicalLine.IsEmpty) { return; } _logicalLine.IsVisible = !ShouldBeHided() && ShouldBeShown(); if ( (_logicalLine.IsVisible || _afterCounter > 0) && !SkipFromNumLines()) { var forPrinting = new LogicalLinesHistoryQueue(); PrepareLogicalLineForPrinting(_logicalLine, forPrinting); if (logicalLinesHistoryQueue == null) { PrintLogicalLines(forPrinting); } else { logicalLinesHistoryQueue.Enqueue(forPrinting); } if (_logicalLine.IsVisible) { _afterCounter = Configs.ContextAfter; } } _logicalLinesHistoryQueue.Enqueue(_logicalLine); _logicalLine = new LogicalLine(); }
private void ProcessStreamFromLastPosToEnd(Stream stream, LogicalLinesHistoryQueue logicalLines) { var encoding = Encoding.Default; if (FileType == FileTypes.Console) { if (!stream.CanRead) { return; } encoding = Console.InputEncoding; } using var sr = new StreamReader(stream, encoding, true); SeekToLastPos(sr); var s = ReadLine(sr); while (s != null) { ProcessReadLine(s, logicalLines); s = ReadLine(sr); } // for archives, LastPos remains 0, so update-it to FileSize // NOTE: do not set LastPos = FileSize for files, cause FileSize is just // a cached value, and do not reflect actual file size! if (!sr.BaseStream.CanSeek) { LastPos = FileSize; } }
public File(string file, TailPbl bl, int fileIndex) { if (string.IsNullOrEmpty(file)) { throw new ArgumentException(@"Param cannot be empty", nameof(file)); } _bl = bl ?? throw new ArgumentNullException(nameof(bl)); _file = file; _fileIndex = fileIndex; _logicalLinesHistoryQueue = new LogicalLinesHistoryQueue(Math.Max(1, Configs.ContextBefore)); _startFromNum = Configs.LinesStartNumber; if (_file == Constants.CONSOLE_FILENAME) { _fileType = FileTypes.Console; } else if (ArchiveSupport.TryGetArchivePath(file, out var archive, out var finalFile) && ArchiveSupport.IsValidArchive(archive)) { _fileType = string.IsNullOrWhiteSpace(finalFile) ? FileTypes.Archive : FileTypes.ArchivedFile; } UpdateFileInfo(); ResetCounters(); }
public void Enqueue(LogicalLinesHistoryQueue historyQueue) { if (historyQueue is null) { throw new ArgumentNullException(nameof(historyQueue)); } foreach (var line in historyQueue) { Enqueue(line); } }
private void PrepareLogicalLineForPrinting(LogicalLine logicalLine, LogicalLinesHistoryQueue prepared) { if (Configs.IsContextBeforeUsed && _logicalLinesHistoryQueue.Any() && _logicalLine.IsVisible) { prepared.Enqueue(_logicalLinesHistoryQueue); _logicalLinesHistoryQueue.Clear(); } prepared.Enqueue(logicalLine); if (_afterCounter > 0) { --_afterCounter; } }
private void FindLastLinesInStream(Stream stream) { if (_lastLinesProcessed || Configs.LinesStartFrom != NumLinesStart.End) { return; } if (_startFromNum != 0) { var logicalLinesHistory = new LogicalLinesHistoryQueue( _startFromNum * (Configs.ContextLines + 1)); if (stream.CanSeek) { if (!ProcessStreamInPages(stream, logicalLinesHistory)) { // optimization is not working, try without optimization ResetCounters(); logicalLinesHistory.Clear(); ProcessStreamFromLastPosToEnd(stream, logicalLinesHistory); } } else { ProcessStreamFromLastPosToEnd(stream, logicalLinesHistory); } FlushLogicalLine(logicalLinesHistory); while (logicalLinesHistory.Any()) { var forPrinting = new LogicalLinesHistoryQueue(); PrepareLogicalLineForPrinting(logicalLinesHistory.Dequeue(), forPrinting); PrintLogicalLines(forPrinting); } } LastPos = FileSize; _lastLinesProcessed = true; }
private void ProcessReadLine(string readLine, LogicalLinesHistoryQueue logicalLinesHistoryQueue) { var isLogicalContinuation = !string.IsNullOrEmpty(Configs.LogicalLineMarker) && ( readLine.Length < Configs.LogicalLineMarker.Length || readLine.Substring(0, Configs.LogicalLineMarker.Length) .Contains(Configs.LogicalLineMarker, Configs.ComparisonOptions) ); if (!isLogicalContinuation) // a new line begins, flush memory { ++_lineNumber; FlushLogicalLine(logicalLinesHistoryQueue); } var line = new Line(readLine, isLogicalContinuation, _lineNumber); line.CheckFilters(Configs.FiltersShow, Configs.FiltersHide, Configs.FiltersHighlight); AddLineNumberIfApplicable(line); TruncateIfApplicable(line); _logicalLine.Add(line); }
private void PrintLogicalLines(LogicalLinesHistoryQueue logicalLines) { lock (_bl.PrintLock) { PrintFileName(); while (logicalLines.Any()) { var logicalLine = logicalLines.Dequeue(); if (!logicalLine.IsPrinted) { if (Configs.IsContextUsed && Math.Abs(logicalLine.LineNumber - _lastPrintedLine) > 1) { _bl.PrintLogicalLine(TailPbl.GetContextDelimiter(), _fileIndex); } _bl.PrintLogicalLine(logicalLine, _fileIndex); _lastPrintedLine = logicalLine.LineNumber; } } } }
// Optimization used: // read from end in pages by XXX bytes to a memory stream // and stops to read if _startFromNum lines found // // Will not works, if line length is greater than PAGE_SIZE private bool ProcessStreamInPages(Stream stream, LogicalLinesHistoryQueue logicalLines) { if (!CanProcessInPages()) { return(false); } var encoding = DetectEncoding(stream); var historyDeep = _startFromNum * (Configs.ContextLines + 1); var foundLines = new LogicalLinesHistoryQueue(historyDeep); var from = FileSize; while (from != 0 && foundLines.Count != historyDeep) { var buf = new byte[Constants.REVERS_SEARCH_PAGE_SIZE]; var pageLines = new LogicalLinesHistoryQueue(historyDeep); from = Math.Max(0, from - Constants.REVERS_SEARCH_PAGE_SIZE); stream.Seek(from, SeekOrigin.Begin); var sz = stream.Read(buf, 0, Constants.REVERS_SEARCH_PAGE_SIZE); Stream ms = null; try { ms = new MemoryStream(buf, 0, sz); using var sr = new StreamReader(ms, encoding, from == 0 // ignore BOM only at file beginning ); ms = null; // prevent disposing several times if (from != 0) { var nul = sr.ReadLine(); // ignore first line, may be incomplete var szBytes = encoding.GetByteCount(nul ?? string.Empty); if (szBytes >= Constants.REVERS_SEARCH_PAGE_SIZE) // extra long line { return(false); } from += szBytes; } var s = sr.ReadLine(); while (s != null) { if (encoding.GetByteCount(s) >= Constants.REVERS_SEARCH_PAGE_SIZE) { return(false); } ProcessReadLine(s, pageLines); s = sr.ReadLine(); } } finally { #pragma warning disable CA1508 // Avoid dead conditional code ms?.Dispose(); #pragma warning restore CA1508 // Avoid dead conditional code } FlushLogicalLine(pageLines); pageLines.Enqueue(foundLines); foundLines.ReplaceBy(pageLines); LastPos = FileSize - from; } logicalLines.ReplaceBy(foundLines); // because lines was searched in pages, line numbers are irrelevant logicalLines.SetLinesNumberToUnknown(); LastPos = FileSize; return(true); }
public void ReplaceBy(LogicalLinesHistoryQueue historyQueue) { Clear(); Enqueue(historyQueue); }