Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/corefx.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAtsushi Kanamori <AtsushiKan@users.noreply.github.com>2016-11-16 00:15:16 +0300
committerStephen Toub <stoub@microsoft.com>2016-11-16 00:15:16 +0300
commitccc44b7b2e9145251e2ca98bbe0936938ed94379 (patch)
treeea7795310a027a6f0ac72bc83245ab5be7394159 /src
parent0300e2c42e3ce96240e05daff6f6c5a5e03081a6 (diff)
Portable (and thus slower) version of Span<> (#13562)
* Rebase to make review readable again. * Remove the type casts from DangerousGetPinnedReference - store object as Pinnable<T> * Removed stable version list. * DangerousCreate gets more Dangerous... * Updated dependency versions *again*. * IsReferenceFree fast-paths for common primitives. * Move exception throwing out of inline code. * Remove Windows-only restriction. * Remove baseline version. * Don't hardcode array header size. * Another dependency version change! What's the matter, don't we have any version numbers you like?? * Version => 4.0.0.0 * Use Unsafe.Add rather than SpanHeloer.Add in this[index] (all this repeated source is annoying - unfortunately, Rosylin at this point at least, insists that "ref" variables be initialized once only at declaration point and doesn't allow the conditional ternary operator on ref types. This makes it hard to manually inline DangerousGetPinnableReference in a nice way...) * ThrowHelper.Throw ==> throw ThrowHelper.Create Allow JIT to recognize the throw path as a no-return path so it can throw it into the cold region. * Revert the specific primitive type checks in IsReferenceFree(). It pessimizes the routine on the desktop CLR. * Fix DangerousGetPinnableReference to be a method to match spec. * PR feedback. Description: "Provide classes" => "Provide types" Add fastpath for specific types in IsReferenceFree Make MeasureArrayAdjustment a PerTypeValues private * More concise operator== Eh... hard to tell with all the noise but it did seems to shave a couple of percentage points off. * Follow Ben's suggested ThrowHelper pattern. * Update ThrowHelper comments based on Ben's feedback. * Remove trailing whitespace. * Dependency bump. Again... * Fix up some tests so the IL is Span-legal. Ah, the fun of writing Span code w/out stack-only enforcement. * Add a reference assembly and some of the plumbing... ...to typeforward on CoreClr. Right now, CoreClr fails 44 out of 55 tests so I believe it's premature to throw the switch just yet. This just gets some of the grunt work out of the way. * Add C#6.0 workaround. After chatting offline, we'll accomodate C# 6.0 for now by having the indexer return "T" rather than "ref T". Some performant code require the "ref T" version so as another stopgap, we'll provide a GetItem() method that returns "ref T". Once the tooling story is in better shape, we'll merge GetItem() and the indexer into a single indexer that returns "ref T". * Dependency update again. This is getting really monotonous.
Diffstat (limited to 'src')
-rw-r--r--src/System.Memory/System.Memory.sln27
-rw-r--r--src/System.Memory/pkg/System.Memory.builds9
-rw-r--r--src/System.Memory/pkg/System.Memory.pkgproj10
-rw-r--r--src/System.Memory/ref/System.Memory.builds8
-rw-r--r--src/System.Memory/ref/System.Memory.cs35
-rw-r--r--src/System.Memory/ref/System.Memory.csproj19
-rw-r--r--src/System.Memory/ref/project.json9
-rw-r--r--src/System.Memory/src/Resources/Strings.resx135
-rw-r--r--src/System.Memory/src/System.Memory.builds9
-rw-r--r--src/System.Memory/src/System.Memory.csproj29
-rw-r--r--src/System.Memory/src/System/Pinnable.cs18
-rw-r--r--src/System.Memory/src/System/Span.cs431
-rw-r--r--src/System.Memory/src/System/SpanHelpers.cs132
-rw-r--r--src/System.Memory/src/System/ThrowHelper.cs58
-rw-r--r--src/System.Memory/src/project.json13
-rw-r--r--src/System.Memory/tests/Span/CopyTo.cs87
-rw-r--r--src/System.Memory/tests/Span/CtorArray.cs121
-rw-r--r--src/System.Memory/tests/Span/CtorArrayInt.cs54
-rw-r--r--src/System.Memory/tests/Span/CtorArrayIntInt.cs80
-rw-r--r--src/System.Memory/tests/Span/CtorPointerInt.cs78
-rw-r--r--src/System.Memory/tests/Span/DangerousCreate.cs51
-rw-r--r--src/System.Memory/tests/Span/DangerousGetPinnableReference.cs68
-rw-r--r--src/System.Memory/tests/Span/Empty.cs26
-rw-r--r--src/System.Memory/tests/Span/Equality.cs93
-rw-r--r--src/System.Memory/tests/Span/Overflow.cs64
-rw-r--r--src/System.Memory/tests/Span/Slice.cs70
-rw-r--r--src/System.Memory/tests/Span/TestHelpers.cs79
-rw-r--r--src/System.Memory/tests/Span/ToArray.cs30
-rw-r--r--src/System.Memory/tests/System.Memory.Tests.builds16
-rw-r--r--src/System.Memory/tests/System.Memory.Tests.csproj32
30 files changed, 1891 insertions, 0 deletions
diff --git a/src/System.Memory/System.Memory.sln b/src/System.Memory/System.Memory.sln
new file mode 100644
index 0000000000..9fcf61cee4
--- /dev/null
+++ b/src/System.Memory/System.Memory.sln
@@ -0,0 +1,27 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.25831.1
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Memory", "src\System.Memory.csproj", "{4BBC8F69-D03E-4432-AA8A-D458FA5B235A}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Memory.Tests", "tests\System.Memory.Tests.csproj", "{15DC55FA-E644-4B87-A62A-DCF849031633}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {4BBC8F69-D03E-4432-AA8A-D458FA5B235A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4BBC8F69-D03E-4432-AA8A-D458FA5B235A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4BBC8F69-D03E-4432-AA8A-D458FA5B235A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4BBC8F69-D03E-4432-AA8A-D458FA5B235A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {15DC55FA-E644-4B87-A62A-DCF849031633}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {15DC55FA-E644-4B87-A62A-DCF849031633}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {15DC55FA-E644-4B87-A62A-DCF849031633}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {15DC55FA-E644-4B87-A62A-DCF849031633}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/src/System.Memory/pkg/System.Memory.builds b/src/System.Memory/pkg/System.Memory.builds
new file mode 100644
index 0000000000..ad45fd5075
--- /dev/null
+++ b/src/System.Memory/pkg/System.Memory.builds
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+ <ItemGroup>
+ <Project Include="System.Memory.pkgproj" Condition="'$(OS)'=='Windows_NT'" />
+ </ItemGroup>
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.traversal.targets))\dir.traversal.targets" />
+</Project>
+
diff --git a/src/System.Memory/pkg/System.Memory.pkgproj b/src/System.Memory/pkg/System.Memory.pkgproj
new file mode 100644
index 0000000000..8200460e64
--- /dev/null
+++ b/src/System.Memory/pkg/System.Memory.pkgproj
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+ <ItemGroup>
+ <ProjectReference Include="..\src\System.Memory.csproj">
+ <SupportedFramework>net45;netcore45;wp8;wpa81;netcoreapp1.0;$(AllXamarinFrameworks)</SupportedFramework>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>
diff --git a/src/System.Memory/ref/System.Memory.builds b/src/System.Memory/ref/System.Memory.builds
new file mode 100644
index 0000000000..87b2f7a053
--- /dev/null
+++ b/src/System.Memory/ref/System.Memory.builds
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+ <ItemGroup>
+ <Project Include="System.Memory.csproj" />
+ </ItemGroup>
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.traversal.targets))\dir.traversal.targets" />
+</Project>
diff --git a/src/System.Memory/ref/System.Memory.cs b/src/System.Memory/ref/System.Memory.cs
new file mode 100644
index 0000000000..5716a32bd9
--- /dev/null
+++ b/src/System.Memory/ref/System.Memory.cs
@@ -0,0 +1,35 @@
+#pragma warning disable 0809 //warning CS0809: Obsolete member 'Span<T>.Equals(object)' overrides non-obsolete member 'object.Equals(object)'
+namespace System
+{
+ [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
+ public partial struct Span<T>
+ {
+ public static readonly System.Span<T> Empty;
+ [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]public Span(T[] array) { throw null;}
+ [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]public Span(T[] array, int start) { throw null;}
+ [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]public Span(T[] array, int start, int length) { throw null;}
+ [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]public unsafe Span(void* pointer, int length) { throw null;}
+ public bool IsEmpty { get { throw null; } }
+ public T this[int index] { [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]get { throw null; } [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]set { throw null; }}
+ [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]public ref T GetItem(int index) { throw null; }
+ public int Length { get { throw null; } }
+ public void CopyTo(System.Span<T> destination) { }
+ [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]public static System.Span<T> DangerousCreate(object obj, ref T objectData, int length) { throw null; }
+ [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]public ref T DangerousGetPinnableReference() { throw null; }
+ [System.ObsoleteAttribute("Equals() on Span will always throw an exception. Use == instead.")]
+ [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
+ public override bool Equals(object obj) { throw null; }
+ [System.ObsoleteAttribute("GetHashCode() on Span will always throw an exception.")]
+ [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
+ public override int GetHashCode() { throw null; }
+ public static bool operator ==(System.Span<T> left, System.Span<T> right) { throw null; }
+ public static implicit operator System.Span<T> (T[] array) { throw null; }
+ public static implicit operator System.Span<T> (System.ArraySegment<T> arraySegment) { throw null; }
+ public static bool operator !=(System.Span<T> left, System.Span<T> right) { throw null; }
+ [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]public System.Span<T> Slice(int start) { throw null; }
+ [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]public System.Span<T> Slice(int start, int length) { throw null; }
+ public T[] ToArray() { throw null; }
+ public bool TryCopyTo(System.Span<T> destination) { throw null; }
+ }
+}
+
diff --git a/src/System.Memory/ref/System.Memory.csproj b/src/System.Memory/ref/System.Memory.csproj
new file mode 100644
index 0000000000..f1256bbb32
--- /dev/null
+++ b/src/System.Memory/ref/System.Memory.csproj
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build">
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+ <PropertyGroup>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ <OutputType>Library</OutputType>
+ <CLSCompliant>false</CLSCompliant>
+ <AssemblyVersion>4.0.0.0</AssemblyVersion>
+ <NuGetTargetMoniker>.NETStandard,Version=v1.0</NuGetTargetMoniker>
+ <ProjectGuid>{0EF9D369-7097-44F9-BEBA-C32AF5EB4756}</ProjectGuid>
+ </PropertyGroup>
+ <ItemGroup>
+ <Compile Include="System.Memory.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="project.json" />
+ </ItemGroup>
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>
diff --git a/src/System.Memory/ref/project.json b/src/System.Memory/ref/project.json
new file mode 100644
index 0000000000..9bda631612
--- /dev/null
+++ b/src/System.Memory/ref/project.json
@@ -0,0 +1,9 @@
+{
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "1.0.1",
+ "System.Runtime": "4.1.0"
+ },
+ "frameworks": {
+ "netstandard1.0": {}
+ }
+}
diff --git a/src/System.Memory/src/Resources/Strings.resx b/src/System.Memory/src/Resources/Strings.resx
new file mode 100644
index 0000000000..e5bc8b905b
--- /dev/null
+++ b/src/System.Memory/src/Resources/Strings.resx
@@ -0,0 +1,135 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+ <!--
+ Microsoft ResX Schema
+
+ Version 2.0
+
+ The primary goals of this format is to allow a simple XML format
+ that is mostly human readable. The generation and parsing of the
+ various data types are done through the TypeConverter classes
+ associated with the data types.
+
+ Example:
+
+ ... ado.net/XML headers & schema ...
+ <resheader name="resmimetype">text/microsoft-resx</resheader>
+ <resheader name="version">2.0</resheader>
+ <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+ <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+ <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+ <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+ <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+ <value>[base64 mime encoded serialized .NET Framework object]</value>
+ </data>
+ <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+ <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+ <comment>This is a comment</comment>
+ </data>
+
+ There are any number of "resheader" rows that contain simple
+ name/value pairs.
+
+ Each data row contains a name, and value. The row also contains a
+ type or mimetype. Type corresponds to a .NET class that support
+ text/value conversion through the TypeConverter architecture.
+ Classes that don't support this are serialized and stored with the
+ mimetype set.
+
+ The mimetype is used for serialized objects, and tells the
+ ResXResourceReader how to depersist the object. This is currently not
+ extensible. For a given mimetype the value must be set accordingly:
+
+ Note - application/x-microsoft.net.object.binary.base64 is the format
+ that the ResXResourceWriter will generate, however the reader can
+ read any of the formats listed below.
+
+ mimetype: application/x-microsoft.net.object.binary.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.soap.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.bytearray.base64
+ value : The object must be serialized into a byte array
+ : using a System.ComponentModel.TypeConverter
+ : and then encoded with base64 encoding.
+ -->
+ <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+ <xsd:element name="root" msdata:IsDataSet="true">
+ <xsd:complexType>
+ <xsd:choice maxOccurs="unbounded">
+ <xsd:element name="metadata">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" />
+ </xsd:sequence>
+ <xsd:attribute name="name" use="required" type="xsd:string" />
+ <xsd:attribute name="type" type="xsd:string" />
+ <xsd:attribute name="mimetype" type="xsd:string" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="assembly">
+ <xsd:complexType>
+ <xsd:attribute name="alias" type="xsd:string" />
+ <xsd:attribute name="name" type="xsd:string" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="data">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+ <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+ <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="resheader">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" />
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:schema>
+ <resheader name="resmimetype">
+ <value>text/microsoft-resx</value>
+ </resheader>
+ <resheader name="version">
+ <value>2.0</value>
+ </resheader>
+ <resheader name="reader">
+ <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <resheader name="writer">
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <data name="ArrayTypeMustBeExactMatch" xml:space="preserve">
+ <value>The array type must be exactly {0}.</value>
+ </data>
+ <data name="CannotCallEqualsOnSpan" xml:space="preserve">
+ <value>Equals() on Span and ReadOnlySpan is not supported. Use operator== instead.</value>
+ </data>
+ <data name="CannotCallGetHashCodeOnSpan" xml:space="preserve">
+ <value>GetHashCode() on Span and ReadOnlySpan is not supported.</value>
+ </data>
+ <data name="Argument_InvalidTypeWithPointersNotSupported" xml:space="preserve">
+ <value>Cannot use type '{0}'. Only value types without pointers or references are supported.</value>
+ </data>
+ <data name="Argument_DestinationTooShort" xml:space="preserve">
+ <value>Destination is too short.</value>
+ </data>
+</root>
diff --git a/src/System.Memory/src/System.Memory.builds b/src/System.Memory/src/System.Memory.builds
new file mode 100644
index 0000000000..441802d247
--- /dev/null
+++ b/src/System.Memory/src/System.Memory.builds
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+ <ItemGroup>
+ <Project Include="System.Memory.csproj" />
+ </ItemGroup>
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.traversal.targets))\dir.traversal.targets" />
+</Project>
+
diff --git a/src/System.Memory/src/System.Memory.csproj b/src/System.Memory/src/System.Memory.csproj
new file mode 100644
index 0000000000..c0ddd27cf6
--- /dev/null
+++ b/src/System.Memory/src/System.Memory.csproj
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+ <PropertyGroup>
+ <ProjectGuid>{4BBC8F69-D03E-4432-AA8A-D458FA5B235A}</ProjectGuid>
+ <AssemblyName>System.Memory</AssemblyName>
+ <AssemblyVersion>4.0.0.0</AssemblyVersion>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ <CLSCompliant>false</CLSCompliant>
+ <NuGetTargetMoniker>.NETStandard,Version=v1.0</NuGetTargetMoniker>
+ <DocumentationFile>$(OutputPath)$(AssemblyName).xml</DocumentationFile>
+ <IsPartialFacadeAssembly>false</IsPartialFacadeAssembly>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'" />
+ <ItemGroup Condition="'$(IsPartialFacadeAssembly)' != 'true'">
+ <Compile Include="System\Pinnable.cs" />
+ <Compile Include="System\Span.cs" />
+ <Compile Include="System\SpanHelpers.cs" />
+ <Compile Include="System\ThrowHelper.cs" />
+ </ItemGroup>
+ <ItemGroup Condition="'$(IsPartialFacadeAssembly)' == 'true'">
+ <TargetingPackReference Include="System.Private.CoreLib" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="project.json" />
+ </ItemGroup>
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>
diff --git a/src/System.Memory/src/System/Pinnable.cs b/src/System.Memory/src/System/Pinnable.cs
new file mode 100644
index 0000000000..0f9b02ad94
--- /dev/null
+++ b/src/System.Memory/src/System/Pinnable.cs
@@ -0,0 +1,18 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+
+namespace System
+{
+ //
+ // This class exists solely so that arbitrary objects can be Unsafe-casted to it to get a ref to the start of the user data.
+ //
+ [StructLayout(LayoutKind.Sequential)]
+ internal sealed class Pinnable<T>
+ {
+ public T Data;
+ }
+}
diff --git a/src/System.Memory/src/System/Span.cs b/src/System.Memory/src/System/Span.cs
new file mode 100644
index 0000000000..cf7a059216
--- /dev/null
+++ b/src/System.Memory/src/System/Span.cs
@@ -0,0 +1,431 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using EditorBrowsableState = System.ComponentModel.EditorBrowsableState;
+using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute;
+
+#pragma warning disable 0809 //warning CS0809: Obsolete member 'Span<T>.Equals(object)' overrides non-obsolete member 'object.Equals(object)'
+
+namespace System
+{
+ /// <summary>
+ /// Span represents a contiguous region of arbitrary memory. Unlike arrays, it can point to either managed
+ /// or native memory, or to memory allocated on the stack. It is type- and memory-safe.
+ /// </summary>
+ public struct Span<T>
+ {
+ /// <summary>
+ /// Creates a new span over the entirety of the target array.
+ /// </summary>
+ /// <param name="array">The target array.</param>
+ /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="array"/> is a null
+ /// reference (Nothing in Visual Basic).</exception>
+ /// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="array"/> is covariant and array's type is not exactly T[].</exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Span(T[] array)
+ {
+ if (array == null)
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
+ if (default(T) == null && array.GetType() != typeof(T[]))
+ ThrowHelper.ThrowArrayTypeMismatchException_ArrayTypeMustBeExactMatch(typeof(T));
+
+ _length = array.Length;
+ _pinnable = Unsafe.As<Pinnable<T>>(array);
+ _byteOffset = SpanHelpers.PerTypeValues<T>.ArrayAdjustment;
+ }
+
+ /// <summary>
+ /// Creates a new span over the portion of the target array beginning
+ /// at 'start' index and covering the remainder of the array.
+ /// </summary>
+ /// <param name="array">The target array.</param>
+ /// <param name="start">The index at which to begin the span.</param>
+ /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="array"/> is a null
+ /// reference (Nothing in Visual Basic).</exception>
+ /// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="array"/> is covariant and array's type is not exactly T[].</exception>
+ /// <exception cref="System.ArgumentOutOfRangeException">
+ /// Thrown when the specified <paramref name="start"/> is not in the range (&lt;0 or &gt;=Length).
+ /// </exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Span(T[] array, int start)
+ {
+ if (array == null)
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
+ if (default(T) == null && array.GetType() != typeof(T[]))
+ ThrowHelper.ThrowArrayTypeMismatchException_ArrayTypeMustBeExactMatch(typeof(T));
+
+ int arrayLength = array.Length;
+ if ((uint)start > (uint)arrayLength)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+
+ _length = arrayLength - start;
+ _pinnable = Unsafe.As<Pinnable<T>>(array);
+ _byteOffset = SpanHelpers.PerTypeValues<T>.ArrayAdjustment.Add<T>(start);
+ }
+
+ /// <summary>
+ /// Creates a new span over the portion of the target array beginning
+ /// at 'start' index and ending at 'end' index (exclusive).
+ /// </summary>
+ /// <param name="array">The target array.</param>
+ /// <param name="start">The index at which to begin the span.</param>
+ /// <param name="length">The number of items in the span.</param>
+ /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="array"/> is a null
+ /// reference (Nothing in Visual Basic).</exception>
+ /// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="array"/> is covariant and array's type is not exactly T[].</exception>
+ /// <exception cref="System.ArgumentOutOfRangeException">
+ /// Thrown when the specified <paramref name="start"/> or end index is not in the range (&lt;0 or &gt;=Length).
+ /// </exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Span(T[] array, int start, int length)
+ {
+ if (array == null)
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
+ if (default(T) == null && array.GetType() != typeof(T[]))
+ ThrowHelper.ThrowArrayTypeMismatchException_ArrayTypeMustBeExactMatch(typeof(T));
+ if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start))
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+
+ _length = length;
+ _pinnable = Unsafe.As<Pinnable<T>>(array);
+ _byteOffset = SpanHelpers.PerTypeValues<T>.ArrayAdjustment.Add<T>(start);
+ }
+
+ /// <summary>
+ /// Creates a new span over the target unmanaged buffer. Clearly this
+ /// is quite dangerous, because we are creating arbitrarily typed T's
+ /// out of a void*-typed block of memory. And the length is not checked.
+ /// But if this creation is correct, then all subsequent uses are correct.
+ /// </summary>
+ /// <param name="pointer">An unmanaged pointer to memory.</param>
+ /// <param name="length">The number of <typeparamref name="T"/> elements the memory contains.</param>
+ /// <exception cref="System.ArgumentException">
+ /// Thrown when <typeparamref name="T"/> is reference type or contains pointers and hence cannot be stored in unmanaged memory.
+ /// </exception>
+ /// <exception cref="System.ArgumentOutOfRangeException">
+ /// Thrown when the specified <paramref name="length"/> is negative.
+ /// </exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public unsafe Span(void* pointer, int length)
+ {
+ if (!SpanHelpers.IsReferenceFree<T>())
+ ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(T));
+ if (length < 0)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+
+ _length = length;
+ _pinnable = null;
+ _byteOffset = new IntPtr(pointer);
+ }
+
+ /// <summary>
+ /// Create a new span over a portion of a regular managed object. This can be useful
+ /// if part of a managed object represents a "fixed array." This is dangerous because
+ /// "length" is not checked, nor is the fact that "rawPointer" actually lies within the object.
+ /// </summary>
+ /// <param name="obj">The managed object that contains the data to span over.</param>
+ /// <param name="objectData">A reference to data within that object.</param>
+ /// <param name="length">The number of <typeparamref name="T"/> elements the memory contains.</param>
+ /// <exception cref="System.ArgumentNullException">
+ /// Thrown when the specified object is null.
+ /// </exception>
+ /// <exception cref="System.ArgumentOutOfRangeException">
+ /// Thrown when the specified <paramref name="length"/> is negative.
+ /// </exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Span<T> DangerousCreate(object obj, ref T objectData, int length)
+ {
+ if (obj == null)
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.obj);
+ if (length < 0)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.length);
+
+ Pinnable<T> pinnable = Unsafe.As<Pinnable<T>>(obj);
+ IntPtr byteOffset = Unsafe.ByteOffset<T>(ref pinnable.Data, ref objectData);
+ return new Span<T>(pinnable, byteOffset, length);
+ }
+
+ // Constructor for internal use only.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private Span(Pinnable<T> pinnable, IntPtr byteOffset, int length)
+ {
+ Debug.Assert(length >= 0);
+
+ _length = length;
+ _pinnable = pinnable;
+ _byteOffset = byteOffset;
+ }
+
+ /// <summary>
+ /// The number of items in the span.
+ /// </summary>
+ public int Length => _length;
+
+ /// <summary>
+ /// Returns true if Length is 0.
+ /// </summary>
+ public bool IsEmpty => _length == 0;
+
+ /// <summary>
+ /// Returns a reference to specified element of the Span.
+ /// </summary>
+ /// <param name="index"></param>
+ /// <returns></returns>
+ /// <exception cref="System.IndexOutOfRangeException">
+ /// Thrown when index less than 0 or index greater than or equal to Length
+ /// </exception>
+
+ // TODO: https://github.com/dotnet/corefx/issues/13681
+ // Until we get over the hurdle of C# 7 tooling, this indexer will return "T" and have a setter rather than a "ref T". (The doc comments
+ // continue to reflect the original intent of returning "ref T")
+ public T this[int index]
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ if ((uint)index >= ((uint)_length))
+ ThrowHelper.ThrowIndexOutOfRangeException();
+
+ if (_pinnable == null)
+ unsafe { return Unsafe.Add<T>(ref Unsafe.AsRef<T>(_byteOffset.ToPointer()), index); }
+ else
+ return Unsafe.Add<T>(ref Unsafe.AddByteOffset<T>(ref _pinnable.Data, _byteOffset), index);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set
+ {
+ if ((uint) index >= ((uint) _length))
+ ThrowHelper.ThrowIndexOutOfRangeException();
+
+ if (_pinnable == null)
+ unsafe { Unsafe.Add<T>(ref Unsafe.AsRef<T>(_byteOffset.ToPointer()), index) = value; }
+ else
+ Unsafe.Add<T>(ref Unsafe.AddByteOffset<T>(ref _pinnable.Data, _byteOffset), index) = value;
+ }
+ }
+
+ /// <summary>
+ /// Returns a reference to specified element of the Span.
+ /// </summary>
+ /// <param name="index"></param>
+ /// <returns></returns>
+ /// <exception cref="System.IndexOutOfRangeException">
+ /// Thrown when index less than 0 or index greater than or equal to Length
+ /// </exception>
+
+ // TODO: https://github.com/dotnet/corefx/issues/13681
+ // Until we get over the hurdle of C# 7 tooling, this temporary method will simulate the intended "ref T" indexer for those
+ // who need bypass the workaround for performance.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ref T GetItem(int index)
+ {
+ if ((uint)index >= ((uint)_length))
+ ThrowHelper.ThrowIndexOutOfRangeException();
+
+ if (_pinnable == null)
+ unsafe { return ref Unsafe.Add<T>(ref Unsafe.AsRef<T>(_byteOffset.ToPointer()), index); }
+ else
+ return ref Unsafe.Add<T>(ref Unsafe.AddByteOffset<T>(ref _pinnable.Data, _byteOffset), index);
+ }
+
+ /// <summary>
+ /// Copies the contents of this span into destination span. If the source
+ /// and destinations overlap, this method behaves as if the original values in
+ /// a temporary location before the destination is overwritten.
+ ///
+ /// <param name="destination">The span to copy items into.</param>
+ /// <exception cref="System.ArgumentException">
+ /// Thrown when the destination Span is shorter than the source Span.
+ /// </exception>
+ /// </summary>
+ public void CopyTo(Span<T> destination)
+ {
+ if (!TryCopyTo(destination))
+ ThrowHelper.ThrowArgumentException_DestinationTooShort();
+ }
+
+
+ /// <summary>
+ /// Copies the contents of this span into destination span. If the source
+ /// and destinations overlap, this method behaves as if the original values in
+ /// a temporary location before the destination is overwritten.
+ ///
+ /// <returns>If the destination span is shorter than the source span, this method
+ /// return false and no data is written to the destination.</returns>
+ /// </summary>
+ /// <param name="destination">The span to copy items into.</param>
+ public bool TryCopyTo(Span<T> destination)
+ {
+ if ((uint)_length > (uint)destination._length)
+ return false;
+
+ // TODO: This is a tide-over implementation as we plan to add a overlap-safe cpblk-based api to Unsafe. (https://github.com/dotnet/corefx/issues/13427)
+ unsafe
+ {
+ ref T src = ref DangerousGetPinnableReference();
+ ref T dst = ref destination.DangerousGetPinnableReference();
+ IntPtr srcMinusDst = Unsafe.ByteOffset<T>(ref dst, ref src);
+ int length = _length;
+
+ bool srcGreaterThanDst = (sizeof(IntPtr) == sizeof(int)) ? srcMinusDst.ToInt32() >= 0 : srcMinusDst.ToInt64() >= 0;
+ if (srcGreaterThanDst)
+ {
+ // Source address greater than or equal to destination address. Can do normal copy.
+ for (int i = 0; i < length; i++)
+ {
+ Unsafe.Add<T>(ref dst, i) = Unsafe.Add<T>(ref src, i);
+ }
+ }
+ else
+ {
+ // Source address less than destination address. Must do backward copy.
+ int i = length;
+ while (i-- != 0)
+ {
+ Unsafe.Add<T>(ref dst, i) = Unsafe.Add<T>(ref src, i);
+ }
+ }
+ return true;
+ }
+ }
+
+ /// <summary>
+ /// Returns true if left and right point at the same memory and have the same length. Note that
+ /// this does *not* check to see if the *contents* are equal.
+ /// </summary>
+ public static bool operator ==(Span<T> left, Span<T> right)
+ {
+ return left._length == right._length && Unsafe.AreSame<T>(ref left.DangerousGetPinnableReference(), ref right.DangerousGetPinnableReference());
+ }
+
+ /// <summary>
+ /// Returns false if left and right point at the same memory and have the same length. Note that
+ /// this does *not* check to see if the *contents* are equal.
+ /// </summary>
+ public static bool operator !=(Span<T> left, Span<T> right) => !(left == right);
+
+ /// <summary>
+ /// This method is not supported as spans cannot be boxed. To compare two spans, use operator==.
+ /// <exception cref="System.NotSupportedException">
+ /// Always thrown by this method.
+ /// </exception>
+ /// </summary>
+ [Obsolete("Equals() on Span will always throw an exception. Use == instead.")]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public override bool Equals(object obj)
+ {
+ throw new NotSupportedException(SR.CannotCallEqualsOnSpan);
+ }
+
+ /// <summary>
+ /// This method is not supported as spans cannot be boxed.
+ /// <exception cref="System.NotSupportedException">
+ /// Always thrown by this method.
+ /// </exception>
+ /// </summary>
+ [Obsolete("GetHashCode() on Span will always throw an exception.")]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public override int GetHashCode()
+ {
+ throw new NotSupportedException(SR.CannotCallGetHashCodeOnSpan);
+ }
+
+ /// <summary>
+ /// Defines an implicit conversion of an array to a <see cref="Span{T}"/>
+ /// </summary>
+ public static implicit operator Span<T>(T[] array) => new Span<T>(array);
+
+ /// <summary>
+ /// Defines an implicit conversion of a <see cref="ArraySegment{T}"/> to a <see cref="Span{T}"/>
+ /// </summary>
+ public static implicit operator Span<T>(ArraySegment<T> arraySegment) => new Span<T>(arraySegment.Array, arraySegment.Offset, arraySegment.Count);
+
+ /// <summary>
+ /// Forms a slice out of the given span, beginning at 'start'.
+ /// </summary>
+ /// <param name="start">The index at which to begin this slice.</param>
+ /// <exception cref="System.ArgumentOutOfRangeException">
+ /// Thrown when the specified <paramref name="start"/> index is not in range (&lt;0 or &gt;=Length).
+ /// </exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Span<T> Slice(int start)
+ {
+ if ((uint)start > (uint)_length)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+
+ IntPtr newOffset = _byteOffset.Add<T>(start);
+ int length = _length - start;
+ return new Span<T>(_pinnable, newOffset, length);
+ }
+
+ /// <summary>
+ /// Forms a slice out of the given span, beginning at 'start', of given length
+ /// </summary>
+ /// <param name="start">The index at which to begin this slice.</param>
+ /// <param name="length">The desired length for the slice (exclusive).</param>
+ /// <exception cref="System.ArgumentOutOfRangeException">
+ /// Thrown when the specified <paramref name="start"/> or end index is not in range (&lt;0 or &gt;=Length).
+ /// </exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Span<T> Slice(int start, int length)
+ {
+ if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start))
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+
+ IntPtr newOffset = _byteOffset.Add<T>(start);
+ return new Span<T>(_pinnable, newOffset, length);
+ }
+
+ /// <summary>
+ /// Copies the contents of this span into a new array. This heap
+ /// allocates, so should generally be avoided, however it is sometimes
+ /// necessary to bridge the gap with APIs written in terms of arrays.
+ /// </summary>
+ public T[] ToArray()
+ {
+ if (_length == 0)
+ return SpanHelpers.PerTypeValues<T>.EmptyArray;
+
+ T[] result = new T[_length];
+ CopyTo(result);
+ return result;
+ }
+
+ /// <summary>
+ /// Returns a 0-length span whose base is the null pointer.
+ /// </summary>
+ public static readonly Span<T> Empty = default(Span<T>);
+
+ /// <summary>
+ /// Returns a reference to the 0th element of the Span. If the Span is empty, returns a reference to the location where the 0th element
+ /// would have been stored. Such a reference can be used for pinning but must never be dereferenced.
+ /// </summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ref T DangerousGetPinnableReference()
+ {
+ if (_pinnable == null)
+ unsafe { return ref Unsafe.AsRef<T>(_byteOffset.ToPointer()); }
+ else
+ return ref Unsafe.AddByteOffset<T>(ref _pinnable.Data, _byteOffset);
+ }
+
+ //
+ // If the Span was constructed from an object,
+ //
+ // _pinnable = that object (unsafe-casted to a Pinnable<T>)
+ // _byteOffset = offset in bytes from "ref _pinnable.Data" to "ref span[0]"
+ //
+ // If the Span was constructed from a native pointer,
+ //
+ // _pinnable = null
+ // _byteOffset = the pointer
+ //
+ private readonly Pinnable<T> _pinnable;
+ private readonly IntPtr _byteOffset;
+ private readonly int _length;
+ }
+}
diff --git a/src/System.Memory/src/System/SpanHelpers.cs b/src/System.Memory/src/System/SpanHelpers.cs
new file mode 100644
index 0000000000..fc9c5a28cb
--- /dev/null
+++ b/src/System.Memory/src/System/SpanHelpers.cs
@@ -0,0 +1,132 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Reflection;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Runtime.CompilerServices;
+
+namespace System
+{
+ internal static class SpanHelpers
+ {
+ /// <summary>
+ /// Computes "start + index * sizeof(T)", using the unsigned IntPtr-sized multiplication for 32 and 64 bits.
+ ///
+ /// Assumptions:
+ /// Start and index are non-negative, and already pre-validated to be within the valid range of their containing Span.
+ ///
+ /// If the byte length (Span.Length * sizeof(T)) does an unsigned overflow (i.e. the buffer wraps or is too big to fit within the address space),
+ /// the behavior is undefined.
+ ///
+ /// </summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static IntPtr Add<T>(this IntPtr start, int index)
+ {
+ Debug.Assert(start.ToInt64() >= 0);
+ Debug.Assert(index >= 0);
+
+ unsafe
+ {
+ if (sizeof(IntPtr) == sizeof(int))
+ {
+ // 32-bit path.
+ uint byteLength = (uint)index * (uint)Unsafe.SizeOf<T>();
+ return (IntPtr)(((byte*)start) + byteLength);
+ }
+ else
+ {
+ // 64-bit path.
+ ulong byteLength = (ulong)index * (ulong)Unsafe.SizeOf<T>();
+ return (IntPtr)(((byte*)start) + byteLength);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Determine if a type is eligible for storage in unmanaged memory. TODO: To be replaced by a ContainsReference() api.
+ /// </summary>
+ public static bool IsReferenceFree<T>() => PerTypeValues<T>.IsReferenceFree;
+
+ private static bool IsReferenceFreeCore<T>()
+ {
+ // Under the JIT, these become constant-folded.
+ if (typeof(T) == typeof(byte))
+ return true;
+ if (typeof(T) == typeof(sbyte))
+ return true;
+ if (typeof(T) == typeof(bool))
+ return true;
+ if (typeof(T) == typeof(char))
+ return true;
+ if (typeof(T) == typeof(short))
+ return true;
+ if (typeof(T) == typeof(ushort))
+ return true;
+ if (typeof(T) == typeof(int))
+ return true;
+ if (typeof(T) == typeof(uint))
+ return true;
+ if (typeof(T) == typeof(long))
+ return true;
+ if (typeof(T) == typeof(ulong))
+ return true;
+ if (typeof(T) == typeof(IntPtr))
+ return true;
+ if (typeof(T) == typeof(UIntPtr))
+ return true;
+
+ return IsReferenceFreeCoreSlow(typeof(T));
+ }
+
+ private static bool IsReferenceFreeCoreSlow(Type type)
+ {
+ if (type.GetTypeInfo().IsPrimitive) // This is hopefully the common case. All types that return true for this are value types w/out embedded references.
+ return true;
+
+ if (!type.GetTypeInfo().IsValueType)
+ return false;
+
+ // If type is a Nullable<> of something, unwrap it first.
+ Type underlyingNullable = Nullable.GetUnderlyingType(type);
+ if (underlyingNullable != null)
+ type = underlyingNullable;
+
+ if (type.GetTypeInfo().IsEnum)
+ return true;
+
+ foreach (FieldInfo field in type.GetTypeInfo().DeclaredFields)
+ {
+ if (field.IsStatic)
+ continue;
+ if (!IsReferenceFreeCoreSlow(field.FieldType))
+ return false;
+ }
+ return true;
+ }
+
+ public static class PerTypeValues<T>
+ {
+ //
+ // Latch to ensure that excruciatingly expensive validation check for constructing a Span around a raw pointer is done
+ // only once per type (unless of course, the validation fails.)
+ //
+ // false == not yet computed or found to be not reference free.
+ // true == confirmed reference free
+ //
+ public static readonly bool IsReferenceFree = IsReferenceFreeCore<T>();
+
+ public static readonly T[] EmptyArray = new T[0];
+
+ public static readonly IntPtr ArrayAdjustment = MeasureArrayAdjustment();
+
+ // Array header sizes are a runtime implementation detail and aren't the same across all runtimes. (The CLR made a tweak after 4.5, and Mono has an extra Bounds pointer.)
+ private static IntPtr MeasureArrayAdjustment()
+ {
+ T[] sampleArray = new T[1];
+ return Unsafe.ByteOffset<T>(ref Unsafe.As<Pinnable<T>>(sampleArray).Data, ref sampleArray[0]);
+ }
+ }
+ }
+}
diff --git a/src/System.Memory/src/System/ThrowHelper.cs b/src/System.Memory/src/System/ThrowHelper.cs
new file mode 100644
index 0000000000..c44556f158
--- /dev/null
+++ b/src/System.Memory/src/System/ThrowHelper.cs
@@ -0,0 +1,58 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Runtime.CompilerServices;
+
+namespace System
+{
+ //
+ // This pattern of easily inlinable "void Throw" routines that stack on top of NoInlining factory methods
+ // is a compromise between older JITs and newer JITs (RyuJIT in Core CLR 1.1.0+ and desktop CLR in 4.6.3+).
+ // This package is explictly targeted at older JITs as newer runtimes expect to implement Span intrinsically for
+ // best performance.
+ //
+ // The aim of this pattern is three-fold
+ // 1. Extracting the throw makes the method preforming the throw in a conditional branch smaller and more inlinable
+ // 2. Extracting the throw from generic method to non-generic method reduces the repeated codegen size for value types
+ // 3a. Newer JITs will not inline the methods that only throw and also recognise them, move the call to cold section
+ // and not add stack prep and unwind before calling https://github.com/dotnet/coreclr/pull/6103
+ // 3b. Older JITs will inline the throw itself and move to cold section; but not inline the non-inlinable exception
+ // factory methods - still maintaining advantages 1 & 2
+ //
+
+ internal static class ThrowHelper
+ {
+ internal static void ThrowArgumentNullException(ExceptionArgument argument) { throw CreateArgumentNullException(argument); }
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static Exception CreateArgumentNullException(ExceptionArgument argument) { return new ArgumentNullException(argument.ToString()); }
+
+ internal static void ThrowArrayTypeMismatchException_ArrayTypeMustBeExactMatch(Type type) { throw CreateArrayTypeMismatchException_ArrayTypeMustBeExactMatch(type); }
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static Exception CreateArrayTypeMismatchException_ArrayTypeMustBeExactMatch(Type type) { return new ArrayTypeMismatchException(SR.Format(SR.ArrayTypeMustBeExactMatch, type)); }
+
+ internal static void ThrowArgumentException_InvalidTypeWithPointersNotSupported(Type type) { throw CreateArgumentException_InvalidTypeWithPointersNotSupported(type); }
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static Exception CreateArgumentException_InvalidTypeWithPointersNotSupported(Type type) { return new ArgumentException(SR.Format(SR.Argument_InvalidTypeWithPointersNotSupported, type)); }
+
+ internal static void ThrowArgumentException_DestinationTooShort() { throw CreateArgumentException_DestinationTooShort(); }
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static Exception CreateArgumentException_DestinationTooShort() { return new ArgumentException(SR.Argument_DestinationTooShort); }
+
+ internal static void ThrowIndexOutOfRangeException() { throw CreateIndexOutOfRangeException(); }
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static Exception CreateIndexOutOfRangeException() { return new IndexOutOfRangeException(); }
+
+ internal static void ThrowArgumentOutOfRangeException(ExceptionArgument argument) { throw CreateArgumentOutOfRangeException(argument); }
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static Exception CreateArgumentOutOfRangeException(ExceptionArgument argument) { return new ArgumentOutOfRangeException(argument.ToString()); }
+ }
+
+ internal enum ExceptionArgument
+ {
+ array,
+ length,
+ start,
+ obj,
+ }
+}
diff --git a/src/System.Memory/src/project.json b/src/System.Memory/src/project.json
new file mode 100644
index 0000000000..e4874791d6
--- /dev/null
+++ b/src/System.Memory/src/project.json
@@ -0,0 +1,13 @@
+{
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "1.0.1",
+ "System.Diagnostics.Debug": "4.0.11",
+ "System.Resources.ResourceManager": "4.0.1",
+ "System.Runtime": "4.1.0",
+ "System.Reflection": "4.1.0",
+ "System.Runtime.CompilerServices.Unsafe": "4.4.0-beta-24715-02"
+ },
+ "frameworks": {
+ "netstandard1.0": {}
+ }
+}
diff --git a/src/System.Memory/tests/Span/CopyTo.cs b/src/System.Memory/tests/Span/CopyTo.cs
new file mode 100644
index 0000000000..9c895cbdf3
--- /dev/null
+++ b/src/System.Memory/tests/Span/CopyTo.cs
@@ -0,0 +1,87 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Xunit;
+
+namespace System.SpanTests
+{
+ public static partial class SpanTests
+ {
+ [Fact]
+ public static void TryCopyTo()
+ {
+ int[] src = { 1, 2, 3 };
+ int[] dst = { 99, 100, 101 };
+
+ Span<int> srcSpan = new Span<int>(src);
+ bool success = srcSpan.TryCopyTo(dst);
+ Assert.True(success);
+ Assert.Equal<int>(src, dst);
+ }
+
+ [Fact]
+ public static void TryCopyToLonger()
+ {
+ int[] src = { 1, 2, 3 };
+ int[] dst = { 99, 100, 101, 102 };
+
+ Span<int> srcSpan = new Span<int>(src);
+ bool success = srcSpan.TryCopyTo(dst);
+ Assert.True(success);
+ int[] expected = { 1, 2, 3, 102 };
+ Assert.Equal<int>(expected, dst);
+ }
+
+ [Fact]
+ public static void TryCopyToShorter()
+ {
+ int[] src = { 1, 2, 3 };
+ int[] dst = { 99, 100 };
+
+ Span<int> srcSpan = new Span<int>(src);
+ bool success = srcSpan.TryCopyTo(dst);
+ Assert.False(success);
+ int[] expected = { 99, 100 };
+ Assert.Equal<int>(expected, dst); // TryCopyTo() checks for sufficient space before doing any copying.
+ }
+
+ [Fact]
+ public static void CopyToShorter()
+ {
+ int[] src = { 1, 2, 3 };
+ int[] dst = { 99, 100 };
+
+ Span<int> srcSpan = new Span<int>(src);
+ AssertThrows<ArgumentException, int>(srcSpan, (_srcSpan) => _srcSpan.CopyTo(dst));
+ int[] expected = { 99, 100 };
+ Assert.Equal<int>(expected, dst); // CopyTo() checks for sufficient space before doing any copying.
+ }
+
+ [Fact]
+ public static void Overlapping1()
+ {
+ int[] a = { 90, 91, 92, 93, 94, 95, 96, 97 };
+
+ Span<int> src = new Span<int>(a, 1, 6);
+ Span<int> dst = new Span<int>(a, 2, 6);
+ src.CopyTo(dst);
+
+ int[] expected = { 90, 91, 91, 92, 93, 94, 95, 96 };
+ Assert.Equal<int>(expected, a);
+ }
+
+ [Fact]
+ public static void Overlapping2()
+ {
+ int[] a = { 90, 91, 92, 93, 94, 95, 96, 97 };
+
+ Span<int> src = new Span<int>(a, 2, 6);
+ Span<int> dst = new Span<int>(a, 1, 6);
+ src.CopyTo(dst);
+
+ int[] expected = { 90, 92, 93, 94, 95, 96, 97, 97 };
+ Assert.Equal<int>(expected, a);
+ }
+ }
+}
diff --git a/src/System.Memory/tests/Span/CtorArray.cs b/src/System.Memory/tests/Span/CtorArray.cs
new file mode 100644
index 0000000000..625dc62b81
--- /dev/null
+++ b/src/System.Memory/tests/Span/CtorArray.cs
@@ -0,0 +1,121 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Xunit;
+
+namespace System.SpanTests
+{
+ //
+ // Tests for Span<T>.ctor(T[])
+ //
+ // These tests will also exercise the matching codepaths in Span<T>.ctor(T[], int) and .ctor(T[], int, int). This makes it easier to ensure
+ // that these parallel tests stay consistent, and avoid excess repetition in the files devoted to those specific overloads.
+ //
+ public static partial class SpanTests
+ {
+ [Fact]
+ public static void CtorArray1()
+ {
+ int[] a = { 91, 92, -93, 94 };
+ Span<int> span;
+
+ span = new Span<int>(a);
+ span.Validate<int>(91, 92, -93, 94);
+
+ span = new Span<int>(a, 0);
+ span.Validate<int>(91, 92, -93, 94);
+
+ span = new Span<int>(a, 0, a.Length);
+ span.Validate<int>(91, 92, -93, 94);
+ }
+
+ [Fact]
+ public static void CtorArray2()
+ {
+ long[] a = { 91, -92, 93, 94, -95 };
+ Span<long> span;
+
+ span = new Span<long>(a);
+ span.Validate<long>(91, -92, 93, 94, -95);
+
+ span = new Span<long>(a, 0);
+ span.Validate<long>(91, -92, 93, 94, -95);
+
+ span = new Span<long>(a, 0, a.Length);
+ span.Validate<long>(91, -92, 93, 94, -95);
+ }
+
+ [Fact]
+ public static void CtorArray3()
+ {
+ object o1 = new object();
+ object o2 = new object();
+ object[] a = { o1, o2 };
+ Span<object> span;
+
+ span = new Span<object>(a);
+ span.Validate<object>(o1, o2);
+
+ span = new Span<object>(a, 0);
+ span.Validate<object>(o1, o2);
+
+ span = new Span<object>(a, 0, a.Length);
+ span.Validate<object>(o1, o2);
+ }
+
+ [Fact]
+ public static void CtorArrayZeroLength()
+ {
+ int[] empty = Array.Empty<int>();
+ Span<int> span;
+
+ span = new Span<int>(empty);
+ span.Validate<int>();
+
+ span = new Span<int>(empty, 0);
+ span.Validate<int>();
+
+ span = new Span<int>(empty, 0, empty.Length);
+ span.Validate<int>();
+ }
+
+ [Fact]
+ public static void CtorArrayNullArray()
+ {
+ Assert.Throws<ArgumentNullException>(() => new Span<int>((int[])null).DontBox());
+ Assert.Throws<ArgumentNullException>(() => new Span<int>((int[])null, 0).DontBox());
+ Assert.Throws<ArgumentNullException>(() => new Span<int>((int[])null, 0, 0).DontBox());
+ }
+
+ [Fact]
+ public static void CtorArrayWrongArrayType()
+ {
+ // Cannot pass variant array, if array type is not a valuetype.
+ string[] a = { "Hello" };
+ Assert.Throws<ArrayTypeMismatchException>(() => new Span<object>(a).DontBox());
+ Assert.Throws<ArrayTypeMismatchException>(() => new Span<object>(a, 0).DontBox());
+ Assert.Throws<ArrayTypeMismatchException>(() => new Span<object>(a, 0, a.Length).DontBox());
+ }
+
+ [Fact]
+ public static void CtorArrayWrongValueType()
+ {
+ // Can pass variant array, if array type is a valuetype.
+
+ uint[] a = { 42u, 0xffffffffu };
+ int[] aAsIntArray = (int[])(object)a;
+ Span<int> span;
+
+ span = new Span<int>(aAsIntArray);
+ span.Validate<int>(42, -1);
+
+ span = new Span<int>(aAsIntArray, 0);
+ span.Validate<int>(42, -1);
+
+ span = new Span<int>(aAsIntArray, 0, aAsIntArray.Length);
+ span.Validate<int>(42, -1);
+ }
+ }
+}
+
diff --git a/src/System.Memory/tests/Span/CtorArrayInt.cs b/src/System.Memory/tests/Span/CtorArrayInt.cs
new file mode 100644
index 0000000000..e838d6268b
--- /dev/null
+++ b/src/System.Memory/tests/Span/CtorArrayInt.cs
@@ -0,0 +1,54 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Xunit;
+
+namespace System.SpanTests
+{
+ //
+ // Tests for Span<T>.ctor(T[], int). If the test is not specific to this overload, consider putting it in CtorArray.cs instread.
+ //
+ public static partial class SpanTests
+ {
+ [Fact]
+ public static void CtorArrayInt1()
+ {
+ int[] a = { 90, 91, 92, 93, 94, 95, 96, 97, 98 };
+ Span<int> span = new Span<int>(a, 3);
+ span.Validate<int>(93, 94, 95, 96, 97, 98);
+ }
+
+ [Fact]
+ public static void CtorArrayInt2()
+ {
+ long[] a = { 90, 91, 92, 93, 94, 95, 96, 97, 98 };
+ Span<long> span = new Span<long>(a, 3);
+ span.Validate<long>(93, 94, 95, 96, 97, 98);
+ }
+
+ [Fact]
+ public static void CtorArrayIntNegativeStart()
+ {
+ int[] a = new int[3];
+ Assert.Throws<ArgumentOutOfRangeException>(() => new Span<int>(a, -1).DontBox());
+ }
+
+ [Fact]
+ public static void CtorArrayIntStartTooLarge()
+ {
+ int[] a = new int[3];
+ Assert.Throws<ArgumentOutOfRangeException>(() => new Span<int>(a, 4).DontBox());
+ }
+
+ [Fact]
+ public static void CtorArrayIntStartEqualsLength()
+ {
+ // Valid for start to equal the array length. This returns an empty span that starts "just past the array."
+ int[] a = { 91, 92, 93 };
+ Span<int> span = new Span<int>(a, 3);
+ span.Validate<int>();
+ }
+ }
+}
+
diff --git a/src/System.Memory/tests/Span/CtorArrayIntInt.cs b/src/System.Memory/tests/Span/CtorArrayIntInt.cs
new file mode 100644
index 0000000000..5618db5fbf
--- /dev/null
+++ b/src/System.Memory/tests/Span/CtorArrayIntInt.cs
@@ -0,0 +1,80 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Xunit;
+
+namespace System.SpanTests
+{
+ //
+ // Tests for Span<T>.ctor(T[], int, int). If the test is not specific to this overload, consider putting it in CtorArray.cs instread.
+ //
+ public static partial class SpanTests
+ {
+ [Fact]
+ public static void CtorArrayIntInt1()
+ {
+ int[] a = { 90, 91, 92, 93, 94, 95, 96, 97, 98 };
+ Span<int> span = new Span<int>(a, 3, 2);
+ span.Validate<int>(93, 94);
+ }
+
+ [Fact]
+ public static void CtorArrayIntInt2()
+ {
+ long[] a = { 90, 91, 92, 93, 94, 95, 96, 97, 98 };
+ Span<long> span = new Span<long>(a, 4, 3);
+ span.Validate<long>(94, 95, 96);
+ }
+
+ [Fact]
+ public static void CtorArrayIntIntRangeExtendsToEndOfArray()
+ {
+ long[] a = { 90, 91, 92, 93, 94, 95, 96, 97, 98 };
+ Span<long> span = new Span<long>(a, 4, 5);
+ span.Validate<long>(94, 95, 96, 97, 98);
+ }
+
+ [Fact]
+ public static void CtorArrayIntIntNegativeStart()
+ {
+ int[] a = new int[3];
+ Assert.Throws<ArgumentOutOfRangeException>(() => new Span<int>(a, -1, 0).DontBox());
+ }
+
+ [Fact]
+ public static void CtorArrayIntIntStartTooLarge()
+ {
+ int[] a = new int[3];
+ Assert.Throws<ArgumentOutOfRangeException>(() => new Span<int>(a, 4, 0).DontBox());
+ }
+
+ [Fact]
+ public static void CtorArrayIntIntNegativeLength()
+ {
+ int[] a = new int[3];
+ Assert.Throws<ArgumentOutOfRangeException>(() => new Span<int>(a, 0, -1).DontBox());
+ }
+
+ [Fact]
+ public static void CtorArrayIntIntStartAndLengthTooLarge()
+ {
+ int[] a = new int[3];
+ Assert.Throws<ArgumentOutOfRangeException>(() => new Span<int>(a, 3, 1).DontBox());
+ Assert.Throws<ArgumentOutOfRangeException>(() => new Span<int>(a, 2, 2).DontBox());
+ Assert.Throws<ArgumentOutOfRangeException>(() => new Span<int>(a, 1, 3).DontBox());
+ Assert.Throws<ArgumentOutOfRangeException>(() => new Span<int>(a, 0, 4).DontBox());
+ Assert.Throws<ArgumentOutOfRangeException>(() => new Span<int>(a, int.MaxValue, int.MaxValue).DontBox());
+ }
+
+ [Fact]
+ public static void CtorArrayIntIntStartEqualsLength()
+ {
+ // Valid for start to equal the array length. This returns an empty span that starts "just past the array."
+ int[] a = { 91, 92, 93 };
+ Span<int> span = new Span<int>(a, 3, 0);
+ span.Validate<int>();
+ }
+ }
+}
+
diff --git a/src/System.Memory/tests/Span/CtorPointerInt.cs b/src/System.Memory/tests/Span/CtorPointerInt.cs
new file mode 100644
index 0000000000..6bb247600c
--- /dev/null
+++ b/src/System.Memory/tests/Span/CtorPointerInt.cs
@@ -0,0 +1,78 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Xunit;
+using System.Runtime.CompilerServices;
+
+#pragma warning disable 0649 //Field 'SpanTests.InnerStruct.J' is never assigned to, and will always have its default value 0
+
+namespace System.SpanTests
+{
+ public static partial class SpanTests
+ {
+ [Fact]
+ public static void CtorPointerInt()
+ {
+ unsafe
+ {
+ int[] a = { 90, 91, 92 };
+ fixed (int *pa = a)
+ {
+ Span<int> span = new Span<int>(pa, 3);
+ span.Validate<int>(90, 91, 92);
+ Assert.True(Unsafe.AreSame<int>(ref Unsafe.AsRef<int>(pa), ref span.DangerousGetPinnableReference()));
+ }
+ }
+ }
+
+ [Fact]
+ public static void CtorPointerNull()
+ {
+ unsafe
+ {
+ Span<int> span = new Span<int>((void*)null, 0);
+ span.Validate<int>();
+ Assert.True(Unsafe.AreSame<int>(ref Unsafe.AsRef<int>((void*)null), ref span.DangerousGetPinnableReference()));
+ }
+ }
+
+ [Fact]
+ public static void CtorPointerRangeChecks()
+ {
+ unsafe
+ {
+ Assert.Throws<ArgumentOutOfRangeException>(
+ delegate ()
+ {
+ int i = 42;
+ Span<int> span = new Span<int>(&i, -1);
+ });
+ }
+ }
+
+ [Fact]
+ public static void CtorPointerNoContainsReferenceEnforcement()
+ {
+ unsafe
+ {
+ new Span<int>((void*)null, 0);
+ new Span<int?>((void*)null, 0);
+ Assert.Throws<ArgumentException>(() => new Span<object>((void*)null, 0).DontBox());
+ Assert.Throws<ArgumentException>(() => new Span<StructWithReferences>((void*)null, 0).DontBox());
+ }
+ }
+
+ private struct StructWithReferences
+ {
+ public int I;
+ public InnerStruct Inner;
+ }
+
+ private struct InnerStruct
+ {
+ public int J;
+ public object O;
+ }
+ }
+}
diff --git a/src/System.Memory/tests/Span/DangerousCreate.cs b/src/System.Memory/tests/Span/DangerousCreate.cs
new file mode 100644
index 0000000000..55eaf075bc
--- /dev/null
+++ b/src/System.Memory/tests/Span/DangerousCreate.cs
@@ -0,0 +1,51 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Xunit;
+using System.Runtime.InteropServices;
+using System.Runtime.CompilerServices;
+
+namespace System.SpanTests
+{
+ public static partial class SpanTests
+ {
+ [Fact]
+ public static void DangerousCreateNullObject()
+ {
+ Assert.Throws<ArgumentNullException>(
+ delegate ()
+ {
+ int dummy = 4;
+ Span<int>.DangerousCreate(null, ref dummy, 0);
+ });
+ }
+
+ [Fact]
+ public static void DangerousCreateBadLength()
+ {
+ Assert.Throws<ArgumentOutOfRangeException>(
+ delegate ()
+ {
+ TestClass testClass = new TestClass();
+ Span<char> span = Span<char>.DangerousCreate(testClass, ref testClass.C1, -1);
+ });
+ }
+
+ [Fact]
+ public static void DangerousCreate1()
+ {
+ TestClass testClass = new TestClass();
+ testClass.C0 = 'a';
+ testClass.C1 = 'b';
+ testClass.C2 = 'c';
+ testClass.C3 = 'd';
+ testClass.C4 = 'e';
+ Span<char> span = Span<char>.DangerousCreate(testClass, ref testClass.C1, 3);
+ span.Validate<char>('b', 'c', 'd');
+
+ ref char pc1 = ref span.DangerousGetPinnableReference();
+ Assert.True(Unsafe.AreSame<char>(ref testClass.C1, ref pc1));
+ }
+ }
+}
diff --git a/src/System.Memory/tests/Span/DangerousGetPinnableReference.cs b/src/System.Memory/tests/Span/DangerousGetPinnableReference.cs
new file mode 100644
index 0000000000..199e7048a3
--- /dev/null
+++ b/src/System.Memory/tests/Span/DangerousGetPinnableReference.cs
@@ -0,0 +1,68 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Xunit;
+using System.Runtime.InteropServices;
+using System.Runtime.CompilerServices;
+
+namespace System.SpanTests
+{
+ public static partial class SpanTests
+ {
+ [Fact]
+ public static void DangerousGetPinnableReferenceArray()
+ {
+ int[] a = { 91, 92, 93, 94, 95 };
+ Span<int> span = new Span<int>(a, 1, 3);
+ ref int pinnableReference = ref span.DangerousGetPinnableReference();
+ Assert.True(Unsafe.AreSame<int>(ref a[1], ref pinnableReference));
+ }
+
+ [Fact]
+ public static void DangerousGetPinnableReferenceArrayPastEnd()
+ {
+ // The only real difference between DangerousGetPinnableReference() and "ref span[0]" is that
+ // DangerousGetPinnableReference() of a zero-length won't throw an IndexOutOfRange.
+
+ int[] a = { 91, 92, 93, 94, 95 };
+ Span<int> span = new Span<int>(a, a.Length, 0);
+ ref int pinnableReference = ref span.DangerousGetPinnableReference();
+ ref int expected = ref Unsafe.Add<int>(ref a[a.Length - 1], 1);
+ Assert.True(Unsafe.AreSame<int>(ref expected, ref pinnableReference));
+ }
+
+ [Fact]
+ public static void DangerousGetPinnableReferencePointer()
+ {
+ unsafe
+ {
+ int i = 42;
+ Span<int> span = new Span<int>(&i, 1);
+ ref int pinnableReference = ref span.DangerousGetPinnableReference();
+ Assert.True(Unsafe.AreSame<int>(ref i, ref pinnableReference));
+ }
+ }
+
+ [Fact]
+ public static void DangerousGetPinnableReferencePointerDangerousCreate1()
+ {
+ TestClass testClass = new TestClass();
+ Span<char> span = Span<char>.DangerousCreate(testClass, ref testClass.C1, 3);
+
+ ref char pinnableReference = ref span.DangerousGetPinnableReference();
+ Assert.True(Unsafe.AreSame<char>(ref testClass.C1, ref pinnableReference));
+ }
+
+ [Fact]
+ public static void DangerousGetPinnableReferenceEmpty()
+ {
+ unsafe
+ {
+ Span<int> span = Span<int>.Empty;
+ ref int pinnableReference = ref span.DangerousGetPinnableReference();
+ Assert.True(Unsafe.AreSame<int>(ref Unsafe.AsRef<int>(null), ref pinnableReference));
+ }
+ }
+ }
+}
diff --git a/src/System.Memory/tests/Span/Empty.cs b/src/System.Memory/tests/Span/Empty.cs
new file mode 100644
index 0000000000..50e2e5196b
--- /dev/null
+++ b/src/System.Memory/tests/Span/Empty.cs
@@ -0,0 +1,26 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Xunit;
+using System.Runtime.CompilerServices;
+
+namespace System.SpanTests
+{
+ public static partial class SpanTests
+ {
+ [Fact]
+ public static void Empty()
+ {
+ Span<int> empty = Span<int>.Empty;
+ Assert.True(empty.IsEmpty);
+ Assert.Equal(0, empty.Length);
+ unsafe
+ {
+ ref int expected = ref Unsafe.AsRef<int>(null);
+ ref int actual = ref empty.DangerousGetPinnableReference();
+ Assert.True(Unsafe.AreSame<int>(ref expected, ref actual));
+ }
+ }
+ }
+}
diff --git a/src/System.Memory/tests/Span/Equality.cs b/src/System.Memory/tests/Span/Equality.cs
new file mode 100644
index 0000000000..688682446a
--- /dev/null
+++ b/src/System.Memory/tests/Span/Equality.cs
@@ -0,0 +1,93 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Xunit;
+
+#pragma warning disable 1718 //Comparison made to same variable; did you mean to compare something else?
+
+namespace System.SpanTests
+{
+ public static partial class SpanTests
+ {
+ [Fact]
+ public static void EqualityTrue()
+ {
+ int[] a = { 91, 92, 93, 94, 95 };
+ Span<int> left = new Span<int>(a, 2, 3);
+ Span<int> right = new Span<int>(a, 2, 3);
+
+ Assert.True(left == right);
+ Assert.True(!(left != right));
+ }
+
+ [Fact]
+ public static void EqualityReflexivity()
+ {
+ int[] a = { 91, 92, 93, 94, 95 };
+ Span<int> left = new Span<int>(a, 2, 3);
+
+ Assert.True(left == left);
+ Assert.True(!(left != left));
+ }
+
+ [Fact]
+ public static void EqualityIncludesLength()
+ {
+ int[] a = { 91, 92, 93, 94, 95 };
+ Span<int> left = new Span<int>(a, 2, 1);
+ Span<int> right = new Span<int>(a, 2, 3);
+
+ Assert.False(left == right);
+ Assert.False(!(left != right));
+ }
+
+ [Fact]
+ public static void EqualityIncludesBase()
+ {
+ int[] a = { 91, 92, 93, 94, 95 };
+ Span<int> left = new Span<int>(a, 1, 3);
+ Span<int> right = new Span<int>(a, 2, 3);
+
+ Assert.False(left == right);
+ Assert.False(!(left != right));
+ }
+
+ [Fact]
+ public static void EqualityBasedOnLocationNotConstructor()
+ {
+ unsafe
+ {
+ int[] a = { 91, 92, 93, 94, 95 };
+ fixed (int* pa = a)
+ {
+ Span<int> left = new Span<int>(a, 2, 3);
+ Span<int> right = new Span<int>(pa + 2, 3);
+
+ Assert.True(left == right);
+ Assert.True(!(left != right));
+ }
+ }
+ }
+
+ [Fact]
+ public static void EqualityComparesRangeNotContent()
+ {
+ Span<int> left = new Span<int>(new int[] { 0, 1, 2 }, 1, 1);
+ Span<int> right = new Span<int>(new int[] { 0, 1, 2 }, 1, 1);
+
+ Assert.False(left == right);
+ Assert.False(!(left != right));
+ }
+
+ [Fact]
+ public static void EmptySpansNotUnified()
+ {
+ Span<int> left = new Span<int>(new int[0]);
+ Span<int> right = new Span<int>(new int[0]);
+
+ Assert.False(left == right);
+ Assert.False(!(left != right));
+ }
+ }
+}
diff --git a/src/System.Memory/tests/Span/Overflow.cs b/src/System.Memory/tests/Span/Overflow.cs
new file mode 100644
index 0000000000..228ee75bca
--- /dev/null
+++ b/src/System.Memory/tests/Span/Overflow.cs
@@ -0,0 +1,64 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Xunit;
+using System.Runtime.InteropServices;
+using System.Runtime.CompilerServices;
+
+namespace System.SpanTests
+{
+ public static partial class SpanTests
+ {
+ [Fact]
+ public static void IndexOverflow()
+ {
+ //
+ // Although Span constrains indexes to 0..2Gb, it does not similarly constrain index * sizeof(T).
+ // Make sure that internal offset calculcations handle the >2Gb case properly.
+ //
+ unsafe
+ {
+ byte* pMemory;
+ try
+ {
+ pMemory = (byte*)Marshal.AllocHGlobal((IntPtr)ThreeGiB);
+ }
+ catch (Exception)
+ {
+ return; // It's not implausible to believe that a 3gb allocation will fail - if so, skip this test to avoid unnecessary test flakiness.
+ }
+
+ try
+ {
+ Span<Guid> span = new Span<Guid>(pMemory, GuidThreeGiBLimit);
+
+ int bigIndex = checked(GuidTwoGiBLimit + 1);
+ uint byteOffset = checked((uint)bigIndex * (uint)sizeof(Guid));
+ Assert.True(byteOffset > (uint)int.MaxValue); // Make sure byteOffset actually overflows 2Gb, or this test is pointless.
+ ref Guid expected = ref Unsafe.AsRef<Guid>(((byte*)pMemory) + byteOffset);
+
+ Assert.True(Unsafe.AreSame<Guid>(ref expected, ref span.GetItem(bigIndex)));
+
+ Span<Guid> slice = span.Slice(bigIndex);
+ Assert.True(Unsafe.AreSame<Guid>(ref expected, ref slice.DangerousGetPinnableReference()));
+
+ slice = span.Slice(bigIndex, 1);
+ Assert.True(Unsafe.AreSame<Guid>(ref expected, ref slice.DangerousGetPinnableReference()));
+ }
+ finally
+ {
+ Marshal.FreeHGlobal((IntPtr)pMemory);
+ }
+ }
+ }
+
+ private const long ThreeGiB = 3L * 1024L * 1024L * 1024L;
+ private const long TwoGiB = 2L * 1024L * 1024L * 1024L;
+ private const long OneGiB = 1L * 1024L * 1024L * 1024L;
+
+ private static readonly int GuidThreeGiBLimit = (int)(ThreeGiB / Unsafe.SizeOf<Guid>()); // sizeof(Guid) requires unsafe keyword and I don't want to mark the entire class unsafe.
+ private static readonly int GuidTwoGiBLimit = (int)(TwoGiB / Unsafe.SizeOf<Guid>());
+ private static readonly int GuidOneGiBLimit = (int)(OneGiB / Unsafe.SizeOf<Guid>());
+ }
+}
diff --git a/src/System.Memory/tests/Span/Slice.cs b/src/System.Memory/tests/Span/Slice.cs
new file mode 100644
index 0000000000..e15f4cb2bb
--- /dev/null
+++ b/src/System.Memory/tests/Span/Slice.cs
@@ -0,0 +1,70 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Xunit;
+using System.Runtime.CompilerServices;
+
+namespace System.SpanTests
+{
+ public static partial class SpanTests
+ {
+ [Fact]
+ public static void SliceInt()
+ {
+ int[] a = { 90, 91, 92, 93, 94, 95, 96, 97, 98, 99 };
+ Span<int> span = new Span<int>(a).Slice(6);
+ Assert.Equal(4, span.Length);
+ Assert.True(Unsafe.AreSame<int>(ref a[6], ref span.DangerousGetPinnableReference()));
+ }
+
+ [Fact]
+ public static void SliceIntPastEnd()
+ {
+ int[] a = { 90, 91, 92, 93, 94, 95, 96, 97, 98, 99 };
+ Span<int> span = new Span<int>(a).Slice(a.Length);
+ Assert.Equal(0, span.Length);
+ Assert.True(Unsafe.AreSame<int>(ref a[a.Length - 1], ref Unsafe.Subtract<int>(ref span.DangerousGetPinnableReference(), 1)));
+ }
+
+ [Fact]
+ public static void SliceIntInt()
+ {
+ int[] a = { 90, 91, 92, 93, 94, 95, 96, 97, 98, 99 };
+ Span<int> span = new Span<int>(a).Slice(3, 5);
+ Assert.Equal(5, span.Length);
+ Assert.True(Unsafe.AreSame<int>(ref a[3], ref span.DangerousGetPinnableReference()));
+ }
+
+ [Fact]
+ public static void SliceIntIntUpToEnd()
+ {
+ int[] a = { 90, 91, 92, 93, 94, 95, 96, 97, 98, 99 };
+ Span<int> span = new Span<int>(a).Slice(4, 6);
+ Assert.Equal(6, span.Length);
+ Assert.True(Unsafe.AreSame<int>(ref a[4], ref span.DangerousGetPinnableReference()));
+ }
+
+ [Fact]
+ public static void SliceIntIntPastEnd()
+ {
+ int[] a = { 90, 91, 92, 93, 94, 95, 96, 97, 98, 99 };
+ Span<int> span = new Span<int>(a).Slice(a.Length, 0);
+ Assert.Equal(0, span.Length);
+ Assert.True(Unsafe.AreSame<int>(ref a[a.Length - 1], ref Unsafe.Subtract<int>(ref span.DangerousGetPinnableReference(), 1)));
+ }
+
+ [Fact]
+ public static void SliceIntRangeChecksd()
+ {
+ int[] a = { 90, 91, 92, 93, 94, 95, 96, 97, 98, 99 };
+ Assert.Throws<ArgumentOutOfRangeException>(() => new Span<int>(a).Slice(-1).DontBox());
+ Assert.Throws<ArgumentOutOfRangeException>(() => new Span<int>(a).Slice(a.Length + 1).DontBox());
+ Assert.Throws<ArgumentOutOfRangeException>(() => new Span<int>(a).Slice(-1, 0).DontBox());
+ Assert.Throws<ArgumentOutOfRangeException>(() => new Span<int>(a).Slice(0, a.Length + 1).DontBox());
+ Assert.Throws<ArgumentOutOfRangeException>(() => new Span<int>(a).Slice(2, a.Length + 1 - 2).DontBox());
+ Assert.Throws<ArgumentOutOfRangeException>(() => new Span<int>(a).Slice(a.Length + 1, 0).DontBox());
+ Assert.Throws<ArgumentOutOfRangeException>(() => new Span<int>(a).Slice(a.Length, 1).DontBox());
+ }
+ }
+}
diff --git a/src/System.Memory/tests/Span/TestHelpers.cs b/src/System.Memory/tests/Span/TestHelpers.cs
new file mode 100644
index 0000000000..0837eeab9a
--- /dev/null
+++ b/src/System.Memory/tests/Span/TestHelpers.cs
@@ -0,0 +1,79 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Xunit;
+using System.Runtime.InteropServices;
+
+namespace System.SpanTests
+{
+ public static partial class SpanTests
+ {
+ private static void Validate<T>(this Span<T> span, params T[] expected)
+ {
+ bool isValueType = default(T) != null || Nullable.GetUnderlyingType(typeof(T)) != null;
+ Assert.Equal(span.Length, expected.Length);
+ for (int i = 0; i < expected.Length; i++)
+ {
+ T actual = span[i];
+ if (isValueType)
+ Assert.Equal(expected[i], actual);
+ else
+ Assert.Same(expected[i], actual);
+ }
+
+ object ignore;
+ AssertThrows<IndexOutOfRangeException, T>(span, (_span) => ignore = _span[expected.Length]);
+ }
+
+ private delegate void AssertThrowsAction<T>(Span<T> span);
+
+ // Cannot use standard Assert.Throws() when testing Span - Span and closures don't get along.
+ private static void AssertThrows<E, T>(Span<T> span, AssertThrowsAction<T> action) where E:Exception
+ {
+ try
+ {
+ action(span);
+ Assert.False(true, "Expected exception: " + typeof(E).GetType());
+ }
+ catch (E)
+ {
+ }
+ catch (Exception wrongException)
+ {
+ Assert.False(true, "Wrong exception thrown: Expected " + typeof(E).GetType() + ": Actual: " + wrongException.GetType());
+ }
+ }
+
+ //
+ // The innocent looking construct:
+ //
+ // Assert.Throws<E>( () => new Span() );
+ //
+ // generates a hidden box of the Span as the return value of the lambda. This makes the IL illegal and unloadable on
+ // runtimes that enforce the actual Span rules (never mind that we expect never to reach the box instruction...)
+ //
+ // The workaround is to code it like this:
+ //
+ // Assert.Throws<E>( () => new Span().DontBox() );
+ //
+ // which turns the lambda return type back to "void" and eliminates the troublesome box instruction.
+ //
+ private static void DontBox<T>(this Span<T> span)
+ {
+ // This space intentionally left blank.
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ private sealed class TestClass
+ {
+ private double _d;
+ public char C0;
+ public char C1;
+ public char C2;
+ public char C3;
+ public char C4;
+ }
+ }
+}
+
diff --git a/src/System.Memory/tests/Span/ToArray.cs b/src/System.Memory/tests/Span/ToArray.cs
new file mode 100644
index 0000000000..ecabef0684
--- /dev/null
+++ b/src/System.Memory/tests/Span/ToArray.cs
@@ -0,0 +1,30 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Xunit;
+using System.Runtime.CompilerServices;
+
+namespace System.SpanTests
+{
+ public static partial class SpanTests
+ {
+ [Fact]
+ public static void ToArray1()
+ {
+ int[] a = { 91, 92, 93 };
+ Span<int> span = new Span<int>(a);
+ int[] copy = span.ToArray();
+ Assert.Equal<int>(a, copy);
+ Assert.NotSame(a, copy);
+ }
+
+ [Fact]
+ public static void ToArrayEmpty()
+ {
+ Span<int> span = Span<int>.Empty;
+ int[] copy = span.ToArray();
+ Assert.Equal(0, copy.Length);
+ }
+ }
+}
diff --git a/src/System.Memory/tests/System.Memory.Tests.builds b/src/System.Memory/tests/System.Memory.Tests.builds
new file mode 100644
index 0000000000..75b5c3f9cb
--- /dev/null
+++ b/src/System.Memory/tests/System.Memory.Tests.builds
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+ <ItemGroup>
+ <Project Include="System.Memory.Tests.csproj">
+ <OSGroup>Windows_NT</OSGroup>
+ <TestTFMs>netcore50;netcoreapp1.0;net46</TestTFMs>
+ </Project>
+ <Project Include="System.Memory.Tests.csproj">
+ <TargetGroup>netcoreapp1.1</TargetGroup>
+ <OSGroup>Windows_NT</OSGroup>
+ <TestTFMs>netcoreapp1.1</TestTFMs>
+ </Project>
+ </ItemGroup>
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.traversal.targets))\dir.traversal.targets" />
+</Project>
diff --git a/src/System.Memory/tests/System.Memory.Tests.csproj b/src/System.Memory/tests/System.Memory.Tests.csproj
new file mode 100644
index 0000000000..aae27bdcc5
--- /dev/null
+++ b/src/System.Memory/tests/System.Memory.Tests.csproj
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+ <PropertyGroup>
+ <AssemblyName>System.Memory.Tests</AssemblyName>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' " />
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' " />
+ <ItemGroup>
+ <Compile Include="Span\CopyTo.cs" />
+ <Compile Include="Span\CtorArray.cs" />
+ <Compile Include="Span\CtorArrayInt.cs" />
+ <Compile Include="Span\CtorArrayIntInt.cs" />
+ <Compile Include="Span\CtorPointerInt.cs" />
+ <Compile Include="Span\DangerousCreate.cs" />
+ <Compile Include="Span\DangerousGetPinnableReference.cs" />
+ <Compile Include="Span\Empty.cs" />
+ <Compile Include="Span\Equality.cs" />
+ <Compile Include="Span\Overflow.cs" />
+ <Compile Include="Span\Slice.cs" />
+ <Compile Include="Span\TestHelpers.cs" />
+ <Compile Include="Span\ToArray.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\pkg\System.Memory.pkgproj">
+ <Name>Memory</Name>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>
+