WutheringWaves/KcpSharp/NetstandardShim/LinkedListNetstandard.cs
2024-02-08 00:41:39 +03:00

213 lines
5.6 KiB
C#

#if NEED_LINKEDLIST_SHIM
using System;
using System.Diagnostics;
namespace KcpSharp.NetstandardShim
{
internal class LinkedList<T>
{
// This LinkedList is a doubly-Linked circular list.
internal LinkedListNode<T>? head;
internal int count;
internal int version;
public int Count
{
get { return count; }
}
public LinkedListNode<T>? First
{
get { return head; }
}
public LinkedListNode<T>? Last
{
get { return head == null ? null : head.prev; }
}
public void AddAfter(LinkedListNode<T> node, LinkedListNode<T> newNode)
{
ValidateNode(node);
ValidateNewNode(newNode);
InternalInsertNodeBefore(node.next!, newNode);
newNode.list = this;
}
public void AddBefore(LinkedListNode<T> node, LinkedListNode<T> newNode)
{
ValidateNode(node);
ValidateNewNode(newNode);
InternalInsertNodeBefore(node, newNode);
newNode.list = this;
if (node == head)
{
head = newNode;
}
}
public void AddFirst(LinkedListNode<T> node)
{
ValidateNewNode(node);
if (head == null)
{
InternalInsertNodeToEmptyList(node);
}
else
{
InternalInsertNodeBefore(head, node);
head = node;
}
node.list = this;
}
public void AddLast(LinkedListNode<T> node)
{
ValidateNewNode(node);
if (head == null)
{
InternalInsertNodeToEmptyList(node);
}
else
{
InternalInsertNodeBefore(head, node);
}
node.list = this;
}
public void Clear()
{
LinkedListNode<T>? current = head;
while (current != null)
{
LinkedListNode<T> temp = current;
current = current.Next; // use Next the instead of "next", otherwise it will loop forever
temp.Invalidate();
}
head = null;
count = 0;
version++;
}
public void Remove(LinkedListNode<T> node)
{
ValidateNode(node);
InternalRemoveNode(node);
}
public void RemoveFirst()
{
if (head == null) { throw new InvalidOperationException(); }
InternalRemoveNode(head);
}
private void InternalInsertNodeBefore(LinkedListNode<T> node, LinkedListNode<T> newNode)
{
newNode.next = node;
newNode.prev = node.prev;
node.prev!.next = newNode;
node.prev = newNode;
version++;
count++;
}
private void InternalInsertNodeToEmptyList(LinkedListNode<T> newNode)
{
Debug.Assert(head == null && count == 0, "LinkedList must be empty when this method is called!");
newNode.next = newNode;
newNode.prev = newNode;
head = newNode;
version++;
count++;
}
internal void InternalRemoveNode(LinkedListNode<T> node)
{
Debug.Assert(node.list == this, "Deleting the node from another list!");
Debug.Assert(head != null, "This method shouldn't be called on empty list!");
if (node.next == node)
{
Debug.Assert(count == 1 && head == node, "this should only be true for a list with only one node");
head = null;
}
else
{
node.next!.prev = node.prev;
node.prev!.next = node.next;
if (head == node)
{
head = node.next;
}
}
node.Invalidate();
count--;
version++;
}
internal static void ValidateNewNode(LinkedListNode<T> node)
{
if (node == null)
{
throw new ArgumentNullException(nameof(node));
}
if (node.list != null)
{
throw new InvalidOperationException();
}
}
internal void ValidateNode(LinkedListNode<T> node)
{
if (node == null)
{
throw new ArgumentNullException(nameof(node));
}
if (node.list != this)
{
throw new InvalidOperationException();
}
}
}
// Note following class is not serializable since we customized the serialization of LinkedList.
internal sealed class LinkedListNode<T>
{
internal LinkedList<T>? list;
internal LinkedListNode<T>? next;
internal LinkedListNode<T>? prev;
internal T item;
public LinkedListNode(T value)
{
item = value;
}
public LinkedListNode<T>? Next
{
get { return next == null || next == list!.head ? null : next; }
}
public LinkedListNode<T>? Previous
{
get { return prev == null || this == list!.head ? null : prev; }
}
/// <summary>Gets a reference to the value held by the node.</summary>
public ref T ValueRef => ref item;
internal void Invalidate()
{
list = null;
next = null;
prev = null;
}
}
}
#endif