Skip to content

Commit a9f5255

Browse files
committed
mutation: Pass hasFixedSize into closedRangeBiasedTowardsSmall
This is a pure refactoring, the value will be used in the follow-up commit. The function is renamed to reflect that it may no longer return a biased value.
1 parent 833a5f9 commit a9f5255

File tree

9 files changed

+68
-60
lines changed

9 files changed

+68
-60
lines changed

src/main/java/com/code_intelligence/jazzer/mutation/api/PseudoRandom.java

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -103,16 +103,13 @@ public interface PseudoRandom {
103103
double closedRange(double lowerInclusive, double upperInclusive);
104104

105105
/**
106-
* @return a random value in the closed range [0, upperInclusive] that is heavily biased towards
107-
* being small
108-
*/
109-
int closedRangeBiasedTowardsSmall(int upperInclusive);
110-
111-
/**
112-
* @return a random value in the closed range [lowerInclusive, upperInclusive] that is heavily
113-
* biased towards being small
106+
* Returns random value in the closed range [lowerInclusive, upperInclusive], meant to be used as
107+
* the size of a collection or subset thereof.
108+
*
109+
* @param elementsHaveFixedSize Whether the elements of the collection have a fixed size
110+
* representation.
114111
*/
115-
int closedRangeBiasedTowardsSmall(int lowerInclusive, int upperInclusive);
112+
int sizeInClosedRange(int lowerInclusive, int upperInclusive, boolean elementsHaveFixedSize);
116113

117114
/** Fills the given array with random bytes. */
118115
void bytes(byte[] bytes);

src/main/java/com/code_intelligence/jazzer/mutation/engine/SeededPseudoRandom.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -214,8 +214,7 @@ public void bytes(byte[] bytes) {
214214
RandomSupport.nextBytes(random, bytes);
215215
}
216216

217-
@Override
218-
public int closedRangeBiasedTowardsSmall(int upperInclusive) {
217+
private int closedRangeBiasedTowardsSmall(int upperInclusive) {
219218
if (upperInclusive == 0) {
220219
return 0;
221220
}
@@ -251,7 +250,8 @@ public int closedRangeBiasedTowardsSmall(int upperInclusive) {
251250
}
252251

253252
@Override
254-
public int closedRangeBiasedTowardsSmall(int lowerInclusive, int upperInclusive) {
253+
public int sizeInClosedRange(
254+
int lowerInclusive, int upperInclusive, boolean elementsHaveFixedSize) {
255255
return lowerInclusive + closedRangeBiasedTowardsSmall(upperInclusive - lowerInclusive);
256256
}
257257

src/main/java/com/code_intelligence/jazzer/mutation/mutator/collection/ChunkCrossOvers.java

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,19 @@
2929
final class ChunkCrossOvers {
3030
private ChunkCrossOvers() {}
3131

32-
static <T> void insertChunk(List<T> list, List<T> otherList, int maxSize, PseudoRandom prng) {
32+
static <T> void insertChunk(
33+
List<T> list, List<T> otherList, int maxSize, PseudoRandom prng, boolean hasFixedSize) {
3334
int maxChunkSize = Math.min(maxSize - list.size(), otherList.size());
34-
int chunkSize = prng.closedRangeBiasedTowardsSmall(1, maxChunkSize);
35+
int chunkSize = prng.sizeInClosedRange(1, maxChunkSize, hasFixedSize);
3536
int fromPos = prng.closedRange(0, otherList.size() - chunkSize);
3637
int toPos = prng.closedRange(0, list.size());
3738
List<T> chunk = otherList.subList(fromPos, fromPos + chunkSize);
3839
list.addAll(toPos, chunk);
3940
}
4041

41-
static <T> void overwriteChunk(List<T> list, List<T> otherList, PseudoRandom prng) {
42-
onCorrespondingChunks(list, otherList, prng, list::set);
42+
static <T> void overwriteChunk(
43+
List<T> list, List<T> otherList, PseudoRandom prng, boolean hasFixedSize) {
44+
onCorrespondingChunks(list, otherList, prng, list::set, hasFixedSize);
4345
}
4446

4547
static <T> void crossOverChunk(
@@ -50,7 +52,8 @@ static <T> void crossOverChunk(
5052
prng,
5153
(toPos, element) -> {
5254
list.set(toPos, elementMutator.crossOver(list.get(toPos), element, prng));
53-
});
55+
},
56+
elementMutator.hasFixedSize());
5457
}
5558

5659
@FunctionalInterface
@@ -59,9 +62,13 @@ private interface ChunkListElementOperation<T> {
5962
}
6063

6164
private static <T> void onCorrespondingChunks(
62-
List<T> list, List<T> otherList, PseudoRandom prng, ChunkListElementOperation<T> operation) {
65+
List<T> list,
66+
List<T> otherList,
67+
PseudoRandom prng,
68+
ChunkListElementOperation<T> operation,
69+
boolean hasFixedSize) {
6370
int maxChunkSize = Math.min(list.size(), otherList.size());
64-
int chunkSize = prng.closedRangeBiasedTowardsSmall(1, maxChunkSize);
71+
int chunkSize = prng.sizeInClosedRange(1, maxChunkSize, hasFixedSize);
6572
int fromPos = prng.closedRange(0, otherList.size() - chunkSize);
6673
int toPos = prng.closedRange(0, list.size() - chunkSize);
6774
List<T> chunk = otherList.subList(fromPos, fromPos + chunkSize);
@@ -71,10 +78,10 @@ private static <T> void onCorrespondingChunks(
7178
}
7279

7380
static <K, V> void insertChunk(
74-
Map<K, V> map, Map<K, V> otherMap, int maxSize, PseudoRandom prng) {
81+
Map<K, V> map, Map<K, V> otherMap, int maxSize, PseudoRandom prng, boolean hasFixedSize) {
7582
int originalSize = map.size();
7683
int maxChunkSize = Math.min(maxSize - originalSize, otherMap.size());
77-
int chunkSize = prng.closedRangeBiasedTowardsSmall(1, maxChunkSize);
84+
int chunkSize = prng.sizeInClosedRange(1, maxChunkSize, hasFixedSize);
7885
int fromChunkOffset = prng.closedRange(0, otherMap.size() - chunkSize);
7986
Iterator<Entry<K, V>> fromIterator = otherMap.entrySet().iterator();
8087
for (int i = 0; i < fromChunkOffset; i++) {
@@ -91,7 +98,8 @@ static <K, V> void insertChunk(
9198
}
9299
}
93100

94-
static <K, V> void overwriteChunk(Map<K, V> map, Map<K, V> otherMap, PseudoRandom prng) {
101+
static <K, V> void overwriteChunk(
102+
Map<K, V> map, Map<K, V> otherMap, PseudoRandom prng, boolean hasFixedSize) {
95103
onCorrespondingChunks(
96104
map,
97105
otherMap,
@@ -105,7 +113,8 @@ static <K, V> void overwriteChunk(Map<K, V> map, Map<K, V> otherMap, PseudoRando
105113
Entry<K, V> to = toIterator.next();
106114
to.setValue(from.getValue());
107115
}
108-
});
116+
},
117+
hasFixedSize);
109118
}
110119

111120
static <K, V> void crossOverChunk(
@@ -154,7 +163,8 @@ private static <K, V> void crossOverChunkKeys(
154163
}
155164
}
156165
map.putAll(entriesToAdd);
157-
});
166+
},
167+
keyMutator.hasFixedSize());
158168
}
159169

