4343import java .lang .reflect .AnnotatedType ;
4444import java .nio .ByteBuffer ;
4545import java .nio .charset .Charset ;
46+ import java .util .Arrays ;
4647import java .util .Optional ;
4748import java .util .function .BiFunction ;
4849import java .util .function .Function ;
@@ -75,6 +76,8 @@ public static final class PrimitiveArrayMutator<T> extends SerializingMutator<T>
7576 private static final Charset FUZZED_DATA_CHARSET = Charset .forName ("CESU-8" );
7677 private long minRange ;
7778 private long maxRange ;
79+ private int minLength ;
80+ private int maxLength ;
7881 private boolean allowNaN ;
7982 private float minFloatRange ;
8083 private float maxFloatRange ;
@@ -90,6 +93,7 @@ public static final class PrimitiveArrayMutator<T> extends SerializingMutator<T>
9093 public PrimitiveArrayMutator (AnnotatedType type ) {
9194 elementType = ((AnnotatedArrayType ) type ).getAnnotatedGenericComponentType ();
9295 extractRange (elementType );
96+ extractLength (type );
9397 AnnotatedType innerByteArray =
9498 forwardAnnotations (
9599 type , convertWithLength (type , new TypeHolder <byte []>() {}.annotatedType ()));
@@ -209,11 +213,14 @@ private void extractRange(AnnotatedType type) {
209213 }
210214 }
211215
212- private static AnnotatedType convertWithLength (AnnotatedType type , AnnotatedType newType ) {
213- AnnotatedType elementType = ((AnnotatedArrayType ) type ).getAnnotatedGenericComponentType ();
216+ private void extractLength (AnnotatedType type ) {
214217 Optional <WithLength > withLength = Optional .ofNullable (type .getAnnotation (WithLength .class ));
215- int minLength = withLength .map (WithLength ::min ).orElse (DEFAULT_MIN_LENGTH );
216- int maxLength = withLength .map (WithLength ::max ).orElse (DEFAULT_MAX_LENGTH );
218+ minLength = withLength .map (WithLength ::min ).orElse (DEFAULT_MIN_LENGTH );
219+ maxLength = withLength .map (WithLength ::max ).orElse (DEFAULT_MAX_LENGTH );
220+ }
221+
222+ private AnnotatedType convertWithLength (AnnotatedType type , AnnotatedType newType ) {
223+ AnnotatedType elementType = ((AnnotatedArrayType ) type ).getAnnotatedGenericComponentType ();
217224 switch (elementType .getType ().getTypeName ()) {
218225 case "int" :
219226 case "float" :
@@ -222,8 +229,11 @@ private static AnnotatedType convertWithLength(AnnotatedType type, AnnotatedType
222229 case "double" :
223230 return withLength (newType , minLength * 8 , maxLength * 8 );
224231 case "short" :
225- case "char" :
226232 return withLength (newType , minLength * 2 , maxLength * 2 );
233+ case "char" :
234+ // CESU-8 encodes each UTF-16 char (including surrogates) as at most 3 bytes.
235+ // So a char[] of length n needs at most 3 * n bytes.
236+ return withLength (newType , minLength * 3 , maxLength * 3 );
227237 case "boolean" :
228238 case "byte" :
229239 return withLength (newType , minLength , maxLength );
@@ -241,7 +251,7 @@ private static AnnotatedType convertWithLength(AnnotatedType type, AnnotatedType
241251 case "short" :
242252 return getShortPrimitiveArray (minRange , maxRange );
243253 case "char" :
244- return getCharPrimitiveArray (minRange , maxRange );
254+ return getCharPrimitiveArray (minRange , maxRange , minLength , maxLength );
245255 case "float" :
246256 return getFloatPrimitiveArray (minFloatRange , maxFloatRange , allowNaN );
247257 case "double" :
@@ -263,9 +273,16 @@ public char[] postMutateChars(byte[] bytes, PseudoRandom prng) {
263273 return (char []) toPrimitive .apply (bytes );
264274 } else {
265275 char [] chars = new String (bytes , FUZZED_DATA_CHARSET ).toCharArray ();
276+
266277 for (int i = 0 ; i < chars .length ; i ++) {
267278 chars [i ] = (char ) forceInRange (chars [i ], minRange , maxRange );
268279 }
280+
281+ if (chars .length < minLength ) {
282+ return Arrays .copyOf (chars , minLength );
283+ } else if (chars .length > maxLength ) {
284+ return Arrays .copyOf (chars , maxLength );
285+ }
269286 return chars ;
270287 }
271288 }
@@ -407,10 +424,18 @@ public static Function<byte[], short[]> getShortPrimitiveArray(long minRange, lo
407424 };
408425 }
409426
410- public static Function <byte [], char []> getCharPrimitiveArray (long minRange , long maxRange ) {
427+ public static Function <byte [], char []> getCharPrimitiveArray (
428+ long minRange , long maxRange , int minLength , int maxLength ) {
411429 int nBytes = 2 ;
412430 return (byte [] byteArray ) -> {
413431 if (byteArray == null ) return null ;
432+
433+ if (byteArray .length < minLength * 2 ) {
434+ byteArray = Arrays .copyOf (byteArray , minLength * 2 );
435+ } else if (byteArray .length > maxLength * 2 ) {
436+ byteArray = Arrays .copyOf (byteArray , maxLength * 2 );
437+ }
438+
414439 char extraBytes = (char ) (byteArray .length % nBytes );
415440 char [] result = new char [byteArray .length / nBytes + (extraBytes > 0 ? 1 : 0 )];
416441 ByteBuffer buffer = ByteBuffer .wrap (byteArray );
0 commit comments