using System; using System.Collections.Generic; namespace Capnp { /// /// The segment allocator default implementation. /// public class SegmentAllocator : ISegmentAllocator { class Segment { public Segment(Memory mem, uint id) { Mem = mem; Id = id; } public uint Id { get; } public Memory Mem { get; } public int FreeOffset { get; set; } public bool TryAllocate(uint nwords, out int offset) { if (checked(FreeOffset + (int)nwords) <= Mem.Length) { offset = FreeOffset; int count = (int)nwords; FreeOffset += count; return true; } else { offset = -1; return false; } } public bool IsFull => FreeOffset >= Mem.Length; } readonly int _defaultSegmentSize; readonly List _segments = new List(); readonly List _nonFullSegments = new List(); /// /// Constructs an instance. /// /// Default size (in words) of a newly allocated segment. If a single allocation requires /// a bigger size, a bigger dedicated segment will be allocated. On the wire, segments will be truncated to their actual /// occupancies. public SegmentAllocator(int defaultSegmentSize = 64) { _defaultSegmentSize = defaultSegmentSize; } /// /// The list of currently allocated segments, each one truncated to its actual occupancy. /// public IReadOnlyList> Segments => _segments.LazyListSelect(s => s.Mem.Slice(0, s.FreeOffset)); /// /// Allocates memory. /// /// Number of words to allocate /// Preferred segment index. If enough space is available, /// memory will be allocated inside that segment. Otherwise, a different segment will be chosen, or /// a new one will be allocated, or allocation will fail (depending on ). /// The allocated memory slice in case of success (default(SegmentSlice) otherwise) /// Whether using the preferred segment is mandatory. If it is and there is not /// enough space available, allocation will fail. /// Whether allocation was successful. public bool Allocate(uint nwords, uint preferredSegmentIndex, out SegmentSlice result, bool forcePreferredSegment) { result = default; Segment segment; if (preferredSegmentIndex < _segments.Count) { segment = _segments[(int)preferredSegmentIndex]; if (segment.TryAllocate(nwords, out result.Offset)) { result.SegmentIndex = preferredSegmentIndex; return true; } } if (forcePreferredSegment) { return false; } for (int i = 0; i < _nonFullSegments.Count; i++) { segment = _nonFullSegments[i]; if (segment.TryAllocate(nwords, out result.Offset)) { result.SegmentIndex = segment.Id; if (segment.IsFull) { int n = _nonFullSegments.Count - 1; var tmp = _nonFullSegments[i]; _nonFullSegments[i] = _nonFullSegments[n]; _nonFullSegments[n] = tmp; _nonFullSegments.RemoveAt(n); } else if (i > 0) { var tmp = _nonFullSegments[i]; _nonFullSegments[i] = _nonFullSegments[0]; _nonFullSegments[0] = tmp; } return true; } } int size = Math.Max((int)nwords, _defaultSegmentSize); var storage = new ulong[size]; var mem = new Memory(storage); segment = new Segment(mem, (uint)_segments.Count); _segments.Add(segment); if (!segment.TryAllocate(nwords, out result.Offset)) throw new InvalidProgramException(); result.SegmentIndex = segment.Id; if (!segment.IsFull) { _nonFullSegments.Add(segment); int n = _nonFullSegments.Count - 1; var tmp = _nonFullSegments[0]; _nonFullSegments[0] = _nonFullSegments[n]; _nonFullSegments[n] = tmp; } return true; } } }