Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 88 additions & 13 deletions csharp/Platform.Data.Doublets/ILinksExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1043,19 +1043,66 @@ public static TLinkAddress DeleteAllUsages<TLinkAddress>(this ILinks<TLinkAddres
var any = constants.Any;
var usagesAsSourceQuery = new Link<TLinkAddress>(any, linkIndex, any);
var usagesAsTargetQuery = new Link<TLinkAddress>(any, any, linkIndex);
var usages = new List<IList<TLinkAddress>?>();
var usagesFiller = new ListFiller<IList<TLinkAddress>?, TLinkAddress>(usages, constants.Continue);
links.Each(usagesFiller.AddAndReturnConstant, usagesAsSourceQuery);
links.Each(usagesFiller.AddAndReturnConstant, usagesAsTargetQuery);

// Count total usages first to determine allocation strategy
var sourceUsagesCount = (int)long.CreateTruncating(links.Count(usagesAsSourceQuery));
var targetUsagesCount = (int)long.CreateTruncating(links.Count(usagesAsTargetQuery));
var totalUsagesCount = sourceUsagesCount + targetUsagesCount;

WriteHandlerState<TLinkAddress> handlerState = new(constants.Continue, constants.Break, handler);
foreach (var usage in usages)

// Use a more efficient approach - process usages directly without collecting into a list
// This avoids heap allocation entirely for the common case
const int MaxUsagesBatchSize = 128;

if (totalUsagesCount <= MaxUsagesBatchSize && totalUsagesCount > 0)
{
if (links.GetIndex(usage) == linkIndex || !links.Exists(links.GetIndex(usage)))
// Use a small array for bounded cases to reduce heap allocation
var usageIndices = new TLinkAddress[totalUsagesCount];
var usageCount = 0;

// Collect usage indices
links.Each(link =>
{
continue;
usageIndices[usageCount++] = links.GetIndex(link);
return constants.Continue;
}, usagesAsSourceQuery);

links.Each(link =>
{
usageIndices[usageCount++] = links.GetIndex(link);
return constants.Continue;
}, usagesAsTargetQuery);

// Process deletions
for (var i = 0; i < usageCount; i++)
{
var usageIndex = usageIndices[i];
if (usageIndex == linkIndex || !links.Exists(usageIndex))
{
continue;
}
handlerState.Apply(links.Delete(usageIndex, handlerState.Handler));
}
handlerState.Apply(links.Delete(links.GetIndex(usage), handlerState.Handler));
}
else
{
// For larger collections or when count is unknown, use the original approach
var usages = new List<IList<TLinkAddress>?>();
var usagesFiller = new ListFiller<IList<TLinkAddress>?, TLinkAddress>(usages, constants.Continue);
links.Each(usagesFiller.AddAndReturnConstant, usagesAsSourceQuery);
links.Each(usagesFiller.AddAndReturnConstant, usagesAsTargetQuery);

foreach (var usage in usages)
{
if (links.GetIndex(usage) == linkIndex || !links.Exists(links.GetIndex(usage)))
{
continue;
}
handlerState.Apply(links.Delete(links.GetIndex(usage), handlerState.Handler));
}
}

return handlerState.Result;
}

Expand All @@ -1080,12 +1127,40 @@ public static TLinkAddress DeleteAllUsages<TLinkAddress>(this ILinks<TLinkAddres
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void DeleteByQuery<TLinkAddress>(this ILinks<TLinkAddress> links, Link<TLinkAddress> query) where TLinkAddress : IUnsignedNumber<TLinkAddress>
{
var queryResult = new List<TLinkAddress>();
var queryResultFiller = new ListFiller<TLinkAddress, TLinkAddress>(queryResult, links.Constants.Continue);
links.Each(queryResultFiller.AddFirstAndReturnConstant, query);
foreach (var link in queryResult)
var queryResultCount = (int)long.CreateTruncating(links.Count(query));

// Use a more efficient approach for bounded cases to reduce heap allocation
const int MaxResultsBatchSize = 128;

if (queryResultCount <= MaxResultsBatchSize && queryResultCount > 0)
{
links.Delete(link);
// Use a small array for bounded cases to reduce heap allocation
var linkIndices = new TLinkAddress[queryResultCount];
var linkCount = 0;

// Collect link indices
links.Each(link =>
{
linkIndices[linkCount++] = links.GetIndex(link);
return links.Constants.Continue;
}, query);

// Process deletions
for (var i = 0; i < linkCount; i++)
{
links.Delete(linkIndices[i]);
}
}
else if (queryResultCount > 0)
{
// Fall back to dynamic collection for larger results
var queryResult = new List<TLinkAddress>();
var queryResultFiller = new ListFiller<TLinkAddress, TLinkAddress>(queryResult, links.Constants.Continue);
links.Each(queryResultFiller.AddFirstAndReturnConstant, query);
foreach (var link in queryResult)
{
links.Delete(link);
}
}
}

Expand Down
Loading