160170
private static <K, V> void crossOverChunkValues(
@@ -179,7 +189,8 @@ private static <K, V> void crossOverChunkValues(
179189
// through the iterator to be sure.
180190
toEntry.setValue(newValue);
181191
}
182-
});
192+
},
193+
valueMutator.hasFixedSize());
183194
}
184195

185196
@FunctionalInterface
@@ -188,9 +199,13 @@ private interface ChunkMapOperation<K, V> {
188199
}
189200

190201
static <K, V> void onCorrespondingChunks(
191-
Map<K, V> map, Map<K, V> otherMap, PseudoRandom prng, ChunkMapOperation<K, V> operation) {
202+
Map<K, V> map,
203+
Map<K, V> otherMap,
204+
PseudoRandom prng,
205+
ChunkMapOperation<K, V> operation,
206+
boolean hasFixedSize) {
192207
int maxChunkSize = Math.min(map.size(), otherMap.size());
193-
int chunkSize = prng.closedRangeBiasedTowardsSmall(1, maxChunkSize);
208+
int chunkSize = prng.sizeInClosedRange(1, maxChunkSize, hasFixedSize);
194209
int fromChunkOffset = prng.closedRange(0, otherMap.size() - chunkSize);
195210
int toChunkOffset = prng.closedRange(0, map.size() - chunkSize);
196211
Iterator<Entry<K, V>> fromIterator = otherMap.entrySet().iterator();

src/main/java/com/code_intelligence/jazzer/mutation/mutator/collection/ChunkMutations.java

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,19 +40,21 @@ final class ChunkMutations {
4040

4141
private ChunkMutations() {}
4242

43-
static <T> void deleteRandomChunk(List<T> list, int minSize, PseudoRandom prng) {
43+
static <T> void deleteRandomChunk(
44+
List<T> list, int minSize, PseudoRandom prng, boolean hasFixedSize) {
4445
int oldSize = list.size();
4546
int minFinalSize = Math.max(minSize, oldSize / 2);
46-
int chunkSize = prng.closedRangeBiasedTowardsSmall(1, oldSize - minFinalSize);
47+
int chunkSize = prng.sizeInClosedRange(1, oldSize - minFinalSize, hasFixedSize);
4748
int chunkOffset = prng.closedRange(0, oldSize - chunkSize);
4849

4950
list.subList(chunkOffset, chunkOffset + chunkSize).clear();
5051
}
5152

52-
static <T> void deleteRandomChunk(Collection<T> collection, int minSize, PseudoRandom prng) {
53+
static <T> void deleteRandomChunk(
54+
Collection<T> collection, int minSize, PseudoRandom prng, boolean hasFixedSize) {
5355
int oldSize = collection.size();
5456
int minFinalSize = Math.max(minSize, oldSize / 2);
55-
int chunkSize = prng.closedRangeBiasedTowardsSmall(1, oldSize - minFinalSize);
57+
int chunkSize = prng.sizeInClosedRange(1, oldSize - minFinalSize, hasFixedSize);
5658
int chunkOffset = prng.closedRange(0, oldSize - chunkSize);
5759

5860
Iterator<T> it = collection.iterator();
@@ -68,7 +70,7 @@ static <T> void deleteRandomChunk(Collection<T> collection, int minSize, PseudoR
6870
static <T> void insertRandomChunk(
6971
List<T> list, int maxSize, SerializingMutator<T> elementMutator, PseudoRandom prng) {
7072
int oldSize = list.size();
71-
int chunkSize = prng.closedRangeBiasedTowardsSmall(1, maxSize - oldSize);
73+
int chunkSize = prng.sizeInClosedRange(1, maxSize - oldSize, elementMutator.hasFixedSize());
7274
int chunkOffset = prng.closedRange(0, oldSize);
7375

7476
T baseElement = elementMutator.init(prng);
@@ -88,13 +90,13 @@ static <T> boolean insertRandomChunk(
8890
ValueMutator<T> elementMutator,
8991
PseudoRandom prng) {
9092
int oldSize = set.size();
91-
int chunkSize = prng.closedRangeBiasedTowardsSmall(1, maxSize - oldSize);
93+
int chunkSize = prng.sizeInClosedRange(1, maxSize - oldSize, elementMutator.hasFixedSize());
9294
return growBy(set, addIfNew, chunkSize, () -> elementMutator.init(prng));
9395
}
9496

9597
static <T> void mutateRandomChunk(List<T> list, ValueMutator<T> mutator, PseudoRandom prng) {
9698
int size = list.size();
97-
int chunkSize = prng.closedRangeBiasedTowardsSmall(1, size);
99+
int chunkSize = prng.sizeInClosedRange(1, size, mutator.hasFixedSize());
98100
int chunkOffset = prng.closedRange(0, size - chunkSize);
99101

100102
for (int i = chunkOffset; i < chunkOffset + chunkSize; i++) {
@@ -105,7 +107,7 @@ static <T> void mutateRandomChunk(List<T> list, ValueMutator<T> mutator, PseudoR
105107
static <K, V, KW, VW> boolean mutateRandomKeysChunk(
106108
Map<K, V> map, SerializingMutator<K> keyMutator, PseudoRandom prng) {
107109
int originalSize = map.size();
108-
int chunkSize = prng.closedRangeBiasedTowardsSmall(1, originalSize);
110+
int chunkSize = prng.sizeInClosedRange(1, originalSize, keyMutator.hasFixedSize());
109111
int chunkOffset = prng.closedRange(0, originalSize - chunkSize);
110112

111113
// To ensure that mutating keys actually results in the set of keys changing and not just their
@@ -159,7 +161,7 @@ public static <K, V> void mutateRandomValuesChunk(
159161
Map<K, V> map, ValueMutator<V> valueMutator, PseudoRandom prng) {
160162
Collection<Map.Entry<K, V>> collection = map.entrySet();
161163
int oldSize = collection.size();
162-
int chunkSize = prng.closedRangeBiasedTowardsSmall(1, oldSize);
164+
int chunkSize = prng.sizeInClosedRange(1, oldSize, valueMutator.hasFixedSize());
163165
int chunkOffset = prng.closedRange(0, oldSize - chunkSize);
164166

165167
Iterator<Map.Entry<K, V>> it = collection.iterator();

src/main/java/com/code_intelligence/jazzer/mutation/mutator/collection/ListMutatorFactory.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ public void initInPlace(List<T> list, PseudoRandom prng) {
114114
public void mutateInPlace(List<T> list, PseudoRandom prng) {
115115
switch (pickRandomMutationAction(list, minSize, maxSize, prng)) {
116116
case DELETE_CHUNK:
117-
deleteRandomChunk(list, minSize, prng);
117+
deleteRandomChunk(list, minSize, prng, elementMutator.hasFixedSize());
118118
break;
119119
case INSERT_CHUNK:
120120
insertRandomChunk(list, maxSize, elementMutator, prng);
@@ -133,10 +133,10 @@ public void crossOverInPlace(List<T> reference, List<T> otherReference, PseudoRa
133133
// the appropriate mutations on the result.
134134
switch (pickRandomCrossOverAction(reference, otherReference, maxSize, prng)) {
135135
case INSERT_CHUNK:
136-
insertChunk(reference, otherReference, maxSize, prng);
136+
insertChunk(reference, otherReference, maxSize, prng, elementMutator.hasFixedSize());
137137
break;
138138
case OVERWRITE_CHUNK:
139-
overwriteChunk(reference, otherReference, prng);
139+
overwriteChunk(reference, otherReference, prng, elementMutator.hasFixedSize());
140140
break;
141141
case CROSS_OVER_CHUNK:
142142
crossOverChunk(reference, otherReference, elementMutator, prng);

src/main/java/com/code_intelligence/jazzer/mutation/mutator/collection/MapMutatorFactory.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ public void initInPlace(Map<K, V> map, PseudoRandom prng) {
155155
public void mutateInPlace(Map<K, V> map, PseudoRandom prng) {
156156
switch (pickRandomMutationAction(map.keySet(), minSize, maxSize, prng)) {
157157
case DELETE_CHUNK:
158-
deleteRandomChunk(map.keySet(), minSize, prng);
158+
deleteRandomChunk(map.keySet(), minSize, prng, entriesHaveFixedSize());
159159
break;
160160
case INSERT_CHUNK:
161161
insertRandomChunk(
@@ -180,10 +180,10 @@ public void crossOverInPlace(Map<K, V> reference, Map<K, V> otherReference, Pseu
180180
switch (pickRandomCrossOverAction(
181181
reference.keySet(), otherReference.keySet(), maxSize, prng)) {
182182
case INSERT_CHUNK:
183-
insertChunk(reference, otherReference, maxSize, prng);
183+
insertChunk(reference, otherReference, maxSize, prng, entriesHaveFixedSize());
184184
break;
185185
case OVERWRITE_CHUNK:
186-
overwriteChunk(reference, otherReference, prng);
186+
overwriteChunk(reference, otherReference, prng, entriesHaveFixedSize());
187187
break;
188188
case CROSS_OVER_CHUNK:
189189
crossOverChunk(reference, otherReference, keyMutator, valueMutator, prng);
@@ -193,6 +193,10 @@ public void crossOverInPlace(Map<K, V> reference, Map<K, V> otherReference, Pseu
193193
}
194194
}
195195

196+
private boolean entriesHaveFixedSize() {
197+
return keyMutator.hasFixedSize() && valueMutator.hasFixedSize();
198+
}
199+
196200
@Override
197201
public boolean hasFixedSize() {
198202
return false;

src/test/java/com/code_intelligence/jazzer/mutation/engine/SeededPseudoRandomTest.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -133,18 +133,18 @@ void testFloatForceInRange(float minValue, float maxValue, boolean throwsExcepti
133133
void testClosedRangeBiasedTowardsSmall() {
134134
SeededPseudoRandom prng = new SeededPseudoRandom(1337133371337L);
135135

136-
assertThrows(IllegalArgumentException.class, () -> prng.closedRangeBiasedTowardsSmall(-1));
137-
assertThrows(IllegalArgumentException.class, () -> prng.closedRangeBiasedTowardsSmall(2, 1));
138-
assertThat(prng.closedRangeBiasedTowardsSmall(0)).isEqualTo(0);
139-
assertThat(prng.closedRangeBiasedTowardsSmall(5, 5)).isEqualTo(5);
136+
assertThrows(IllegalArgumentException.class, () -> prng.sizeInClosedRange(2, 1, false));
137+
assertThrows(IllegalArgumentException.class, () -> prng.sizeInClosedRange(2, 1, true));
138+
assertThat(prng.sizeInClosedRange(5, 5, false)).isEqualTo(5);
139+
assertThat(prng.sizeInClosedRange(5, 5, true)).isEqualTo(5);
140140
}
141141

142142
@Test
143143
void testClosedRangeBiasedTowardsSmall_distribution() {
144144
int num = 5000000;
145145
SeededPseudoRandom prng = new SeededPseudoRandom(1337133371337L);
146146
Map<Integer, Double> frequencies =
147-
Stream.generate(() -> prng.closedRangeBiasedTowardsSmall(9))
147+
Stream.generate(() -> prng.sizeInClosedRange(0, 9, false))
148148
.limit(num)
149149
.collect(
150150
groupingBy(i -> i, collectingAndThen(counting(), count -> ((double) count) / num)));

src/test/java/com/code_intelligence/jazzer/mutation/mutator/collection/ChunkMutationsTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ void testDeleteRandomChunk() {
4444
List<Integer> list = Stream.of(1, 2, 3, 4, 5, 6).collect(toList());
4545

4646
try (MockPseudoRandom prng = mockPseudoRandom(2, 3)) {
47-
ChunkMutations.deleteRandomChunk(list, 2, prng);
47+
ChunkMutations.deleteRandomChunk(list, 2, prng, false);
4848
}
4949
assertThat(list).containsExactly(1, 2, 3, 6).inOrder();
5050
}

src/test/java/com/code_intelligence/jazzer/mutation/support/TestSupport.java

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -363,18 +363,8 @@ public double closedRange(double lowerInclusive, double upperInclusive) {
363363
}
364364

365365
@Override
366-
public int closedRangeBiasedTowardsSmall(int upperInclusive) {
367-
assertThat(upperInclusive).isAtLeast(0);
368-
369-
assertThat(elements).isNotEmpty();
370-
int result = (int) elements.poll();
371-
assertThat(result).isAtLeast(0);
372-
assertThat(result).isAtMost(upperInclusive);
373-
return result;
374-
}
375-
376-
@Override
377-
public int closedRangeBiasedTowardsSmall(int lowerInclusive, int upperInclusive) {
366+
public int sizeInClosedRange(
367+
int lowerInclusive, int upperInclusive, boolean elementsHaveFixedSize) {
378368
assertThat(lowerInclusive).isAtMost(upperInclusive);
379369

380370
assertThat(elements).isNotEmpty();

0 commit comments

Comments
 (0)