/* * SPDX-License-Identifier: AGPL-3.0-or-later * Copyright (C) 2025 Sergej Görzen * This file is part of OmiLAXR. */ using System; using System.IO; using System.Text; namespace OmiLAXR.Utils { /// /// High-performance UTF-8 file writer with internal buffering for efficient text output. /// Provides thread-safe writing operations with automatic UTF-8 encoding. /// Optimized for scenarios with frequent small writes like logging and analytics data. /// public class BufferedUtf8Writer : IDisposable { /// /// Buffered stream wrapper providing write buffering for improved performance. /// Reduces the number of actual disk I/O operations. /// private readonly BufferedStream _bufferedStream; /// /// Underlying file stream for direct file system access. /// Managed by the BufferedStream for actual I/O operations. /// private readonly FileStream _fileStream; /// /// Reusable byte buffer for UTF-8 encoding operations. /// Prevents repeated memory allocations during string encoding. /// private readonly byte[] _writeBuffer; /// /// File path this writer is associated with. /// Stored for reference and debugging purposes. /// public string FilePath { get; private set; } /// /// Thread synchronization object for ensuring thread-safe operations. /// All public methods acquire this lock before performing I/O operations. /// private readonly object _lock = new object(); /// /// Initializes a new BufferedUtf8Writer for the specified file path. /// Creates or opens the file and sets up buffering for optimal performance. /// /// File path to write to /// Size of the internal buffer in bytes (default: 8192) public BufferedUtf8Writer(string path, int bufferSize = 8192) { // Open file with optimized settings for sequential writing _fileStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Read, 8192); _bufferedStream = new BufferedStream(_fileStream, bufferSize); _writeBuffer = new byte[bufferSize]; // Reusable encoding buffer FilePath = path; } /// /// Writes a single line of text followed by a newline character. /// Thread-safe operation with automatic UTF-8 encoding and buffering. /// /// Text line to write (newline will be appended automatically) public void WriteLine(string line) { lock (_lock) { // Append platform-specific newline var full = line + Environment.NewLine; // Encode to UTF-8 using reusable buffer to avoid allocations var byteCount = Encoding.UTF8.GetBytes(full, 0, full.Length, _writeBuffer, 0); // Write encoded bytes to buffered stream _bufferedStream.Write(_writeBuffer, 0, byteCount); } } /// /// Writes multiple lines of text efficiently in a single thread-safe operation. /// More efficient than multiple WriteLine calls for bulk operations. /// /// Array of text lines to write public void WriteLines(string[] lines) { lock (_lock) { // Write each line within the same lock for better performance foreach (var line in lines) WriteLine(line); } } /// /// Forces all buffered data to be written to the underlying file system. /// Thread-safe operation that ensures data persistence. /// public void Flush() { lock (_lock) { _bufferedStream.Flush(); } } /// /// Gets the current position in the buffered stream. /// Thread-safe access to the stream position for debugging or state tracking. /// /// Current byte position in the stream public int GetPosition() { lock (_lock) { return (int)_bufferedStream.Position; } } /// /// Sets the position in the buffered stream for random access scenarios. /// Thread-safe positioning operation with bounds checking by the underlying stream. /// /// Byte position to seek to public void SetPosition(int position) { lock (_lock) { _bufferedStream.Position = position; } } /// /// Disposes all resources including streams and buffers. /// Ensures final flush before disposal to prevent data loss. /// Thread-safe cleanup operation. /// public void Dispose() { lock (_lock) { // Ensure all data is written before disposing _bufferedStream.Flush(); _bufferedStream.Dispose(); _fileStream.Dispose(); } } } }