# Chapter 23 - Span and Memory

Slicing

```void Main()
{
var numbers = new int [1000];
for (int i = 0; i < numbers.Length; i++) numbers [i] = i;

Sum (numbers).Dump ("total - using implicit conversion");
Sum (numbers.AsSpan()).Dump ("total - using .AsSpan()");
Sum (numbers.AsSpan (250, 500)).Dump ("total - slicing");

Span<int> span = numbers;
Console.WriteLine (span [^1]);            // Last element
Console.WriteLine (Sum (span [..10]));    // First 10 elements
Console.WriteLine (Sum (span [100..]));   // 100th element to end
Console.WriteLine (Sum (span [^5..]));    // Last 5 elements
}

{
int total = 0;
foreach (int i in numbers) total += i;
}```

CopyTo

```{
Span<int> x = new[] { 1, 2, 3, 4 };
Span<int> y = new int [4];
x.CopyTo (y);
y.Dump ("Copy");
}

{
Span<int> x = new[] { 1, 2, 3, 4 };
Span<int> y = new[] { 10, 20, 30, 40 };
x [..2].CopyTo (y [2..]);                 // y is now { 10, 20, 1, 2 }

y.Dump ("Copy - with span");
}```

Working with strings

```void Main()
{
CountWhitespace ("Word1 Word2").Dump();
CountWhitespace ("1 2 3 4 5".AsSpan (3,3)).Dump();

var span = "This ".AsSpan();                    // ReadOnlySpan<char>
Console.WriteLine (span.StartsWith ("This"));   // True
Console.WriteLine (span.Trim().Length);         // 4
}

{
int count = 0;
foreach (char c in s)
if (char.IsWhiteSpace (c))
count++;
return count;
}```

Memory of T

```void Main()
{
Split ("Word1 Word2 Word3".AsMemory()).Dump();
}

{
int wordStart = 0;
for (int i = 0; i <= input.Length; i++)
if (i == input.Length || char.IsWhiteSpace (input.Span [i]))
{
yield return input [wordStart..i];   // Slice with C# range operator
wordStart = i + 1;
}
}```

Splitting into Ranges

```void Main()
{
ReadOnlySpan<char> source = "The quick brown fox";
foreach (var range in Split (source))
{
wordSpan.Dump();
}
}

{
int pos = 0;
var list = new List<Range>();
for (int i = 0; i <= input.Length; i++)
if (i == input.Length || char.IsWhiteSpace (input [i]))
{
pos = i + 1;
}
return list.ToArray();
}```

CharSpanSplitter

```void Main()
{
ReadOnlySpan<char> source = "The quick brown fox";
foreach (var word in source.Split())
word.ToString().Dump();
}

{
public CharSpanSplitter (ReadOnlySpan<char> input) => _input = input;
public Enumerator GetEnumerator() => new Enumerator (_input);

public ref struct Enumerator   // Forward-only enumerator
{
int _wordPos;
public ReadOnlySpan<char> Current { get; private set; }

{
_input = input;
_wordPos = 0;
Current = default;
}

public bool MoveNext()
{
for (int i = _wordPos; i <= _input.Length; i++)
if (i == _input.Length || char.IsWhiteSpace (_input [i]))
{
Current = _input [_wordPos..i];
_wordPos = i + 1;
return true;
}
return false;
}
}
}

public static class CharSpanExtensions
{
public static CharSpanSplitter Split (this ReadOnlySpan<char> input)
=> new CharSpanSplitter (input);
}```

Summing with pointers

```unsafe void Main()
{
int* numbers = stackalloc int [1000];   // Allocate array on the stack

for (int i = 0; i < 1000; i++)
numbers [i] = i;

int total = Sum (numbers, 1000).Dump();
}

unsafe int Sum (int* numbers, int length)
{
int total = 0;
for (int i = 0; i < length; i++) total += numbers [i];
}```

Span and stackalloc

```unsafe void Main()
{
Span<int> numbers = stackalloc int [1000];

for (int i = 0; i < numbers.Length; i++)
numbers[i] = i;

int total = Sum (numbers).Dump();
}

{
int total = 0;
foreach (int i in numbers) total += numbers [i];
}```

Span and unmanaged memory

```unsafe void Main()
{
var source = "The quick brown fox".AsSpan();
var ptr = Marshal.AllocHGlobal (source.Length * sizeof (char));
try
{
var unmanaged = new Span<char> ((char*)ptr, source.Length);
source.CopyTo (unmanaged);
foreach (var word in unmanaged.Split())
Console.WriteLine (word.ToString());
}
finally { Marshal.FreeHGlobal (ptr); }
}

{
public CharSpanSplitter (ReadOnlySpan<char> input) => _input = input;
public Rator GetEnumerator() => new Rator (_input);

public ref struct Rator   // Forward-only enumerator
{
int _wordPos;
public ReadOnlySpan<char> Current { get; private set; }

{
_input = input;
_wordPos = 0;
Current = default;
}

public bool MoveNext()
{
for (int i = _wordPos; i <= _input.Length; i++)
if (i == _input.Length || char.IsWhiteSpace (_input [i]))
{
Current = _input [_wordPos..i];
_wordPos = i + 1;
return true;
}
return false;
}
}
}

public static class CharSpanExtensions
{
public static CharSpanSplitter Split (this ReadOnlySpan<char> input)
=> new CharSpanSplitter (input);

public static CharSpanSplitter Split (this Span<char> input)
=> new CharSpanSplitter (input);
}```