diff --git a/SnapshotList.cs b/SnapshotList.cs new file mode 100644 index 0000000..9736f06 --- /dev/null +++ b/SnapshotList.cs @@ -0,0 +1,210 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Text; + +namespace NBTExplorer +{ + public class SnapshotState : IDisposable + { + private SnapshotList _list; + + internal SnapshotState (SnapshotList list) + { + _list = list; + _list.Begin(); + } + + public void Dispose () + { + _list.End(); + GC.SuppressFinalize(this); + } + } + + public class SnapshotList : Collection + { + private IList _snapshot; + private IList _recycled; + private int _snapshots; + + public SnapshotList () + : base(new ProxyList()) + { } + + public SnapshotList (IList list) + : base(new ProxyList(new List(list))) + { } + + public SnapshotList (int capacity) + : base(new ProxyList(new List(capacity))) + { } + + private new ProxyList Items + { + get { return base.Items as ProxyList; } + } + + public IList Begin () + { + Modified(); + _snapshot = Items.InnerList; + _snapshots++; + return _snapshot; + } + + public void End () + { + _snapshots = Math.Max(0, _snapshots - 1); + if (_snapshot == null) + return; + + // The backing array was copied, keep around the old array + if (_snapshot != Items.InnerList && _snapshots == 0) { + _recycled = _snapshot; + _recycled.Clear(); + //for (int i = 0, n = _recycled.Count; i < n; i++) + // _recycled[i] = default(T); + } + + _snapshot = null; + } + + public SnapshotState Snapshot () + { + return new SnapshotState(this); + } + + private void Modified () + { + if (_snapshot == null || _snapshot != Items.InnerList) + return; + + // Snapshot is in use, copy backing array to recycled array or create new backing array + if (_recycled != null) { + for (int i = 0; i < Count; i++) + _recycled.Add(Items[i]); + Items.InnerList = _recycled; + _recycled = null; + } + else + Resize(Items.Count); + } + + private void Resize (int newSize) + { + IList oldList = Items.InnerList; + List newList = new List(newSize); + for (int i = 0, n = oldList.Count; i < n; i++) + newList.Add(oldList[i]); + + Items.InnerList = newList; + } + + protected override void InsertItem (int index, T item) + { + Modified(); + base.InsertItem(index, item); + } + + protected override void SetItem (int index, T item) + { + Modified(); + base.SetItem(index, item); + } + + protected override void RemoveItem (int index) + { + Modified(); + base.RemoveItem(index); + } + + protected override void ClearItems () + { + Modified(); + base.ClearItems(); + } + + private class ProxyList : IList + { + public IList InnerList { get; set; } + + public ProxyList () + { + InnerList = new List(); + } + + public ProxyList (IList list) + { + InnerList = list; + } + + public int IndexOf (K item) + { + return InnerList.IndexOf(item); + } + + public void Insert (int index, K item) + { + InnerList.Insert(index, item); + } + + public void RemoveAt (int index) + { + InnerList.RemoveAt(index); + } + + public K this[int index] + { + get { return InnerList[index]; } + set { InnerList[index] = value; } + } + + public void Add (K item) + { + InnerList.Add(item); + } + + public void Clear () + { + InnerList.Clear(); + } + + public bool Contains (K item) + { + return InnerList.Contains(item); + } + + public void CopyTo (K[] array, int arrayIndex) + { + InnerList.CopyTo(array, arrayIndex); + } + + public int Count + { + get { return InnerList.Count; } + } + + public bool IsReadOnly + { + get { return InnerList.IsReadOnly; } + } + + public bool Remove (K item) + { + return InnerList.Remove(item); + } + + public IEnumerator GetEnumerator () + { + return InnerList.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator () + { + return InnerList.GetEnumerator(); + } + } + } +}