Skip to content

Commit 0a72538

Browse files
author
Steve Powell
committed
First cut at rewriting merge in IntAllocator
1 parent a72b0f3 commit 0a72538

File tree

1 file changed

+96
-54
lines changed

1 file changed

+96
-54
lines changed

src/com/rabbitmq/utility/IntAllocator.java

Lines changed: 96 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -19,62 +19,107 @@
1919
import java.util.Arrays;
2020

2121
/**
22-
* A class for allocating integer IDs in a given range.
22+
* A class for allocating integers from a given range.
23+
* <p/><strong>Concurrent Semantics:</strong><br />
24+
* This class is <b><i>not</i></b> thread safe.
25+
*
26+
* <p/><b>Implementation notes:</b>
27+
* <br/>This could really use being a balanced binary tree. However for normal
28+
* usages it doesn't actually matter.
29+
*
30+
* <p/><b>Invariants:</b>
31+
* <br/>Sorted in order of first element.
32+
* <br/>Intervals are non-overlapping, non-adjacent.
33+
*
2334
*/
24-
public class IntAllocator{
35+
public class IntAllocator {
2536

26-
// Invariant: Sorted in order of first element.
27-
// Invariant: Intervals are non-overlapping, non-adjacent.
28-
// This could really use being a balanced binary tree. However for normal
29-
// usages it doesn't actually matter.
3037
private IntervalList base;
3138

3239
private final int[] unsorted;
3340
private int unsortedCount = 0;
3441

3542
/**
36-
* A class representing an inclusive interval from start to end.
43+
* A node in a singly-linked list of inclusive intervals.
44+
* <br/>A single node denotes the interval of integers from
45+
* <code>start</code> to <code>end</code> inclusive.
3746
*/
3847
private static class IntervalList{
48+
int start;
49+
int end;
50+
IntervalList next; // next interval in the list.
51+
3952
IntervalList(int start, int end){
4053
this.start = start;
4154
this.end = end;
4255
}
4356

44-
int start;
45-
int end;
46-
IntervalList next;
47-
4857
int length(){ return end - start + 1; }
4958
}
5059

51-
/** Destructively merge two IntervalLists.
52-
* Invariant: None of the Intervals in the two lists may overlap
53-
* intervals in this list.
54-
*/
55-
public static IntervalList merge(IntervalList x, IntervalList y){
56-
if(x == null) return y;
57-
if(y == null) return x;
58-
59-
if(x.end > y.start) return merge(y, x);
60-
61-
// We now have x, y non-null and x.End < y.Start.
62-
if(y.start == x.end + 1){
63-
// The two intervals adjoin. Merge them into one and then
64-
// merge the tails.
65-
x.end = y.end;
66-
x.next = merge(x.next, y.next);
67-
return x;
60+
private static class IntervalListAccumulator {
61+
/** <b>Invariant:</b> either both null, or both non-null. */
62+
private IntervalList accumListStart, accumListEnd = null;
63+
64+
/**
65+
* Add a single node to the end of the accumulated list, merging the
66+
* last two nodes if abutting.<br/>
67+
* <b>Note:</b> the node added is not modified by add;
68+
* in particular, the <code>next</code> field is preserved,
69+
* and copied if the nodes are merged.
70+
* @param iListNode node to add, terminate list if this is null.
71+
*/
72+
public void add(IntervalList iListNode) {
73+
if (accumListStart == null) {
74+
accumListStart = accumListEnd = iListNode;
75+
} else {
76+
if (iListNode == null) {
77+
accumListEnd.next = null;
78+
} else if (accumListEnd.end + 1 == iListNode.start) {
79+
accumListEnd.end = iListNode.end;
80+
accumListEnd.next = iListNode.next;
81+
} else {
82+
accumListEnd.next = iListNode;
83+
accumListEnd = iListNode;
84+
}
85+
}
86+
}
87+
/**
88+
* Append the list from this node and return the accumulated list
89+
* including this node chain.<br/>
90+
* <b>Note:</b> The accumulated list is cleared after this call.
91+
* @param iListNode chain to append to accumListEnd.
92+
* @return the accumulated list
93+
*/
94+
public IntervalList getAppendedResult(IntervalList iListNode) {
95+
add(iListNode); // note: this preserves the chain
96+
IntervalList result = accumListStart;
97+
accumListStart = accumListEnd = null;
98+
return result;
6899
}
69-
70-
// y belongs in the tail of x.
71-
72-
x.next = merge(y, x.next);
73-
return x;
74100
}
75101

102+
/**
103+
* Merge two IntervalLists.
104+
* <p/><b>Preconditions:</b><br/>
105+
* None of the intervals in the two lists overlap.
106+
*/
107+
private static IntervalList merge(IntervalList x, IntervalList y){
108+
IntervalListAccumulator outList = new IntervalListAccumulator();
109+
while (true) {
110+
if(x == null) { return outList.getAppendedResult(y); }
111+
if(y == null) { return outList.getAppendedResult(x); }
112+
if (x.start < y.start) {
113+
outList.add(x);
114+
x = x.next;
115+
} else {
116+
outList.add(y);
117+
y = y.next;
118+
}
119+
}
120+
}
76121

77-
public static IntervalList fromArray(int[] xs, int length){
122+
private static IntervalList fromArray(int[] xs, int length){
78123
Arrays.sort(xs, 0, length);
79124

80125
IntervalList result = null;
@@ -100,14 +145,13 @@ public static IntervalList fromArray(int[] xs, int length){
100145
return result;
101146
}
102147

103-
104148
/**
105149
* Creates an IntAllocator allocating integer IDs within the inclusive range
106150
* [start, end]
107151
*/
108152
public IntAllocator(int start, int end){
109153
if(start > end)
110-
throw new IllegalArgumentException("illegal range [" + start +
154+
throw new IllegalArgumentException("illegal range [" + start +
111155
", " + end + "]");
112156

113157
// Fairly arbitrary heuristic for a good size for the unsorted set.
@@ -116,8 +160,8 @@ public IntAllocator(int start, int end){
116160
}
117161

118162
/**
119-
* Allocate a fresh integer from the range, or return -1 if no more integers
120-
* are available. This operation is guaranteed to run in O(1)
163+
* Allocate an unallocated integer from the range, or return -1 if no
164+
* more integers are available. This operation is guaranteed to run in O(1)
121165
*/
122166
public int allocate(){
123167
if(unsortedCount > 0){
@@ -183,7 +227,7 @@ else if(current.start == id)
183227
return true;
184228
}
185229

186-
public void flush(){
230+
private void flush(){
187231
if(unsortedCount == 0) return;
188232

189233
base = merge(base, fromArray(unsorted, unsortedCount));
@@ -193,26 +237,24 @@ public void flush(){
193237
@Override public String toString(){
194238
StringBuilder builder = new StringBuilder();
195239

196-
builder.append("IntAllocator{");
197-
198-
builder.append("intervals = [");
240+
builder.append("IntAllocator{intervals = [");
199241
IntervalList it = base;
200-
while(it != null){
242+
if (it != null) {
201243
builder.append(it.start).append("..").append(it.end);
202-
if(it.next != null) builder.append(", ");
203244
it = it.next;
204245
}
205-
builder.append("]");
206-
207-
builder.append(", unsorted = [");
208-
for(int i = 0; i < unsortedCount; i++){
209-
builder.append(unsorted[i]);
210-
if( i < unsortedCount - 1) builder.append(", ");
246+
while (it != null) {
247+
builder.append(", ").append(it.start).append("..").append(it.end);
248+
it = it.next;
211249
}
212-
builder.append("]");
213-
214-
215-
builder.append("}");
250+
builder.append("], unsorted = [");
251+
if (unsortedCount > 0) {
252+
builder.append(unsorted[0]);
253+
}
254+
for(int i = 1; i < unsortedCount; ++i){
255+
builder.append(", ").append(unsorted[i]);
256+
}
257+
builder.append("]}");
216258
return builder.toString();
217259
}
218260
}

0 commit comments

Comments
 (0)