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