// System using System; using System.Collections; using System.Collections.Generic; // GUPS - AntiCheat - Core using GUPS.AntiCheat.Core.Integrity; namespace GUPS.AntiCheat.Protected.Collection { /// /// Represents a protected stack that implements the , , /// , and interfaces. This stack allows tracking changes /// and provides a hash code for verification purposes. Before interacting with the stack, you should call the /// to verify its integrity. /// /// The type of elements in the stack. public class ProtectedStack : IEnumerable, IEnumerable, IReadOnlyCollection, ICollection, IDataIntegrity where T : struct { /// /// Stores the stack of items. /// private readonly Stack stack; /// /// Gets a value indicating whether access to the this.stack is synchronized (thread-safe). /// bool ICollection.IsSynchronized => ((ICollection)this.stack).IsSynchronized; /// /// Gets an object that can be used to synchronize access to the this.stack. /// object ICollection.SyncRoot => ((ICollection)this.stack).SyncRoot; /// /// Gets the hash code associated with the current state of the stack. /// public Int32 Hash { get; private set; } /// /// Get if the protected value has integrity, i.e., whether it has maintained its original state. /// public bool HasIntegrity { get; private set; } = true; /// /// Initializes a new instance of the class. /// public ProtectedStack() { this.stack = new Stack(); this.Hash = this.GetHashCode(); } /// /// Initializes a new instance of the class that contains elements copied from the specified collection. /// /// The collection whose elements are copied to the new stack. public ProtectedStack(IEnumerable _Collection) { this.stack = new Stack(_Collection); this.Hash = this.GetHashCode(); } /// /// Initializes a new instance of the class with the specified initial capacity. /// /// The initial number of elements that the stack can contain. public ProtectedStack(int _Capacity) { this.stack = new Stack(_Capacity); this.Hash = this.GetHashCode(); } /// /// Gets the number of elements contained in the stack. /// public int Count => this.stack.Count; /// /// Determines whether the stack contains a specific value. /// /// The object to locate in the stack. /// true if item is found in the stack; otherwise, false. public bool Contains(T _Item) => this.stack.Contains(_Item); /// /// Copies the elements of the stack to an array, starting at a particular array index. /// /// The one-dimensional Array that is the destination of the elements copied from the stack. /// The zero-based index in array at which copying begins. public void CopyTo(T[] _Array, int _ArrayIndex) => this.stack.CopyTo(_Array, _ArrayIndex); /// /// Copies the elements of the ICollection to an Array, starting at a particular Array index. /// /// The one-dimensional Array that is the destination of the elements copied from the ICollection. /// The zero-based index in _Array at which copying begins. void ICollection.CopyTo(Array _Array, int _Index) => ((ICollection)this.stack).CopyTo(_Array, _Index); /// /// Inserts an object at the top of the stack. /// /// The object to push onto the stack. public void Push(T _Item) { // Push the new item to the stack. this.stack.Push(_Item); // Add the new item to the hash code. this.Hash = this.AddToHashCode(this.Hash, _Item); } /// /// Returns the object at the top of the stack without removing it. /// /// The object at the top of the stack. public T Peek() => this.stack.Peek(); /// /// Removes and returns the object at the top of the stack. /// /// The object removed from the top of the stack. public T Pop() { // Pop the item from the queue. T var_Item = this.stack.Pop(); // Remove the item from the hash code. this.Hash = this.RemoveFromHashCode(this.Hash, var_Item); // Return the popped item. return var_Item; } /// /// Sets the capacity to the actual number of elements in the stack, if that number is less than a threshold value. /// public void TrimExcess() => this.stack.TrimExcess(); /// /// Returns the object at the top of the stack without removing it and returns whether the operation succeeded. /// /// When this method returns, contains the object at the top of the stack, if the stack is not empty; otherwise, the default value for the element type. /// true if there was an object to return; otherwise, false. public bool TryPeek(out T _Result) => this.stack.TryPeek(out _Result); /// /// Removes and returns the object at the top of the stack and returns whether the operation succeeded. /// /// When this method returns, contains the object removed from the top of the stack, if the stack is not empty; otherwise, the default value for the element type. /// true if there was an object to return; otherwise, false. public bool TryPop(out T _Result) { if (this.stack.Count > 0) { _Result = this.Pop(); return true; } _Result = default; return false; } /// /// Copies the elements of the stack to a new array. /// /// An array containing copies of the elements of the stack. public T[] ToArray() => this.stack.ToArray(); /// /// Removes all elements from the stack. /// public void Clear() => this.stack.Clear(); /// /// Verifies the integrity of the stack by comparing the current hash with the computed hash. /// /// True if the stack is verified successfully; otherwise, false. public bool CheckIntegrity() { Int32 currentHash = this.GetHashCode(); if (this.Hash != currentHash) { this.HasIntegrity = false; } return this.HasIntegrity; } /// /// Returns a hash code for the queue based on its elements. /// /// A hash code for the current queue. public override int GetHashCode() { // Initialize the hash code with a prime number. int var_Hash = 17; // Iterate through the queue and add each item to the hash code. foreach (T var_Item in this.stack) { var_Hash = this.AddToHashCode(var_Hash, var_Item); } // Return the final hash code. return var_Hash; } /// /// Add a new item to the hash, instead of calculating the hash code from scratch. /// /// The current hash code. /// The item to add to the hash code. /// The new hash code. private int AddToHashCode(int _HashCode, T _Item) { // Make sure to not throw an exception when an overflow occurs and wrap the result. unchecked { return _HashCode + _Item.GetHashCode() * 23; } } /// /// Remove an existing item from the hash, instead of calculating the hash code from scratch. /// /// The current hash code. /// The item to remove from the hash code. /// The new hash code. private int RemoveFromHashCode(int _HashCode, T _Item) { // Make sure to not throw an exception when an overflow occurs and wrap the result. unchecked { return _HashCode - _Item.GetHashCode() * 23; } } /// /// Returns an enumerator that iterates through the stack. /// /// An enumerator for the stack. public IEnumerator GetEnumerator() => this.stack.GetEnumerator(); /// /// Returns an enumerator that iterates through the stack. /// /// An enumerator for the stack. IEnumerator IEnumerable.GetEnumerator() => this.stack.GetEnumerator(); } }