'Memory sequence - use ArrayPool instead and make each chunk 128 bytes long

I had to create a MakeSequence method for the Message implementation. The Message class is part of a library, so I cannot change it. If you have a look at the Message class, you'll see that using an ArrayPool is mandatory and I would also like each chunk to be 128 bytes long.

In my attempt I used Encoding.UTF8.GetBytes which is wrong and results in an ArgumentException thrown at .Return(...) in Message.Dispose.

System.ArgumentException: The buffer is not associated with this pool and may not be returned to it. (Parameter 'array') at System.Buffers.TlsOverPerCoreLockedStacksArrayPool`1.Return(T[] array, Boolean clearArray)

So how do I make MakeSequence use an ArrayPool instead and if the input array is greater than 128 bytes, to split it on 128 bytes long chunks?

var bytes = Encoding.UTF8.GetBytes("hellohellohellohellohellohellohellohello");
var sequence = Helpers.MakeSequence(bytes);

var message = new Message(sequence);
...

message.Dispose(); // System.ArgumentException: The buffer is not associated with this pool and may not be returned to it
public readonly struct Message : IDisposable
{
    public Message(ReadOnlySequence<byte> sequence)
    {
        Sequence = sequence;
    }

    public ReadOnlySequence<byte> Sequence { get; }

    public void Dispose()
    {
        foreach (var chunk in Sequence)
        {
            if (MemoryMarshal.TryGetArray(chunk, out var segment))
            {
                ArrayPool<byte>.Shared.Return(segment.Array!);
            }
        }
    }
}

public static class Helpers
{
    public static ReadOnlySequence<byte> MakeSequence(params byte[][] parts)
    {
        if (parts.Length == 0)
        {
            return ReadOnlySequence<byte>.Empty;
        }

        if (parts.Length == 1)
        {
            return new ReadOnlySequence<byte>(parts[0]);
        }

        Chunk<byte> first = null;
        Chunk<byte> last = null;
        foreach (var part in parts)
        {
            if (first == null)
            {
                first = new Chunk<byte>(part);
                last = first;
            }
            else
            {
                last = last.Add(part);
            }
        }

        return new ReadOnlySequence<byte>(first, 0, last, last.Memory.Length);
    }
}

public sealed class Chunk<T> : ReadOnlySequenceSegment<T>
{
    public Chunk(ReadOnlyMemory<T> memory)
    {
        Memory = memory;
    }

    public Chunk<T> Add(ReadOnlyMemory<T> segment)
    {
        var chunk = new Chunk<T>(segment)
        {
            RunningIndex = RunningIndex + Memory.Length
        };

        Next = chunk;
        return chunk;
    }
}


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source