diff --git a/projects/RabbitMQ.Client/Util/IntAllocator.cs b/projects/RabbitMQ.Client/Util/IntAllocator.cs
index 3bbcc53e2..eafba7753 100644
--- a/projects/RabbitMQ.Client/Util/IntAllocator.cs
+++ b/projects/RabbitMQ.Client/Util/IntAllocator.cs
@@ -30,183 +30,83 @@
//---------------------------------------------------------------------------
using System;
-using System.Diagnostics;
+using System.Collections;
namespace RabbitMQ.Client.Util
{
- /**
- * A class for allocating integer IDs in a given range.
- */
+ ///
+ ///
+ ///
internal class IntAllocator
{
- private readonly int[] _unsorted;
- private IntervalList? _base;
- private int _unsortedCount = 0;
-
- /**
- * A class representing a list of inclusive intervals
- */
-
- /**
- * Creates an IntAllocator allocating integer IDs within the inclusive range [start, end]
- */
-
- public IntAllocator(int start, int end)
+ private readonly int _loRange; // the integer that bit 0 represents
+ private readonly int _hiRange; // one more than the integer the highest bit represents
+ private readonly int _numberOfBits; //
+
+ ///
+ /// A bit is SET/true in _freeSet if the corresponding integer is FREE
+ /// A bit is UNSET/false in freeSet if the corresponding integer is ALLOCATED
+ ///
+ private readonly BitArray _freeSet;
+
+ ///
+ /// Creates an IntAllocator allocating integer IDs within the
+ /// inclusive range [bottom, top].
+ ///
+ /// lower end of range
+ /// upper end of range (incusive)
+ ///
+ public IntAllocator(int bottom, int top)
{
- if (start > end)
+ if (bottom > top)
{
- throw new ArgumentException($"illegal range [{start}, {end}]");
+ throw new ArgumentException($"illegal range [{bottom}, {top}]");
}
- // Fairly arbitrary heuristic for a good size for the unsorted set.
- _unsorted = new int[Math.Max(32, (int)Math.Sqrt(end - start))];
- _base = new IntervalList(start, end);
+ _loRange = bottom;
+ _hiRange = top + 1;
+ _numberOfBits = _hiRange - _loRange;
+ _freeSet = new BitArray(_numberOfBits, true); // All integers are FREE initially
}
- /**
- * Allocate a fresh integer from the range, or return -1 if no more integers
- * are available. This operation is guaranteed to run in O(1)
- */
-
public int Allocate()
{
- if (_unsortedCount > 0)
- {
- return _unsorted[--_unsortedCount];
- }
- else if (_base != null)
- {
- int result = _base.Start;
- if (_base.Start == _base.End)
- {
- _base = _base.Next;
- }
- else
- {
- _base.Start++;
- }
- return result;
- }
- else
+ int setIndex = nextSetBit();
+ if (setIndex < 0) // no free integers are available
{
return -1;
}
+ _freeSet.Set(setIndex, false);
+ return setIndex + _loRange;
}
- /**
- * Make the provided integer available for allocation again. This operation
- * runs in amortized O(sqrt(range size)) time: About every sqrt(range size)
- * operations will take O(range_size + number of intervals) to complete and
- * the rest run in constant time.
- *
- * No error checking is performed, so if you double Free or Free an integer
- * that was not originally Allocated the results are undefined. Sorry.
- */
-
- public void Free(int id)
- {
- if (_unsortedCount >= _unsorted.Length)
- {
- Flush();
- }
- _unsorted[_unsortedCount++] = id;
- }
-
- private void Flush()
+ ///
+ /// Makes the provided integer available for allocation again.
+ ///
+ /// the previously allocated integer to free
+ public void Free(int reservation)
{
- if (_unsortedCount > 0)
- {
- _base = IntervalList.Merge(_base, IntervalList.FromArray(_unsorted, _unsortedCount));
- _unsortedCount = 0;
- }
+ int setIndex = reservation - _loRange;
+ _freeSet.Set(setIndex, true); // true means "unallocated"
}
-
- public class IntervalList
+ ///
+ /// Note: this is different than the Java implementation, because we need to
+ /// preserve the prior behavior of always reserving low integers, if available.
+ /// See Test.Integration.AllocateAfterFreeingMany
+ ///
+ /// index of the next unallocated bit
+ private int nextSetBit()
{
- public int End;
-
- // Invariant: If Next != Null then Next.Start > this.End + 1
- public IntervalList? Next;
- public int Start;
-
- public IntervalList(int start, int end)
+ for (int i = 0; i < _freeSet.Count; i++)
{
- Start = start;
- End = end;
- }
-
- // Destructively merge two IntervalLists.
- // Invariant: None of the Intervals in the two lists may overlap
- // intervals in this list.
-
- public static IntervalList? FromArray(int[] xs, int length)
- {
- Array.Sort(xs, 0, length);
-
- IntervalList? result = null;
- IntervalList? current = null;
-
- int i = 0;
- while (i < length)
+ if (_freeSet.Get(i)) // true means "unallocated"
{
- int start = i;
- while ((i < length - 1) && (xs[i + 1] == xs[i] + 1))
- {
- i++;
- }
-
- var interval = new IntervalList(xs[start], xs[i]);
-
- if (result is null)
- {
- result = interval;
- current = interval;
- }
- else
- {
- current!.Next = interval;
- current = interval;
- }
- i++;
+ return i;
}
- return result;
}
- public static IntervalList? Merge(IntervalList? x, IntervalList? y)
- {
- if (x is null)
- {
- return y;
- }
- if (y is null)
- {
- return x;
- }
-
- if (x.Start > y.Start)
- {
- (x, y) = (y, x);
- }
-
- Debug.Assert(x.End != y.Start);
-
- // We now have x, y non-null and x.End < y.Start.
-
- if (y.Start == x.End + 1)
- {
- // The two intervals adjoin. Merge them into one and then
- // merge the tails.
- x.End = y.End;
- x.Next = Merge(x.Next, y.Next);
- return x;
- }
-
- // y belongs in the tail of x.
-
- x.Next = Merge(y, x.Next);
- return x;
- }
+ return -1;
}
}
}