File: System\Text\RegularExpressions\Symbolic\DoublyLinkedList.cs
Web Access
Project: src\src\libraries\System.Text.RegularExpressions\src\System.Text.RegularExpressions.csproj (System.Text.RegularExpressions)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
 
namespace System.Text.RegularExpressions.Symbolic
{
    /// <summary>Represents a doubly linked list data structure.</summary>
    /// <typeparam name="T">Element type of the list that must be not null</typeparam>
    /// <remarks>
    /// Used to support O(1) append of two lists that is currently not possible by using <see cref="LinkedList{T}"/>.
    /// <see cref="AddLast(DoublyLinkedList{T})"/> operation is made use of in the <see cref="RegexNodeConverter.ConvertToSymbolicRegexNode(RegexNode)"/> method
    /// where it maintains linear construction time in terms of the overall number of AST nodes in a given <see cref="RegexNode"/> input.
    /// Enumeration is performed in reverse of the order added, yielding items from last to first.
    /// </remarks>
    internal sealed class DoublyLinkedList<T> : IEnumerable<T> where T : notnull
    {
        /// <summary>First node of the list</summary>
        private Node? _first;
        /// <summary>Last node of the list</summary>
        private Node? _last;
        /// <summary>The number of elements in the list.</summary>
        private int _size;
 
        /// <summary>Creates a new empty list</summary>
        public DoublyLinkedList() { }
 
        /// <summary>Creates a new singleton list containing the given element</summary>
        public DoublyLinkedList(T elem)
        {
            _first = _last = new Node(elem);
            _size = 1;
        }
 
        /// <summary>Number of elements in the list (positive integer)</summary>
        public int Count => _size;
 
        internal T FirstElement
        {
            get
            {
                Debug.Assert(_first is not null);
                AssertInvariants();
 
                return _first.Value;
            }
        }
 
        /// <summary>Append all the elements from the other list at the end of this list (O(1) operation, the other list must be discarded)</summary>
        public void AddLast(DoublyLinkedList<T> other)
        {
            Debug.Assert(other != this, "self append not allowed to avoid circularity");
            AssertInvariants();
            other.AssertInvariants();
 
            if (other._first is null)
            {
                other._size = -1;
                return;
            }
 
            if (_first is null)
            {
                //this list is empty
                _first = other._first;
                _last = other._last;
                _size = other._size;
                other._size = -1;
                return;
            }
 
            Debug.Assert(_last is not null);
 
            _last.Next = other._first;
            other._first.Prev = _last;
            _last = other._last;
            _size += other._size;
 
            other._size = -1;
        }
 
        /// <summary>Insert the given element at the end of this list (O(1) operation)</summary>
        public void AddLast(T elem)
        {
            AssertInvariants();
 
            if (_last is null)
            {
                //this list is empty
                _first = new(elem);
                _last = _first;
                _size = 1;
                return;
            }
 
            _last = _last.Next = new Node(elem, _last, null);
            _size++;
        }
 
        /// <summary>Insert the given element at the start of this list (O(1) operation)</summary>
        public void AddFirst(T elem)
        {
            AssertInvariants();
 
            if (_first is null)
            {
                //this list is empty
                _first = new(elem);
                _last = _first;
                _size = 1;
                return;
            }
 
            _first.Prev = new(elem, null, _first);
            _first = _first.Prev;
            _size++;
        }
 
        /// <summary>Enumerates the elements in the list from last to first.</summary>
        public IEnumerator<T> GetEnumerator()
        {
            AssertInvariants();
 
            for (Node? current = _last; current is not null; current = current.Prev)
            {
                yield return current.Value;
            }
        }
 
        /// <summary>Enumerates the elements in the list from last to first.</summary>
        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
 
        [Conditional("DEBUG")]
        private void AssertInvariants()
        {
            if (_size == 0)
            {
 
                Debug.Assert(_first is null && _last is null, "empty list");
            }
            else
            {
                Debug.Assert(_size > 0, "_size < 0 means that the list has been invalidated after Append");
                Debug.Assert(_first is not null && _last is not null && _first.Prev is null && _last.Next is null, "non-empty list");
            }
        }
 
        private sealed class Node
        {
            public Node? Next;
            public Node? Prev;
            public readonly T Value;
 
            public Node(T elem) { Value = elem; }
 
            public Node(T elem, Node? prev, Node? next)
            {
                Value = elem;
                Prev = prev;
                Next = next;
            }
        }
    }
}