/// <summary> /// Construct a Table. /// </summary> public Table(int capacity = DefaultCapacity) { if (capacity <= 0) { throw new ArgumentException($"Capacity {capacity} must be >= 0"); } SingleValues = new SpannableList <TValue>(capacity); }
/// <summary> /// Construct a derived Table sharing the IDs of the given base Table. /// </summary> public Table(ITable <TId> baseTable) { if (baseTable == null) { throw new ArgumentException("Base table must not be null"); } BaseTableOpt = baseTable; SingleValues = new SpannableList <TValue>(baseTable.Count == 0 ? DefaultCapacity : baseTable.Count); }
/// <summary> /// All relationships have been added; sort them all and build the final relation table. /// </summary> public void Complete() { m_list.AsSpan().Sort((tuple1, tuple2) => { int fromIdCompare = tuple1.fromId.FromId().CompareTo(tuple2.fromId.FromId()); if (fromIdCompare != 0) { return(fromIdCompare); } return(tuple1.toId.FromId().CompareTo(tuple2.toId.FromId())); }); // and bin them by groups int listIndex = 0; SpannableList <TToId> buffer = new SpannableList <TToId>(); int listCount = m_list.Count; Table.SetMultiValueCapacity(listCount); foreach (TFromId id in Table.BaseTableOpt.Ids) { if (listIndex >= m_list.Count) { // ran outta entries, rest all 0 break; } // Count up how many are for id. int count = 0; buffer.Clear(); // create a to-ID that will never equal any other ID (even default) TToId lastToId = default(TToId).ToId(-1); while (listIndex + count < m_list.Count) { var(fromId, toId) = m_list[listIndex + count]; if (fromId.Equals(id)) { // drop duplicates (silently...) // TODO: are duplicates here a logic bug? Because they do happen in practice. if (!toId.Equals(lastToId)) { buffer.Add(toId); } count++; lastToId = toId; continue; } // ok we're done break; } Table.Add(buffer.AsSpan()); listIndex += count; } }
/// <summary> /// Save this list of unmanaged values to the given filename in the given directory. /// </summary> public static void SaveToFile <TValue>(string directory, string name, SpannableList <TValue> values) where TValue : unmanaged { string path = Path.Combine(directory, name); using (Stream writer = File.OpenWrite(path)) { // kind of a lot of work to write out an int, but whatevs int[] length = new int[] { values.Count }; writer.Write(MemoryMarshal.Cast <int, byte>(new Span <int>(length))); writer.Write(MemoryMarshal.Cast <TValue, byte>(values.AsSpan())); } }
/// <summary> /// Load a list of unmanaged values from the given filename in the given directory. /// </summary> public static void LoadFromFile <TValue>(string directory, string name, SpannableList <TValue> values) where TValue : unmanaged { values.Clear(); string path = Path.Combine(directory, name); using (Stream reader = File.OpenRead(path)) { int[] lengthBuf = new int[1]; reader.Read(MemoryMarshal.Cast <int, byte>(new Span <int>(lengthBuf))); int length = lengthBuf[0]; values.Capacity = length; values.Fill(length, default); Span <TValue> valueSpan = values.AsSpan(); Span <byte> byteValueSpan = MemoryMarshal.Cast <TValue, byte>(valueSpan); reader.Read(byteValueSpan); } }
/// <summary> /// Create a new RelationTable that is the inverse of this relation. /// </summary> /// <remarks> /// Very useful for calculating, for example, pip dependents based on pip dependencies, or /// per-file producers based on per-pip files produced. /// </remarks> public RelationTable <TToId, TFromId> Invert() { RelationTable <TToId, TFromId> result = new RelationTable <TToId, TFromId>(RelatedTable, BaseTableOpt); // We will use result.Values to accumulate the counts as usual. result.SingleValues.Fill(RelatedTable.Count, 0); // And we will use result.m_offsets to store the offsets as usual. result.Offsets.Fill(RelatedTable.Count, 0); int sum = 0; foreach (TFromId id in BaseTableOpt.Ids) { foreach (TToId relatedId in this[id]) { result.SingleValues[relatedId.FromId() - 1]++; sum++; } } // Now we can calculate m_offsets. result.CalculateOffsets(); // And we know the necessary size of m_relations. result.MultiValues.Capacity = sum; result.MultiValues.Fill(sum, default); // Allocate an array of positions to track how many relations we have filled in. SpannableList <int> positions = new SpannableList <int>(RelatedTable.Count + 1); positions.Fill(RelatedTable.Count + 1, 0); // And accumulate all the inverse relations. foreach (TFromId id in BaseTableOpt.Ids) { foreach (TToId relatedId in this[id]) { int relatedIdInt = relatedId.FromId() - 1; int idInt = id.FromId() - 1; int offset = result.Offsets[relatedIdInt]; int position = positions[relatedIdInt]; int relationIndex = result.Offsets[relatedIdInt] + positions[relatedIdInt]; result.MultiValues[relationIndex] = id; positions[relatedIdInt]++; if (positions[relatedIdInt] > result.SingleValues[relatedIdInt]) { // this is a logic bug, should never happen throw new Exception( $"RelationTable.Inverse: logic exception: positions[{relatedIdInt}] = {positions[relatedIdInt]}, result.SingleValues[{relatedIdInt}] = {result.SingleValues[relatedIdInt]}"); } else if (positions[relatedIdInt] == result.SingleValues[relatedIdInt]) { // all the relations for this ID are known. now, we have to sort them. Span <TFromId> finalSpan = result.MultiValues.AsSpan().Slice(result.Offsets[relatedIdInt], result.SingleValues[relatedIdInt]); SpanUtilities.Sort(finalSpan, (id1, id2) => id1.FromId().CompareTo(id2.FromId())); } } } // TODO: error check that there are no zero entries in m_relations return(result); }
/// <summary> /// Construct a MultiValueTable. /// </summary> /// <remarks> /// This must be called after baseTable has been fully populated, /// or this table will not be able to preallocate its capacity. /// </remarks> public MultiValueTable(int capacity = DefaultCapacity) : base(capacity) { Offsets = new SpannableList <int>(capacity); MultiValues = new SpannableList <TValue>(); }
/// <summary> /// Construct a MultiValueTable. /// </summary> /// <remarks> /// This must be called after baseTable has been fully populated, /// or this table will not be able to preallocate its capacity. /// </remarks> public MultiValueTable(ITable <TId> baseTable) : base(baseTable) { Offsets = new SpannableList <int>(baseTable.Count == 0 ? DefaultCapacity : baseTable.Count); MultiValues = new SpannableList <TValue>(); }