private static unsafe void FilterPollList(IList socketList, Interop.Sys.PollEvent* arr, int arrEndOffset, Interop.Sys.PollEvents desiredEvents)
{
if (socketList == null)
return;
// The Select API requires leaving in the input lists only those sockets that were ready. As such, we need to loop
// through each poll event, and for each that wasn't ready, remove the corresponding Socket from its list. Technically
// this is O(n^2), due to removing from the list requiring shifting down all elements after it. However, this doesn't
// happen with the most common cases. If very few sockets were ready, then as we iterate from the end of the list, each
// removal will typically be O(1) rather than O(n). If most sockets were ready, then we only need to remove a few, in
// which case we're only doing a small number of O(n) shifts. It's only for the intermediate case, where a non-trivial
// number of sockets are ready and a non-trivial number of sockets are not ready that we end up paying the most. We could
// avoid these costs by, for example, allocating a side list that we fill with the sockets that should remain, clearing
// the original list, and then populating the original list with the contents of the side list. That of course has its
// own costs, and so for now we do the "simple" thing. This can be changed in the future as needed.
for (int i = socketList.Count - 1; i >= 0; --i, --arrEndOffset)
{
if (arrEndOffset < 0)
{
Debug.Fail("IList.Count must have been faulty, returning a negative value and/or returning a different value across calls.");
throw new ArgumentOutOfRangeException(nameof(arrEndOffset));
}
if ((arr[arrEndOffset].TriggeredEvents & desiredEvents) == 0)
{
socketList.RemoveAt(i);
}
}
}