File: System\SpanWriter.cs
Web Access
Project: src\src\System.Private.Windows.Core\src\System.Private.Windows.Core.csproj (System.Private.Windows.Core)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
 
namespace System;
 
/// <summary>
///  Fast stack based <see cref="Span{T}"/> writer.
/// </summary>
internal unsafe ref struct SpanWriter<T>(Span<T> span) where T : unmanaged, IEquatable<T>
{
    private Span<T> _unwritten = span;
    public Span<T> Span { get; } = span;
 
    public int Position
    {
        readonly get => Span.Length - _unwritten.Length;
        set => _unwritten = Span[value..];
    }
 
    public readonly int Length => Span.Length;
 
    /// <summary>
    ///  Try to write the given value.
    /// </summary>
    public bool TryWrite(T value)
    {
        bool success = false;
 
        if (!_unwritten.IsEmpty)
        {
            success = true;
            _unwritten[0] = value;
            UnsafeAdvance(1);
        }
 
        return success;
    }
 
    /// <summary>
    ///  Try to write the given value.
    /// </summary>
    public bool TryWrite(params ReadOnlySpan<T> values)
    {
        bool success = false;
 
        if (_unwritten.Length >= values.Length)
        {
            success = true;
            values.CopyTo(_unwritten);
            UnsafeAdvance(values.Length);
        }
 
        return success;
    }
 
    /// <summary>
    ///  Try to write the given value <paramref name="count"/> times.
    /// </summary>
    public bool TryWriteCount(int count, T value)
    {
        bool success = false;
 
        if (_unwritten.Length >= count)
        {
            success = true;
            _unwritten[..count].Fill(value);
            UnsafeAdvance(count);
        }
 
        return success;
    }
 
    /// <summary>
    ///  Advance the writer by the given <paramref name="count"/>.
    /// </summary>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public void Advance(int count) => _unwritten = _unwritten[count..];
 
    /// <summary>
    ///  Rewind the writer by the given <paramref name="count"/>.
    /// </summary>
    public void Rewind(int count) => _unwritten = Span[(Span.Length - _unwritten.Length - count)..];
 
    /// <summary>
    ///  Reset the reader to the beginning of the span.
    /// </summary>
    public void Reset() => _unwritten = Span;
 
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    private void UnsafeAdvance(int count)
    {
        Debug.Assert((uint)count <= (uint)_unwritten.Length);
        UncheckedSlice(ref _unwritten, count, _unwritten.Length - count);
    }
 
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    private static void UncheckedSlice(ref Span<T> span, int start, int length)
    {
        Debug.Assert((uint)start <= (uint)span.Length && (uint)length <= (uint)(span.Length - start));
        span = MemoryMarshal.CreateSpan(ref Unsafe.Add(ref MemoryMarshal.GetReference(span), (nint)(uint)start), length);
    }
}