FreeListManager.cs 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using Best.HTTP.Shared.Databases.Utils;
  5. namespace Best.HTTP.Shared.Databases
  6. {
  7. public sealed class FreeListManager : IDisposable
  8. {
  9. struct FreeSpot
  10. {
  11. public int pos;
  12. public int length;
  13. }
  14. private Stream stream;
  15. private List<FreeSpot> freeList = new List<FreeSpot>();
  16. public FreeListManager(Stream stream)
  17. {
  18. this.stream = stream;
  19. Load();
  20. }
  21. private void Load()
  22. {
  23. this.freeList.Clear();
  24. this.stream.Seek(0, SeekOrigin.Begin);
  25. if (this.stream.Length == 0)
  26. return;
  27. try
  28. {
  29. uint count = (uint)stream.DecodeUnsignedVariableByteInteger();
  30. for (int i = 0; i < count; ++i)
  31. {
  32. int pos = (int)stream.DecodeUnsignedVariableByteInteger();
  33. int length = (int)stream.DecodeUnsignedVariableByteInteger();
  34. this.freeList.Add(new FreeSpot { pos = pos, length = length });
  35. }
  36. }
  37. catch
  38. {
  39. this.freeList.Clear();
  40. this.stream.SetLength(0);
  41. }
  42. }
  43. public void Save()
  44. {
  45. if (this.freeList.Count == 0)
  46. {
  47. this.stream.SetLength(0);
  48. return;
  49. }
  50. int count = this.freeList.Count;
  51. this.stream.Seek(0, SeekOrigin.Begin);
  52. stream.EncodeUnsignedVariableByteInteger((uint)count);
  53. for (int i = 0; i < count; ++i)
  54. {
  55. FreeSpot spot = this.freeList[i];
  56. stream.EncodeUnsignedVariableByteInteger((uint)spot.pos);
  57. stream.EncodeUnsignedVariableByteInteger((uint)spot.length);
  58. }
  59. this.stream.Flush();
  60. }
  61. public int FindFreeIndex(int length)
  62. {
  63. for (int i = 0; i < this.freeList.Count; ++i)
  64. {
  65. FreeSpot spot = this.freeList[i];
  66. if (spot.length >= length)
  67. return i;
  68. }
  69. return -1;
  70. }
  71. public int Occupy(int idx, int length)
  72. {
  73. FreeSpot spot = this.freeList[idx];
  74. int position = spot.pos;
  75. if (spot.length < length)
  76. throw new Exception($"Can't Occupy a free spot with smaller space ({spot.length} < {length})!");
  77. if (spot.length > length)
  78. {
  79. spot.pos += length;
  80. spot.length -= length;
  81. this.freeList[idx] = spot;
  82. }
  83. else
  84. this.freeList.RemoveAt(idx);
  85. return position;
  86. }
  87. public void Add(int pos, int length)
  88. {
  89. int insertToIdx = 0;
  90. while (insertToIdx < this.freeList.Count && this.freeList[insertToIdx].pos < pos)
  91. insertToIdx++;
  92. if (insertToIdx > this.freeList.Count)
  93. throw new Exception($"Couldn't find free spot with position '{pos}'!");
  94. bool merged = false;
  95. FreeSpot spot = new FreeSpot { pos = pos, length = length };
  96. if (insertToIdx > 0)
  97. {
  98. var prev = this.freeList[insertToIdx - 1];
  99. // Merge with previous
  100. if (prev.pos + prev.length == pos)
  101. {
  102. prev.length += length;
  103. this.freeList[insertToIdx - 1] = prev;
  104. spot = prev;
  105. merged = true;
  106. }
  107. }
  108. if (insertToIdx < this.freeList.Count)
  109. {
  110. var next = this.freeList[insertToIdx];
  111. // merge with next?
  112. if (spot.pos + spot.length == next.pos)
  113. {
  114. spot.length += next.length;
  115. if (!merged)
  116. {
  117. // Not already merged, extend the one in place
  118. this.freeList[insertToIdx] = spot;
  119. merged = true;
  120. }
  121. else
  122. {
  123. // Already merged. Further extend the previous, and remove the next.
  124. this.freeList[insertToIdx - 1] = spot;
  125. this.freeList.RemoveAt(insertToIdx);
  126. }
  127. }
  128. }
  129. if (!merged)
  130. this.freeList.Insert(insertToIdx, spot);
  131. }
  132. public void Clear()
  133. {
  134. this.freeList.Clear();
  135. }
  136. public void Dispose()
  137. {
  138. if (this.stream != null)
  139. this.stream.Close();
  140. this.stream = null;
  141. GC.SuppressFinalize(this);
  142. }
  143. }
  144. }