mirror of
https://github.com/FabInfra/capnproto-dotnetcore_Runtime.git
synced 2025-03-12 14:51:41 +01:00
146 lines
5.2 KiB
C#
146 lines
5.2 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
|
|
namespace Capnp
|
|
{
|
|
/// <summary>
|
|
/// The segment allocator default implementation.
|
|
/// </summary>
|
|
public class SegmentAllocator : ISegmentAllocator
|
|
{
|
|
class Segment
|
|
{
|
|
public Segment(Memory<ulong> mem, uint id)
|
|
{
|
|
Mem = mem;
|
|
Id = id;
|
|
}
|
|
|
|
public uint Id { get; }
|
|
public Memory<ulong> 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<Segment> _segments = new List<Segment>();
|
|
readonly List<Segment> _nonFullSegments = new List<Segment>();
|
|
|
|
/// <summary>
|
|
/// Constructs an instance.
|
|
/// </summary>
|
|
/// <param name="defaultSegmentSize">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.</param>
|
|
public SegmentAllocator(int defaultSegmentSize = 64)
|
|
{
|
|
_defaultSegmentSize = defaultSegmentSize;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The list of currently allocated segments, each one truncated to its actual occupancy.
|
|
/// </summary>
|
|
public IReadOnlyList<Memory<ulong>> Segments => _segments.LazyListSelect(s => s.Mem.Slice(0, s.FreeOffset));
|
|
|
|
/// <summary>
|
|
/// Allocates memory.
|
|
/// </summary>
|
|
/// <param name="nwords">Number of words to allocate</param>
|
|
/// <param name="preferredSegmentIndex">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 <paramref name="forcePreferredSegment"/>).</param>
|
|
/// <param name="result">The allocated memory slice in case of success (<code>default(SegmentSlice) otherwise)</code></param>
|
|
/// <param name="forcePreferredSegment">Whether using the preferred segment is mandatory. If it is and there is not
|
|
/// enough space available, allocation will fail.</param>
|
|
/// <returns>Whether allocation was successful.</returns>
|
|
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<ulong>(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;
|
|
}
|
|
}
|
|
} |