ln.objects/ng/storage/OrganizedFile.cs

228 lines
5.4 KiB
C#

using System;
using System.IO;
using ln.types.btree;
using System.Collections.Generic;
using System.Linq;
using ln.objects.catalog;
namespace ln.types.odb.ng.storage
{
public class OrganizedFile : IDisposable
{
public String FileName { get; private set; }
public OrganizedFileHeader FileHeader { get; private set; } = new OrganizedFileHeader();
public int CurrentStoreLength { get; private set; }
FileStream fileStream;
FileStream lckFileStream;
MappingBTree<int, OrganizedFileArea> freeAreas = new MappingBTree<int, OrganizedFileArea>((a)=>a.Offset);
MappingBTree<int, OrganizedFileArea> usedAreas = new MappingBTree<int, OrganizedFileArea>((a) => a.Offset);
public OrganizedFile(string filename)
{
FileName = filename;
lckFileStream = new FileStream(string.Format("{0}.lck", FileName), FileMode.CreateNew);
fileStream = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.ReadWrite);
if (fileStream.Length > 0)
{
FileHeader.Read(fileStream);
int nextOffset = FileHeader.FirstOffset;
while (nextOffset < fileStream.Length)
{
OrganizedFileArea fileArea = new OrganizedFileArea(fileStream, nextOffset);
if (fileArea.ReadTypeCode(fileStream) == 0)
freeAreas.Add(fileArea);
else
usedAreas.Add(fileArea);
nextOffset = fileArea.NextOffset;
}
CurrentStoreLength = nextOffset;
}
}
public void Close()
{
if (fileStream != null)
{
lock (fileStream)
{
FileHeader.Write(fileStream);
fileStream.Close();
fileStream.Dispose();
lckFileStream.Close();
lckFileStream.Dispose();
File.Delete(string.Format("{0}.lck", FileName));
freeAreas.Clear();
usedAreas.Clear();
}
fileStream = null;
lckFileStream = null;
}
}
public IEnumerable<OrganizedFileArea> UsedAreas => usedAreas;
public void Clear(OrganizedFileArea fileArea)
{
lock (fileStream)
{
usedAreas.TryRemove(fileArea);
fileArea.Clear(fileStream);
freeAreas.Add(fileArea);
}
}
public OrganizedFileArea Store(ODBEntity value)
{
byte[] storageBytes = value.GetStorageBytes();
return Store(storageBytes);
}
public OrganizedFileArea Store(byte[] bytes)
{
lock (fileStream)
{
OrganizedFileArea fileArea = freeAreas.Where((a) => a.MaxBytes >= bytes.Length).FirstOrDefault();
if (fileArea == null)
{
fileArea = new OrganizedFileArea(CurrentStoreLength, bytes.Length + 4);
CurrentStoreLength = fileArea.NextOffset;
}
else
{
freeAreas.Remove(fileArea);
}
fileArea.Store(fileStream, bytes);
usedAreas.Add(fileArea);
return fileArea;
}
}
public void Dispose() => Close();
public class OrganizedFileHeader
{
public readonly UInt32 CurrentFileMagic = 0x00EEFFC0;
public UInt32 Magic;
public OrganizedFileType FileType;
public Int32 FirstOffset;
public UInt32 Reserve0;
public UInt64 OpenCounter;
public OrganizedFileHeader()
{
Magic = CurrentFileMagic;
FileType = OrganizedFileType.DATA;
FirstOffset = 1024;
Reserve0 = 0;
OpenCounter = 0;
}
public void Read(FileStream stream)
{
stream.Position = 0;
Magic = stream.ReadUInteger();
if (Magic != CurrentFileMagic)
throw new FormatException("File Magic does not match! Possibly corrupted file!");
FileType = (OrganizedFileType)stream.ReadInteger();
FirstOffset = stream.ReadInteger();
Reserve0 = stream.ReadUInteger();
OpenCounter = stream.ReadULong();
OpenCounter++;
}
public void Write(FileStream stream)
{
stream.Position = 0;
stream.WriteUInteger(Magic);
stream.WriteInteger((int)FileType);
stream.WriteInteger(FirstOffset);
stream.WriteUInteger(Reserve0);
stream.WriteULong(OpenCounter);
}
}
public class OrganizedFileArea : IComparable<OrganizedFileArea>,IComparable
{
public Int32 Offset;
public Int32 Size;
public Int32 MaxBytes => Size - 4;
public Int32 NextOffset => Offset + Size;
public OrganizedFileArea(int offset,int size)
{
Offset = offset;
Size = size;
}
public OrganizedFileArea(FileStream fileStream,int offset)
{
Offset = offset;
fileStream.Position = Offset;
Size = fileStream.ReadInteger();
}
public int ReadTypeCode(FileStream fileStream)
{
fileStream.Position = Offset + 4;
return fileStream.ReadInteger();
}
public void Store(FileStream fileStream,byte[] buffer)
{
if (MaxBytes < buffer.Length)
throw new ArgumentException("Area too small for buffer to fit", nameof(buffer));
fileStream.Position = Offset;
fileStream.WriteInteger(Size);
fileStream.Write(buffer, 0, buffer.Length);
if (buffer.Length < MaxBytes)
{
byte[] padding = new byte[MaxBytes - buffer.Length];
fileStream.Write(padding, 0, padding.Length);
}
}
public byte[] Load(FileStream fileStream)
{
byte[] buffer = new byte[MaxBytes];
fileStream.Position = Offset + 4;
fileStream.Read(buffer, 0, buffer.Length);
return buffer;
}
public void Clear(FileStream fileStream)
{
byte[] zero = new byte[MaxBytes];
fileStream.WriteInteger(Size);
fileStream.Write(zero,0,zero.Length);
}
public int CompareTo(OrganizedFileArea other)
{
return Offset - other.Offset;
}
public int CompareTo(object obj)
{
if (obj is OrganizedFileArea)
{
return Offset - (obj as OrganizedFileArea).Offset;
}
throw new NotSupportedException();
}
}
}
}