// 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);
}
}
}
}