Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Length for ReadOnlySequence created out of sliced Memory owned by MemoryManager #57479

Merged
merged 4 commits into from
Aug 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ public ReadOnlySequence(ReadOnlyMemory<T> memory)
_startObject = manager;
_endObject = manager;
_startInteger = ReadOnlySequence.MemoryManagerToSequenceStart(index);
_endInteger = length;
_endInteger = index + length;
}
else if (MemoryMarshal.TryGetArray(memory, out ArraySegment<T> segment))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@
using System.Buffers;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;

namespace System.Memory.Tests
{
public abstract class ReadOnlySequenceFactory<T>
{
public static ReadOnlySequenceFactory<T> ArrayFactory { get; } = new ArrayTestSequenceFactory();
public static ReadOnlySequenceFactory<T> MemoryFactory { get; } = new MemoryTestSequenceFactory();
public static ReadOnlySequenceFactory<T> MemoryManagerFactory { get; } = new MemoryManagerTestSequenceFactory();
public static ReadOnlySequenceFactory<T> SingleSegmentFactory { get; } = new SingleSegmentTestSequenceFactory();
public static ReadOnlySequenceFactory<T> SegmentPerItemFactory { get; } = new BytePerSegmentTestSequenceFactory();
public static ReadOnlySequenceFactory<T> SplitInThree { get; } = new SegmentsTestSequenceFactory(3);
Expand All @@ -37,7 +41,11 @@ internal class MemoryTestSequenceFactory : ReadOnlySequenceFactory<T>
{
public override ReadOnlySequence<T> CreateOfSize(int size)
{
return CreateWithContent(new T[size]);
#if DEBUG
return new ReadOnlySequence<T>(new ReadOnlyMemory<T>(new T[size + 1]).Slice(1));
#else
return new ReadOnlySequence<T>(new ReadOnlyMemory<T>(new T[size]));
#endif
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I generally prefer for the ifdef to be around the minimal thing that's different, so that it's clear what exactly the difference is. e.g.

ReadOnlyMemory<T> m = new T[size + 1];
#if DEBUG
m = m.Slice(1);
#endif
return new ReadOnlySequence<T>(m);

}

public override ReadOnlySequence<T> CreateWithContent(T[] data)
Expand Down Expand Up @@ -112,6 +120,49 @@ public override ReadOnlySequence<T> CreateWithContent(T[] data)
}
}

internal class MemoryManagerTestSequenceFactory : ReadOnlySequenceFactory<T>
{
public override ReadOnlySequence<T> CreateOfSize(int size)
{
#if DEBUG
return new ReadOnlySequence<T>(new CustomMemoryManager(size + 1).Memory.Slice(1));
#else
return new ReadOnlySequence<T>(new CustomMemoryManager(size).Memory);
#endif
}

public override ReadOnlySequence<T> CreateWithContent(T[] data)
{
return new ReadOnlySequence<T>(new CustomMemoryManager(data).Memory);
}

private unsafe class CustomMemoryManager : MemoryManager<T>
{
private readonly T[] _buffer;

public CustomMemoryManager(int size) => _buffer = new T[size];

public CustomMemoryManager(T[] content) => _buffer = content;

public unsafe override Span<T> GetSpan() => _buffer;

public override unsafe MemoryHandle Pin(int elementIndex = 0)
{
if ((uint)elementIndex > (uint)_buffer.Length)
{
throw new ArgumentOutOfRangeException(nameof(elementIndex));
}

var handle = GCHandle.Alloc(_buffer, GCHandleType.Pinned);
return new MemoryHandle(Unsafe.Add<T>((void*)handle.AddrOfPinnedObject(), elementIndex), handle, this);
}

public override void Unpin() { }

protected override void Dispose(bool disposing) { }
}
}

public static ReadOnlySequence<T> CreateSegments(params T[][] inputs) => CreateSegments(inputs.Select(input => (ReadOnlyMemory<T>)input.AsMemory()));

public static ReadOnlySequence<T> CreateSegments(IEnumerable<ReadOnlyMemory<T>> inputs)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ public class Memory : ReadOnlySequenceTestsByte
public Memory() : base(ReadOnlySequenceFactory<byte>.MemoryFactory) { }
}

public class MemoryManager : ReadOnlySequenceTestsByte
{
public MemoryManager() : base(ReadOnlySequenceFactory<byte>.MemoryManagerFactory) { }
}

public class SingleSegment : ReadOnlySequenceTestsByte
{
public SingleSegment() : base(ReadOnlySequenceFactory<byte>.SingleSegmentFactory) { }
Expand Down