/*
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;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
namespace dnSpy.Contracts.Hex {
///
/// A normalized read-only collection
///
public sealed class NormalizedHexChangeCollection : IList {
///
/// Gets
///
///
///
public HexChange this[int index] {
get => changes[index];
set => throw new NotSupportedException();
}
///
/// Gets the number of elements in this collection
///
public int Count => changes.Length;
bool ICollection.IsReadOnly => true;
readonly HexChange[] changes;
NormalizedHexChangeCollection(HexChange[] changes) => this.changes = changes;
///
/// Creates an instance
///
/// Change
///
public static NormalizedHexChangeCollection Create(HexChange change) {
if (change is null)
throw new ArgumentNullException(nameof(change));
return new NormalizedHexChangeCollection(new[] { change });
}
///
/// Creates an instance
///
/// Changes
///
public static NormalizedHexChangeCollection Create(IList changes) {
if (changes is null)
throw new ArgumentNullException(nameof(changes));
if (changes.Count == 0)
return new NormalizedHexChangeCollection(Array.Empty());
if (changes.Count == 1)
return new NormalizedHexChangeCollection(new[] { changes[0] });
return new NormalizedHexChangeCollection(CreateNormalizedList(changes).ToArray());
}
static IList CreateNormalizedList(IList changes) {
if (changes.Count == 0)
return Array.Empty();
var list = new List(changes.Count);
list.AddRange(changes);
list.Sort(Comparer.Instance);
for (int i = list.Count - 2; i >= 0; i--) {
var a = list[i];
var b = list[i + 1];
// We'll fix these in the next loop, and they must not have been normalized yet
Debug.Assert(a.OldPosition == a.NewPosition && b.OldPosition == b.NewPosition);
if (a.OldSpan.OverlapsWith(b.OldSpan))
throw new NotSupportedException($"Overlapping {nameof(HexChange)}s is not supported");
if (a.OldSpan.IntersectsWith(b.OldSpan)) {
list[i] = new HexChangeImpl(a.OldPosition, Add(a.OldData, b.OldData), Add(a.NewData, b.NewData));
list.RemoveAt(i + 1);
}
}
long deletedBytes = 0;
for (int i = 0; i < list.Count; i++) {
var change = list[i];
if (deletedBytes != 0) {
var newChange = new HexChangeImpl(change.OldPosition, change.OldData, change.NewPosition - deletedBytes, change.NewData);
list[i] = newChange;
}
deletedBytes += -change.Delta;
}
return new NormalizedHexChangeCollection(list.ToArray());
}
static byte[] Add(byte[] a, byte[] b) {
if (a.Length == 0)
return b;
if (b.Length == 0)
return a;
var res = new byte[a.Length + b.Length];
Array.Copy(a, 0, res, 0, a.Length);
Array.Copy(b, 0, res, a.Length, b.Length);
return res;
}
sealed class Comparer : IComparer {
public static readonly Comparer Instance = new Comparer();
public int Compare([AllowNull] HexChange x, [AllowNull] HexChange y) {
if ((object?)x == y)
return 0;
if (x is null)
return -1;
if (y is null)
return 1;
return x.OldPosition.CompareTo(y.OldPosition);
}
}
///
/// Returns true if is a part of this collection
///
/// Item
///
public bool Contains(HexChange item) => Array.IndexOf(changes, item) >= 0;
///
/// Returns the index of in this collection or a value less than 0 if it's not a part of this collection
///
/// Item
///
public int IndexOf(HexChange item) => Array.IndexOf(changes, item);
///
/// Copies this collection to an array
///
/// Destination array
/// Destination array index
public void CopyTo(HexChange[] array, int arrayIndex) => Array.Copy(changes, 0, array, arrayIndex, changes.Length);
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
///
/// Returns an enumerator
///
///
public IEnumerator GetEnumerator() {
foreach (var c in changes)
yield return c;
}
void ICollection.Add(HexChange item) => throw new NotSupportedException();
void ICollection.Clear() => throw new NotSupportedException();
void IList.Insert(int index, HexChange item) => throw new NotSupportedException();
bool ICollection.Remove(HexChange item) => throw new NotSupportedException();
void IList.RemoveAt(int index) => throw new NotSupportedException();
}
}