// 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();
}
}