213 lines
5.6 KiB
C#
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
|