3030//
3131package com .rabbitmq .utility ;
3232
33- import java .util .*;
34-
3533/**
3634 * A class for allocating integer IDs in a given range.
3735 */
@@ -40,35 +38,89 @@ public class IntAllocator{
4038 // Invariant: Sorted in order of first element. Non-overlapping, non-adjacent.
4139 // This could really use being a balanced binary tree. However for normal usages
4240 // it doesn't actually matter.
43- private LinkedList < Interval > intervals = new LinkedList < Interval >() ;
41+ private IntervalList base ;
4442
4543 private final int [] unsorted ;
4644 private int unsortedCount = 0 ;
4745
4846 /**
4947 * A class representing an inclusive interval from start to end.
5048 */
51- private static class Interval {
52- Interval (int start , int end ){
49+ private static class IntervalList {
50+ IntervalList (int start , int end ){
5351 this .start = start ;
5452 this .end = end ;
5553 }
5654
5755 int start ;
5856 int end ;
57+ IntervalList next ;
5958
6059 int length (){ return end - start + 1 ; }
6160 }
6261
63- /**
64- * Creates an IntAllocator allocating integer IDs within the inclusive range [start, end]
62+ /** Destructively merge two IntervalLists.
63+ * Invariant: None of the Intervals in the two lists may overlap
64+ * intervals in this list.
6565 */
66+ public static IntervalList merge (IntervalList x , IntervalList y ){
67+ if (x == null ) return y ;
68+ if (y == null ) return x ;
69+
70+ if (x .end > y .start ) return merge (y , x );
71+
72+ // We now have x, y non-null and x.End < y.Start.
73+ if (y .start == x .end + 1 ){
74+ // The two intervals adjoin. Merge them into one and then
75+ // merge the tails.
76+ x .end = y .end ;
77+ x .next = merge (x .next , y .next );
78+ return x ;
79+ }
80+
81+ // y belongs in the tail of x.
82+
83+ x .next = merge (y , x .next );
84+ return x ;
85+ }
86+
87+
88+ public static IntervalList fromArray (int [] xs , int length ){
89+ Arrays .sort (xs , 0 , length );
90+
91+ IntervalList result = null ;
92+ IntervalList current = null ;
93+
94+ int i = 0 ;
95+ while (i < length ){
96+ int start = i ;
97+ while ((i < length - 1 ) && (xs [i + 1 ] == xs [i ] + 1 ))
98+ i ++;
99+
100+ IntervalList interval = new IntervalList (xs [start ], xs [i ]);
101+
102+ if (result == null ){
103+ result = interval ;
104+ current = interval ;
105+ } else {
106+ current .next = interval ;
107+ current = interval ;
108+ }
109+ i ++;
110+ }
111+ return result ;
112+ }
113+
114+
115+ /**
116+ * Creates an IntAllocator allocating integer IDs within the inclusive range [start, end]
117+ */
66118 public IntAllocator (int start , int end ){
67- if (start > end ) throw new IllegalArgumentException ("illegal range [" + start +", " + end + "]" );
119+ if (start > end ) throw new IllegalArgumentException ("illegal range [" + start +", " + end + "]" );
68120
69- // Fairly arbitrary heuristic for a good size for the unsorted set.
70- unsorted = new int [Math .max (32 , (int )Math .sqrt (end - start ))];
71- intervals . add ( new Interval (start , end ) );
121+ // Fairly arbitrary heuristic for a good size for the unsorted set.
122+ unsorted = new int [Math .max (32 , (int )Math .sqrt (end - start ))];
123+ base = new IntervalList (start , end );
72124 }
73125
74126 /**
@@ -78,10 +130,10 @@ public IntAllocator(int start, int end){
78130 public int allocate (){
79131 if (unsortedCount > 0 ){
80132 return unsorted [--unsortedCount ];
81- } else if (! intervals . isEmpty ()) {
82- Interval first = intervals . getFirst () ;
83- if (first .length () == 1 ) intervals . removeFirst () ;
84- return first .start ++;
133+ } else if (base != null ) {
134+ IntervalList source = base ;
135+ if (base .length () == 1 ) base = base . next ;
136+ return source .start ++;
85137 } else {
86138 return -1 ;
87139 }
@@ -113,86 +165,50 @@ public void free(int id){
113165 */
114166 public boolean reserve (int id ){
115167 flush ();
116- ListIterator <Interval > it = intervals .listIterator ();
117-
118- while (it .hasNext ()){
119- Interval i = it .next ();
120- if (i .start <= id && id <= i .end ){
121- if (i .length () == 1 ) it .remove ();
122- else if (i .start == id ) i .start ++;
123- else if (i .end == id ) i .end --;
124- else {
125- it .add (new Interval (id + 1 , i .end ));
126- i .end = id - 1 ;
127- }
128- return true ;
129- }
168+
169+ IntervalList current = base ;
170+
171+ while (current != null && current .end < id ){
172+ current = current .next ;
173+ }
174+
175+ if (current == null ) return false ;
176+ if (current .start > id ) return false ;
177+
178+ if (current .end == id )
179+ current .end --;
180+ else if (current .start == id )
181+ current .start ++;
182+ else {
183+ // The ID is in the middle of this interval.
184+ // We need to split the interval into two.
185+ IntervalList rest = new IntervalList (id + 1 , current .end );
186+ current .end = id - 1 ;
187+ rest .next = current .next ;
188+ current .next = rest ;
130189 }
131190
132- return false ;
191+ return true ;
133192 }
134193
135194 public void flush (){
136195 if (unsortedCount == 0 ) return ;
137196
138- Arrays .sort (unsorted , 0 , unsortedCount );
139-
140- ListIterator <Interval > it = intervals .listIterator ();
141-
142- int i = 0 ;
143- while (i < unsortedCount ){
144- int start = i ;
145- while ((i < unsortedCount - 1 ) && (unsorted [i + 1 ] == unsorted [i ] + 1 ))
146- i ++;
147-
148- Interval interval = new Interval (unsorted [start ], unsorted [i ]);
149-
150- // Scan to an appropriate point in the list to insert this interval
151- // this may well be the end
152- while (it .hasNext ()){
153- if (it .next ().start > interval .end ){
154- it .previous ();
155- break ;
156- }
157- }
158-
159- it .add (interval );
160- i ++;
161- }
162-
163- normalize ();
197+ base = merge (base , fromArray (unsorted , unsortedCount ));
164198 unsortedCount = 0 ;
165199 }
166200
167- private void normalize (){
168- if (intervals .isEmpty ()) return ;
169- Iterator <Interval > it = intervals .iterator ();
170-
171- Interval trailing , leading ;
172- leading = it .next ();
173- while (it .hasNext ()){
174- trailing = leading ;
175- leading = it .next ();
176-
177- if (leading .start == trailing .end + 1 ) {
178- it .remove ();
179- trailing .end = leading .end ;
180- leading = trailing ;
181- }
182- }
183- }
184-
185201 @ Override public String toString (){
186202 StringBuilder builder = new StringBuilder ();
187203
188204 builder .append ("IntAllocator{" );
189205
190206 builder .append ("intervals = [" );
191- Iterator < Interval > it = intervals . iterator () ;
192- while (it . hasNext () ){
193- Interval i = it .next ( );
194- builder . append ( i . start ) .append (".." ). append ( i . end );
195- if ( it . hasNext ()) builder . append ( ", " );
207+ IntervalList it = base ;
208+ while (it != null ){
209+ builder . append ( it .start ). append ( ".." ). append ( it . end );
210+ if ( it . next != null ) builder .append (", " );
211+ it = it . next ;
196212 }
197213 builder .append ("]" );
198214
0 commit comments