From 23df86967618d38c7a81cadfca90ac9219a5d3f6 Mon Sep 17 00:00:00 2001 From: Andrea Cavalli Date: Tue, 18 Apr 2023 14:48:31 +0200 Subject: [PATCH] Test buffers --- data-generator-runtime/pom.xml | 14 +- .../main/java/it/cavallium/buffer/Buf.java | 6 +- .../java/it/cavallium/buffer/ByteListBuf.java | 73 +++--- .../java/it/cavallium/buffer/TestBuffer.java | 241 ++++++++++++++++++ 4 files changed, 298 insertions(+), 36 deletions(-) create mode 100644 data-generator-runtime/src/test/java/it/cavallium/buffer/TestBuffer.java diff --git a/data-generator-runtime/pom.xml b/data-generator-runtime/pom.xml index e236e0d..2221bd8 100644 --- a/data-generator-runtime/pom.xml +++ b/data-generator-runtime/pom.xml @@ -143,7 +143,7 @@ org.jetbrains annotations - + it.unimi.dsi fastutil @@ -159,6 +159,11 @@ + + org.junit.jupiter + junit-jupiter-params + test + @@ -175,7 +180,7 @@ org.junit.jupiter junit-jupiter-api - 5.9.0 + 5.9.2 org.hamcrest @@ -183,6 +188,11 @@ + + org.junit.jupiter + junit-jupiter-params + 5.9.2 + \ No newline at end of file diff --git a/data-generator-runtime/src/main/java/it/cavallium/buffer/Buf.java b/data-generator-runtime/src/main/java/it/cavallium/buffer/Buf.java index dc56e3c..5403272 100644 --- a/data-generator-runtime/src/main/java/it/cavallium/buffer/Buf.java +++ b/data-generator-runtime/src/main/java/it/cavallium/buffer/Buf.java @@ -72,7 +72,7 @@ public interface Buf extends ByteList, RandomAccess { * Get this element as an array with equal or bigger size, converting it if needed * The returned array may be bigger than expected! */ - byte @Nullable[] asUnboundedArray(); + byte[] asUnboundedArray(); /** * Get this element as an array with equal or bigger size, only if it's already an array, otherwise return null @@ -82,11 +82,13 @@ public interface Buf extends ByteList, RandomAccess { boolean isMutable(); - void freeze(); + Buf freeze(); @Override Buf subList(int from, int to); + Buf copyOfRange(int from, int to); + Buf copy(); SafeByteArrayInputStream binaryInputStream(); diff --git a/data-generator-runtime/src/main/java/it/cavallium/buffer/ByteListBuf.java b/data-generator-runtime/src/main/java/it/cavallium/buffer/ByteListBuf.java index 954b2bd..912cd93 100644 --- a/data-generator-runtime/src/main/java/it/cavallium/buffer/ByteListBuf.java +++ b/data-generator-runtime/src/main/java/it/cavallium/buffer/ByteListBuf.java @@ -3,16 +3,8 @@ package it.cavallium.buffer; import it.cavallium.stream.SafeByteArrayInputStream; import it.cavallium.stream.SafeByteArrayOutputStream; import it.cavallium.stream.SafeDataOutput; -import it.unimi.dsi.fastutil.bytes.AbstractByteList; -import it.unimi.dsi.fastutil.bytes.ByteArrayList; -import it.unimi.dsi.fastutil.bytes.ByteCollection; -import it.unimi.dsi.fastutil.bytes.ByteConsumer; -import it.unimi.dsi.fastutil.bytes.ByteIterator; -import it.unimi.dsi.fastutil.bytes.ByteIterators; -import it.unimi.dsi.fastutil.bytes.ByteList; -import it.unimi.dsi.fastutil.bytes.ByteListIterator; -import it.unimi.dsi.fastutil.bytes.ByteSpliterator; -import it.unimi.dsi.fastutil.bytes.ByteSpliterators; +import it.unimi.dsi.fastutil.bytes.*; + import java.io.Serial; import java.nio.charset.Charset; import java.util.Arrays; @@ -22,6 +14,8 @@ import java.util.NoSuchElementException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import static it.unimi.dsi.fastutil.Arrays.ensureFromTo; + class ByteListBuf extends ByteArrayList implements Buf { private boolean mutable = true; @@ -77,7 +71,7 @@ class ByteListBuf extends ByteArrayList implements Buf { * @return a new array list of the given size, wrapping the given array. */ public static ByteListBuf wrap(final byte[] a, final int length) { - if (length > a.length) throw new IllegalArgumentException("The specified length (" + length + ") is greater than the array size (" + a.length + ")"); + ByteArrays.ensureFromTo(a, 0, length); final ByteListBuf l = new ByteListBuf(a, true); l.size = length; return l; @@ -137,7 +131,7 @@ class ByteListBuf extends ByteArrayList implements Buf { } @Override - public byte @Nullable [] asUnboundedArray() { + public byte[] asUnboundedArray() { return a; } @@ -152,24 +146,30 @@ class ByteListBuf extends ByteArrayList implements Buf { } @Override - public void freeze() { + public ByteListBuf freeze() { mutable = false; + return this; } @Override public Buf subList(int from, int to) { if (from == 0 && to == size()) return this; - ensureIndex(from); - ensureIndex(to); - if (from > to) throw new IndexOutOfBoundsException("Start index (" + from + ") is greater than end index (" + to + ")"); + ensureFromTo(this.size(), from, to); return new SubList(from, to); } + @Override + public Buf copyOfRange(int from, int to) { + if (from == 0 && to == size()) { + return copy(); + } else { + return ByteListBuf.wrap(Arrays.copyOfRange(this.a, from, to), to - from); + } + } + @Override public Buf copy() { - var copied = ByteListBuf.wrap(this.a.clone()); - copied.size = this.size; - return copied; + return ByteListBuf.wrap(this.a.clone(), this.size); } @Override @@ -184,7 +184,7 @@ class ByteListBuf extends ByteArrayList implements Buf { @Override public SafeByteArrayOutputStream binaryOutputStream(int from, int to) { - it.unimi.dsi.fastutil.Arrays.ensureFromTo(size, from, to); + ensureFromTo(size, from, to); return new SafeByteArrayOutputStream(a, from, to); } @@ -207,12 +207,10 @@ class ByteListBuf extends ByteArrayList implements Buf { return new String(a, 0, size, charset); } - private class SubList extends AbstractByteList.ByteRandomAccessSubList implements Buf { + class SubList extends AbstractByteList.ByteRandomAccessSubList implements Buf { @Serial private static final long serialVersionUID = -3185226345314976296L; - private boolean subMutable = true; - protected SubList(int from, int to) { super(ByteListBuf.this, from, to); } @@ -226,11 +224,21 @@ class ByteListBuf extends ByteArrayList implements Buf { @Override public @NotNull Buf subList(int from, int to) { - it.unimi.dsi.fastutil.Arrays.ensureFromTo(a.length, from, to); - if (from > to) throw new IllegalArgumentException("Start index (" + from + ") is greater than end index (" + to + ")"); + ensureFromTo(this.to, from, to); + var fromAbs = this.from + from; + var toAbs = this.from + to; // Sadly we have to rewrap this, because if there is a sublist of a sublist, and the // subsublist adds, both sublists need to update their "to" value. - return new SubList(from, to); + return new SubList(fromAbs, toAbs); + } + + @Override + public Buf copyOfRange(int from, int to) { + if (from == 0 && to == size()) { + return copy(); + } else { + return Buf.wrap(Arrays.copyOfRange(a, this.from + from, this.from + to)); + } } @Override @@ -250,7 +258,7 @@ class ByteListBuf extends ByteArrayList implements Buf { @Override public SafeByteArrayOutputStream binaryOutputStream(int from, int to) { - it.unimi.dsi.fastutil.Arrays.ensureFromTo(size(), from, to); + ensureFromTo(size(), from, to); return new SafeByteArrayOutputStream(a, from + this.from, to + this.from); } @@ -279,7 +287,7 @@ class ByteListBuf extends ByteArrayList implements Buf { if (this.from == 0 && this.to == a.length) { return a; } else { - return toByteArray(); + return SubList.this.toByteArray(); } } @@ -293,7 +301,7 @@ class ByteListBuf extends ByteArrayList implements Buf { } @Override - public byte @Nullable [] asUnboundedArray() { + public byte[] asUnboundedArray() { if (from == 0) { return a; } else { @@ -312,12 +320,13 @@ class ByteListBuf extends ByteArrayList implements Buf { @Override public boolean isMutable() { - return mutable && subMutable; + return mutable; } @Override - public void freeze() { - subMutable = false; + public SubList freeze() { + mutable = false; + return this; } private final class SubListIterator extends ByteIterators.AbstractIndexBasedListIterator { diff --git a/data-generator-runtime/src/test/java/it/cavallium/buffer/TestBuffer.java b/data-generator-runtime/src/test/java/it/cavallium/buffer/TestBuffer.java new file mode 100644 index 0000000..6133781 --- /dev/null +++ b/data-generator-runtime/src/test/java/it/cavallium/buffer/TestBuffer.java @@ -0,0 +1,241 @@ +package it.cavallium.buffer; + +import it.unimi.dsi.fastutil.bytes.ByteList; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.Arrays; +import java.util.HexFormat; +import java.util.List; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.*; + +public class TestBuffer { + + private record BufArg(String name, Buf b, int initialSize, byte[] initialContent) { + private static final HexFormat HEX = HexFormat.of(); + + @Override + public String toString() { + var bs = toStringArrayb(b.toByteArray()); + var ic = toStringArrayb(initialContent); + return "BufArg{" + + "name='" + name + '\'' + + ", b=" + bs + + ", initialSize=" + initialSize + + ", initialContent=" + ic + + '}'; + } + + private String toStringArraya(byte[] byteArray) { + var bs = Arrays.toString(Arrays.copyOf(byteArray, 24)); + if (byteArray.length > 24) { + bs = bs.substring(0, bs.length() - 1) + ", ...]"; + } + return bs; + } + + private String toStringArrayb(byte[] byteArray) { + var out = HEX.formatHex(byteArray, 0, Math.min(byteArray.length, 24)); + if (byteArray.length > 24) { + out += "..."; + } + return out; + } + + public BufArg subList(int from, int to) { + return new BufArg(name + ".subList(" + from + "," + to + ")", b.subList(from, to), to - from, Arrays.copyOfRange(initialContent, from, to)); + } + + public BufArg copyOfRange(int from, int to) { + return new BufArg(name + ".copyOfRange(" + from + "," + to + ")", b.copyOfRange(from, to), to - from, Arrays.copyOfRange(initialContent, from, to)); + } + } + + static Stream provideBufs() { + List primaryBufs = createPrimaryBufs(); + List subListBufs = createSubListBufs(); + return Stream.concat(primaryBufs.stream(), subListBufs.stream()); + } + + private static List createPrimaryBufs() { + var emptyBuf = new BufArg("create()", Buf.create(), 0, new byte[0]); + var def0Buf = new BufArg("create(0)", Buf.create(0), 0, new byte[0]); + var def10Buf = new BufArg("create(10)", Buf.create(10), 0, new byte[0]); + var def10000Buf = new BufArg("create(10000)", Buf.create(10000), 0, new byte[0]); + var zeroedBuf = new BufArg("createZeroes(0)", Buf.createZeroes(0), 0, new byte[0]); + var zeroed10Buf = new BufArg("createZeroes(10)", Buf.createZeroes(10), 10, new byte[10]); + var zeroed10000Buf = new BufArg("createZeroes(10000)", Buf.createZeroes(10000), 10000, new byte[10000]); + var copyOfEmpty = new BufArg("copyOf(empty)", Buf.copyOf(new byte[0]), 0, new byte[0]); + var small = new byte[] {126, 19, 118, 33, -24, -65, 56, 17, 0, 90}; + var copyOfSmall = new BufArg("copyOfSmall(small)", Buf.copyOf(small), small.length, small); + var big = new byte[10000]; + for (int i = 0; i < big.length; i++) { + big[i] = (byte) (i * i); + } + var copyOfBig = new BufArg("copyOfBig(big)", Buf.copyOf(big), big.length, big); + var wrapSmallArray = new BufArg("wrap(small array)", Buf.wrap(small.clone()), small.length, small); + var wrapBigArray = new BufArg("wrap(big array)", Buf.wrap(big.clone()), big.length, big); + var wrapSmallByteList = new BufArg("wrap(small byte list)", Buf.wrap(ByteList.of(small)), small.length, small); + var wrapBigByteList = new BufArg("wrap(big byte list)", Buf.wrap(ByteList.of(big)), big.length, big); + var wrapSmallCapped = new BufArg("wrap(small array, 10)", Buf.wrap(small.clone(), 10), 10, Arrays.copyOf(small, 10)); + var wrapBigCapped = new BufArg("wrap(big array, 10)", Buf.wrap(big.clone(), 10), 10, Arrays.copyOf(big, 10)); + var wrapSmallCappedSame = new BufArg("wrap(small array, same)", Buf.wrap(small.clone(), small.length), small.length, small); + var wrapBigCappedSame = new BufArg("wrap(big array, same)", Buf.wrap(big.clone(), big.length), big.length, big); + var wrapSmallCappedMinusOne = new BufArg("wrap(small array, same-1)", Buf.wrap(small.clone(), small.length - 1), small.length - 1, Arrays.copyOf(small, small.length - 1)); + var wrapBigCappedMinusOne = new BufArg("wrap(big array, same-1)", Buf.wrap(big.clone(), big.length - 1), big.length - 1, Arrays.copyOf(big, big.length - 1)); + var wrapSmallCappedRangeSame = new BufArg("wrap(small array, 0, same)", Buf.wrap(small.clone(), 0, small.length), small.length, small); + var wrapBigCappedRangeSame = new BufArg("wrap(big array, 0, same)", Buf.wrap(big.clone(), 0, big.length), big.length, big); + var wrapSmallCappedRangeOffset = new BufArg("wrap(small array, 5, same)", Buf.wrap(small.clone(), 5, small.length), small.length - 5, Arrays.copyOfRange(small, 5, small.length)); + var wrapBigCappedRangeOffset = new BufArg("wrap(big array, 500, same)", Buf.wrap(big.clone(), 500, big.length), big.length - 500, Arrays.copyOfRange(big, 500, big.length)); + var wrapSmallCappedRangeOffsetAndLen = new BufArg("wrap(small array, 5, same-3)", Buf.wrap(small.clone(), 5, small.length - 3), small.length - 5 - 3, Arrays.copyOfRange(small, 5, small.length - 3)); + var wrapBigCappedRangeOffsetAndLen = new BufArg("wrap(big array, 500, same-100)", Buf.wrap(big.clone(), 500, big.length - 100), big.length - 500 - 100, Arrays.copyOfRange(big, 500, big.length - 100)); + var wrapSmallCappedRangeLen = new BufArg("wrap(small array, 0, same-5)", Buf.wrap(small.clone(), 0, small.length - 5), small.length - 5, Arrays.copyOf(small, small.length - 5)); + var wrapBigCappedRangeLen = new BufArg("wrap(big array, 0, same-500)", Buf.wrap(big.clone(), 0, big.length - 500), big.length - 500, Arrays.copyOf(big, big.length - 500)); + + return List.of(emptyBuf, def0Buf, def10Buf, def10000Buf, zeroedBuf, zeroed10Buf, zeroed10000Buf, copyOfEmpty, + copyOfSmall, copyOfBig, wrapSmallArray, wrapBigArray, wrapSmallByteList, wrapBigByteList, + wrapSmallCapped, wrapBigCapped, wrapSmallCappedSame, wrapBigCappedSame, wrapSmallCappedMinusOne, + wrapBigCappedMinusOne, wrapSmallCappedRangeSame, wrapBigCappedRangeSame, wrapSmallCappedRangeOffset, + wrapBigCappedRangeOffset, wrapSmallCappedRangeOffsetAndLen, wrapBigCappedRangeOffsetAndLen, + wrapSmallCappedRangeLen, wrapBigCappedRangeLen); + } + + private static List createSubListBufs() { + var sameSizeArgs = createPrimaryBufs().stream().filter(b -> b.initialSize > 0).map(bufArg -> new BufArg(bufArg.name + ".subList(0, same)", bufArg.b.subList(0, bufArg.initialSize), bufArg.initialSize, bufArg.initialContent)).toList(); + var firstHalfArgs = createPrimaryBufs().stream().filter(b -> b.initialSize > 0).map(bufArg -> new BufArg(bufArg.name + ".subList(0, half)", bufArg.b.subList(0, bufArg.initialSize/2), bufArg.initialSize/2, Arrays.copyOfRange(bufArg.initialContent, 0, bufArg.initialSize/2))).toList(); + var lastHalfArgs = createPrimaryBufs().stream().filter(b -> b.initialSize > 0).map(bufArg -> new BufArg(bufArg.name + ".subList(half, same)", bufArg.b.subList(bufArg.initialSize/2, bufArg.initialSize), bufArg.initialSize - bufArg.initialSize/2, Arrays.copyOfRange(bufArg.initialContent, bufArg.initialSize/2, bufArg.initialSize))).toList(); + return Stream.concat(Stream.concat(sameSizeArgs.stream(), firstHalfArgs.stream()), lastHalfArgs.stream()).toList(); + } + + @Test + public void testCreate() { + var buf = Buf.create(); + assertEquals(0, buf.size()); + assertTrue(buf.isMutable()); + } + + @ParameterizedTest + @MethodSource("provideBufs") + public void testInitialValidity(BufArg bufArg) { + assertEquals(bufArg.initialSize, bufArg.b.size()); + assertEquals(bufArg.initialSize, bufArg.initialContent.length); + assertEquals(bufArg.initialSize == 0, bufArg.b.isEmpty()); + assertTrue(bufArg.b.isMutable()); + if (bufArg.b instanceof ByteListBuf.SubList subList) { + assertArrayEquals(bufArg.initialContent, subList.toByteArray()); + } else if (bufArg.b instanceof ByteListBuf bytes) { + assertArrayEquals(bufArg.initialContent, bytes.toByteArray()); + } else { + assertArrayEquals(bufArg.initialContent, bufArg.b.toByteArray()); + } + } + + @ParameterizedTest + @MethodSource("provideBufs") + public void testGet(BufArg bufArg) { + assertThrows(Exception.class, () -> bufArg.b.getByte(-1)); + assertThrows(Exception.class, () -> bufArg.b.getByte(bufArg.initialContent.length)); + if (bufArg.initialSize > 0) { + // Test first + var expected = bufArg.initialContent[0]; + var bi = bufArg.b.getByte(0); + assertEquals(expected, bi, "The first element does not match"); + + // Test last + var expectedLast = bufArg.initialContent[bufArg.initialSize - 1]; + var biLast = bufArg.b.getByte(bufArg.initialSize - 1); + assertEquals(expectedLast, biLast, "The last element does not match"); + } + + // Test the other + for (int i = 1; i < bufArg.initialContent.length - 1; i++) { + var expected = bufArg.initialContent[i]; + var bi = bufArg.b.getByte(i); + assertEquals(expected, bi, "The element index " + i + " does not match"); + } + } + + @ParameterizedTest + @MethodSource("provideBufs") + public void testPut(BufArg bufArg) { + bufArg.b.copy().isMutable(); + bufArg.b.size(10); + bufArg.b.size(); + } + + @ParameterizedTest + @MethodSource("provideBufs") + public void testFreeze(BufArg bufArg) { + var buf = bufArg.b; + assertTrue(buf.isMutable()); + Buf subList1 = null; + Buf subList2 = null; + if (bufArg.initialSize >= 3) { + subList1 = buf.subList(0, 2); + subList2 = subList1.subList(0, 1); + } + buf.freeze(); + assertFalse(buf.isMutable()); + if (subList1 != null) { + Buf subList3 = buf.subList(0, 2); + Buf subList4 = subList3.subList(0, 1); + assertFalse(subList1.isMutable()); + assertFalse(subList2.isMutable()); + assertFalse(subList3.isMutable()); + assertFalse(subList4.isMutable()); + } + buf.freeze(); + assertFalse(buf.isMutable()); + } + + @ParameterizedTest + @MethodSource("provideBufs") + public void testAsArray(BufArg bufArg) { + assertArrayEquals(bufArg.initialContent, bufArg.b.asArray()); + assertArrayEquals(bufArg.initialContent, bufArg.b.toByteArray()); + assertArrayEquals(bufArg.initialContent, Arrays.copyOf(bufArg.b.asUnboundedArray(), bufArg.initialSize)); + var strictArray = bufArg.b.asArrayStrict(); + if (strictArray != null) { + assertArrayEquals(bufArg.initialContent, strictArray); + } + var strictUnboundedArray = bufArg.b.asUnboundedArrayStrict(); + if (strictUnboundedArray != null) { + assertArrayEquals(bufArg.initialContent, Arrays.copyOf(strictUnboundedArray, bufArg.initialSize)); + } + } + + @ParameterizedTest + @MethodSource("generateWrapped") + public void testWrapSubList(BufArg bufArg) { + testInitialValidity(bufArg); + testAsArray(bufArg.subList(0, 5)); + testAsArray(bufArg.subList(0, 2)); + testAsArray(bufArg.subList(3, 5)); + testAsArray(bufArg.subList(3, 4)); + testAsArray(bufArg.subList(3, 3)); + } + + @ParameterizedTest + @MethodSource("generateWrapped") + public void testWrapCopyOfRange(BufArg bufArg) { + testInitialValidity(bufArg); + testAsArray(bufArg.copyOfRange(0, 5)); + testAsArray(bufArg.copyOfRange(0, 2)); + testAsArray(bufArg.copyOfRange(3, 5)); + testAsArray(bufArg.copyOfRange(3, 4)); + testAsArray(bufArg.copyOfRange(3, 3)); + } + + public static Stream generateWrapped() { + byte[] source = new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + return Stream.of( + new BufArg("0-len", Buf.wrap(source), source.length, source), + new BufArg("2-len", Buf.wrap(source, 2, source.length), source.length - 2, Arrays.copyOfRange(source, 2, source.length)), + new BufArg("2-9", Buf.wrap(source, 2, 9), 9 - 2, Arrays.copyOfRange(source, 2, 9)), + new BufArg("0-9", Buf.wrap(source, 0, 9), 9, Arrays.copyOfRange(source, 0, 9)) + ); + } +}