using System.Text; using Inspectron.Fastbuffer.Interfaces; using Serilog; namespace Inspectron.Fastbuffer; public class IndexFile { private readonly string _indexFilePath; private readonly int _bufferSize; private readonly IFilesystem _filesystem; private readonly string? _bufferPath; private const int RecordSize = 512; private const int IndexSize = 4; private const int HeaderSize = 4; public IndexFile(string path, int bufferSize, IFilesystem filesystem) { _indexFilePath = path; _bufferSize = bufferSize; _filesystem = filesystem; ValidateFile(); _bufferPath = Path.Combine(Path.GetDirectoryName(_indexFilePath), ".."); } private void ValidateFile() { if (!File.Exists(_indexFilePath)) { CreateFile(); } // check file size var fileInfo = new FileInfo(_indexFilePath); if (fileInfo.Length != _bufferSize * RecordSize + HeaderSize) { UpdateFile(); } } private void UpdateFile() { using var fileStream = File.Open(_indexFilePath, FileMode.Open); var actualSize = fileStream.Length; if (actualSize < _bufferSize+ HeaderSize) { fileStream.Seek(0, SeekOrigin.End); var buffer = new byte[_bufferSize * RecordSize - actualSize]; fileStream.Write(buffer, HeaderSize, buffer.Length); } } private void CreateFile() { using var fileStream = File.Create(_indexFilePath); var buffer = new byte[_bufferSize* RecordSize + HeaderSize]; fileStream.Write(buffer, 0, buffer.Length); fileStream.Seek(0, SeekOrigin.Begin); var bytes = BitConverter.GetBytes(-1); fileStream.Write(bytes, 0, bytes.Length); } public int GetCurrentArtifactId() { using var fileStream = File.Open(_indexFilePath, FileMode.Open); using var reader = new BinaryReader(fileStream); return reader.ReadInt32(); } public void SetCurrentArtifactId(int id) { using var fileStream = File.Open(_indexFilePath, FileMode.Open); using var writer = new BinaryWriter(fileStream); writer.Write(id); } public string GetArtifactPath(int id) { using var fileStream = File.Open(_indexFilePath, FileMode.Open); fileStream.Seek(id * RecordSize+HeaderSize, SeekOrigin.Begin); byte[] bytes = new byte[RecordSize]; fileStream.Read(bytes, 0, RecordSize); return Encoding.UTF8.GetString(bytes).TrimEnd('\0'); } public int GetNextId() { var currentId = GetCurrentArtifactId(); if (currentId + 1 >= _bufferSize) { currentId = 0; } else { currentId++; } return currentId; } public void DeleteArtifact(int id) { var relPath = GetArtifactPath(id); var filePath = Path.Combine(_bufferPath, relPath); try { _filesystem.Delete(filePath); } catch (Exception e) { Log.Error("Error deleting artifact {id}: {message}",id,e.Message); } } public void WriteNextArtifact(byte[] data, string fileName) { var id = GetNextId(); Log.Debug("Writing artifact {id}",id); var existingPath = GetArtifactPath(id); Log.Debug("Existing path: {existingPath}", existingPath); DeleteArtifact(id); var filePath = Path.Combine(_bufferPath, fileName); _filesystem.Write(filePath, data); SetCurrentArtifactId(id); using var fileStream = File.Open(_indexFilePath, FileMode.Open); fileStream.Seek(id * RecordSize+ HeaderSize, SeekOrigin.Begin); var bytes = Encoding.UTF8.GetBytes(fileName); fileStream.Write(bytes, 0, bytes.Length); var padding = new byte[RecordSize - bytes.Length]; fileStream.Write(padding, 0, padding.Length); } public byte[] ReadArtifact(int id) { using var fileStream = File.Open(_indexFilePath, FileMode.Open); fileStream.Seek(id * RecordSize+ HeaderSize, SeekOrigin.Begin); var bytes = new byte[RecordSize]; fileStream.Read(bytes, 0, RecordSize); var fileName = Encoding.UTF8.GetString(bytes).TrimEnd('\0'); var filePath = Path.Combine(_bufferPath, fileName); return _filesystem.Read(filePath); } }