将字符串转换为 IMemoryOwner / 将字符串复制到租用的缓冲区中

Convert string to IMemoryOwner / Copy string into a rented buffer

我有一个自定义记忆序列OwnedMemorySequence。我想在里面放一根绳子。问题是我无法编译它。通常像 Encoding.UTF8.GetBytes 这样的东西,然后将该缓冲区复制到租用的缓冲区中应该可以工作,但在我的情况下存在编译时错误。

var message = "Hello";

var buffer = MemoryPool<byte>.Shared.Rent(Encoding.UTF8.GetByteCount(message));
Encoding.UTF8.Convert(message, buffer.Memory, true, out _, out _, out _); // no such overload

var seq = new OwnedMemorySequence<byte>();
seq.Append(buffer);

代码

public sealed class OwnedMemorySequence<T> : IDisposable
{
    private readonly CollectionDisposable _disposable = new();
    private readonly MemorySequence<T> _sequence = new();

    public ReadOnlySequence<T> ReadOnlySequence => _sequence.ReadOnlySequence;

    public void Dispose()
    {
        _disposable.Dispose();
    }

    public OwnedMemorySequence<T> Append(IMemoryOwner<T> memoryOwner)
    {
        _disposable.Add(memoryOwner);
        _sequence.Append(memoryOwner.Memory);
        return this;
    }

    public ReadOnlySequence<T> CreateReadOnlySequence(int firstBufferStartIndex, int lastBufferEndIndex)
    {
        return _sequence.CreateReadOnlySequence(firstBufferStartIndex, lastBufferEndIndex);
    }
}

public sealed class MemorySequence<T>
{
    private MemorySegment? _head;
    private MemorySegment? _tail;

    public ReadOnlySequence<T> ReadOnlySequence => CreateReadOnlySequence(0, _tail?.Memory.Length ?? 0);

    public MemorySequence<T> Append(ReadOnlyMemory<T> buffer)
    {
        if (_tail == null)
            _head = _tail = new MemorySegment(buffer, 0);
        else
            _tail = _tail.Append(buffer);
        return this;
    }

    public ReadOnlySequence<T> CreateReadOnlySequence(int firstBufferStartIndex, int lastBufferEndIndex)
    {
        return _tail == null ? new ReadOnlySequence<T>(Array.Empty<T>()) : new ReadOnlySequence<T>(_head!, firstBufferStartIndex, _tail, lastBufferEndIndex);
    }

    private sealed class MemorySegment : ReadOnlySequenceSegment<T>
    {
        public MemorySegment(ReadOnlyMemory<T> memory, long runningIndex)
        {
            Memory = memory;
            RunningIndex = runningIndex;
        }

        public MemorySegment Append(ReadOnlyMemory<T> nextMemory)
        {
            var next = new MemorySegment(nextMemory, RunningIndex + Memory.Length);
            Next = next;
            return next;
        }
    }
}

public static class MemoryOwnerExtensions
{
    /// <summary>
    ///     Rent a buffer from a pool with an exact length.
    /// </summary>
    /// <param name="pool">The <see cref="MemoryPool{T}" /> instance.</param>
    /// <param name="exactBufferSize">The exact size of the buffer.</param>
    public static IMemoryOwner<T> RentExact<T>(this MemoryPool<T> pool, int exactBufferSize)
    {
        if (pool == null) throw new ArgumentNullException(nameof(pool));

        var rented = pool.Rent(exactBufferSize);

        if (exactBufferSize == rented.Memory.Length)
            return rented;

        return new SliceOwner<T>(rented, 0, exactBufferSize);
    }

    /// <summary>
    ///     Wrap an existing <see cref="IMemoryOwner{T}" /> instance in a lightweight manner, but allow
    ///     the <see cref="IMemoryOwner{T}.Memory" /> member to have a different length.
    /// </summary>
    /// <param name="owner">The original instance.</param>
    /// <param name="start">The starting offset of the slice.</param>
    /// <param name="length">The length of the slice.</param>
    public static IMemoryOwner<T> Slice<T>(this IMemoryOwner<T> owner, int start, int length)
    {
        if (owner == null) throw new ArgumentNullException(nameof(owner));

        if (start == 0 && length == owner.Memory.Length)
            return owner;

        if ((uint)start >= (uint)owner.Memory.Length) throw new ArgumentOutOfRangeException(nameof(start));
        if ((uint)length > (uint)(owner.Memory.Length - start)) throw new ArgumentOutOfRangeException(nameof(length));

        return new SliceOwner<T>(owner, start, length);
    }

    /// <summary>
    ///     Wrap an existing <see cref="IMemoryOwner{T}" /> instance in a lightweight manner, but allow
    ///     the <see cref="IMemoryOwner{T}.Memory" /> member to have a different length.
    /// </summary>
    /// <param name="owner">The original instance.</param>
    /// <param name="start">The starting offset of the slice.</param>
    public static IMemoryOwner<T> Slice<T>(this IMemoryOwner<T> owner, int start)
    {
        if (owner == null) throw new ArgumentNullException(nameof(owner));

        if (start == 0)
            return owner;

        if ((uint)start >= (uint)owner.Memory.Length) throw new ArgumentOutOfRangeException(nameof(start));

        return new SliceOwner<T>(owner, start);
    }

    private sealed class SliceOwner<T> : IMemoryOwner<T>
    {
        private IMemoryOwner<T> _owner;

        public SliceOwner(IMemoryOwner<T> owner, int start, int length)
        {
            _owner = owner;
            Memory = _owner.Memory.Slice(start, length);
        }

        public SliceOwner(IMemoryOwner<T> owner, int start)
        {
            _owner = owner;
            Memory = _owner.Memory.Slice(start);
        }

        public Memory<T> Memory { get; private set; }

        public void Dispose()
        {
            if (_owner != null)
            {
                _owner.Dispose();
                _owner = null;
            }

            Memory = default;
        }
    }
}

的确,不存在这样的过载。您在文档中的哪个位置查看了该参数?

Encoding.Convert Method

编码器有类似的方法。

Encoder.Convert Method

EncodingExtensions.Convert Method

using System;
using System.Buffers;
using System.Text;

public class Program
{
    public static void Main()
    {
        var message = "Hello";

        var buffer = MemoryPool<byte>.Shared.Rent(Encoding.UTF8.GetByteCount(message));
        Encoding.UTF8.GetEncoder().Convert(message, buffer.Memory.Span, true, out _, out _, out _);

        Console.WriteLine(Encoding.UTF8.GetString(buffer.Memory.Span));
    }
}