I've been playing around a bit with trying to get a value typed struct with an 'array like data load' into a good state where I feel happy using it. I'll let this serve as a template for future reference to highlight what the syntax look like.
Beware that using the 'in' parameter comes with a caveat. C# treats all methods as 'maybe mutable' and hence will do a defensive copy on the referenced object prior to invoking the function.
The alternative is to use ref, but then you are cluttering the invocation.
Example usage with static anonymous function to avoid lambda allocation:
hits = new int[3];
var container = new List<int>();
range.ForeachMarkedIndexWithCount(hits, container, static (entityIndex, hits, container, i) =>
{
hits[i] = entityIndex;
container.Add(entityIndex);
});
range.CombineAndMaskWith(combine, mask);
Code-snippet using unsafe struct, with fixed ulong words with constant size etc
public unsafe struct NetIndexRange {
private const int BitsPerWord = 64;
private const int NumWords = (NetConstants.NumMaxNetEntities + BitsPerWord - 1) / BitsPerWord; // Ceiling of for example : 1000/64 = 16
private fixed ulong _words[NumWords];
private ref ulong ModifyWord(int index)
{
return ref _words[index];
}
public void MarkIndex(int index)
{
int wordIndex = index / 64;
int bitPosition = index % 64;
ulong mask = 1UL << bitPosition;
ModifyWord(wordIndex) |= mask;
}
public void ClearIndex(int index)
{
int wordIndex = index / 64;
int bitPosition = index % 64;
ulong mask = ~(1UL << bitPosition);
ModifyWord(wordIndex) &= mask;
}
public void CombineAndMaskWith(in NetIndexRange combine, in NetIndexRange mask)
{
for (int wordIndex = 0; wordIndex < NumWords; ++wordIndex)
{
ulong combinedWord = _words[wordIndex] | combine._words[wordIndex];
ulong maskWord = mask._words[wordIndex];
ulong result = combinedWord & maskWord;
ModifyWord(wordIndex) = result;
}
}
public void ForeachMarkedIndexWithCount<T, U>(T t, U u, Action<int, T, U, int> eachMarkedIndexAction)
where T : class
where U : class
{
int count = 0;
int entityIndex = 0;
try
{
for (int wordIndex = 0; wordIndex < NumWords; wordIndex++)
{
ulong word = ModifyWord(wordIndex);
int bitPosition = 0;
while (word != 0)
{
if ((word & 1UL) != 0)
{
entityIndex = (wordIndex * 64) + bitPosition;
eachMarkedIndexAction(entityIndex, t, u, count++);
}
word >>= 1;
bitPosition++;
}
}
}
catch (Exception e)
{
throw new Exception($"Error while invoking action on net_{entityIndex} {Environment.NewLine} OriginalException: {e}");
}
}