/// <summary> /// Sets the provided item (ref or null) to all available cells. /// The Drive must be N. /// </summary> /// <remarks>The method is synchronized.</remarks> /// <param name="item">The ref to be set</param> /// <exception cref="System.InvalidOperationException">Drive is not N or Straight</exception> public void Format(T item) { lock (commonLock) { Interlocked.Increment(ref i[CCOPS]); try { if (Drive != TesseractGear.N) { throw new InvalidOperationException("Wrong drive"); } var p = new TesseractPos(AllocatedSlots - 1); for (int d0 = 0; d0 <= p.D0; d0++) { for (int d1 = 0; d1 <= p.D1; d1++) { for (int d2 = 0; d2 <= p.D2; d2++) { for (int d3 = 0; d3 <= p.D3; d3++) { blocks[d0][d1][d2][d3] = item; } } } } } finally { Interlocked.Decrement(ref i[CCOPS]); } } }
/// <summary> /// Sets value at index if the current value equals the comparand. /// </summary> /// <param name="index">The index must be less than AllocatedSlots.</param> /// <param name="value">The value to be set at index.</param> /// <param name="comparand">Will be compared to this[index]</param> /// <returns>The original value at index.</returns> public T CAS(int index, T value, T comparand) { if (Drive == TesseractGear.P) { throw new InvalidOperationException("Wrong drive"); } if (index < 0 || index >= AllocatedSlots) { throw new ArgumentOutOfRangeException("index"); } var p = new TesseractPos(index); var r = Interlocked.CompareExchange(ref blocks[p.D0][p.D1][p.D2][p.D3], value, comparand); if (CountNotNulls) { if (r != null) { if (value == null) { Interlocked.Decrement(ref i[NNCNT]); } } else if (value != null) { Interlocked.Increment(ref i[NNCNT]); } } return(r); }
/// <summary> /// Access to the individual cells. /// </summary> /// <param name="index">Must be less than AllocatedSlots</param> /// <returns>The object reference at the index</returns> /// <exception cref="ArgumentOutOfRangeException">index</exception> /// <exception cref="System.InvalidOperationException">If the Drive is wrong</exception> public T this[int index] { get { if (Drive == TesseractGear.P) { throw new InvalidOperationException("Wrong drive"); } if (index < 0 || index >= AllocatedSlots) { throw new ArgumentOutOfRangeException("index"); } var p = new TesseractPos(index); return(Volatile.Read(ref blocks[p.D0][p.D1][p.D2][p.D3])); } set { if (Drive == TesseractGear.P) { throw new InvalidOperationException("Wrong drive"); } if (index < 0 || index >= AllocatedSlots) { throw new ArgumentOutOfRangeException("index"); } set(index, value); } }
/// <summary> /// Searches for an item by traversing all cells up to AppendIndex. /// The reads are volatile, the comparison Object.Equals(). /// </summary> /// <param name="item">The object ref</param> /// <returns>A positive value if the item is found, -1 otherwise.</returns> public int IndexOf(T item) { int result = -1; int aIdx = AppendIndex; var p = new TesseractPos(0); for (var i = 0; i <= aIdx; i++) { p.Set(i); var r = Volatile.Read(ref blocks[p.D0][p.D1][p.D2][p.D3]); if (r != null && r.Equals(item)) { result = i; break; } } return(result); }
/// <summary> /// Iterates all cells from 0 up to AppendIndex and yields each item /// if it's not null at the time of the check. /// </summary> /// <param name="assertGear">If true volatile-reads the Drive at each iteration. False by default. </param> /// <returns>A not null item.</returns> /// <exception cref="InvalidOperationException">If the Drive is P</exception> public IEnumerable <T> NotNullItems(bool assertGear = false, bool allSlots = false) { T item = null; var j = allSlots ? AllocatedSlots - 1 : AppendIndex; var p = new TesseractPos(j); for (int i = 0; i <= j; i++) { if (assertGear && Drive == TesseractGear.P) { throw new InvalidOperationException("Wrong drive"); } p.Set(i); item = Volatile.Read(ref blocks[p.D0][p.D1][p.D2][p.D3]); if (item != null) { yield return(item); } } }
/// <summary> /// Expands or shrinks the virtual array to the number of SIDE tiles fitting the requested length. /// If the AppendIndex is greater that the new length, it's cut to length -1. /// If shrinking and counting, the number of not-null values (ItemsCount) is also updated. /// The Drive must be P when shrinking. /// </summary> /// <param name="length">The new length.</param> /// <param name="expand">The intent of the caller.</param> /// <exception cref="System.ArgumentOutOfRangeException">If length is negative</exception> /// <exception cref="System.InvalidOperationException"> /// If the Drive is not P when shrinking.</exception> public bool Resize(int length, bool expand) { Interlocked.Increment(ref i[CCOPS]); TesseractGear inDrive = Drive; try { var slots = Volatile.Read(ref i[SLOTS]); if (length < 0) { throw new ArgumentOutOfRangeException("length"); } if (length == slots) { return(false); } lock (commonLock) { var als = Volatile.Read(ref i[SLOTS]); if (length == als) { return(false); } if (length > als) { return(expand ? alloc(length, als) > als : false); } else { if (expand) { return(false); } if (inDrive != TesseractGear.P) { throw new InvalidOperationException("Wrong drive"); } var toSize = length > 0 ? length + SIDE : 0; var p = new TesseractPos(als); while (als > toSize) { p.D2--; if (p.D2 < 1) { blocks[p.D0][p.D1] = null; p.D1--; if (p.D1 < 1) { p.D0--; p.D1 = SIDE - 1; } p.D2 = SIDE - 1; } else { blocks[p.D0][p.D1][p.D2] = null; } als -= SIDE; } Volatile.Write(ref i[SLOTS], als); if (AppendIndex >= length) { Interlocked.Exchange(ref i[INDEX], length - 1); } if (CountNotNulls) { int notNull = 0; foreach (var c in NotNullItems()) { notNull++; } Interlocked.Exchange(ref i[NNCNT], notNull); } return(true); } } } finally { if (Interlocked.Decrement(ref i[CCOPS]) == 0 && inDrive != Drive) { gearShift.Set(); } } }