182 lines
4.8 KiB
C#
182 lines
4.8 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
|
|
using Best.HTTP.Shared.Databases.Utils;
|
|
|
|
namespace Best.HTTP.Shared.Databases
|
|
{
|
|
public sealed class FreeListManager : IDisposable
|
|
{
|
|
struct FreeSpot
|
|
{
|
|
public int pos;
|
|
public int length;
|
|
}
|
|
|
|
private Stream stream;
|
|
private List<FreeSpot> freeList = new List<FreeSpot>();
|
|
|
|
public FreeListManager(Stream stream)
|
|
{
|
|
this.stream = stream;
|
|
Load();
|
|
}
|
|
|
|
private void Load()
|
|
{
|
|
this.freeList.Clear();
|
|
this.stream.Seek(0, SeekOrigin.Begin);
|
|
|
|
if (this.stream.Length == 0)
|
|
return;
|
|
|
|
try
|
|
{
|
|
uint count = (uint)stream.DecodeUnsignedVariableByteInteger();
|
|
for (int i = 0; i < count; ++i)
|
|
{
|
|
int pos = (int)stream.DecodeUnsignedVariableByteInteger();
|
|
int length = (int)stream.DecodeUnsignedVariableByteInteger();
|
|
|
|
this.freeList.Add(new FreeSpot { pos = pos, length = length });
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
this.freeList.Clear();
|
|
this.stream.SetLength(0);
|
|
}
|
|
}
|
|
|
|
public void Save()
|
|
{
|
|
if (this.freeList.Count == 0)
|
|
{
|
|
this.stream.SetLength(0);
|
|
return;
|
|
}
|
|
|
|
int count = this.freeList.Count;
|
|
|
|
this.stream.Seek(0, SeekOrigin.Begin);
|
|
|
|
stream.EncodeUnsignedVariableByteInteger((uint)count);
|
|
|
|
for (int i = 0; i < count; ++i)
|
|
{
|
|
FreeSpot spot = this.freeList[i];
|
|
|
|
stream.EncodeUnsignedVariableByteInteger((uint)spot.pos);
|
|
stream.EncodeUnsignedVariableByteInteger((uint)spot.length);
|
|
}
|
|
|
|
this.stream.Flush();
|
|
}
|
|
|
|
public int FindFreeIndex(int length)
|
|
{
|
|
for (int i = 0; i < this.freeList.Count; ++i)
|
|
{
|
|
FreeSpot spot = this.freeList[i];
|
|
|
|
if (spot.length >= length)
|
|
return i;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
public int Occupy(int idx, int length)
|
|
{
|
|
FreeSpot spot = this.freeList[idx];
|
|
int position = spot.pos;
|
|
|
|
if (spot.length < length)
|
|
throw new Exception($"Can't Occupy a free spot with smaller space ({spot.length} < {length})!");
|
|
|
|
if (spot.length > length)
|
|
{
|
|
spot.pos += length;
|
|
spot.length -= length;
|
|
|
|
this.freeList[idx] = spot;
|
|
}
|
|
else
|
|
this.freeList.RemoveAt(idx);
|
|
|
|
return position;
|
|
}
|
|
|
|
public void Add(int pos, int length)
|
|
{
|
|
int insertToIdx = 0;
|
|
|
|
while (insertToIdx < this.freeList.Count && this.freeList[insertToIdx].pos < pos)
|
|
insertToIdx++;
|
|
|
|
if (insertToIdx > this.freeList.Count)
|
|
throw new Exception($"Couldn't find free spot with position '{pos}'!");
|
|
|
|
bool merged = false;
|
|
FreeSpot spot = new FreeSpot { pos = pos, length = length };
|
|
|
|
if (insertToIdx > 0)
|
|
{
|
|
var prev = this.freeList[insertToIdx - 1];
|
|
|
|
// Merge with previous
|
|
if (prev.pos + prev.length == pos)
|
|
{
|
|
prev.length += length;
|
|
|
|
this.freeList[insertToIdx - 1] = prev;
|
|
|
|
spot = prev;
|
|
merged = true;
|
|
}
|
|
}
|
|
|
|
if (insertToIdx < this.freeList.Count)
|
|
{
|
|
var next = this.freeList[insertToIdx];
|
|
|
|
// merge with next?
|
|
if (spot.pos + spot.length == next.pos)
|
|
{
|
|
spot.length += next.length;
|
|
|
|
if (!merged)
|
|
{
|
|
// Not already merged, extend the one in place
|
|
this.freeList[insertToIdx] = spot;
|
|
merged = true;
|
|
}
|
|
else
|
|
{
|
|
// Already merged. Further extend the previous, and remove the next.
|
|
this.freeList[insertToIdx - 1] = spot;
|
|
this.freeList.RemoveAt(insertToIdx);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!merged)
|
|
this.freeList.Insert(insertToIdx, spot);
|
|
}
|
|
|
|
public void Clear()
|
|
{
|
|
this.freeList.Clear();
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
if (this.stream != null)
|
|
this.stream.Close();
|
|
this.stream = null;
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
}
|
|
}
|