C#で確保済みbyte配列でmallocモドキ

GC任せにしてbyte配列何度も確保したくないし、Spanが来るから積極的に部分配列を使いたい。 byteに対して標準Cライブラリのmallocみたいなことできないかなーとほんのり思っていました。

mallocぽいというのはこういう感じですね。フラグメンテーション上等! f:id:enrike3:20180710220418p:plain

やってみたらSortedSetが優秀だったのでえらくあっさり作れました… スレッドセーフではないですけどね。シリアライズ用にちょいちょいSpanを借りるなら ArrayPoolからbyte借りてくるよりも向くのではないかなーとかとか。

using System;
using System.Collections.Generic;

public struct ByteSegment : IComparable<ByteSegment>
{
    public int Offset { get; }
    public int Length { get; }

    public ByteSegment(int offset, int length)
    {
        Offset = offset;
        Length = length;
    }

    public int CompareTo(ByteSegment other)
    {
        return Offset - other.Offset;
    }

    public override string ToString()
    {
        return $"{Offset},{Length}";
    }
}

public class BytePool
{
    SortedSet<ByteSegment> _segments = new SortedSet<ByteSegment>();
    public byte[] Buffer { get; private set; }

    public BytePool(int capacity)
    {
        Buffer = new byte[capacity];
    }

    public ByteSegment Alloc(int length)
    {
        int offset = 0;
        if(_segments.Count != 0)
        {
            foreach(var s in _segments)
            {
                var desired = offset + length;
                if(s.Offset < desired)
                {
                    offset = s.Offset + s.Length;
                    continue;
                }
                else
                {
                    break;
                }
            }
        }

        if (Buffer.Length < (offset + length))
            throw new Exception("無理ぽ");

        var newSegment = new ByteSegment(offset, length);
        _segments.Add(newSegment);

        return newSegment;
    }

    public void Release(ByteSegment segment)
    {
        _segments.Remove(segment);
    }

    public Span<byte> ToSpan(ByteSegment segment)
    {
        return new Span<byte>(Buffer, segment.Offset, segment.Length);
    }
}