-
Notifications
You must be signed in to change notification settings - Fork 32
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement Span/Memory overloads for .NET 6 Streams, TextReaders, and …
…TextWriters. fix #97
- Loading branch information
Showing
7 changed files
with
314 additions
and
88 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Reflection; | ||
using Medallion.Shell; | ||
using NUnit.Framework; | ||
|
||
namespace MedallionShell.Tests.Streams; | ||
|
||
internal class StreamImplementationTest | ||
{ | ||
#if NETCOREAPP | ||
[TestCase(typeof(Stream))] | ||
[TestCase(typeof(TextWriter))] | ||
[TestCase(typeof(TextReader))] | ||
public void TestAllStreamImplementationsOverrideSpanMethods(Type baseType) | ||
{ | ||
var requiredMethods = baseType.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) | ||
.Where( | ||
m => m.IsVirtual | ||
&& (m.IsPublic || m.Attributes.HasFlag(MethodAttributes.FamORAssem) || m.Attributes.HasFlag(MethodAttributes.Family)) | ||
&& m.GetParameters().Select(p => p.ParameterType) | ||
.Any( | ||
t => t.IsConstructedGenericType | ||
&& new[] { typeof(Span<>), typeof(ReadOnlySpan<>), typeof(Memory<>), typeof(ReadOnlyMemory<>) }.Contains(t.GetGenericTypeDefinition()) | ||
) | ||
) | ||
.ToArray(); | ||
|
||
var implementingTypes = typeof(Command).Assembly.GetTypes() | ||
.Where(t => !t.IsAbstract && t.IsSubclassOf(baseType)); | ||
|
||
List<string> violations = new(); | ||
foreach (var implementation in implementingTypes) | ||
{ | ||
var implementationMethods = implementation.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); | ||
|
||
foreach (var method in requiredMethods) | ||
{ | ||
var implementingMethod = implementationMethods.Single( | ||
m => m.Name == method.Name | ||
&& m.GetParameters().Select(p => p.ParameterType).SequenceEqual(method.GetParameters().Select(p => p.ParameterType)) | ||
); | ||
if (implementingMethod.DeclaringType!.Assembly != typeof(Command).Assembly) | ||
{ | ||
violations.Add($"{implementation} must implement {method}"); | ||
} | ||
} | ||
} | ||
|
||
Assert.IsEmpty(violations); | ||
} | ||
#endif | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
using System; | ||
using System.Diagnostics; | ||
using System.Runtime.CompilerServices; | ||
|
||
namespace Medallion.Shell; | ||
|
||
internal static class Shims | ||
{ | ||
public static T[] EmptyArray<T>() => | ||
#if !NET45 | ||
Array.Empty<T>(); | ||
#else | ||
Empty<T>.Array; | ||
|
||
private static class Empty<T> | ||
{ | ||
public static readonly T[] Array = new T[0]; | ||
} | ||
#endif | ||
|
||
#if !NETCOREAPP2_1_OR_GREATER && !NETSTANDARD2_1 | ||
public static Span<T> AsSpan<T>(this T[] array, int start, int length) => new(new(array, start, length)); | ||
#endif | ||
} | ||
|
||
#pragma warning disable SA1306 // Field names should begin with lower-case letter | ||
|
||
#if !NETCOREAPP2_1_OR_GREATER && !NETSTANDARD2_1 | ||
internal readonly struct Memory<T> | ||
{ | ||
public readonly T[] Array; | ||
public readonly int Offset; | ||
public readonly int Length; | ||
|
||
public Span<T> Span => new(this); | ||
|
||
public Memory(T[] array, int offset, int length) | ||
{ | ||
Debug.Assert(offset >= 0 && length >= 0 && offset + length <= array.Length, "buffer must be valid"); | ||
|
||
this.Array = array; | ||
this.Offset = offset; | ||
this.Length = length; | ||
} | ||
|
||
public Memory<T> Slice(int start) => this.Slice(start, this.Length - start); | ||
public Memory<T> Slice(int start, int length) => new(this.Array, this.Offset + start, length); | ||
|
||
public void CopyTo(Memory<T> destination) => | ||
System.Array.Copy(sourceArray: this.Array, sourceIndex: this.Offset, destinationArray: destination.Array, destinationIndex: destination.Offset, length: this.Length); | ||
} | ||
|
||
internal readonly ref struct Span<T> | ||
{ | ||
public readonly Memory<T> Memory; | ||
|
||
public int Length => this.Memory.Length; | ||
|
||
public Span(Memory<T> memory) { this.Memory = memory; } | ||
|
||
public static implicit operator Span<T>(T[] array) => new(new(array, 0, array.Length)); | ||
|
||
public void CopyTo(Span<T> destination) => this.Memory.CopyTo(destination.Memory); | ||
|
||
public Span<T> Slice(int start) => new(this.Memory.Slice(start)); | ||
} | ||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.