Skip to content

Commit 2d373f8

Browse files
author
David R. MacIver
committed
port .NET client implementation of IntAllocator back to Java client
1 parent 4b513a3 commit 2d373f8

File tree

1 file changed

+95
-79
lines changed

1 file changed

+95
-79
lines changed

src/com/rabbitmq/utility/IntAllocator.java

Lines changed: 95 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,6 @@
3030
//
3131
package 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

Comments
 (0)