228 lines
5.4 KiB
C#
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();
|
|
}
|
|
}
|
|
}
|
|
}
|