From 47a33153360dfc4a26803aa0c7772de52fbb0813 Mon Sep 17 00:00:00 2001 From: Viktor Stojanovic Date: Thu, 13 Aug 2026 15:39:15 +0200 Subject: [PATCH 1/2] Add LongSpan ref struct for memory regions Introduced LongSpan to represent contiguous memory, supporting both managed and unmanaged sources. Added field for length, constructors, Length and IsEmpty properties, indexer with bounds checking, and XML documentation. --- src/LongSpan.cs | 82 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/src/LongSpan.cs b/src/LongSpan.cs index f9a6259..0941118 100644 --- a/src/LongSpan.cs +++ b/src/LongSpan.cs @@ -1,11 +1,91 @@ // Copyright (c) 2026 Viktor Stojanović. All rights reserved. // Licensed under the MIT License. See LICENSE.txt in the project root for license information. +using System; +using System.Runtime.CompilerServices; + namespace SharpAllocators; +/// +/// 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-safe and memory-safe. +/// public readonly ref struct LongSpan { + /// A byref or a native ptr. private readonly ref T _reference; - public readonly long Length { get; } + + /// The number of elements this Span contains. + private readonly long _length; + + /// + /// The number of items in the span. + /// + public readonly long Length => _length; + + /// + /// Gets a value indicating whether this is empty. + /// + /// if this span is empty; otherwise, . public readonly bool IsEmpty => Length is 0; + + /// Creates a new of length 1 around the specified reference. + /// A reference to data. + public LongSpan(ref T reference) + { + _reference = ref reference; + _length = 1; + } + + /// + /// 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. + /// + /// An unmanaged pointer to memory. + /// The number of elements the memory contains. + /// + /// Thrown when is reference type or contains pointers and hence cannot be stored in unmanaged memory. + /// + /// + /// Thrown when the specified is negative. + /// + public unsafe LongSpan(void* pointer, long length) + { + if (length < 0) + { + throw new ArgumentOutOfRangeException(nameof(length), "Length cannot negative"); + } + + if (RuntimeHelpers.IsReferenceOrContainsReferences()) + { + throw new ArgumentException("Generic type parameter T cannot be a refrence type or contain refrences", nameof(T)); + } + + _reference = ref *(T*)pointer; + _length = length; + } + + /// + /// Returns a reference to specified element of the Span. + /// + /// The zero-based index. + /// + /// + /// Thrown when index less than 0 or index greater than or equal to Length + /// + public ref T this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + if ((uint)index >= (uint)_length) + { + throw new IndexOutOfRangeException("Index cannot be less then zero or greater then or equal to length"); + } + + return ref Unsafe.Add(ref _reference, (nint)(uint)index); + } + } } From 6b723e6fb3e1756a459c22737f04d827c15511a4 Mon Sep 17 00:00:00 2001 From: Viktor Stojanovic Date: Thu, 13 Aug 2026 16:46:36 +0200 Subject: [PATCH 2/2] Change LongSpan indexer to use long index The indexer now accepts a long index instead of int. Bounds checking uses ulong, and Unsafe.Add uses (nint)index. Added a comment about possible undefined behavior on 32-bit systems. --- src/LongSpan.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/LongSpan.cs b/src/LongSpan.cs index 0941118..906e0e2 100644 --- a/src/LongSpan.cs +++ b/src/LongSpan.cs @@ -75,17 +75,17 @@ public unsafe LongSpan(void* pointer, long length) /// /// Thrown when index less than 0 or index greater than or equal to Length /// - public ref T this[int index] + public ref T this[long index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { - if ((uint)index >= (uint)_length) + if ((ulong)index >= (ulong)_length) { throw new IndexOutOfRangeException("Index cannot be less then zero or greater then or equal to length"); } - return ref Unsafe.Add(ref _reference, (nint)(uint)index); + return ref Unsafe.Add(ref _reference, (nint)index); // right now this can cause UB on 32 bit systems } } }