From ba07e5bf41b97ad061f75008c6e8fd31e9a4991e Mon Sep 17 00:00:00 2001 From: disarray2077 <86157825+disarray2077@users.noreply.github.com> Date: Mon, 21 Feb 2022 01:52:19 -0300 Subject: [PATCH] Add `LinkedList` to corlib --- BeefLibs/corlib/src/Collections/LinkedList.bf | 490 ++++++++++++++++++ 1 file changed, 490 insertions(+) create mode 100644 BeefLibs/corlib/src/Collections/LinkedList.bf diff --git a/BeefLibs/corlib/src/Collections/LinkedList.bf b/BeefLibs/corlib/src/Collections/LinkedList.bf new file mode 100644 index 00000000..43fda23c --- /dev/null +++ b/BeefLibs/corlib/src/Collections/LinkedList.bf @@ -0,0 +1,490 @@ +// This file contains portions of code released by Microsoft under the MIT license as part +// of an open-sourcing initiative in 2014 of the C# core libraries. +// The original source was submitted to https://github.com/Microsoft/referencesource + +#if PARANOID +#define VERSION_LINKEDLIST +#endif + +using System; +using System.Diagnostics; + +using internal System.Collections; // to allow the deletion of LinkedListNode. + +namespace System.Collections +{ + public class LinkedList : ICollection, IEnumerable + { + // This LinkedList is a doubly-Linked circular list. + private LinkedListNode mHead; + private int_cosize mCount; +#if VERSION_LINKEDLIST + private int32 mVersion; +#endif + + public this() + { + } + + public this(IEnumerator collection) + { + for (T item in collection) + AddLast(item); + } + + public ~this() + { + Clear(); + } + + public int Count + { + get + { + return mCount; + } + } + + public LinkedListNode First + { + get + { + return mHead; + } + } + + public LinkedListNode Last + { + get + { + return mHead == null ? null : mHead.mPrev; + } + } + + void ICollection.Add(T value) + { + AddLast(value); + } + + public LinkedListNode AddAfter(LinkedListNode node, T value) + { + Debug.Assert(node != null); + Debug.Assert(node.mList == this, "The LinkedList node already belongs to a LinkedList"); + LinkedListNode result = new LinkedListNode(node.mList, value); + publicInsertNodeBefore(node.mNext, result); + return result; + } + + public void AddAfter(LinkedListNode node, LinkedListNode newNode) + { + Debug.Assert(node != null && newNode != null); + Debug.Assert(node.mList == this, "The LinkedList node does not belong to current LinkedList"); + Debug.Assert(newNode.mList == null, "The LinkedList node already belongs to a LinkedList"); + publicInsertNodeBefore(node.mNext, newNode); + newNode.mList = this; + } + + public LinkedListNode AddBefore(LinkedListNode node, T value) + { + Debug.Assert(node != null); + Debug.Assert(node.mList == this, "The LinkedList node does not belong to current LinkedList"); + LinkedListNode result = new LinkedListNode(node.mList, value); + publicInsertNodeBefore(node, result); + if (node == mHead) + mHead = result; + return result; + } + + public void AddBefore(LinkedListNode node, LinkedListNode newNode) + { + Debug.Assert(node != null && newNode != null); + Debug.Assert(node.mList == this, "The LinkedList node does not belong to current LinkedList"); + Debug.Assert(newNode.mList == null, "The LinkedList node already belongs to a LinkedList"); + publicInsertNodeBefore(node, newNode); + newNode.mList = this; + if (node == mHead) + mHead = newNode; + } + + public LinkedListNode AddFirst(T value) + { + LinkedListNode result = new LinkedListNode(this, value); + if (mHead == null) + { + publicInsertNodeToEmptyList(result); + } + else + { + publicInsertNodeBefore(mHead, result); + mHead = result; + } + return result; + } + + public void AddFirst(LinkedListNode node) + { + Debug.Assert(node != null); + Debug.Assert(node.mList == null, "The LinkedList node already belongs to a LinkedList"); + + if (mHead == null) + { + publicInsertNodeToEmptyList(node); + } + else + { + publicInsertNodeBefore(mHead, node); + mHead = node; + } + node.mList = this; + } + + public LinkedListNode AddLast(T value) + { + LinkedListNode result = new LinkedListNode(this, value); + if (mHead == null) + { + publicInsertNodeToEmptyList(result); + } + else + { + publicInsertNodeBefore(mHead, result); + } + return result; + } + + public void AddLast(LinkedListNode node) + { + Debug.Assert(node != null); + Debug.Assert(node.mList == null, "The LinkedList node already belongs to a LinkedList"); + + if (mHead == null) + { + publicInsertNodeToEmptyList(node); + } + else + { + publicInsertNodeBefore(mHead, node); + } + node.mList = this; + } + + public void Clear() + { + LinkedListNode current = mHead; + while (current != null) + { + LinkedListNode temp = current; + current = current.Next; // use Next the instead of "next", otherwise it will loop forever + delete temp; + } + + mHead = null; + mCount = 0; +#if VERSION_LINKEDLIST + mVersion++; +#endif + } + + public bool Contains(T value) + { + return Find(value) != null; + } + + public void CopyTo(Span array) + { + Debug.Assert(array.Length < Count); + + LinkedListNode node = mHead; + if (node != null) + { + int index = 0; + repeat + { + array[index++] = node.mItem; + node = node.mNext; + } while (node != mHead); + } + } + + public LinkedListNode Find(T value) + { + LinkedListNode node = mHead; + if (node != null) + { + if (value != null) + { + repeat + { + if (node.mItem == value) + { + return node; + } + node = node.mNext; + } while (node != mHead); + } + else + { + repeat + { + if (node.mItem == null) + { + return node; + } + node = node.mNext; + } while (node != mHead); + } + } + return null; + } + + public LinkedListNode FindLast(T value) + { + if (mHead == null) return null; + + LinkedListNode last = mHead.mPrev; + LinkedListNode node = last; + if (node != null) + { + if (value != null) + { + repeat + { + if (node.mItem == value) + { + return node; + } + + node = node.mPrev; + } while (node != last); + } + else + { + repeat + { + if (node.mItem == null) + { + return node; + } + node = node.mPrev; + } while (node != last); + } + } + return null; + } + + public Enumerator GetEnumerator() + { + return Enumerator(this); + } + + public bool Remove(T value) + { + LinkedListNode node = Find(value); + if (node != null) + { + publicRemoveNode(node); + return true; + } + return false; + } + + public void Remove(LinkedListNode node) + { + Debug.Assert(node != null); + Debug.Assert(node.mList == this, "The LinkedList node does not belong to current LinkedList"); + publicRemoveNode(node); + } + + public void RemoveFirst() + { + Debug.Assert(mHead != null, "RemoveFirst called on a LinkedList with no nodes"); + publicRemoveNode(mHead); + } + + public void RemoveLast() + { + Debug.Assert(mHead != null, "RemoveLast called on a LinkedList with no nodes"); + publicRemoveNode(mHead.mPrev); + } + + private void publicInsertNodeBefore(LinkedListNode node, LinkedListNode newNode) + { + newNode.mNext = node; + newNode.mPrev = node.mPrev; + node.mPrev.mNext = newNode; + node.mPrev = newNode; +#if VERSION_LINKEDLIST + mVersion++; +#endif + mCount++; + } + + private void publicInsertNodeToEmptyList(LinkedListNode newNode) + { + Debug.Assert(mHead == null && mCount == 0, "LinkedList must be empty when this method is called!"); + newNode.mNext = newNode; + newNode.mPrev = newNode; + mHead = newNode; +#if VERSION_LINKEDLIST + mVersion++; +#endif + mCount++; + } + + private void publicRemoveNode(LinkedListNode node) + { + Debug.Assert(node.mList == this, "Deleting the node from another list!"); + Debug.Assert(mHead != null, "This method shouldn't be called on empty list!"); + if (node.mNext == node) + { + Debug.Assert(mCount == 1 && mHead == node, "this should only be true for a list with only one node"); + DeleteAndNullify!(mHead); + } + else + { + node.mNext.mPrev = node.mPrev; + node.mPrev.mNext = node.mNext; + if (mHead == node) + { + mHead = node.mNext; + } + delete node; + } + mCount--; +#if VERSION_LINKEDLIST + mVersion++; +#endif + } + + public struct Enumerator : IEnumerator, IResettable + { + private LinkedList mList; + private LinkedListNode mNode; +#if VERSION_LINKEDLIST + private int32 mVersion; +#endif + private T mCurrent; + private int mIndex; + + public this(LinkedList list) + { + mList = list; +#if VERSION_LINKEDLIST + mVersion = list.mVersion; +#endif + mNode = list.mHead; + mCurrent = default(T); + mIndex = 0; + } + + public T Current + { + get + { + return mCurrent; + } + } + + public bool MoveNext() mut + { +#if VERSION_LINKEDLIST + if (mVersion != mList.mVersion) + Runtime.FatalError("LinkedList changed during enumeration"); +#endif + + if (mNode == null) + { + mIndex = mList.Count + 1; + return false; + } + + ++mIndex; + mCurrent = mNode.mItem; + mNode = mNode.mNext; + if (mNode == mList.mHead) + mNode = null; + return true; + } + + public Result GetNext() mut + { + if (MoveNext()) + return .Ok(Current); + return .Err; + } + + void IResettable.Reset() mut + { +#if VERSION_LINKEDLIST + if (mVersion != mList.mVersion) + Runtime.FatalError("LinkedList changed during enumeration"); +#endif + + mCurrent = default(T); + mNode = mList.mHead; + mIndex = 0; + } + } + } + + public sealed class LinkedListNode + { + public LinkedList mList; + public LinkedListNode mNext; + public LinkedListNode mPrev; + public T mItem; + + public this(T value) + { + mItem = value; + } + + public this(LinkedList list, T value) + { + mList = list; + mItem = value; + } + + // This class can only be deleted by the LinkedList. + internal ~this() + { + } + + public LinkedList List + { + get + { + return mList; + } + } + + public LinkedListNode Next + { + get + { + return mNext == null || mNext == mList.[Friend]mHead ? null : mNext; + } + } + + public LinkedListNode Previous + { + get + { + return mPrev == null || this == mList.[Friend]mHead ? null : mPrev; + } + } + + public T Value + { + get + { + return mItem; + } + + set + { + mItem = value; + } + } + } +}