/* Copyright (C) 2014-2019 de4dot@gmail.com This file is part of dnSpy dnSpy is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. dnSpy is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with dnSpy. If not, see . */ using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; namespace dnSpy.Contracts.Hex { /// /// Normalized collection /// public sealed class NormalizedHexBufferSpanCollection : IList, IList, IEquatable { /// /// An empty collection /// public static readonly NormalizedHexBufferSpanCollection Empty = new NormalizedHexBufferSpanCollection(); readonly NormalizedHexSpanCollection coll; readonly HexBuffer? buffer; /// /// Constructor /// public NormalizedHexBufferSpanCollection() { coll = NormalizedHexSpanCollection.Empty; buffer = null; } /// /// Constructor /// /// Buffer /// Spans public NormalizedHexBufferSpanCollection(HexBuffer buffer, NormalizedHexSpanCollection spans) { coll = spans ?? throw new ArgumentNullException(nameof(spans)); this.buffer = buffer ?? throw new ArgumentNullException(nameof(buffer)); } /// /// Constructor /// /// Buffer /// Spans public NormalizedHexBufferSpanCollection(HexBuffer buffer, IEnumerable spans) { if (spans is null) throw new ArgumentNullException(nameof(spans)); coll = new NormalizedHexSpanCollection(spans); this.buffer = buffer ?? throw new ArgumentNullException(nameof(buffer)); } /// /// Constructor /// /// Buffer /// Span public NormalizedHexBufferSpanCollection(HexBuffer buffer, HexSpan span) { coll = new NormalizedHexSpanCollection(span); this.buffer = buffer ?? throw new ArgumentNullException(nameof(buffer)); } /// /// Constructor /// /// Span public NormalizedHexBufferSpanCollection(HexBufferSpan span) { if (span.IsDefault) throw new ArgumentException(); coll = new NormalizedHexSpanCollection(span.Span); buffer = span.Buffer; } /// /// Constructor /// /// Spans public NormalizedHexBufferSpanCollection(IEnumerable spans) { if (spans is null) throw new ArgumentNullException(nameof(spans)); var list = new List(); HexBuffer? buffer = null; foreach (var span in spans) { if (span.IsDefault) throw new ArgumentException(); if (buffer is not null && buffer != span.Buffer) throw new ArgumentException(); buffer = span.Buffer; list.Add(span.Span); } this.buffer = buffer; coll = new NormalizedHexSpanCollection(list); } /// /// implicit operator NormalizedHexSpanCollection /// /// public static implicit operator NormalizedHexSpanCollection(NormalizedHexBufferSpanCollection spans) { if (spans is null) throw new ArgumentNullException(nameof(spans)); return spans.coll; } /// /// Returns true if any of the spans in this instance overlaps with /// /// Span /// public bool OverlapsWith(HexBufferSpan span) { if (span.IsDefault) throw new ArgumentException(); // buffer could be null if Count is 0 if (Count == 0) return false; if (span.Buffer != buffer) throw new ArgumentException(); return coll.OverlapsWith(span.Span); } /// /// Returns true if any of the spans in this instance intersects with /// /// Span /// public bool IntersectsWith(HexBufferSpan span) { if (span.IsDefault) throw new ArgumentException(); // buffer could be null if Count is 0 if (Count == 0) return false; if (span.Buffer != buffer) throw new ArgumentException(); return coll.IntersectsWith(span.Span); } /// /// Gets the span at /// /// Index /// public HexBufferSpan this[int index] { get => new HexBufferSpan(buffer!, coll[index]);// if buffer's null, coll is empty and throws set => throw new NotSupportedException(); } object? IList.this[int index] { get => this[index]; set => throw new NotSupportedException(); } /// /// Gets the number of elements in the collection /// public int Count => coll.Count; bool IList.IsFixedSize => true; bool ICollection.IsReadOnly => true; bool IList.IsReadOnly => true; bool ICollection.IsSynchronized => false; object ICollection.SyncRoot => ((IList)coll).SyncRoot; IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); /// /// Gets the enumerator /// /// public IEnumerator GetEnumerator() { foreach (var span in coll) { Debug2.Assert(buffer is not null);// Can't be null if coll is non-empty yield return new HexBufferSpan(buffer, span); } } // These don't seem very useful bool ICollection.Contains(HexBufferSpan item) => throw new NotImplementedException(); void ICollection.CopyTo(HexBufferSpan[] array, int arrayIndex) => throw new NotImplementedException(); int IList.IndexOf(HexBufferSpan item) => throw new NotImplementedException(); void ICollection.CopyTo(Array array, int index) => throw new NotImplementedException(); bool IList.Contains(object? value) => throw new NotImplementedException(); int IList.IndexOf(object? value) => throw new NotImplementedException(); // It's a read-only collection int IList.Add(object? value) => throw new NotSupportedException(); void ICollection.Add(HexBufferSpan item) => throw new NotSupportedException(); void IList.Clear() => throw new NotSupportedException(); void ICollection.Clear() => throw new NotSupportedException(); void IList.Insert(int index, object? value) => throw new NotSupportedException(); void IList.Insert(int index, HexBufferSpan item) => throw new NotSupportedException(); void IList.Remove(object? value) => throw new NotSupportedException(); bool ICollection.Remove(HexBufferSpan item) => throw new NotSupportedException(); void IList.RemoveAt(int index) => throw new NotSupportedException(); void IList.RemoveAt(int index) => throw new NotSupportedException(); /// /// operator ==() /// /// /// /// public static bool operator ==(NormalizedHexBufferSpanCollection? left, NormalizedHexBufferSpanCollection? right) { if ((object?)left == right) return true; if (left is null || right is null) return false; return left.Equals(right); } /// /// operator !=() /// /// /// /// public static bool operator !=(NormalizedHexBufferSpanCollection? left, NormalizedHexBufferSpanCollection? right) => !(left == right); /// /// Equals() /// /// Other instance /// public bool Equals(NormalizedHexBufferSpanCollection? other) { if (other is null) return false; if (Count != other.Count) return false; if (buffer != other.buffer) return false; for (int i = 0; i < Count; i++) { if (coll[i] != other.coll[i]) return false; } return true; } /// /// Equals() /// /// Object /// public override bool Equals(object? obj) => Equals(obj as NormalizedHexBufferSpanCollection); /// /// GetHashCode() /// /// public override int GetHashCode() { int hc = buffer?.GetHashCode() ?? 0; for (int i = 0; i < Count; i++) hc ^= this[i].GetHashCode(); return hc; } /// /// ToString() /// /// public override string ToString() { if (Count == 1) return coll[0].ToString(); return coll.ToString(); } } }