// dnlib: See LICENSE.txt for more info using System; using System.Collections.Generic; using dnlib.IO; using dnlib.PE; namespace dnlib.DotNet.Writer { /// /// Re-uses existing chunks to save space /// /// Chunk type public sealed class UniqueChunkList : ChunkListBase where T : class, IChunk { Dictionary dict; /// /// Default constructor /// public UniqueChunkList() : this(EqualityComparer.Default) { } /// /// Constructor /// /// Compares the chunk type public UniqueChunkList(IEqualityComparer chunkComparer) { chunks = new List(); dict = new Dictionary(new ElemEqualityComparer(chunkComparer)); } /// public override void SetOffset(FileOffset offset, RVA rva) { dict = null; base.SetOffset(offset, rva); } /// /// Adds a if not already present /// /// The chunk to add or null if none /// Chunk alignment /// The original input if it wasn't present, or the cached one public T Add(T chunk, uint alignment) { if (setOffsetCalled) throw new InvalidOperationException("SetOffset() has already been called"); if (chunk is null) return null; var elem = new Elem(chunk, alignment); if (dict.TryGetValue(elem, out var other)) return other.chunk; dict[elem] = elem; chunks.Add(elem); return elem.chunk; } /// public override uint CalculateAlignment() { uint alignment = base.CalculateAlignment(); var keys = new KeyValuePair[chunks.Count]; for (var i = 0; i < chunks.Count; i++) keys[i] = new KeyValuePair(i, chunks[i]); Array.Sort(keys, DescendingStableComparer.Instance); for (var i = 0; i < keys.Length; i++) chunks[i] = keys[i].Value; return alignment; } sealed class DescendingStableComparer : IComparer> { internal static readonly DescendingStableComparer Instance = new DescendingStableComparer(); public int Compare(KeyValuePair x, KeyValuePair y) { var result = -x.Value.alignment.CompareTo(y.Value.alignment); if (result != 0) return result; return x.Key.CompareTo(y.Key); } } } }