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 22a5254..67eb12f 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 @@ -9,6 +9,7 @@ import java.nio.charset.Charset; import java.util.RandomAccess; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.VisibleForTesting; public interface Buf extends ByteList, RandomAccess { static Buf wrap(ByteList bytes) { @@ -30,7 +31,7 @@ public interface Buf extends ByteList, RandomAccess { } } - static Buf wrap(byte[] bytes) { + static Buf wrap(byte... bytes) { return ByteListBuf.wrap(bytes); } @@ -87,6 +88,9 @@ public interface Buf extends ByteList, RandomAccess { @Override Buf subList(int from, int to); + @VisibleForTesting + Buf subListForced(int from, int to); + Buf copyOfRange(int from, int to); Buf copy(); diff --git a/data-generator-runtime/src/main/java/it/cavallium/buffer/BufDataInput.java b/data-generator-runtime/src/main/java/it/cavallium/buffer/BufDataInput.java index 604f2ef..8b8ab21 100644 --- a/data-generator-runtime/src/main/java/it/cavallium/buffer/BufDataInput.java +++ b/data-generator-runtime/src/main/java/it/cavallium/buffer/BufDataInput.java @@ -25,6 +25,7 @@ public class BufDataInput extends SafeDataInputStream { @Deprecated @Override public void close() { + super.close(); } @Override @@ -42,14 +43,21 @@ public class BufDataInput extends SafeDataInputStream { return false; } + @Deprecated @Override public @NotNull String readUTF() { - var length = this.readUnsignedShort(); - return this.in.readString(length, StandardCharsets.UTF_8); + return readShortText(StandardCharsets.UTF_8); } @Override - public String readString(int length, Charset charset) { - return in.readString(length, charset); + public @NotNull String readShortText(Charset charset) { + var length = this.readUnsignedShort(); + return this.readString(length, charset); + } + + @Override + public @NotNull String readMediumText(Charset charset) { + var length = this.readInt(); + return this.readString(length, charset); } } diff --git a/data-generator-runtime/src/main/java/it/cavallium/buffer/BufDataOutput.java b/data-generator-runtime/src/main/java/it/cavallium/buffer/BufDataOutput.java index a9b6e0b..f01e108 100644 --- a/data-generator-runtime/src/main/java/it/cavallium/buffer/BufDataOutput.java +++ b/data-generator-runtime/src/main/java/it/cavallium/buffer/BufDataOutput.java @@ -5,10 +5,13 @@ import it.cavallium.stream.SafeDataOutput; import it.cavallium.stream.SafeDataOutputStream; import it.unimi.dsi.fastutil.Arrays; import java.io.IOException; +import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.Objects; import org.jetbrains.annotations.NotNull; +import static java.util.Objects.checkFromToIndex; + public class BufDataOutput implements SafeDataOutput { private final SafeByteArrayOutputStream buf; @@ -60,7 +63,7 @@ public class BufDataOutput implements SafeDataOutput { } public static BufDataOutput wrap(Buf buf, int from, int to) { - Arrays.ensureFromTo(buf.size(), from, to); + checkFromToIndex(from, to, buf.size()); if (buf.isEmpty()) { return createLimited(0); } else { @@ -201,9 +204,15 @@ public class BufDataOutput implements SafeDataOutput { + actualLength + " bytes"; } + @Deprecated @Override public void writeUTF(@NotNull String str) { - var out = str.getBytes(StandardCharsets.UTF_8); + writeShortText(str, StandardCharsets.UTF_8); + } + + @Override + public void writeShortText(String s, Charset charset) { + var out = s.getBytes(charset); if (out.length > Short.MAX_VALUE) { throw new IndexOutOfBoundsException("String too long: " + out.length + " bytes"); } @@ -212,6 +221,14 @@ public class BufDataOutput implements SafeDataOutput { dOut.write(out); } + @Override + public void writeMediumText(String s, Charset charset) { + var out = s.getBytes(charset); + checkOutOfBounds(Integer.BYTES + out.length); + dOut.writeInt(out.length); + dOut.write(out); + } + public Buf asList() { dOut.flush(); return Buf.wrap(this.buf.array, this.buf.length); 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 3dcad20..07ff663 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 @@ -1,33 +1,29 @@ package it.cavallium.buffer; import static it.unimi.dsi.fastutil.Arrays.ensureFromTo; +import static java.util.Objects.checkFromToIndex; 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.ByteArrays; -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; -import java.util.Collection; -import java.util.Iterator; -import java.util.NoSuchElementException; +import java.util.*; +import java.util.function.IntPredicate; +import java.util.function.IntUnaryOperator; +import java.util.function.Predicate; +import java.util.function.UnaryOperator; + import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.VisibleForTesting; class ByteListBuf extends ByteArrayList implements Buf { - private boolean mutable = true; + private static final String IMMUTABLE_ERROR = "The buffer is immutable"; + private boolean immutable; protected ByteListBuf(byte[] a, boolean wrapped) { super(a, wrapped); @@ -151,19 +147,24 @@ class ByteListBuf extends ByteArrayList implements Buf { @Override public boolean isMutable() { - return mutable; + return !immutable; } @Override public ByteListBuf freeze() { - mutable = false; + immutable = true; return this; } @Override public Buf subList(int from, int to) { if (from == 0 && to == size()) return this; - ensureFromTo(this.size(), from, to); + return subListForced(from, to); + } + + @VisibleForTesting + public Buf subListForced(int from, int to) { + checkFromToIndex(from, to, this.size()); return new SubList(from, to); } @@ -193,12 +194,15 @@ class ByteListBuf extends ByteArrayList implements Buf { @Override public SafeByteArrayOutputStream binaryOutputStream(int from, int to) { - ensureFromTo(size, from, to); + checkFromToIndex(from, to, size); return new SafeByteArrayOutputStream(a, from, to); } @Override public boolean equals(int aStartIndex, Buf b, int bStartIndex, int length) { + if (aStartIndex + length > size()) { + return false; + } return b.equals(bStartIndex, this.a, aStartIndex, length); } @@ -208,6 +212,9 @@ class ByteListBuf extends ByteArrayList implements Buf { if (aStartIndex + length > this.size) { return false; } + if (bStartIndex + length > b.length) { + return false; + } return Arrays.equals(a, aStartIndex, aStartIndex + length, b, bStartIndex, bStartIndex + length); } @@ -227,13 +234,21 @@ class ByteListBuf extends ByteArrayList implements Buf { // Most of the inherited methods should be fine, but we can override a few of them for performance. // Needed because we can't access the parent class' instance variables directly in a different // instance of SubList. + @IgnoreCoverage private byte[] getParentArray() { return a; } @Override public @NotNull Buf subList(int from, int to) { - ensureFromTo(this.to, 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 subListForced(from, to); + } + + @Override + public Buf subListForced(int from, int to) { + checkFromToIndex(from, to, this.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 @@ -267,12 +282,15 @@ class ByteListBuf extends ByteArrayList implements Buf { @Override public SafeByteArrayOutputStream binaryOutputStream(int from, int to) { - ensureFromTo(size(), from, to); + checkFromToIndex(from, to, size()); return new SafeByteArrayOutputStream(a, from + this.from, to + this.from); } @Override public boolean equals(int aStartIndex, Buf b, int bStartIndex, int length) { + if (aStartIndex + length > size()) { + return false; + } return b.equals(bStartIndex, a, aStartIndex + from, length); } @@ -280,9 +298,11 @@ class ByteListBuf extends ByteArrayList implements Buf { public boolean equals(int aStartIndex, byte[] b, int bStartIndex, int length) { var aFrom = from + aStartIndex; var aTo = from + aStartIndex + length; + var bTo = bStartIndex + length; if (aFrom < from) return false; if (aTo > to) return false; - return Arrays.equals(a, aFrom, aTo, b, bStartIndex, bStartIndex + length); + if (bTo > b.length) return false; + return Arrays.equals(a, aFrom, aTo, b, bStartIndex, bTo); } @Override @@ -329,12 +349,12 @@ class ByteListBuf extends ByteArrayList implements Buf { @Override public boolean isMutable() { - return mutable; + return ByteListBuf.this.isMutable(); } @Override public SubList freeze() { - mutable = false; + immutable = true; return this; } @@ -345,23 +365,30 @@ class ByteListBuf extends ByteArrayList implements Buf { super(0, index); } + @IgnoreCoverage @Override protected byte get(int i) { - return a[from + i]; + return ByteListBuf.SubList.this.getByte(i); } + @IgnoreCoverage @Override protected void add(int i, byte k) { + assert isMutable() : IMMUTABLE_ERROR; ByteListBuf.SubList.this.add(i, k); } + @IgnoreCoverage @Override protected void set(int i, byte k) { + assert isMutable() : IMMUTABLE_ERROR; ByteListBuf.SubList.this.set(i, k); } + @IgnoreCoverage @Override protected void remove(int i) { + assert isMutable() : IMMUTABLE_ERROR; ByteListBuf.SubList.this.removeByte(i); } @@ -411,6 +438,7 @@ class ByteListBuf extends ByteArrayList implements Buf { return to; } + @IgnoreCoverage @Override protected byte get(int i) { return a[i]; @@ -482,4 +510,336 @@ class ByteListBuf extends ByteArrayList implements Buf { return new String(a, from, size(), charset); } } + + @IgnoreCoverage + @Override + public void add(int index, byte k) { + assert isMutable() : IMMUTABLE_ERROR; + super.add(index, k); + } + + @IgnoreCoverage + @Override + public boolean add(byte k) { + assert isMutable() : IMMUTABLE_ERROR; + return super.add(k); + } + + @IgnoreCoverage + @Override + public byte removeByte(int index) { + assert isMutable() : IMMUTABLE_ERROR; + return super.removeByte(index); + } + + @IgnoreCoverage + @Override + public boolean rem(byte k) { + assert isMutable() : IMMUTABLE_ERROR; + return super.rem(k); + } + + @IgnoreCoverage + @Override + public byte set(int index, byte k) { + assert isMutable() : IMMUTABLE_ERROR; + return super.set(index, k); + } + + @IgnoreCoverage + @Override + public void clear() { + assert isMutable() : IMMUTABLE_ERROR; + super.clear(); + } + + @IgnoreCoverage + @Override + public void trim() { + assert isMutable() : IMMUTABLE_ERROR; + super.trim(); + } + + @IgnoreCoverage + @Override + public void trim(int n) { + assert isMutable() : IMMUTABLE_ERROR; + super.trim(n); + } + + @IgnoreCoverage + @Override + public void removeElements(int from, int to) { + assert isMutable() : IMMUTABLE_ERROR; + super.removeElements(from, to); + } + + @IgnoreCoverage + @Override + public void addElements(int index, byte[] a, int offset, int length) { + assert isMutable() : IMMUTABLE_ERROR; + super.addElements(index, a, offset, length); + } + + @IgnoreCoverage + @Override + public void setElements(int index, byte[] a, int offset, int length) { + assert isMutable() : IMMUTABLE_ERROR; + super.setElements(index, a, offset, length); + } + + @IgnoreCoverage + @Override + public boolean addAll(int index, ByteCollection c) { + assert isMutable() : IMMUTABLE_ERROR; + return super.addAll(index, c); + } + + @IgnoreCoverage + @Override + public boolean addAll(int index, ByteList l) { + assert isMutable() : IMMUTABLE_ERROR; + return super.addAll(index, l); + } + + @IgnoreCoverage + @Override + public boolean removeAll(ByteCollection c) { + assert isMutable() : IMMUTABLE_ERROR; + return super.removeAll(c); + } + + @IgnoreCoverage + @Override + public boolean addAll(int index, @NotNull Collection c) { + assert isMutable() : IMMUTABLE_ERROR; + return super.addAll(index, c); + } + + @IgnoreCoverage + @Override + public boolean addAll(@NotNull Collection c) { + assert isMutable() : IMMUTABLE_ERROR; + return super.addAll(c); + } + + @IgnoreCoverage + @Override + public void addElements(int index, byte[] a) { + assert isMutable() : IMMUTABLE_ERROR; + super.addElements(index, a); + } + + @IgnoreCoverage + @Override + public void push(byte o) { + assert isMutable() : IMMUTABLE_ERROR; + super.push(o); + } + + @IgnoreCoverage + @Override + public byte popByte() { + assert isMutable() : IMMUTABLE_ERROR; + return super.popByte(); + } + + @IgnoreCoverage + @Override + public byte topByte() { + assert isMutable() : IMMUTABLE_ERROR; + return super.topByte(); + } + + @IgnoreCoverage + @Override + public boolean addAll(ByteCollection c) { + assert isMutable() : IMMUTABLE_ERROR; + return super.addAll(c); + } + + @IgnoreCoverage + @SuppressWarnings("deprecation") + @Deprecated + @Override + public boolean add(Byte key) { + assert isMutable() : IMMUTABLE_ERROR; + return super.add(key); + } + + @IgnoreCoverage + @SuppressWarnings("deprecation") + @Deprecated + @Override + public boolean remove(Object key) { + assert isMutable() : IMMUTABLE_ERROR; + return super.remove(key); + } + + @IgnoreCoverage + @Override + public boolean removeAll(@NotNull Collection c) { + assert isMutable() : IMMUTABLE_ERROR; + return super.removeAll(c); + } + + @IgnoreCoverage + @Override + public boolean retainAll(ByteCollection c) { + assert isMutable() : IMMUTABLE_ERROR; + return super.retainAll(c); + } + + @IgnoreCoverage + @Override + public boolean retainAll(@NotNull Collection c) { + assert isMutable() : IMMUTABLE_ERROR; + return super.retainAll(c); + } + + @IgnoreCoverage + @Override + public void setElements(byte[] a) { + assert isMutable() : IMMUTABLE_ERROR; + super.setElements(a); + } + + @IgnoreCoverage + @Override + public void setElements(int index, byte[] a) { + assert isMutable() : IMMUTABLE_ERROR; + super.setElements(index, a); + } + + @IgnoreCoverage + @SuppressWarnings("deprecation") + @Deprecated + @Override + public void add(int index, Byte key) { + assert isMutable() : IMMUTABLE_ERROR; + super.add(index, key); + } + + @IgnoreCoverage + @Override + public void replaceAll(ByteUnaryOperator operator) { + assert isMutable() : IMMUTABLE_ERROR; + super.replaceAll(operator); + } + + @IgnoreCoverage + @Override + public void replaceAll(IntUnaryOperator operator) { + assert isMutable() : IMMUTABLE_ERROR; + super.replaceAll(operator); + } + + @IgnoreCoverage + @SuppressWarnings("deprecation") + @Deprecated + @Override + public void replaceAll(UnaryOperator operator) { + assert isMutable() : IMMUTABLE_ERROR; + super.replaceAll(operator); + } + + @IgnoreCoverage + @SuppressWarnings("deprecation") + @Deprecated + @Override + public Byte remove(int index) { + assert isMutable() : IMMUTABLE_ERROR; + return super.remove(index); + } + + @IgnoreCoverage + @SuppressWarnings("deprecation") + @Deprecated + @Override + public Byte set(int index, Byte k) { + assert isMutable() : IMMUTABLE_ERROR; + return super.set(index, k); + } + + @IgnoreCoverage + @Override + public boolean addAll(ByteList l) { + assert isMutable() : IMMUTABLE_ERROR; + return super.addAll(l); + } + + @IgnoreCoverage + @SuppressWarnings("deprecation") + @Deprecated + @Override + public void sort(Comparator comparator) { + assert isMutable() : IMMUTABLE_ERROR; + super.sort(comparator); + } + + @IgnoreCoverage + @SuppressWarnings("deprecation") + @Deprecated + @Override + public void unstableSort(Comparator comparator) { + assert isMutable() : IMMUTABLE_ERROR; + super.unstableSort(comparator); + } + + @IgnoreCoverage + @SuppressWarnings("deprecation") + @Deprecated + @Override + public boolean removeIf(Predicate filter) { + assert isMutable() : IMMUTABLE_ERROR; + return super.removeIf(filter); + } + + @IgnoreCoverage + @Override + public boolean removeIf(BytePredicate filter) { + assert isMutable() : IMMUTABLE_ERROR; + return super.removeIf(filter); + } + + @IgnoreCoverage + @Override + public boolean removeIf(IntPredicate filter) { + assert isMutable() : IMMUTABLE_ERROR; + return super.removeIf(filter); + } + + @IgnoreCoverage + @SuppressWarnings("deprecation") + @Deprecated + @Override + public void push(Byte o) { + assert isMutable() : IMMUTABLE_ERROR; + super.push(o); + } + + @IgnoreCoverage + @SuppressWarnings("deprecation") + @Deprecated + @Override + public Byte pop() { + assert isMutable() : IMMUTABLE_ERROR; + return super.pop(); + } + + @IgnoreCoverage + @SuppressWarnings("deprecation") + @Deprecated + @Override + public Byte top() { + assert isMutable() : IMMUTABLE_ERROR; + return super.top(); + } + + @IgnoreCoverage + private void ensureMutable() { + if (!isMutable()) { + throw new UnsupportedOperationException(IMMUTABLE_ERROR); + } + } } diff --git a/data-generator-runtime/src/main/java/it/cavallium/buffer/IgnoreCoverage.java b/data-generator-runtime/src/main/java/it/cavallium/buffer/IgnoreCoverage.java new file mode 100644 index 0000000..7553325 --- /dev/null +++ b/data-generator-runtime/src/main/java/it/cavallium/buffer/IgnoreCoverage.java @@ -0,0 +1,8 @@ +package it.cavallium.buffer; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.CLASS) +public @interface IgnoreCoverage { +} diff --git a/data-generator-runtime/src/main/java/it/cavallium/data/generator/nativedata/ArrayStringSerializer.java b/data-generator-runtime/src/main/java/it/cavallium/data/generator/nativedata/ArrayStringSerializer.java index fb7e971..5b9dce1 100644 --- a/data-generator-runtime/src/main/java/it/cavallium/data/generator/nativedata/ArrayStringSerializer.java +++ b/data-generator-runtime/src/main/java/it/cavallium/data/generator/nativedata/ArrayStringSerializer.java @@ -3,6 +3,8 @@ package it.cavallium.data.generator.nativedata; import it.cavallium.data.generator.DataSerializer; import it.cavallium.stream.SafeDataInput; import it.cavallium.stream.SafeDataOutput; + +import java.nio.charset.StandardCharsets; import java.util.List; import org.jetbrains.annotations.NotNull; @@ -21,7 +23,7 @@ public class ArrayStringSerializer implements DataSerializer> { public List deserialize(SafeDataInput dataInput) { var data = new String[dataInput.readInt()]; for (int i = 0; i < data.length; i++) { - data[i] = dataInput.readUTF(); + data[i] = dataInput.readShortText(StandardCharsets.UTF_8); } return List.of(data); } diff --git a/data-generator-runtime/src/main/java/it/cavallium/data/generator/nativedata/NullableStringSerializer.java b/data-generator-runtime/src/main/java/it/cavallium/data/generator/nativedata/NullableStringSerializer.java index 2d8a845..e6cf45c 100644 --- a/data-generator-runtime/src/main/java/it/cavallium/data/generator/nativedata/NullableStringSerializer.java +++ b/data-generator-runtime/src/main/java/it/cavallium/data/generator/nativedata/NullableStringSerializer.java @@ -5,6 +5,8 @@ import it.cavallium.stream.SafeDataInput; import it.cavallium.stream.SafeDataOutput; import org.jetbrains.annotations.NotNull; +import java.nio.charset.StandardCharsets; + public class NullableStringSerializer implements DataSerializer { public static final NullableStringSerializer INSTANCE = new NullableStringSerializer(); @@ -27,7 +29,7 @@ public class NullableStringSerializer implements DataSerializer if (!isPresent) { return NullableString.empty(); } else { - return NullableString.of(dataInput.readUTF()); + return NullableString.of(dataInput.readShortText(StandardCharsets.UTF_8)); } } } diff --git a/data-generator-runtime/src/main/java/it/cavallium/stream/ByteBufferBackedInputStream.java b/data-generator-runtime/src/main/java/it/cavallium/stream/ByteBufferBackedInputStream.java deleted file mode 100644 index bde42a0..0000000 --- a/data-generator-runtime/src/main/java/it/cavallium/stream/ByteBufferBackedInputStream.java +++ /dev/null @@ -1,28 +0,0 @@ -package it.cavallium.stream; - -import java.io.InputStream; -import java.nio.ByteBuffer; -import org.jetbrains.annotations.NotNull; - -/** - * Simple {@link InputStream} implementation that exposes currently - * available content of a {@link ByteBuffer}. - */ -public class ByteBufferBackedInputStream extends InputStream { - protected final ByteBuffer _b; - - public ByteBufferBackedInputStream(ByteBuffer buf) { _b = buf; } - - @Override public int available() { return _b.remaining(); } - - @Override - public int read() { return _b.hasRemaining() ? (_b.get() & 0xFF) : -1; } - - @Override - public int read(byte @NotNull [] bytes, int off, int len) { - if (!_b.hasRemaining()) return -1; - len = Math.min(len, _b.remaining()); - _b.get(bytes, off, len); - return len; - } -} \ No newline at end of file diff --git a/data-generator-runtime/src/main/java/it/cavallium/stream/DataInputOutput.java b/data-generator-runtime/src/main/java/it/cavallium/stream/DataInputOutput.java deleted file mode 100644 index edd739f..0000000 --- a/data-generator-runtime/src/main/java/it/cavallium/stream/DataInputOutput.java +++ /dev/null @@ -1,11 +0,0 @@ -package it.cavallium.stream; - -import java.io.DataInput; -import java.io.DataOutput; - -public interface DataInputOutput extends DataInput, DataOutput { - - DataInput getIn(); - - DataOutput getOut(); -} diff --git a/data-generator-runtime/src/main/java/it/cavallium/stream/DataInputOutputImpl.java b/data-generator-runtime/src/main/java/it/cavallium/stream/DataInputOutputImpl.java deleted file mode 100644 index 91a4a6f..0000000 --- a/data-generator-runtime/src/main/java/it/cavallium/stream/DataInputOutputImpl.java +++ /dev/null @@ -1,173 +0,0 @@ -package it.cavallium.stream; - -import java.io.DataInput; -import java.io.DataOutput; -import java.io.IOException; -import org.jetbrains.annotations.NotNull; - -public class DataInputOutputImpl implements DataInputOutput { - - private final DataInput in; - private final DataOutput out; - - public DataInputOutputImpl(DataInput in, DataOutput out) { - this.in = in; - this.out = out; - } - - @Override - public DataInput getIn() { - return this; - } - - @Override - public DataOutput getOut() { - return this; - } - - @Override - public void readFully(byte @NotNull [] bytes) throws IOException { - in.readFully(bytes); - } - - @Override - public void readFully(byte @NotNull [] bytes, int i, int i1) throws IOException { - in.readFully(bytes, i, i1); - } - - @Override - public int skipBytes(int i) throws IOException { - return in.skipBytes(i); - } - - @Override - public boolean readBoolean() throws IOException { - return in.readBoolean(); - } - - @Override - public byte readByte() throws IOException { - return in.readByte(); - } - - @Override - public int readUnsignedByte() throws IOException { - return in.readUnsignedByte(); - } - - @Override - public short readShort() throws IOException { - return in.readShort(); - } - - @Override - public int readUnsignedShort() throws IOException { - return in.readUnsignedShort(); - } - - @Override - public char readChar() throws IOException { - return in.readChar(); - } - - @Override - public int readInt() throws IOException { - return in.readInt(); - } - - @Override - public long readLong() throws IOException { - return in.readLong(); - } - - @Override - public float readFloat() throws IOException { - return in.readFloat(); - } - - @Override - public double readDouble() throws IOException { - return in.readDouble(); - } - - @Override - public String readLine() throws IOException { - return in.readLine(); - } - - @NotNull - @Override - public String readUTF() throws IOException { - return in.readUTF(); - } - - @Override - public void write(int i) throws IOException { - out.write(i); - } - - @Override - public void write(byte @NotNull [] bytes) throws IOException { - out.write(bytes); - } - - @Override - public void write(byte @NotNull [] bytes, int i, int i1) throws IOException { - out.write(bytes, i, i1); - } - - @Override - public void writeBoolean(boolean b) throws IOException { - out.writeBoolean(b); - } - - @Override - public void writeByte(int i) throws IOException { - out.writeByte(i); - } - - @Override - public void writeShort(int i) throws IOException { - out.writeShort(i); - } - - @Override - public void writeChar(int i) throws IOException { - out.writeChar(i); - } - - @Override - public void writeInt(int i) throws IOException { - out.writeInt(i); - } - - @Override - public void writeLong(long l) throws IOException { - out.writeLong(l); - } - - @Override - public void writeFloat(float v) throws IOException { - out.writeFloat(v); - } - - @Override - public void writeDouble(double v) throws IOException { - out.writeDouble(v); - } - - @Override - public void writeBytes(@NotNull String s) throws IOException { - out.writeBytes(s); - } - - @Override - public void writeChars(@NotNull String s) throws IOException { - out.writeChars(s); - } - - @Override - public void writeUTF(@NotNull String s) throws IOException { - out.writeUTF(s); - } -} diff --git a/data-generator-runtime/src/main/java/it/cavallium/stream/DataInputOutputStream.java b/data-generator-runtime/src/main/java/it/cavallium/stream/DataInputOutputStream.java deleted file mode 100644 index be04b8c..0000000 --- a/data-generator-runtime/src/main/java/it/cavallium/stream/DataInputOutputStream.java +++ /dev/null @@ -1,103 +0,0 @@ -package it.cavallium.stream; - -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import org.jetbrains.annotations.NotNull; - -public class DataInputOutputStream extends DataOutputStream implements DataInputOutput { - - private final DataInputStream in; - - public DataInputOutputStream(DataInputStream in, DataOutputStream out) { - super(out); - this.in = in; - } - - @Override - public DataInputStream getIn() { - return in; - } - - @Override - public DataOutputStream getOut() { - return this; - } - - @Override - public void readFully(byte @NotNull [] bytes) throws IOException { - in.readFully(bytes); - } - - @Override - public void readFully(byte @NotNull [] bytes, int i, int i1) throws IOException { - in.readFully(bytes, i, i1); - } - - @Override - public int skipBytes(int i) throws IOException { - return in.skipBytes(i); - } - - @Override - public boolean readBoolean() throws IOException { - return in.readBoolean(); - } - - @Override - public byte readByte() throws IOException { - return in.readByte(); - } - - @Override - public int readUnsignedByte() throws IOException { - return in.readUnsignedByte(); - } - - @Override - public short readShort() throws IOException { - return in.readShort(); - } - - @Override - public int readUnsignedShort() throws IOException { - return in.readUnsignedShort(); - } - - @Override - public char readChar() throws IOException { - return in.readChar(); - } - - @Override - public int readInt() throws IOException { - return in.readInt(); - } - - @Override - public long readLong() throws IOException { - return in.readLong(); - } - - @Override - public float readFloat() throws IOException { - return in.readFloat(); - } - - @Override - public double readDouble() throws IOException { - return in.readDouble(); - } - - @Deprecated - @Override - public String readLine() throws IOException { - return in.readLine(); - } - - @NotNull - @Override - public String readUTF() throws IOException { - return in.readUTF(); - } -} diff --git a/data-generator-runtime/src/main/java/it/cavallium/stream/SafeByteArrayInputStream.java b/data-generator-runtime/src/main/java/it/cavallium/stream/SafeByteArrayInputStream.java index c217c57..0d4229b 100644 --- a/data-generator-runtime/src/main/java/it/cavallium/stream/SafeByteArrayInputStream.java +++ b/data-generator-runtime/src/main/java/it/cavallium/stream/SafeByteArrayInputStream.java @@ -31,13 +31,13 @@ import java.nio.charset.Charset; public class SafeByteArrayInputStream extends SafeMeasurableInputStream implements SafeRepositionableStream { /** The array backing the input stream. */ - public byte[] array; + public final byte[] array; /** The first valid entry. */ - public int offset; + public final int offset; /** The number of valid bytes in {@link #array} starting from {@link #offset}. */ - public int length; + public final int length; /** The current position as a distance from {@link #offset}. */ private int position; @@ -81,6 +81,9 @@ public class SafeByteArrayInputStream extends SafeMeasurableInputStream implemen @Override public void mark(final int dummy) { + if (dummy < 0) { + throw new IllegalArgumentException(); + } mark = position; } @@ -113,7 +116,7 @@ public class SafeByteArrayInputStream extends SafeMeasurableInputStream implemen */ @Override - public int read(final byte b[], final int offset, final int length) { + public int read(final byte[] b, final int offset, final int length) { if (this.length == this.position) return length == 0 ? 0 : -1; final int n = Math.min(length, this.length - this.position); System.arraycopy(array, this.offset + this.position, b, offset, n); diff --git a/data-generator-runtime/src/main/java/it/cavallium/stream/SafeByteArrayOutputStream.java b/data-generator-runtime/src/main/java/it/cavallium/stream/SafeByteArrayOutputStream.java index 0e5464a..803c4b3 100644 --- a/data-generator-runtime/src/main/java/it/cavallium/stream/SafeByteArrayOutputStream.java +++ b/data-generator-runtime/src/main/java/it/cavallium/stream/SafeByteArrayOutputStream.java @@ -16,9 +16,15 @@ package it.cavallium.stream; -import it.unimi.dsi.fastutil.Arrays; +import it.cavallium.buffer.IgnoreCoverage; import it.unimi.dsi.fastutil.bytes.ByteArrays; +import java.util.Arrays; +import java.util.HexFormat; +import java.util.Objects; + +import static java.util.Objects.checkFromToIndex; + /** Simple, fast byte-array output stream that exposes the backing array. * *

{@link java.io.ByteArrayOutputStream} is nice, but to get its content you @@ -36,9 +42,13 @@ public class SafeByteArrayOutputStream extends SafeMeasurableOutputStream implem /** The array backing the output stream. */ public static final int DEFAULT_INITIAL_CAPACITY = 16; + private static final HexFormat HEX = HexFormat.of(); + private static final int MAX_PREVIEW_LENGTH = 128; private final boolean wrapped; private final int initialPosition; private final int initialLength; + private final int wrappedFrom; + private final int wrappedTo; /** The array backing the output stream. */ public byte[] array; @@ -47,7 +57,7 @@ public class SafeByteArrayOutputStream extends SafeMeasurableOutputStream implem public int length; /** The current writing position. */ - private int position; + private int arrayPosition; /** Creates a new array output stream with an initial capacity of {@link #DEFAULT_INITIAL_CAPACITY} bytes. */ public SafeByteArrayOutputStream() { @@ -61,19 +71,17 @@ public class SafeByteArrayOutputStream extends SafeMeasurableOutputStream implem public SafeByteArrayOutputStream(final int initialCapacity) { array = new byte[initialCapacity]; wrapped = false; - initialPosition = 0; - initialLength = 0; + initialPosition = wrappedFrom = length = initialLength = 0; + wrappedTo = Integer.MAX_VALUE; } /** Creates a new array output stream wrapping a given byte array. * * @param a the byte array to wrap. */ + @IgnoreCoverage public SafeByteArrayOutputStream(final byte[] a) { - array = a; - wrapped = true; - initialPosition = 0; - initialLength = a.length; + this(a, 0, a.length); } /** Creates a new array output stream wrapping a given byte array. @@ -81,19 +89,22 @@ public class SafeByteArrayOutputStream extends SafeMeasurableOutputStream implem * @param a the byte array to wrap. */ public SafeByteArrayOutputStream(final byte[] a, int from, int to) { - Arrays.ensureFromTo(a.length, from, to); + checkFromToIndex(from, to, a.length); wrapped = true; array = a; - initialPosition = from; - initialLength = to; - position = from; - length = to - from; + initialPosition = wrappedFrom = arrayPosition = from; + initialLength = length = to - from; + wrappedTo = to; + } + + private void ensureWrappedBounds(int fromArrayPosition, int toArrayPosition) { + Objects.checkFromToIndex(fromArrayPosition - wrappedFrom, toArrayPosition - wrappedFrom, wrappedTo - wrappedFrom); } /** Marks this array output stream as empty. */ public void reset() { length = initialLength; - position = initialPosition; + arrayPosition = initialPosition; } /** Ensures that the length of the backing array is equal to {@link #length}. */ @@ -109,43 +120,55 @@ public class SafeByteArrayOutputStream extends SafeMeasurableOutputStream implem @Override public void write(final int b) { - if (position >= array.length) { - if (wrapped) { - throw new ArrayIndexOutOfBoundsException(position); - } else { - array = ByteArrays.grow(array, position + 1, length); - } + if (wrapped) { + ensureWrappedBounds(arrayPosition, arrayPosition + 1); + } else if (arrayPosition >= array.length) { + array = ByteArrays.grow(array, arrayPosition + 1, length); } - array[position++] = (byte)b; - if (length < position) length = position; + array[arrayPosition++] = (byte)b; + if (length < arrayPosition) length = arrayPosition; } @Override public void write(final byte[] b, final int off, final int len) { - ByteArrays.ensureOffsetLength(b, off, len); + if (wrapped) { + ensureWrappedBounds(arrayPosition, arrayPosition + len); + } + Objects.checkFromIndexSize(off, len, b.length); growBy(len); - System.arraycopy(b, off, array, position, len); - if (position + len > length) length = position += len; + System.arraycopy(b, off, array, arrayPosition, len); + if (arrayPosition + len > length) length = arrayPosition += len; } private void growBy(int len) { - if (position + len > array.length) { + if (wrapped) { + ensureWrappedBounds(arrayPosition, arrayPosition + len); + } + if (arrayPosition + len > array.length) { if (wrapped) { - throw new ArrayIndexOutOfBoundsException(position + len - 1); + throw new ArrayIndexOutOfBoundsException(arrayPosition + len - 1); } else { - array = ByteArrays.grow(array, position + len, position); + array = ByteArrays.grow(array, arrayPosition + len, arrayPosition); } } } @Override public void position(final long newPosition) { - position = (int)newPosition; + if (wrapped) { + arrayPosition = (int) (newPosition + wrappedFrom); + } else { + arrayPosition = (int)newPosition; + } } @Override public long position() { - return position; + if (wrapped) { + return arrayPosition - wrappedFrom; + } else { + return arrayPosition; + } } @Override @@ -157,6 +180,31 @@ public class SafeByteArrayOutputStream extends SafeMeasurableOutputStream implem * This method copies the array */ public byte[] toByteArray() { - return java.util.Arrays.copyOf(array, length); + if (wrapped) { + return Arrays.copyOfRange(array, wrappedFrom, wrappedTo); + } else { + return java.util.Arrays.copyOf(array, length); + } + } + + @Override + public String toString() { + return "SafeByteArrayOutputStream[" + toHexPreview() + "]"; + } + + private String toHexPreview() { + int len; + int from; + String prefix; + if (wrapped) { + prefix = "(wrapped from " + wrappedFrom + " to " + wrappedTo + ") "; + from = wrappedFrom; + len = wrappedTo - wrappedFrom; + } else { + prefix = ""; + from = 0; + len = length; + } + return prefix + HEX.formatHex(this.array, from, (Math.min(len, MAX_PREVIEW_LENGTH) + from)) + ((len > MAX_PREVIEW_LENGTH) ? "..." : ""); } } diff --git a/data-generator-runtime/src/main/java/it/cavallium/stream/SafeDataInput.java b/data-generator-runtime/src/main/java/it/cavallium/stream/SafeDataInput.java index 7619e88..44c1aa7 100644 --- a/data-generator-runtime/src/main/java/it/cavallium/stream/SafeDataInput.java +++ b/data-generator-runtime/src/main/java/it/cavallium/stream/SafeDataInput.java @@ -2,6 +2,8 @@ package it.cavallium.stream; import java.io.Closeable; import java.io.DataInput; +import java.nio.charset.Charset; + import org.jetbrains.annotations.NotNull; /** @@ -142,5 +144,10 @@ public interface SafeDataInput extends Closeable, DataInput { @Deprecated String readLine(); + @Deprecated @NotNull String readUTF(); + + @NotNull String readShortText(Charset charset); + + @NotNull String readMediumText(Charset charset); } diff --git a/data-generator-runtime/src/main/java/it/cavallium/stream/SafeDataInputStream.java b/data-generator-runtime/src/main/java/it/cavallium/stream/SafeDataInputStream.java index 17af811..ed955bf 100644 --- a/data-generator-runtime/src/main/java/it/cavallium/stream/SafeDataInputStream.java +++ b/data-generator-runtime/src/main/java/it/cavallium/stream/SafeDataInputStream.java @@ -27,6 +27,8 @@ package it.cavallium.stream; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; + +import it.cavallium.buffer.IgnoreCoverage; import org.jetbrains.annotations.NotNull; public class SafeDataInputStream extends SafeFilterInputStream implements SafeDataInput { @@ -41,21 +43,11 @@ public class SafeDataInputStream extends SafeFilterInputStream implements SafeDa super(in); } - /** - * working data initialized on demand by readUTF - */ - private byte[] bytearr; - @Override - public final int read(byte[] b) { + public final int read(byte @NotNull[] b) { return in.read(b, 0, b.length); } - @Override - public final int read(byte[] b, int off, int len) { - return in.read(b, off, len); - } - /** * See the general contract of the {@code readFully} * method of {@code DataInput}. @@ -125,11 +117,6 @@ public class SafeDataInputStream extends SafeFilterInputStream implements SafeDa return total; } - @Override - public String readString(int length, Charset charset) { - return in.readString(length, charset); - } - /** * See the general contract of the {@code readBoolean} * method of {@code DataInput}. @@ -349,78 +336,11 @@ public class SafeDataInputStream extends SafeFilterInputStream implements SafeDa return Double.longBitsToDouble(readLong()); } - private char[] lineBuffer; - - /** - * See the general contract of the {@code readLine} - * method of {@code DataInput}. - *

- * Bytes - * for this operation are read from the contained - * input stream. - * - * @deprecated This method does not properly convert bytes to characters. - * As of JDK 1.1, the preferred way to read lines of text is via the - * {@code BufferedReader.readLine()} method. Programs that use the - * {@code DataInputStream} class to read lines can be converted to use - * the {@code BufferedReader} class by replacing code of the form: - *

-	 *     DataInputStream d = new DataInputStream(in);
-	 * 
- * with: - *
-	 *     BufferedReader d
-	 *          = new BufferedReader(new InputStreamReader(in));
-	 * 
- * - * @return the next line of text from this input stream. - * @see java.io.BufferedReader#readLine() - * @see SafeFilterInputStream#in - */ + @IgnoreCoverage @Override @Deprecated public final String readLine() { - char[] buf = lineBuffer; - - if (buf == null) { - buf = lineBuffer = new char[128]; - } - - int room = buf.length; - int offset = 0; - int c; - - loop: while (true) { - switch (c = in.read()) { - case -1: - case '\n': - break loop; - - case '\r': - int c2 = in.read(); - if ((c2 != '\n') && (c2 != -1)) { - if (!(in instanceof SafePushbackInputStream)) { - this.in = new SafePushbackInputStream(in); - } - ((SafePushbackInputStream)in).unread(c2); - } - break loop; - - default: - if (--room < 0) { - buf = new char[offset + 128]; - room = buf.length - offset - 1; - System.arraycopy(lineBuffer, 0, buf, 0, offset); - lineBuffer = buf; - } - buf[offset++] = (char) c; - break; - } - } - if ((c == -1) && (offset == 0)) { - return null; - } - return String.copyValueOf(buf, 0, offset); + throw new UnsupportedOperationException(); } /** @@ -432,40 +352,25 @@ public class SafeDataInputStream extends SafeFilterInputStream implements SafeDa * input stream. * * @return a Unicode string. - * @see SafeDataInputStream#readUTF(SafeDataInputStream) */ + @Deprecated + @IgnoreCoverage @Override public @NotNull String readUTF() { - return readUTF(this); + return readShortText(StandardCharsets.UTF_8); } - /** - * Reads from the - * stream {@code in} a representation - * of a Unicode character string encoded in - * modified UTF-8 format; - * this string of characters is then returned as a {@code String}. - * The details of the modified UTF-8 representation - * are exactly the same as for the {@code readUTF} - * method of {@code DataInput}. - * - * @param in a data input stream. - * @return a Unicode string. - * @see SafeDataInputStream#readUnsignedShort() - */ - public static String readUTF(SafeDataInputStream in) { - int utflen = in.readUnsignedShort(); - byte[] data; - if (utflen <= 80) { - if (in.bytearr == null) { - data = in.bytearr = new byte[80]; - } else { - data = in.bytearr; - } - } else { - data = new byte[utflen]; - } - in.readFully(data, 0, utflen); - return new String(data, 0, utflen, StandardCharsets.UTF_8); + @IgnoreCoverage + @Override + public @NotNull String readShortText(Charset charset) { + int utfLength = this.readUnsignedShort(); + return in.readString(utfLength, charset); + } + + @IgnoreCoverage + @Override + public @NotNull String readMediumText(Charset charset) { + int utfLength = this.readInt(); + return in.readString(utfLength, charset); } } diff --git a/data-generator-runtime/src/main/java/it/cavallium/stream/SafeDataOutput.java b/data-generator-runtime/src/main/java/it/cavallium/stream/SafeDataOutput.java index 870f9cd..d92dddb 100644 --- a/data-generator-runtime/src/main/java/it/cavallium/stream/SafeDataOutput.java +++ b/data-generator-runtime/src/main/java/it/cavallium/stream/SafeDataOutput.java @@ -25,6 +25,10 @@ package it.cavallium.stream; +import org.jetbrains.annotations.NotNull; + +import java.nio.charset.Charset; + /** * The {@code SafeDataOutput} interface provides * for converting data from any of the Java @@ -69,7 +73,7 @@ public interface SafeDataOutput { * * @param b the data. */ - void write(byte b[]); + void write(byte[] b); /** * Writes {@code len} bytes from array @@ -90,7 +94,7 @@ public interface SafeDataOutput { * @param off the start offset in the data. * @param len the number of bytes to write. */ - void write(byte b[], int off, int len); + void write(byte[] b, int off, int len); /** * Writes a {@code boolean} value to this output stream. @@ -359,5 +363,10 @@ public interface SafeDataOutput { * * @param s the string value to be written. */ + @Deprecated void writeUTF(String s); + + void writeShortText(String s, Charset charset); + + void writeMediumText(String s, Charset charset); } diff --git a/data-generator-runtime/src/main/java/it/cavallium/stream/SafeDataOutputStream.java b/data-generator-runtime/src/main/java/it/cavallium/stream/SafeDataOutputStream.java index 48eedf0..b351b82 100644 --- a/data-generator-runtime/src/main/java/it/cavallium/stream/SafeDataOutputStream.java +++ b/data-generator-runtime/src/main/java/it/cavallium/stream/SafeDataOutputStream.java @@ -26,6 +26,7 @@ package it.cavallium.stream; import java.io.DataOutputStream; +import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; /** @@ -44,11 +45,6 @@ public class SafeDataOutputStream extends SafeFilterOutputStream implements Safe */ protected int written; - /** - * bytearr is initialized on demand by writeUTF - */ - private byte[] bytearr = null; - /** * Creates a new data output stream to write data to the specified * underlying output stream. The counter {@code written} is @@ -326,8 +322,14 @@ public class SafeDataOutputStream extends SafeFilterOutputStream implements Safe * @param str a string to be written. * @see #writeChars(String) */ + @Deprecated public final void writeUTF(String str) { - var outString = str.getBytes(StandardCharsets.UTF_8); + writeShortText(str, StandardCharsets.UTF_8); + } + + @Override + public void writeShortText(String s, Charset charset) { + var outString = s.getBytes(charset); if (outString.length > Short.MAX_VALUE) { throw new IndexOutOfBoundsException("String too long: " + outString.length + " bytes"); } @@ -335,7 +337,19 @@ public class SafeDataOutputStream extends SafeFilterOutputStream implements Safe out.write((v >>> 8) & 0xFF); out.write((v) & 0xFF); out.write(outString); - incCount(2 + outString.length); + incCount(Short.BYTES + outString.length); + } + + @Override + public void writeMediumText(String s, Charset charset) { + var outString = s.getBytes(charset); + var v = outString.length; + out.write((v >>> 24) & 0xFF); + out.write((v >>> 16) & 0xFF); + out.write((v >>> 8) & 0xFF); + out.write((v) & 0xFF); + out.write(outString); + incCount(Integer.BYTES + outString.length); } /** diff --git a/data-generator-runtime/src/main/java/it/cavallium/stream/SafeFilterInputStream.java b/data-generator-runtime/src/main/java/it/cavallium/stream/SafeFilterInputStream.java index 1398e11..c2cee74 100644 --- a/data-generator-runtime/src/main/java/it/cavallium/stream/SafeFilterInputStream.java +++ b/data-generator-runtime/src/main/java/it/cavallium/stream/SafeFilterInputStream.java @@ -1,5 +1,8 @@ package it.cavallium.stream; +import it.cavallium.buffer.IgnoreCoverage; +import org.jetbrains.annotations.NotNull; + import java.nio.charset.Charset; /** @@ -23,7 +26,7 @@ public class SafeFilterInputStream extends SafeInputStream { /** * The input stream to be filtered. */ - protected volatile SafeInputStream in; + protected final SafeInputStream in; /** * Creates a {@code FilterInputStream} @@ -54,6 +57,7 @@ public class SafeFilterInputStream extends SafeInputStream { * stream is reached. * @see SafeFilterInputStream#in */ + @IgnoreCoverage public int read() { return in.read(); } @@ -77,7 +81,8 @@ public class SafeFilterInputStream extends SafeInputStream { * the stream has been reached. * @see SafeFilterInputStream#read(byte[], int, int) */ - public int read(byte b[]) { + @IgnoreCoverage + public int read(byte @NotNull [] b) { return read(b, 0, b.length); } @@ -102,7 +107,8 @@ public class SafeFilterInputStream extends SafeInputStream { * {@code b.length - off} * @see SafeFilterInputStream#in */ - public int read(byte b[], int off, int len) { + @IgnoreCoverage + public final int read(byte[] b, int off, int len) { return in.read(b, off, len); } @@ -118,7 +124,8 @@ public class SafeFilterInputStream extends SafeInputStream { * @param n the number of bytes to be skipped. * @return the actual number of bytes skipped. */ - public long skip(long n) { + @IgnoreCoverage + public final long skip(long n) { return in.skip(n); } @@ -134,7 +141,8 @@ public class SafeFilterInputStream extends SafeInputStream { * @return an estimate of the number of bytes that can be read (or skipped * over) from this input stream without blocking. */ - public int available() { + @IgnoreCoverage + public final int available() { return in.available(); } @@ -146,6 +154,7 @@ public class SafeFilterInputStream extends SafeInputStream { * * @see SafeFilterInputStream#in */ + @IgnoreCoverage public void close() { in.close(); } @@ -166,6 +175,7 @@ public class SafeFilterInputStream extends SafeInputStream { * @see SafeFilterInputStream#in * @see SafeFilterInputStream#reset() */ + @IgnoreCoverage public void mark(int readlimit) { in.mark(readlimit); } @@ -189,6 +199,7 @@ public class SafeFilterInputStream extends SafeInputStream { * @see SafeFilterInputStream#in * @see SafeFilterInputStream#mark(int) */ + @IgnoreCoverage public void reset() { in.reset(); } @@ -206,10 +217,12 @@ public class SafeFilterInputStream extends SafeInputStream { * @see java.io.InputStream#mark(int) * @see java.io.InputStream#reset() */ + @IgnoreCoverage public boolean markSupported() { return in.markSupported(); } + @IgnoreCoverage @Override public String readString(int length, Charset charset) { return in.readString(length, charset); diff --git a/data-generator-runtime/src/main/java/it/cavallium/stream/SafeInputStream.java b/data-generator-runtime/src/main/java/it/cavallium/stream/SafeInputStream.java index c8c306b..e802d97 100644 --- a/data-generator-runtime/src/main/java/it/cavallium/stream/SafeInputStream.java +++ b/data-generator-runtime/src/main/java/it/cavallium/stream/SafeInputStream.java @@ -1,5 +1,8 @@ package it.cavallium.stream; +import it.cavallium.buffer.IgnoreCoverage; +import org.jetbrains.annotations.NotNull; + import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -20,11 +23,12 @@ public abstract class SafeInputStream extends InputStream { @Override public abstract int read(); - public int read(byte b[]) { + public int read(byte @NotNull [] b) { return read(b, 0, b.length); } - public int read(byte b[], int off, int len) { + @IgnoreCoverage + public int read(byte[] b, int off, int len) { Objects.checkFromIndexSize(off, len, b.length); if (len == 0) { return 0; @@ -34,15 +38,16 @@ public abstract class SafeInputStream extends InputStream { if (c == -1) { return -1; } - b[off] = (byte)c; + b[off] = (byte) c; int i = 1; - for (; i < len ; i++) { + while (i < len) { c = read(); if (c == -1) { break; } - b[off + i] = (byte)c; + b[off + i] = (byte) c; + i++; } return i; } @@ -53,47 +58,49 @@ public abstract class SafeInputStream extends InputStream { private static final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8; + @IgnoreCoverage public byte[] readNBytes(int len) { if (len < 0) { throw new IllegalArgumentException("len < 0"); } - List bufs = null; + List buffers = null; byte[] result = null; int total = 0; int remaining = len; int n; + //noinspection NonStrictComparisonCanBeEquality do { byte[] buf = new byte[Math.min(remaining, DEFAULT_BUFFER_SIZE)]; - int nread = 0; + int numRead = 0; // read to EOF which may read more or less than buffer size - while ((n = read(buf, nread, - Math.min(buf.length - nread, remaining))) > 0) { - nread += n; + while ((n = read(buf, numRead, + Math.min(buf.length - numRead, remaining))) > 0) { + numRead += n; remaining -= n; } - if (nread > 0) { - if (MAX_BUFFER_SIZE - total < nread) { + if (numRead > 0) { + if (MAX_BUFFER_SIZE - total < numRead) { throw new OutOfMemoryError("Required array size too large"); } - total += nread; + total += numRead; if (result == null) { result = buf; } else { - if (bufs == null) { - bufs = new ArrayList<>(); - bufs.add(result); + if (buffers == null) { + buffers = new ArrayList<>(); + buffers.add(result); } - bufs.add(buf); + buffers.add(buf); } } // if the last call to read returned -1 or the number of bytes // requested have been read then break } while (n >= 0 && remaining > 0); - if (bufs == null) { + if (buffers == null) { if (result == null) { return new byte[0]; } @@ -104,7 +111,7 @@ public abstract class SafeInputStream extends InputStream { result = new byte[total]; int offset = 0; remaining = total; - for (byte[] b : bufs) { + for (byte[] b : buffers) { int count = Math.min(b.length, remaining); System.arraycopy(b, 0, result, offset, count); offset += count; @@ -155,6 +162,7 @@ public abstract class SafeInputStream extends InputStream { public void skipNBytes(long n) { if (n > 0) { long ns = skip(n); + //noinspection ConstantValue if (ns >= 0 && ns < n) { // skipped too few bytes // adjust number to skip n -= ns; @@ -162,7 +170,7 @@ public abstract class SafeInputStream extends InputStream { while (n > 0 && read() != -1) { n--; } - // if not enough skipped, then EOFE + // if not enough skipped, then EOF E if (n != 0) { throw new IndexOutOfBoundsException(); } @@ -172,21 +180,18 @@ public abstract class SafeInputStream extends InputStream { } } + @IgnoreCoverage public int available() { return 0; } - + + @IgnoreCoverage public void close() {} - public void mark(int readlimit) {} - + @IgnoreCoverage public void reset() { throw new UnsupportedOperationException("mark/reset not supported"); } - - public boolean markSupported() { - return false; - } public long transferTo(OutputStream out) { Objects.requireNonNull(out, "out"); diff --git a/data-generator-runtime/src/main/java/it/cavallium/stream/SafePushbackInputStream.java b/data-generator-runtime/src/main/java/it/cavallium/stream/SafePushbackInputStream.java deleted file mode 100644 index e83036b..0000000 --- a/data-generator-runtime/src/main/java/it/cavallium/stream/SafePushbackInputStream.java +++ /dev/null @@ -1,332 +0,0 @@ -package it.cavallium.stream; - -/** - * A {@code PushbackInputStream} adds - * functionality to another input stream, namely - * the ability to "push back" or "unread" bytes, - * by storing pushed-back bytes in an internal buffer. - * This is useful in situations where - * it is convenient for a fragment of code - * to read an indefinite number of data bytes - * that are delimited by a particular byte - * value; after reading the terminating byte, - * the code fragment can "unread" it, so that - * the next read operation on the input stream - * will reread the byte that was pushed back. - * For example, bytes representing the characters - * constituting an identifier might be terminated - * by a byte representing an operator character; - * a method whose job is to read just an identifier - * can read until it sees the operator and - * then push the operator back to be re-read. - * - * @author David Connelly - * @author Jonathan Payne - * @since 1.0 - */ -public class SafePushbackInputStream extends SafeFilterInputStream { - /** - * The pushback buffer. - * @since 1.1 - */ - protected byte[] buf; - - /** - * The position within the pushback buffer from which the next byte will - * be read. When the buffer is empty, {@code pos} is equal to - * {@code buf.length}; when the buffer is full, {@code pos} is - * equal to zero. - * - * @since 1.1 - */ - protected int pos; - - /** - * Check to make sure that this stream has not been closed - */ - private void ensureOpen() { - if (in == null) - throw new IllegalStateException("Stream closed"); - } - - /** - * Creates a {@code PushbackInputStream} - * with a pushback buffer of the specified {@code size}, - * and saves its argument, the input stream - * {@code in}, for later use. Initially, - * the pushback buffer is empty. - * - * @param in the input stream from which bytes will be read. - * @param size the size of the pushback buffer. - * @throws IllegalArgumentException if {@code size <= 0} - * @since 1.1 - */ - public SafePushbackInputStream(SafeInputStream in, int size) { - super(in); - if (size <= 0) { - throw new IllegalArgumentException("size <= 0"); - } - this.buf = new byte[size]; - this.pos = size; - } - - /** - * Creates a {@code PushbackInputStream} - * with a 1-byte pushback buffer, and saves its argument, the input stream - * {@code in}, for later use. Initially, - * the pushback buffer is empty. - * - * @param in the input stream from which bytes will be read. - */ - public SafePushbackInputStream(SafeInputStream in) { - this(in, 1); - } - - /** - * Reads the next byte of data from this input stream. The value - * byte is returned as an {@code int} in the range - * {@code 0} to {@code 255}. If no byte is available - * because the end of the stream has been reached, the value - * {@code -1} is returned. This method blocks until input data - * is available, the end of the stream is detected, or an exception - * is thrown. - * - *

This method returns the most recently pushed-back byte, if there is - * one, and otherwise calls the {@code read} method of its underlying - * input stream and returns whatever value that method returns. - * - * @return the next byte of data, or {@code -1} if the end of the - * stream has been reached. - * or an I/O error occurs. - * @see java.io.InputStream#read() - */ - public int read() { - ensureOpen(); - if (pos < buf.length) { - return buf[pos++] & 0xff; - } - return super.read(); - } - - /** - * Reads up to {@code len} bytes of data from this input stream into - * an array of bytes. This method first reads any pushed-back bytes; after - * that, if fewer than {@code len} bytes have been read then it - * reads from the underlying input stream. If {@code len} is not zero, the method - * blocks until at least 1 byte of input is available; otherwise, no - * bytes are read and {@code 0} is returned. - * - * @param b the buffer into which the data is read. - * @param off the start offset in the destination array {@code b} - * @param len the maximum number of bytes read. - * @return the total number of bytes read into the buffer, or - * {@code -1} if there is no more data because the end of - * the stream has been reached. - * @throws NullPointerException If {@code b} is {@code null}. - * @throws IndexOutOfBoundsException If {@code off} is negative, - * {@code len} is negative, or {@code len} is greater than - * {@code b.length - off} - * or an I/O error occurs. - * @see java.io.InputStream#read(byte[], int, int) - */ - public int read(byte[] b, int off, int len) { - ensureOpen(); - if (b == null) { - throw new NullPointerException(); - } else if (off < 0 || len < 0 || len > b.length - off) { - throw new IndexOutOfBoundsException(); - } else if (len == 0) { - return 0; - } - - int avail = buf.length - pos; - if (avail > 0) { - if (len < avail) { - avail = len; - } - System.arraycopy(buf, pos, b, off, avail); - pos += avail; - off += avail; - len -= avail; - } - if (len > 0) { - len = super.read(b, off, len); - if (len == -1) { - return avail == 0 ? -1 : avail; - } - return avail + len; - } - return avail; - } - - /** - * Pushes back a byte by copying it to the front of the pushback buffer. - * After this method returns, the next byte to be read will have the value - * {@code (byte)b}. - * - * @param b the {@code int} value whose low-order - * byte is to be pushed back. - */ - public void unread(int b) { - ensureOpen(); - if (pos == 0) { - throw new IllegalStateException("Push back buffer is full"); - } - buf[--pos] = (byte)b; - } - - /** - * Pushes back a portion of an array of bytes by copying it to the front - * of the pushback buffer. After this method returns, the next byte to be - * read will have the value {@code b[off]}, the byte after that will - * have the value {@code b[off+1]}, and so forth. - * - * @param b the byte array to push back. - * @param off the start offset of the data. - * @param len the number of bytes to push back. - * @throws NullPointerException If {@code b} is {@code null}. - * @since 1.1 - */ - public void unread(byte[] b, int off, int len) { - ensureOpen(); - if (len > pos) { - throw new IllegalStateException("Push back buffer is full"); - } - pos -= len; - System.arraycopy(b, off, buf, pos, len); - } - - /** - * Pushes back an array of bytes by copying it to the front of the - * pushback buffer. After this method returns, the next byte to be read - * will have the value {@code b[0]}, the byte after that will have the - * value {@code b[1]}, and so forth. - * - * @param b the byte array to push back - * @throws NullPointerException If {@code b} is {@code null}. - * @since 1.1 - */ - public void unread(byte[] b) { - unread(b, 0, b.length); - } - - /** - * Returns an estimate of the number of bytes that can be read (or - * skipped over) from this input stream without blocking by the next - * invocation of a method for this input stream. The next invocation might be - * the same thread or another thread. A single read or skip of this - * many bytes will not block, but may read or skip fewer bytes. - * - *

The method returns the sum of the number of bytes that have been - * pushed back and the value returned by {@link - * SafeFilterInputStream#available available}. - * - * @return the number of bytes that can be read (or skipped over) from - * the input stream without blocking. - * @see SafeFilterInputStream#in - * @see java.io.InputStream#available() - */ - public int available() { - ensureOpen(); - int n = buf.length - pos; - int avail = super.available(); - return n > (Integer.MAX_VALUE - avail) - ? Integer.MAX_VALUE - : n + avail; - } - - /** - * Skips over and discards {@code n} bytes of data from this - * input stream. The {@code skip} method may, for a variety of - * reasons, end up skipping over some smaller number of bytes, - * possibly zero. If {@code n} is negative, no bytes are skipped. - * - *

The {@code skip} method of {@code PushbackInputStream} - * first skips over the bytes in the pushback buffer, if any. It then - * calls the {@code skip} method of the underlying input stream if - * more bytes need to be skipped. The actual number of bytes skipped - * is returned. - * - * @param n {@inheritDoc} - * @return {@inheritDoc} - * @see SafeFilterInputStream#in - * @see java.io.InputStream#skip(long n) - * @since 1.2 - */ - public long skip(long n) { - ensureOpen(); - if (n <= 0) { - return 0; - } - - long pskip = buf.length - pos; - if (pskip > 0) { - if (n < pskip) { - pskip = n; - } - pos += pskip; - n -= pskip; - } - if (n > 0) { - pskip += super.skip(n); - } - return pskip; - } - - /** - * Tests if this input stream supports the {@code mark} and - * {@code reset} methods, which it does not. - * - * @return {@code false}, since this class does not support the - * {@code mark} and {@code reset} methods. - * @see java.io.InputStream#mark(int) - * @see java.io.InputStream#reset() - */ - public boolean markSupported() { - return false; - } - - /** - * Marks the current position in this input stream. - * - *

The {@code mark} method of {@code PushbackInputStream} - * does nothing. - * - * @param readlimit the maximum limit of bytes that can be read before - * the mark position becomes invalid. - * @see java.io.InputStream#reset() - */ - public void mark(int readlimit) { - } - - /** - * Repositions this stream to the position at the time the - * {@code mark} method was last called on this input stream. - * - *

The method {@code reset} for class - * {@code PushbackInputStream} does nothing except throw an - * {@code IOException}. - * - * @see java.io.InputStream#mark(int) - * @see java.io.IOException - */ - public void reset() { - throw new UnsupportedOperationException("mark/reset not supported"); - } - - /** - * Closes this input stream and releases any system resources - * associated with the stream. - * Once the stream has been closed, further read(), unread(), - * available(), reset(), or skip() invocations will throw an IOException. - * Closing a previously closed stream has no effect. - * - */ - public void close() { - if (in == null) - return; - in.close(); - in = null; - buf = null; - } -} 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 index a0a38a9..dabf0dd 100644 --- a/data-generator-runtime/src/test/java/it/cavallium/buffer/TestBuffer.java +++ b/data-generator-runtime/src/test/java/it/cavallium/buffer/TestBuffer.java @@ -1,22 +1,20 @@ package it.cavallium.buffer; -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; import com.google.common.primitives.Longs; +import it.cavallium.stream.SafeByteArrayInputStream; import it.cavallium.stream.SafeByteArrayOutputStream; -import it.unimi.dsi.fastutil.bytes.ByteArrayList; -import it.unimi.dsi.fastutil.bytes.ByteCollections; -import it.unimi.dsi.fastutil.bytes.ByteList; +import it.cavallium.stream.SafeDataOutputStream; +import it.unimi.dsi.fastutil.bytes.*; + import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; -import java.util.Arrays; -import java.util.HexFormat; -import java.util.List; +import java.util.*; +import java.util.concurrent.atomic.LongAdder; import java.util.stream.Stream; +import java.util.stream.StreamSupport; + import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; @@ -121,9 +119,10 @@ public class TestBuffer { public 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 sameSizeArgsBug = createPrimaryBufs().stream().filter(b -> b.initialSize > 0).map(bufArg -> new BufArg(bufArg.name + ".subList(0, same)", bufArg.b.subListForced(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(); + return Stream.concat(Stream.concat(Stream.concat(sameSizeArgs.stream(), sameSizeArgsBug.stream()), firstHalfArgs.stream()), lastHalfArgs.stream()).toList(); } @Test @@ -407,10 +406,147 @@ public class TestBuffer { } } + //todo: private void testBinaryOutputStream(SafeByteArrayOutputStream bos) { } + @ParameterizedTest + @MethodSource("provideBufs") + public void testBinaryInputStream(BufArg bufArg) { + testBinaryInputStream(bufArg.b.binaryInputStream()); + } + + //todo: + private void testBinaryInputStream(SafeByteArrayInputStream bis) { + + } + + @ParameterizedTest + @MethodSource("provideBufs") + public void testWriteTo(BufArg bufArg) { + try (var safeBaOs = new SafeByteArrayOutputStream()) { + try (var safeDaOs = new SafeDataOutputStream(safeBaOs)) { + bufArg.b.writeTo(safeDaOs); + } + assertArrayEquals(bufArg.initialContent, safeBaOs.toByteArray()); + } + } + + @ParameterizedTest + @MethodSource("provideBufs") + public void testEquals(BufArg bufArg) { + var b2 = Buf.copyOf(bufArg.initialContent); + testEquals(bufArg.b, b2); + testEquals(b2, bufArg.b); + } + + private void testEquals(Buf a, Buf b) { + assertEquals(a, b); + assertArrayEquals(a.toByteArray(), b.toByteArray()); + //noinspection SimplifiableAssertion + assertTrue(a.equals(b.subListForced(0, b.size()))); + //noinspection SimplifiableAssertion + assertTrue(a.equals(new ByteArrayList(b))); + //noinspection SimplifiableAssertion + assertTrue(a.equals(new ArrayList<>(b))); + assertTrue(a.equals(0, b, 0, a.size())); + assertTrue(a.equals(0, b.toByteArray(), 0, a.size())); + assertTrue(a.equals(0, b, 0, a.size() / 2)); + assertTrue(a.equals(0, b.toByteArray(), 0, a.size() / 2)); + if (a.size() > 5) { + assertTrue(a.equals(5, b, 5, a.size() - 5)); + assertTrue(a.equals(5, b.toByteArray(), 5, a.size() - 5)); + assertTrue(a.equals(5, b, 5, 0)); + assertTrue(a.equals(5, b.toByteArray(), 5, 0)); + assertFalse(a.equals(0, new byte[1], 100, 1)); + } + if (a.size() >= 10) { + assertTrue(a.equals(5, b, 5, a.size() - 5 - 3)); + assertTrue(a.equals(5, b.toByteArray(), 5, a.size() - 5 - 3)); + assertFalse(a.equals(5, b.toByteArray(), 5, a.size())); + } + assertFalse(a.equals(a.size(), b, 0, 1)); + assertFalse(a.equals(a.size(), b.toByteArray(), 0, 1)); + } + + @ParameterizedTest + @MethodSource("provideBufs") + public void testCompareTo(BufArg bufArg) { + if (bufArg.initialSize > 0) { + var bigger = Arrays.copyOf(bufArg.initialContent, bufArg.initialSize + 1); + assertTrue(bufArg.b.compareTo(Buf.wrap(bigger)) < 0); + assertTrue(bufArg.b.compareTo(Buf.wrap(bigger).subListForced(0, bigger.length)) < 0); + assertTrue(bufArg.b.compareTo(new ByteArrayList(bigger)) < 0); + var smaller = Arrays.copyOf(bufArg.initialContent, bufArg.initialSize - 1); + assertTrue(bufArg.b.compareTo(Buf.wrap(smaller)) > 0); + var equal = Arrays.copyOf(bufArg.initialContent, bufArg.initialSize); + assertEquals(0, bufArg.b.compareTo(Buf.wrap(equal))); + var bigger2 = Arrays.copyOf(bufArg.initialContent, bufArg.initialSize); + if (bigger2[bigger2.length - 1] < 127) { + bigger2[bigger2.length - 1]++; + assertTrue(bufArg.b.compareTo(Buf.wrap(bigger2)) < 0); + } + var smaller2 = Arrays.copyOf(bufArg.initialContent, bufArg.initialSize); + if (smaller2[smaller2.length - 1] > 0) { + smaller2[smaller2.length - 1]--; + assertTrue(bufArg.b.compareTo(Buf.wrap(smaller2)) > 0); + }; + assertTrue(bufArg.b.compareTo(Buf.create()) > 0); + } + } + + @ParameterizedTest + @MethodSource("provideBufs") + public void testIterator(BufArg bufArg) { + var it1 = ByteList.of(bufArg.initialContent).iterator(); + var it2 = bufArg.b.iterator(); + var it3 = bufArg.b.listIterator(); + var it4 = bufArg.b.iterator(); + while (it1.hasNext() && it2.hasNext() && it3.hasNext() && it4.hasNext()) { + Byte a = it1.nextByte(); + byte b = it4.nextByte(); + //noinspection deprecation + byte b2 = it2.next(); + //noinspection deprecation + byte b3 = it3.next(); + //noinspection deprecation + it3.previous(); + byte b4 = it3.nextByte(); + assertEquals(a, b); + assertEquals(a, b2); + assertEquals(a, b3); + assertEquals(a, b4); + } + assertFalse(it1.hasNext()); + assertFalse(it2.hasNext()); + assertFalse(it3.hasNext()); + assertFalse(it4.hasNext()); + + // Test list iterator + { + var fei = bufArg.b.iterator(); + LongAdder adder = new LongAdder(); + bufArg.b.listIterator().forEachRemaining(b -> { + assertEquals(fei.nextByte(), b); + adder.increment(); + }); + assertEquals(bufArg.initialSize, adder.sum()); + } + + // Test list iterator with initial index + if (bufArg.initialSize > 0) { + assertEquals(bufArg.b.getByte(bufArg.initialSize - 1), bufArg.b.listIterator(bufArg.initialSize).previousByte()); + } + + // Test spliterator + //noinspection SimplifyStreamApiCallChains + assertArrayEquals(bufArg.initialContent, new ByteArrayList(StreamSupport.stream(bufArg.b.spliterator(), true).toList()).toByteArray()); + //noinspection SimplifyStreamApiCallChains + assertArrayEquals(bufArg.initialContent, new ByteArrayList(StreamSupport.stream(bufArg.b.spliterator(), false).peek(c -> {}).toList()).toByteArray()); + assertArrayEquals(bufArg.initialContent, new ByteArrayList(Spliterators.iterator(bufArg.b.spliterator())).toByteArray()); + } + @Test public void testByteListBufConstructor() { ByteListBuf blb1 = new ByteListBuf(); @@ -440,6 +576,5 @@ public class TestBuffer { assertEquals(blb1, blb6); assertEquals(blb1, blb7); assertEquals(blb1, blb8); - } } diff --git a/data-generator-runtime/src/test/java/it/cavallium/stream/TestInput.java b/data-generator-runtime/src/test/java/it/cavallium/stream/TestInput.java new file mode 100644 index 0000000..d149803 --- /dev/null +++ b/data-generator-runtime/src/test/java/it/cavallium/stream/TestInput.java @@ -0,0 +1,264 @@ +package it.cavallium.stream; + +import it.cavallium.buffer.Buf; +import it.cavallium.buffer.BufDataInput; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.*; + +@SuppressWarnings("resource") +public class TestInput { + + public static final byte[] DATA = new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + public static Stream provideStreams() { + var dataLarge = new byte[] {-2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; + return Stream.of( + new SafeDataInputStream(new SafeByteArrayInputStream(DATA)), + new SafeByteArrayInputStream(DATA), + BufDataInput.create(Buf.wrap(DATA)), + BufDataInput.create(Buf.wrap(dataLarge).subList(2, 12))); + } + + @Test + public void testBufDataInputValidity() { + var bdi = BufDataInput.create(Buf.wrap((byte) 1, (byte) 2, (byte) 3, (byte) 4)); + assertThrows(UnsupportedOperationException.class, () -> bdi.mark(1)); + assertThrows(UnsupportedOperationException.class, bdi::reset); + //noinspection deprecation + assertDoesNotThrow(bdi::close); + assertFalse(bdi.markSupported()); + } + + @Test + public void testString() throws IOException { + String data = "CiaoƧ\uD83D\uDC6A"; + var shortBaos = new ByteArrayOutputStream(); + var medBaos = new ByteArrayOutputStream(); + var shortDaos = new DataOutputStream(shortBaos); + var medDaos = new DataOutputStream(medBaos); + var sbytes = data.getBytes(StandardCharsets.UTF_8); + shortDaos.writeShort(sbytes.length); + shortDaos.write(sbytes); + medDaos.writeInt(sbytes.length); + medDaos.write(sbytes); + var shortBytes = shortBaos.toByteArray(); + var medBytes = medBaos.toByteArray(); + var bdi = BufDataInput.create(Buf.wrap(shortBytes)); + //noinspection deprecation + assertEquals(data, bdi.readUTF()); + var bdi2 = BufDataInput.create(Buf.wrap(shortBytes).subList(Short.BYTES, shortBytes.length)); + assertEquals(data, bdi2.readString(sbytes.length, StandardCharsets.UTF_8)); + var bdi3 = BufDataInput.create(Buf.wrap(shortBytes)); + assertEquals(data, bdi3.readShortText(StandardCharsets.UTF_8)); + var bdi4 = BufDataInput.create(Buf.wrap(medBytes)); + assertEquals(data, bdi4.readMediumText(StandardCharsets.UTF_8)); + } + + @Test + public void testReadTypes() throws IOException { + var baos = new ByteArrayOutputStream(); + var daos = new DataOutputStream(baos); + daos.write(10); + daos.writeByte(10); + daos.writeShort(10); + daos.writeByte(255); + daos.writeShort(50000); + daos.writeChar(10); + daos.writeUTF("123"); + daos.writeInt(10); + daos.writeLong(10); + daos.writeFloat(10); + daos.writeDouble(10); + daos.writeBoolean(true); + var bytes1 = new byte[] {1, 2, 3, 4}; + daos.write(bytes1); + daos.write(0); + daos.write(0); + daos.write(0); + daos.write("Ciao".getBytes(StandardCharsets.UTF_8)); + { + var writeBuffer = new byte[7]; + var v = (1L << 52) - 1; + writeBuffer[0] = (byte)(v >> 48 & 0xf); + writeBuffer[1] = (byte)(v >> 40 & 0xff); + writeBuffer[2] = (byte)(v >> 32 & 0xff); + writeBuffer[3] = (byte)(v >> 24 & 0xff); + writeBuffer[4] = (byte)(v >> 16 & 0xff); + writeBuffer[5] = (byte)(v >> 8 & 0xff); + writeBuffer[6] = (byte)(v & 0xff); + daos.write(writeBuffer); + } + daos.writeShort(4); + daos.write("Ciao".getBytes(StandardCharsets.UTF_8)); + daos.writeInt(4); + daos.write("Ciao".getBytes(StandardCharsets.UTF_8)); + daos.write(1); + daos.write(2); + var initialArray = baos.toByteArray(); + + var bdi = BufDataInput.create(Buf.wrap(initialArray)); + assertEquals(10, bdi.read()); + assertEquals(10, bdi.readByte()); + assertEquals(10, bdi.readShort()); + assertEquals(255, bdi.readUnsignedByte()); + assertEquals(50000, bdi.readUnsignedShort()); + assertEquals(10, bdi.readChar()); + //noinspection deprecation + assertEquals("123", bdi.readUTF()); + assertEquals(10, bdi.readInt()); + assertEquals(10, bdi.readLong()); + assertEquals(10, bdi.readFloat()); + assertEquals(10, bdi.readDouble()); + assertTrue(bdi.readBoolean()); + { + var in = new byte[4]; + bdi.readFully(in); + assertArrayEquals(bytes1, in); + } + bdi.skipNBytes(1); + assertEquals(1, bdi.skipBytes(1)); + assertEquals(1, bdi.skip(1)); + assertEquals("Ciao", bdi.readString(4, StandardCharsets.UTF_8)); + assertEquals((1L << 52) - 1, bdi.readInt52()); + assertEquals("Ciao", bdi.readShortText(StandardCharsets.UTF_8)); + assertEquals("Ciao", bdi.readMediumText(StandardCharsets.UTF_8)); + { + var buf = new byte[1]; + assertEquals(1, bdi.read(buf)); + assertArrayEquals(new byte[] {1}, buf); + assertEquals(1, bdi.read(buf, 0, 1)); + assertArrayEquals(new byte[] {2}, buf); + } + { + var bdi1 = BufDataInput.create(Buf.create()); + assertEquals(0, bdi1.skip(1)); + assertEquals(0, bdi1.skipBytes(1)); + assertThrows(IndexOutOfBoundsException.class, () -> bdi1.readString(10, StandardCharsets.UTF_8)); + var in = new byte[4]; + assertThrows(IndexOutOfBoundsException.class, () -> bdi1.readFully(in)); + assertThrows(IndexOutOfBoundsException.class, () -> bdi1.readFully(in, 0, -1)); + assertThrows(IndexOutOfBoundsException.class, bdi1::readBoolean); + assertThrows(IndexOutOfBoundsException.class, bdi1::readByte); + assertThrows(IndexOutOfBoundsException.class, bdi1::readShort); + assertThrows(IndexOutOfBoundsException.class, bdi1::readInt); + assertThrows(IndexOutOfBoundsException.class, bdi1::readInt52); + assertThrows(IndexOutOfBoundsException.class, bdi1::readLong); + assertThrows(IndexOutOfBoundsException.class, bdi1::readFloat); + assertThrows(IndexOutOfBoundsException.class, bdi1::readChar); + assertThrows(IndexOutOfBoundsException.class, bdi1::readDouble); + assertThrows(IndexOutOfBoundsException.class, bdi1::readUnsignedShort); + assertThrows(IndexOutOfBoundsException.class, bdi1::readUnsignedByte); + } + } + + @ParameterizedTest + @MethodSource("provideStreams") + public void testSkip(SafeInputStream is) { + assertEquals(10, is.skip(15)); + } + + @ParameterizedTest + @MethodSource("provideStreams") + public void testSkipNBytes(SafeInputStream is) { + assertDoesNotThrow(() -> is.skipNBytes(10)); + assertThrows(Exception.class, () -> is.skipNBytes(1)); + } + + @ParameterizedTest + @MethodSource("provideStreams") + public void testRead(SafeInputStream is) { + for (int i = 0; i < 10; i++) { + assertEquals(i, is.read()); + } + assertEquals(-1, is.read()); + } + + @ParameterizedTest + @MethodSource("provideStreams") + public void testReadSmaller(SafeInputStream is) { + byte[] data = new byte[9]; + assertEquals(9, is.read(data)); + assertArrayEquals(Arrays.copyOf(DATA, 9), data); + assertEquals(9, is.read()); + assertEquals(-1, is.read()); + } + + @ParameterizedTest + @MethodSource("provideStreams") + public void testReadBigger(SafeInputStream is) { + byte[] data = new byte[11]; + assertEquals(10, is.read(data)); + assertArrayEquals(Arrays.copyOf(DATA, 11), data); + assertEquals(-1, is.read()); + } + + @ParameterizedTest + @MethodSource("provideStreams") + public void testReadExact(SafeInputStream is) { + byte[] data = new byte[10]; + assertEquals(10, is.read(data)); + assertArrayEquals(DATA, data); + assertEquals(-1, is.read()); + } + + @ParameterizedTest + @MethodSource("provideStreams") + public void testPosition(SafeInputStream is) { + if (is instanceof SafeByteArrayInputStream bis) { + assertEquals(10, bis.available()); + assertEquals(10, bis.length()); + assertEquals(0, bis.position()); + assertEquals(0, bis.read()); + assertEquals(1, bis.position()); + assertEquals(1, bis.read()); + assertEquals(2, bis.position()); + assertEquals(2, bis.read()); + assertEquals(3, bis.position()); + assertEquals(7, bis.skip(1000)); + assertEquals(-1, bis.read()); + assertEquals(10, bis.position()); + bis.position(0); + assertEquals(0, bis.position()); + assertEquals(0, bis.read()); + assertEquals(1, bis.position()); + assertEquals(9, bis.available()); + assertEquals(10, bis.length()); + } + } + + @ParameterizedTest + @MethodSource("provideStreams") + public void testMark(SafeInputStream is) { + if (is.markSupported()) { + is.mark(1); + assertEquals(0, is.read()); + assertEquals(1, is.read()); + is.reset(); + assertEquals(0, is.read()); + assertEquals(1, is.read()); + is.mark(1); + is.mark(1); + assertEquals(2, is.read()); + assertEquals(3, is.read()); + is.reset(); + is.reset(); + assertEquals(2, is.read()); + assertEquals(3, is.read()); + assertThrows(Exception.class, () -> is.mark(-1)); + } else { + assertThrows(Exception.class, () -> is.mark(0)); + assertThrows(Exception.class, () -> is.mark(10)); + assertThrows(Exception.class, is::reset); + } + } +} diff --git a/data-generator-runtime/src/test/java/it/cavallium/stream/TestOutput.java b/data-generator-runtime/src/test/java/it/cavallium/stream/TestOutput.java new file mode 100644 index 0000000..40fe281 --- /dev/null +++ b/data-generator-runtime/src/test/java/it/cavallium/stream/TestOutput.java @@ -0,0 +1,92 @@ +package it.cavallium.stream; + +import it.cavallium.buffer.Buf; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.List; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class TestOutput { + + @Test + public void testBufOutputStream() throws IOException { + var buf = Buf.createZeroes(Integer.BYTES * 3); + var subMiddleBuf = buf.subList(Integer.BYTES, Integer.BYTES * 2); + buf.setInt(0, 0); + buf.setInt(Integer.BYTES, 5); + buf.setInt(Integer.BYTES * 2, 4); + var subBuf = buf.subList(Integer.BYTES, Integer.BYTES * 3); + var subBufOut = subBuf.binaryOutputStream(); + var subBufOutData = new SafeDataOutputStream(subBufOut); + subBufOutData.writeInt(9); + subBufOut.position(0); + subBufOutData.writeInt(1); + subBufOutData.writeInt(2); + var i0 = buf.getInt(0); + var i1 = buf.getInt(Integer.BYTES); + var i2 = buf.getInt(Integer.BYTES * 2); + assertEquals(List.of(0, 1, 2), List.of(i0, i1, i2)); + { + var baos = new ByteArrayOutputStream(); + var dos = new DataOutputStream(baos); + dos.writeInt(1); + dos.writeInt(2); + assertArrayEquals(baos.toByteArray(), subBufOut.toByteArray()); + } + { + var baos = new ByteArrayOutputStream(); + var dos = new DataOutputStream(baos); + dos.writeInt(0); + dos.writeInt(1); + dos.writeInt(2); + assertArrayEquals(baos.toByteArray(), buf.toByteArray()); + } + { + var baos = new ByteArrayOutputStream(); + var dos = new DataOutputStream(baos); + dos.writeInt(1); + assertArrayEquals(baos.toByteArray(), subMiddleBuf.toByteArray()); + } + } + + @ParameterizedTest + @MethodSource("provideByteArrayOutputStreams") + public void testByteArrayOutputStream(SafeByteArrayOutputStream baos) { + assertArrayEquals(new byte[0], baos.toByteArray()); + baos.write(0); + baos.write(0); + baos.write(0); + assertArrayEquals(new byte[3], baos.toByteArray()); + } + + @ParameterizedTest + @MethodSource("provideByteArrayOutputStreams") + public void testTrim(SafeByteArrayOutputStream baos) { + baos.trim(); + assertEquals(0, baos.array.length); + baos.write(10); + baos.trim(); + assertEquals(1, baos.array.length); + assertArrayEquals(new byte[] {10}, baos.array); + baos.ensureWritable(2); + assertEquals(3, baos.array.length); + assertArrayEquals(new byte[] {10, 0, 0}, baos.array); + } + + public static Stream provideByteArrayOutputStreams() { + return Stream.of(new SafeByteArrayOutputStream(), + new SafeByteArrayOutputStream(10), + new SafeByteArrayOutputStream(8), + new SafeByteArrayOutputStream(20), + new SafeByteArrayOutputStream() + ); + } +}