in src/StructuredLogger/BinaryLogger/BuildEventArgsWriter.cs [825:926]
private void WriteProjectItems(IEnumerable items)
{
if (items == null)
{
Write(0);
return;
}
var type = items.GetType();
if (type.Name == "ItemDictionary`1")
{
var method = Reflector.GetEnumerateItemsPerTypeMethod(type);
if (method != null)
{
method.Invoke(items, new object[] { WriteItems });
}
// signal the end
Write(0);
return;
}
void WriteItems(string itemType, object itemList)
{
WriteDeduplicatedString(itemType);
WriteTaskItemList((IEnumerable)itemList);
CheckForFilesToEmbed(itemType, (IEnumerable)itemList);
}
#if false
if (items is ItemDictionary<ProjectItemInstance> itemInstanceDictionary)
{
// If we have access to the live data from evaluation, it exposes a special method
// to iterate the data structure under a lock and return results grouped by item type.
// There's no need to allocate or call GroupBy this way.
itemInstanceDictionary.EnumerateItemsPerType((itemType, itemList) =>
{
WriteDeduplicatedString(itemType);
WriteTaskItemList(itemList);
CheckForFilesToEmbed(itemType, itemList);
});
// signal the end
Write(0);
}
// not sure when this can get hit, but best to be safe and support this
else if (items is ItemDictionary<ProjectItem> itemDictionary)
{
itemDictionary.EnumerateItemsPerType((itemType, itemList) =>
{
WriteDeduplicatedString(itemType);
WriteTaskItemList(itemList);
CheckForFilesToEmbed(itemType, itemList);
});
// signal the end
Write(0);
}
else
#endif
{
string currentItemType = null;
// Write out a sequence of items for each item type while avoiding GroupBy
// and associated allocations. We rely on the fact that items of each type
// are contiguous. For each item type, write the item type name and the list
// of items. Write 0 at the end (which would correspond to item type null).
// This is how the reader will know how to stop. We can't write out the
// count of item types at the beginning because we don't know how many there
// will be (we'd have to enumerate twice to calculate that). This scheme
// allows us to stream in a single pass with no allocations for intermediate
// results.
Internal.Utilities.EnumerateItems(items, dictionaryEntry =>
{
string key = (string)dictionaryEntry.Key;
// boundary between item types
if (currentItemType != null && currentItemType != key)
{
WriteDeduplicatedString(currentItemType);
WriteTaskItemList(reusableProjectItemList);
CheckForFilesToEmbed(currentItemType, reusableProjectItemList);
reusableProjectItemList.Clear();
}
reusableProjectItemList.Add(dictionaryEntry.Value);
currentItemType = key;
});
// write out the last item type
if (reusableProjectItemList.Count > 0)
{
WriteDeduplicatedString(currentItemType);
WriteTaskItemList(reusableProjectItemList);
CheckForFilesToEmbed(currentItemType, reusableProjectItemList);
reusableProjectItemList.Clear();
}
// signal the end
Write(0);
}
}