123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181 |
- 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);
- }
- }
- }
|