Skip to content

Commit e8835c1

Browse files
katrecopybara-github
authored andcommitted
AttributeContainer.Large now handles more than 127 attributes.
The previous implementation failed with more than 127 attributes because the call to Arrays.binarySearch treats the byte values as signed, so values of 128 and above wrapped around and became negative, breaking binarySearch's precondition that the array be sorted. Also adds a small test. PiperOrigin-RevId: 345302645
1 parent 4ed0dab commit e8835c1

File tree

2 files changed

+51
-27
lines changed

2 files changed

+51
-27
lines changed

src/main/java/com/google/devtools/build/lib/packages/AttributeContainer.java

+26-27
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
import com.google.common.annotations.VisibleForTesting;
1717
import com.google.common.base.Preconditions;
1818
import com.google.errorprone.annotations.CheckReturnValue;
19-
import java.util.Arrays;
2019
import java.util.BitSet;
2120
import javax.annotation.Nullable;
2221

@@ -154,14 +153,33 @@ private static int nonNullCount(Object[] attrValues) {
154153
return numSet;
155154
}
156155

156+
/** Returns index into state array for attrIndex, or -1 if not found */
157+
private static int getStateIndex(byte[] state, int start, int attrIndex, int mask) {
158+
// Binary search, treating values as unsigned bytes.
159+
int lo = start;
160+
int hi = state.length - 1;
161+
while (hi >= lo) {
162+
int mid = (lo + hi) / 2;
163+
int midAttrIndex = state[mid] & mask;
164+
if (midAttrIndex == attrIndex) {
165+
return mid;
166+
} else if (midAttrIndex < attrIndex) {
167+
lo = mid + 1;
168+
} else {
169+
hi = mid - 1;
170+
}
171+
}
172+
return -1;
173+
}
174+
157175
/**
158176
* A frozen implementation of AttributeContainer which supports RuleClasses with up to 126
159177
* attributes.
160178
*/
161179
@VisibleForTesting
162180
static final class Small extends Frozen {
163181

164-
private int maxAttrCount;
182+
private final int maxAttrCount;
165183

166184
// Conceptually an AttributeContainer is an unordered set of triples
167185
// (attribute, value, explicit).
@@ -219,25 +237,6 @@ private Small(Object[] attrValues, BitSet explicitAttrs) {
219237
}
220238
}
221239

222-
/** Returns index into state array for attrIndex, or -1 if not found */
223-
private int getStateIndex(int attrIndex) {
224-
// Binary search on the bottom 7 bits.
225-
int lo = 0;
226-
int hi = state.length - 1;
227-
while (hi >= lo) {
228-
int mid = (lo + hi) / 2;
229-
int midAttrIndex = state[mid] & 0x7f;
230-
if (midAttrIndex == attrIndex) {
231-
return mid;
232-
} else if (midAttrIndex < attrIndex) {
233-
lo = mid + 1;
234-
} else {
235-
hi = mid - 1;
236-
}
237-
}
238-
return -1;
239-
}
240-
241240
/**
242241
* Returns true iff the value of the specified attribute is explicitly set in the BUILD file. In
243242
* addition, this method return false if the rule has no attribute with the given name.
@@ -247,7 +246,7 @@ boolean isAttributeValueExplicitlySpecified(int attrIndex) {
247246
if (attrIndex < 0) {
248247
return false;
249248
}
250-
int stateIndex = getStateIndex(attrIndex);
249+
int stateIndex = getStateIndex(state, 0, attrIndex, 0x7f);
251250
return stateIndex >= 0 && (state[stateIndex] & 0x80) != 0;
252251
}
253252

@@ -258,7 +257,7 @@ Object getAttributeValue(int attrIndex) {
258257
throw new IndexOutOfBoundsException(
259258
"Maximum valid attrIndex is " + (maxAttrCount - 1) + ". Given " + attrIndex);
260259
}
261-
int stateIndex = getStateIndex(attrIndex);
260+
int stateIndex = getStateIndex(state, 0, attrIndex, 0x7f);
262261
return stateIndex < 0 ? null : values[stateIndex];
263262
}
264263
}
@@ -308,16 +307,16 @@ private static int prefixSize(int attrCount) {
308307
/** Set the specified bit in the byte array. Assumes bitIndex is a valid index. */
309308
private static void setBit(byte[] bits, int bitIndex) {
310309
int idx = (bitIndex + 1);
311-
byte explicitByte = bits[idx >> 3];
310+
int explicitByte = bits[idx >> 3];
312311
byte mask = (byte) (1 << (idx & 0x07));
313312
bits[idx >> 3] = (byte) (explicitByte | mask);
314313
}
315314

316315
/** Get the specified bit in the byte array. Assumes bitIndex is a valid index. */
317316
private static boolean getBit(byte[] bits, int bitIndex) {
318317
int idx = (bitIndex + 1);
319-
byte explicitByte = bits[idx >> 3];
320-
byte mask = (byte) (1 << (idx & 0x07));
318+
int explicitByte = bits[idx >> 3];
319+
int mask = (byte) (1 << (idx & 0x07));
321320
return (explicitByte & mask) != 0;
322321
}
323322

@@ -374,7 +373,7 @@ Object getAttributeValue(int attrIndex) {
374373
"Maximum valid attrIndex is " + (maxAttrCount - 1) + ". Given " + attrIndex);
375374
}
376375
int p = prefixSize(maxAttrCount);
377-
int stateIndex = Arrays.binarySearch(state, p, state.length, (byte) attrIndex);
376+
int stateIndex = getStateIndex(state, p, attrIndex, 0xff);
378377
return stateIndex < 0 ? null : values[stateIndex - p];
379378
}
380379
}

src/test/java/com/google/devtools/build/lib/packages/AttributeContainerTest.java

+25
Original file line numberDiff line numberDiff line change
@@ -158,4 +158,29 @@ public void testFreezeWorks_smallImplementation() {
158158
public void testFreezeWorks_largeImplementation() {
159159
checkFreezeWorks((short) 150, AttributeContainer.Large.class);
160160
}
161+
162+
private void testContainerSize(int size) {
163+
AttributeContainer container = new Mutable(size);
164+
for (int i = 0; i < size; i++) {
165+
container.setAttributeValue(i, "value " + i, i % 2 == 0);
166+
}
167+
AttributeContainer frozen = container.freeze();
168+
// Check values.
169+
for (int i = 0; i < size; i++) {
170+
assertThat(frozen.getAttributeValue(i)).isEqualTo("value " + i);
171+
assertThat(frozen.isAttributeValueExplicitlySpecified(i)).isEqualTo(i % 2 == 0);
172+
}
173+
}
174+
175+
@Test
176+
public void testSmallContainer() {
177+
// At 127 attributes, we shift from AttributeContainer.Small to AttributeContainer.Large.
178+
testContainerSize(126);
179+
}
180+
181+
@Test
182+
public void testLargeContainer() {
183+
// AttributeContainer.Large can handle at max 254 attributes.
184+
testContainerSize(254);
185+
}
161186
}

0 commit comments

Comments
 (0)