public sealed class MetadataAggregator
{
// For each heap handle and each delta contains aggregate heap lengths.
- // heapSizes[heap kind][reader index] == Sum { 0..index | reader[i].XxxHeapLength }
+ // heapSizes[heap kind][reader index] == Sum { 0..index | reader[i].XxxHeap.Block.Length }
private readonly ImmutableArray<ImmutableArray<int>> _heapSizes;
private readonly ImmutableArray<ImmutableArray<RowCounts>> _rowCounts;
}
}
+ /// <summary>
+ /// Given a handle of an entity in an aggregate metadata calculates
+ /// a handle of the entity within the metadata generation it is defined in.
+ /// </summary>
+ /// <param name="handle">Handle of an entity in an aggregate metadata.</param>
+ /// <param name="generation">The generation the entity is defined in.</param>
+ /// <returns>Handle of the entity within the metadata generation <paramref name="generation"/>.</returns>
public Handle GetGenerationHandle(Handle handle, out int generation)
{
if (handle.IsVirtual)
var sizes = _heapSizes[(int)heapIndex];
- generation = sizes.BinarySearch(heapOffset);
+ // #Guid heap offset is 1-based, other heaps have 0-based offset:
+ var size = (handle.Type == HandleType.Guid) ? heapOffset - 1 : heapOffset;
+
+ generation = sizes.BinarySearch(size);
if (generation >= 0)
{
- Debug.Assert(sizes[generation] == heapOffset);
+ Debug.Assert(sizes[generation] == size);
// the index points to the start of the next generation that added data to the heap:
do
{
generation++;
}
- while (generation < sizes.Length && sizes[generation] == heapOffset);
+ while (generation < sizes.Length && sizes[generation] == size);
}
else
{
throw new ArgumentException(SR.HandleBelongsToFutureGeneration, nameof(handle));
}
- // GUID heap accumulates - previous heap is copied to the next generation
+ // GUID heap accumulates - previous heap is copied to the next generation
int relativeHeapOffset = (handle.Type == HandleType.Guid || generation == 0) ? heapOffset : heapOffset - sizes[generation - 1];
return new Handle((byte)handle.Type, relativeHeapOffset);
/// to the specified <paramref name="handle"/>.
/// </summary>
/// <returns>
- /// Zero based offset, or -1 if <paramref name="handle"/> can only be interpreted in a context of a specific <see cref="MetadataReader"/> or <see cref="MetadataBuilder"/>.
- /// See <see cref="GetHeapOffset(MetadataReader, Handle)"/>.
+ /// 1-based index into the #Guid heap. Unlike other heaps, which are essentially byte arrays, the #Guid heap is an array of 16-byte GUIDs.
/// </returns>
public static int GetHeapOffset(GuidHandle handle) => handle.Index;
/// to the specified <paramref name="handle"/>.
/// </summary>
/// <returns>
- /// Zero based offset, or -1 if <paramref name="handle"/> can only be interpreted in a context of a specific <see cref="MetadataReader"/> or <see cref="MetadataBuilder"/>.
- /// See <see cref="GetHeapOffset(MetadataReader, Handle)"/>.
+ /// Zero based offset.
/// </returns>
public static int GetHeapOffset(UserStringHandle handle) => handle.GetHeapOffset();
Assert.Equal(expected, string.Join(" | ", actual));
}
- private static void TestGenerationHandle(MetadataAggregator aggregator, Handle handle, Handle expectedHandle, int expectedGeneration)
+ private static void TestGenerationHandle(MetadataAggregator aggregator, Handle aggregateHandle, Handle expectedHandle, int expectedGeneration)
{
int actualGeneration;
- var actualHandle = aggregator.GetGenerationHandle(handle, out actualGeneration);
+ var actualHandle = aggregator.GetGenerationHandle(aggregateHandle, out actualGeneration);
Assert.Equal(expectedGeneration, actualGeneration);
Assert.Equal(expectedHandle, actualHandle);
}
200, // Gen3
400, // Gen4
},
- new int[] // #Guid
+ new int[] // #Guid (sizes are numbers of GUIDs on the heap, not bytes; GUIDs on the heap accumulate, previous content is copied to the next gen).
{
- 2, // Gen0
- 4, // Gen1
- 8, // Gen2
- 16, // Gen3
- 32, // Gen4
+ 1, // Gen0: Guid #1
+ 2, // Gen1: Guid #1, #2
+ 2, // Gen2: Guid #1, #2
+ 2, // Gen3: Guid #1, #2
+ 3, // Gen4: Guid #1, #2, #3
}
};
TestGenerationHandle(aggregator, MetadataTokens.UserStringHandle(12), expectedHandle: MetadataTokens.UserStringHandle(2), expectedGeneration: 2);
TestGenerationHandle(aggregator, MetadataTokens.StringHandle(0), expectedHandle: MetadataTokens.StringHandle(0), expectedGeneration: 2);
+ // GUIDs on the heap accumulate, previous content is copied to the next gen, so the expected handle is the same as the given handle
+ TestGenerationHandle(aggregator, MetadataTokens.GuidHandle(1), expectedHandle: MetadataTokens.GuidHandle(1), expectedGeneration: 0);
+ TestGenerationHandle(aggregator, MetadataTokens.GuidHandle(2), expectedHandle: MetadataTokens.GuidHandle(2), expectedGeneration: 1);
+ TestGenerationHandle(aggregator, MetadataTokens.GuidHandle(3), expectedHandle: MetadataTokens.GuidHandle(3), expectedGeneration: 4);
+
AssertExtensions.Throws<ArgumentException>("handle", () => TestGenerationHandle(aggregator, MetadataTokens.StringHandle(22), expectedHandle: MetadataTokens.StringHandle(0), expectedGeneration: 0));
}
}