てくメモ

trivial な notes

【C#】Span.Overlaps を見る

Span に拡張メソッドとして生えているOverlapsメソッドが気になったので、簡単に確認してみる。
MemoryExtensions.Overlaps メソッド (System) | Microsoft Learn


このメソッドは、ある Span が別の Span とメモリ上で重なっているかを判定するもの。

まず、参照が違うものは当然false

var array = Enumerable.Range(0, 10).ToArray();

var array2 = Enumerable.Range(0, 10).ToArray();
var s1 = array.AsSpan();
var s2 = array2.AsSpan();
Console.WriteLine($"s1 ols s2: {s1.Overlaps(s2)}");
// s1 ols s2: False


ある Span と、そこからスライスした Span とは当然true
異なった Span でも、参照が同じで範囲が重なっていればきちんとtrueが返る。

var s3 = s1[3..4];
Console.WriteLine($"s1 ols s3: {s1.Overlaps(s3)}");

var s4 = array.AsSpan(3, 2);
Console.WriteLine($"s1 ols s4: {s1.Overlaps(s4)}");

// s1 ols s3: True
// s1 ols s4: True


オフセットを得るためのオーバーロードもある。

var s5 = s1[3..5];
var s6 = s1[..2];
var s7 = s1[4..7];
Console.WriteLine($"s1 ols s5: {s1.Overlaps(s5, out var offset)}, {offset}");
Console.WriteLine($"s1 ols s6: {s1.Overlaps(s6, out offset)}, {offset}");
Console.WriteLine($"s1 ols s7: {s1.Overlaps(s7, out offset)}, {offset}");
Console.WriteLine($"s5 ols s1: {s5.Overlaps(s1, out offset)}, {offset}");
Console.WriteLine($"s5 ols s6: {s5.Overlaps(s6, out offset)}, {offset}");
Console.WriteLine($"s5 ols s7: {s5.Overlaps(s7, out offset)}, {offset}");

// s1 ols s5: True, 3
// s1 ols s6: True, 0
// s1 ols s7: True, 4
// s5 ols s1: True, -3
// s5 ols s6: False, 0
// s5 ols s7: True, 1


処理の中身としては、Span 同士のバイトオフセットを取って、Span の長さと型のサイズを乗算したものと較べて判断している。
参考:Source Browser

public static unsafe bool Overlaps<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> other)
{
    if (span.IsEmpty || other.IsEmpty)
    {
        return false;
    }

    nint byteOffset = Unsafe.ByteOffset(
        ref MemoryMarshal.GetReference(span),
        ref MemoryMarshal.GetReference(other));

    return (nuint)byteOffset < (nuint)((nint)span.Length * sizeof(T)) ||
            (nuint)byteOffset > (nuint)(-((nint)other.Length * sizeof(T)));
}


標準ライブラリ内では、例えばReadOnlySpan<char>.ToLower(Span<T>, CultureInfo?)メソッドで、対象 ReadOnlySpan が destination となる Span と重なっていないかを確認、といったふうに使われている。
参考:runtime/MemoryExtensions.Globalization.cs at 6fd40193b9e8b9c5028edf741f266ff01bc75636 · dotnet/runtime · GitHub

source.Overlaps(destination)を確認して例外を投げるのがイディオムのようだ。


なんとなく他でも使えそうな気がするけれれど、パッとは出てこない。
使い所でうまく引き出しから出せるかが課題。