/*************************************************************************
* ModernUO *
* Copyright 2019-2023 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: ValueLinkList.cs *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see . *
*************************************************************************/
using System;
using System.Runtime.CompilerServices;
namespace Server.Collections;
public interface IValueLinkListNode where T : class
{
public T Next { get; set; }
public T Previous { get; set; }
public bool OnLinkList { get; set; }
}
public struct ValueLinkList where T : class, IValueLinkListNode
{
public int Count { get; internal set; }
internal T _first;
internal T _last;
public int Version { get; private set; }
public void Remove(T node)
{
if (node == null)
{
return;
}
if (!node.OnLinkList)
{
throw new ArgumentException("Attempted to remove a node that is not on the list.");
}
if (node.Previous == null)
{
// If previous is null, then it is the first element.
if (_first != node)
{
throw new ArgumentException("Attempted to remove a node that is not on the list.");
}
if (_first == _last)
{
_last = null;
_first = null;
}
else
{
_first = node.Next;
}
if (node.Next != null)
{
node.Next.Previous = null;
}
}
else
{
node.Previous.Next = node.Next;
// If next is null, then it is the last element.
if (node.Next == null)
{
_last = node.Previous;
}
else
{
node.Next.Previous = node.Previous;
}
}
node.Next = null;
node.Previous = null;
node.OnLinkList = false;
Count--;
Version++;
if (Count < 0)
{
throw new Exception("Count is negative!");
}
}
// Remove all entries before this node, not including this node.
public void RemoveAllBefore(T e)
{
if (e == null)
{
return;
}
if (!e.OnLinkList)
{
throw new ArgumentException("Attempted to remove nodes before a node that is not on the list.");
}
if (e.Previous == null)
{
return;
}
var current = e.Previous;
e.Previous = null;
while (current != null)
{
var previous = current.Previous;
current.OnLinkList = false;
current.Next = null;
current.Previous = null;
Count--;
if (Count < 0)
{
throw new Exception("Count is negative!");
}
current = previous;
}
_first = e;
Version++;
}
// Remove all entries after this node, not including this node.
public void RemoveAllAfter(T e)
{
if (e == null)
{
return;
}
if (!e.OnLinkList)
{
throw new ArgumentException("Attempted to remove nodes after a node that is not on the list.");
}
if (e.Next == null)
{
return;
}
var current = e.Next;
e.Next = null;
while (current != null)
{
var next = current.Next;
current.OnLinkList = false;
current.Next = null;
current.Previous = null;
Count--;
if (Count < 0)
{
throw new Exception("Count is negative!");
}
current = next;
}
_last = e;
Version++;
}
public void AddLast(T e)
{
if (e == null)
{
return;
}
if (e.OnLinkList)
{
throw new ArgumentException("Attempted to add a node that is already on a list.");
}
if (_last != null)
{
AddAfter(_last, e);
}
else
{
_first = e;
_last = e;
Count = 1;
Version++;
e.OnLinkList = true;
}
}
public void AddFirst(T e)
{
if (e == null)
{
return;
}
if (e.OnLinkList)
{
throw new ArgumentException("Attempted to add a node that is already on a list.");
}
if (_first != null)
{
AddBefore(_first, e);
}
else
{
_first = e;
_last = e;
Count = 1;
Version++;
e.OnLinkList = true;
}
}
public void AddBefore(T existing, T node)
{
if (node == null)
{
return;
}
ArgumentNullException.ThrowIfNull(existing);
if (!existing.OnLinkList)
{
throw new ArgumentException($"Argument '{nameof(existing)}' must be a node on a list.");
}
if (node.OnLinkList)
{
throw new ArgumentException("Attempted to add a node that is already on a list.");
}
node.Next = existing;
node.Previous = existing.Previous;
if (existing.Previous != null)
{
existing.Previous.Next = node;
}
else
{
_first = node;
}
existing.Previous = node;
node.OnLinkList = true;
Count++;
Version++;
}
public void AddAfter(T existing, T node)
{
if (node == null)
{
return;
}
ArgumentNullException.ThrowIfNull(existing);
if (!existing.OnLinkList)
{
throw new ArgumentException($"Argument '{nameof(existing)}' must be a node on a list.");
}
if (node.OnLinkList)
{
throw new ArgumentException("Attempted to add a node that is already on a list.");
}
node.Previous = existing;
node.Next = existing.Next;
if (existing.Next != null)
{
existing.Next.Previous = node;
}
else
{
_last = node;
}
existing.Next = node;
node.OnLinkList = true;
Count++;
Version++;
}
public void RemoveAll()
{
var current = _first;
while (current != null)
{
var next = current.Next;
current.OnLinkList = false;
current.Next = null;
current.Previous = null;
current = next;
}
_first = null;
_last = null;
Count = 0;
Version++;
}
public void AddLast(ref ValueLinkList otherList, T start, T end)
{
// Should we check if start and end actually exist on the other list?
if (otherList.Count == 0 || otherList.Count == 1 && (start != end || otherList._first != start))
{
throw new ArgumentException("Attempted to add nodes that are not on the specified linklist.");
}
if (start.Previous != null)
{
start.Previous.Next = end.Next;
}
else
{
// Start is first
otherList._first = end.Next;
}
if (end.Next != null)
{
end.Next.Previous = start.Previous;
}
else
{
otherList._last = start.Previous;
}
var count = 1;
var current = start;
// Assume start and end are in the right order, or bad things happen (crash).
while (current != end)
{
count++;
current = current.Next;
}
otherList.Count -= count;
if (otherList.Count < 0)
{
throw new Exception("Count is negative!");
}
if (_last != null)
{
_last.Next = start;
start.Previous = _last;
}
else
{
_first = start;
}
_last = end;
Count += count;
Version++;
}
public T[] ToArray()
{
var arr = new T[Count];
var index = 0;
foreach (var t in this)
{
arr[index++] = t;
}
return arr;
}
public ref struct ValueListEnumerator
{
private bool _started;
private T _current;
private ref readonly ValueLinkList _linkList;
private int _version;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ValueListEnumerator(in ValueLinkList linkList)
{
_linkList = ref linkList;
_started = false;
_current = null;
_version = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
if (!_started)
{
_current = _linkList._first;
_started = true;
_version = _linkList.Version;
}
else if (_linkList.Version != _version)
{
throw new InvalidOperationException(CollectionThrowStrings.InvalidOperation_EnumFailedVersion);
}
else
{
_current = _current.Next;
}
return _current != null;
}
public T Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _current;
}
}
public ref struct DescendingValueListEnumerator
{
private bool _started;
private T _current;
private ref readonly ValueLinkList _linkList;
private int _version;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public DescendingValueListEnumerator(in ValueLinkList linkList)
{
_linkList = ref linkList;
_started = false;
_current = null;
_version = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
if (!_started)
{
_current = _linkList._last;
_started = true;
_version = _linkList.Version;
}
else if (_linkList.Version != _version)
{
throw new InvalidOperationException(CollectionThrowStrings.InvalidOperation_EnumFailedVersion);
}
else
{
_current = _current.Previous;
}
return _current != null;
}
public T Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _current;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public DescendingValueListEnumerator GetEnumerator() => this;
}
}
public static class ValueLinkListExt
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ValueLinkList.ValueListEnumerator GetEnumerator(this in ValueLinkList linkList)
where T : class, IValueLinkListNode => new(in linkList);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ValueLinkList.DescendingValueListEnumerator ByDescending(this in ValueLinkList linkList)
where T : class, IValueLinkListNode => new(in linkList);
}