test: characterize byte codecs

This commit is contained in:
Algent committed 2026-09-02 17:02:49 +02:00
1 parent 47a4700bee
commit b992d9fbb2
1 file changed
+164
@@ -0,0 +1,164 @@
package codechicken.multipart.asm;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotSame;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Random;
import java.util.Set;
import java.util.TreeSet;
import org.junit.jupiter.api.Test;
import org.objectweb.asm.Type;
class ByteCodecsCharacterizationTest {
@Test
void keepsExactFacadeAndCompanionSurface() throws Exception {
Set<String> expected = new TreeSet<>(
Arrays.asList(
"avoidZero([B)[B",
"regenerateZero([B)I",
"encode8to7([B)[B",
"decode7to8([BI)I",
"encode([B)[B",
"decode([B)I"));
for (Class<?> type : new Class<?>[] { ByteCodecs.class, ByteCodecs$.class }) {
assertEquals(Modifier.PUBLIC | Modifier.FINAL, type.getModifiers());
assertSame(Object.class, type.getSuperclass());
assertEquals(0, type.getInterfaces().length);
Set<String> actual = new TreeSet<>();
for (Method method : type.getDeclaredMethods()) {
assertEquals(Modifier.PUBLIC | (type == ByteCodecs.class ? Modifier.STATIC : 0), method.getModifiers());
actual.add(method.getName() + Type.getMethodDescriptor(method));
}
assertEquals(expected, actual);
}
assertEquals(0, ByteCodecs.class.getDeclaredFields().length);
assertEquals(1, ByteCodecs$.class.getDeclaredFields().length);
Field module = ByteCodecs$.class.getDeclaredField("MODULE$");
assertEquals(Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL, module.getModifiers());
assertSame(ByteCodecs$.class, module.getType());
assertSame(ByteCodecs$.MODULE$, module.get(null));
assertTrue(Modifier.isPrivate(ByteCodecs$.class.getDeclaredConstructor().getModifiers()));
}
@Test
void zeroEscapingPreservesSignedByteAndInPlaceSemantics() {
byte[] input = bytes(0, 1, 126, 127, 128, 255);
byte[] original = input.clone();
byte[] escaped = ByteCodecs.avoidZero(input);
assertArrayEquals(bytes(1, 2, 127, 192, 128, 129, 0), escaped);
assertArrayEquals(original, input);
assertNotSame(input, escaped);
assertArrayEquals(escaped, ByteCodecs$.MODULE$.avoidZero(input));
assertEquals(6, ByteCodecs.regenerateZero(escaped));
assertArrayEquals(bytes(0, 1, 126, 127, 128, 127, 0), escaped);
for (int value = 0; value < 256; value++) {
byte[] expected = value == 127 ? bytes(192, 128) : bytes(value + 1);
assertArrayEquals(expected, ByteCodecs.avoidZero(bytes(value)));
byte[] encoded = expected.clone();
if (value == 191) {
assertThrows(ArrayIndexOutOfBoundsException.class, () -> ByteCodecs$.MODULE$.regenerateZero(encoded));
assertArrayEquals(expected, encoded);
continue;
}
assertEquals(1, ByteCodecs$.MODULE$.regenerateZero(encoded));
assertEquals((byte) (value == 255 ? 127 : value), encoded[0]);
}
byte[] nonEscape = bytes(192, 1, 0);
assertEquals(3, ByteCodecs.regenerateZero(nonEscape));
assertArrayEquals(bytes(191, 0, 127), nonEscape);
}
@Test
void goldenPackingAndRoundTripsCoverEveryByteAndRemainder() {
assertArrayEquals(bytes(1, 4, 12, 0), ByteCodecs.encode8to7(bytes(1, 2, 3)));
assertArrayEquals(bytes(2, 5, 13, 1), ByteCodecs.encode(bytes(1, 2, 3)));
byte[] documented = bytes(2, 5, 13, 1);
assertEquals(4, ByteCodecs.decode(documented));
assertArrayEquals(bytes(1, 2, 3, 0), documented);
for (int value = 0; value < 256; value++) checkPacking(bytes(value));
Random random = new Random(0xB17C0DEL);
for (int length = 0; length <= 128; length++) {
byte[] input = new byte[length];
checkPacking(input);
Arrays.fill(input, (byte) 255);
checkPacking(input);
random.nextBytes(input);
checkPacking(input);
}
}
@Test
void decodingUsesOnlyTheRequestedPrefixAndKeepsSignedShifts() {
byte[] prefix = bytes(1, 4, 12, 0, 55, 66, 77);
assertEquals(4, ByteCodecs.decode7to8(prefix, 4));
assertArrayEquals(bytes(1, 2, 3, 0, 55, 66, 77), prefix);
byte[] signedTail = bytes(0, 128);
assertEquals(2, ByteCodecs.decode7to8(signedTail, 2));
assertArrayEquals(bytes(0, 192), signedTail);
byte[] signedBlock = bytes(0, 128, 128, 128, 128, 128, 128, 128);
assertEquals(7, ByteCodecs$.MODULE$.decode7to8(signedBlock, 8));
assertArrayEquals(bytes(0, 192, 224, 240, 248, 252, 254, 128), signedBlock);
}
@Test
void malformedInputKeepsPartialMutationAndUncheckedFailures() {
byte[] truncatedEscape = bytes(1, 192);
assertThrows(ArrayIndexOutOfBoundsException.class, () -> ByteCodecs.regenerateZero(truncatedEscape));
assertArrayEquals(bytes(0, 192), truncatedEscape);
byte[] truncatedDecode = bytes(1, 192);
assertThrows(ArrayIndexOutOfBoundsException.class, () -> ByteCodecs.decode(truncatedDecode));
assertArrayEquals(bytes(0, 192), truncatedDecode);
byte[] excessiveLength = bytes(7, 3);
assertThrows(ArrayIndexOutOfBoundsException.class, () -> ByteCodecs.decode7to8(excessiveLength, 3));
assertArrayEquals(bytes(135, 3), excessiveLength);
assertEquals(0, ByteCodecs.decode7to8(null, 0));
assertEquals(0, ByteCodecs.decode7to8(null, -2));
assertEquals(-1, ByteCodecs.decode7to8(null, -3));
assertThrows(NullPointerException.class, () -> ByteCodecs.encode(null));
assertThrows(NullPointerException.class, () -> ByteCodecs.decode(null));
}
private static void checkPacking(byte[] input) {
byte[] original = input.clone();
byte[] expected = new byte[(input.length * 8 + 6) / 7];
// Independent bit-by-bit oracle for the reference's unrolled byte-packing algorithm.
for (int bit = 0; bit < input.length * 8; bit++) {
expected[bit / 7] |= ((input[bit / 8] >>> (bit % 8)) & 1) << (bit % 7);
}
byte[] packed = ByteCodecs.encode8to7(input);
assertArrayEquals(expected, packed);
assertArrayEquals(packed, ByteCodecs$.MODULE$.encode8to7(input));
assertArrayEquals(original, input);
assertNotSame(input, packed);
byte[] decoded = packed.clone();
int decodedLength = ByteCodecs.decode7to8(decoded, packed.length);
assertEquals(input.length + (input.length % 7 == 0 ? 0 : 1), decodedLength);
assertArrayEquals(Arrays.copyOf(input, decodedLength), Arrays.copyOf(decoded, decodedLength));
assertArrayEquals(
Arrays.copyOfRange(packed, decodedLength, packed.length),
Arrays.copyOfRange(decoded, decodedLength, decoded.length));
byte[] escaped = ByteCodecs.encode(input);
assertArrayEquals(ByteCodecs.avoidZero(expected), escaped);
assertArrayEquals(escaped, ByteCodecs$.MODULE$.encode(input));
assertEquals(decodedLength, ByteCodecs$.MODULE$.decode(escaped));
assertArrayEquals(Arrays.copyOf(input, decodedLength), Arrays.copyOf(escaped, decodedLength));
assertArrayEquals(original, input);
}
private static byte[] bytes(int... values) {
byte[] result = new byte[values.length];
for (int i = 0; i < values.length; i++) result[i] = (byte) values[i];
return result;
}
}