| File: System\Text\RegularExpressions\Symbolic\DoublyLinkedList.cs | Web Access |
| Project: src\runtime\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; } } } }