// dnlib: See LICENSE.txt for more info using System.Collections.Generic; using dnlib.DotNet.MD; namespace dnlib.DotNet.Writer { /// /// MD table interface /// public interface IMDTable { /// /// Gets the table type /// Table Table { get; } /// /// true if the table is empty /// bool IsEmpty { get; } /// /// Gets the number of rows in this table /// int Rows { get; } /// /// Gets/sets a value indicating whether it's sorted /// bool IsSorted { get; set; } /// /// true if has been called /// bool IsReadOnly { get; } /// /// Gets/sets the /// TableInfo TableInfo { get; set; } /// /// Called when the table can't be modified any more /// void SetReadOnly(); } /// /// Creates rows in a table. Rows can optionally be shared to create a compact table. /// /// The raw row type public sealed class MDTable : IMDTable where TRow : struct { readonly Table table; readonly Dictionary cachedDict; readonly List cached; TableInfo tableInfo; bool isSorted; bool isReadOnly; /// public Table Table => table; /// public bool IsEmpty => cached.Count == 0; /// public int Rows => cached.Count; /// public bool IsSorted { get => isSorted; set => isSorted = value; } /// public bool IsReadOnly => isReadOnly; /// public TableInfo TableInfo { get => tableInfo; set => tableInfo = value; } /// /// Gets the value with rid /// /// The row ID public TRow this[uint rid] { get => cached[(int)rid - 1]; set => cached[(int)rid - 1] = value; } /// /// Constructor /// /// The table type /// Equality comparer public MDTable(Table table, IEqualityComparer equalityComparer) { this.table = table; cachedDict = new Dictionary(equalityComparer); cached = new List(); } /// public void SetReadOnly() => isReadOnly = true; /// /// Adds a row. If the row already exists, returns a rid to the existing one, else /// it's created and a new rid is returned. /// /// The row. It's now owned by us and must NOT be modified by the caller. /// The RID (row ID) of the row public uint Add(TRow row) { if (isReadOnly) throw new ModuleWriterException($"Trying to modify table {table} after it's been set to read-only"); if (cachedDict.TryGetValue(row, out uint rid)) return rid; return Create(row); } /// /// Creates a new row even if this row already exists. /// /// The row. It's now owned by us and must NOT be modified by the caller. /// The RID (row ID) of the row public uint Create(TRow row) { if (isReadOnly) throw new ModuleWriterException($"Trying to modify table {table} after it's been set to read-only"); uint rid = (uint)cached.Count + 1; if (!cachedDict.ContainsKey(row)) cachedDict[row] = rid; cached.Add(row); return rid; } /// /// Re-adds all added rows. Should be called if rows have been modified after being /// inserted. /// public void ReAddRows() { if (isReadOnly) throw new ModuleWriterException($"Trying to modify table {table} after it's been set to read-only"); cachedDict.Clear(); for (int i = 0; i < cached.Count; i++) { uint rid = (uint)i + 1; var row = cached[i]; if (!cachedDict.ContainsKey(row)) cachedDict[row] = rid; } } /// /// Reset the table. /// public void Reset() { if (isReadOnly) throw new ModuleWriterException($"Trying to modify table {table} after it's been set to read-only"); cachedDict.Clear(); cached.Clear(); } } }