Test buffers

This commit is contained in:
Andrea Cavalli 2023-04-19 17:52:59 +02:00
parent 95d8811bcb
commit 1eb4b0334c
23 changed files with 1135 additions and 886 deletions

View File

@ -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();

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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<? extends Byte> c) {
assert isMutable() : IMMUTABLE_ERROR;
return super.addAll(index, c);
}
@IgnoreCoverage
@Override
public boolean addAll(@NotNull Collection<? extends Byte> 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<Byte> 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<? super Byte> comparator) {
assert isMutable() : IMMUTABLE_ERROR;
super.sort(comparator);
}
@IgnoreCoverage
@SuppressWarnings("deprecation")
@Deprecated
@Override
public void unstableSort(Comparator<? super Byte> comparator) {
assert isMutable() : IMMUTABLE_ERROR;
super.unstableSort(comparator);
}
@IgnoreCoverage
@SuppressWarnings("deprecation")
@Deprecated
@Override
public boolean removeIf(Predicate<? super Byte> 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);
}
}
}

View File

@ -0,0 +1,8 @@
package it.cavallium.buffer;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.CLASS)
public @interface IgnoreCoverage {
}

View File

@ -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<List<String>> {
public List<String> 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);
}

View File

@ -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<NullableString> {
public static final NullableStringSerializer INSTANCE = new NullableStringSerializer();
@ -27,7 +29,7 @@ public class NullableStringSerializer implements DataSerializer<NullableString>
if (!isPresent) {
return NullableString.empty();
} else {
return NullableString.of(dataInput.readUTF());
return NullableString.of(dataInput.readShortText(StandardCharsets.UTF_8));
}
}
}

View File

@ -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;
}
}

View File

@ -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();
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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);

View File

@ -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.
*
* <p>{@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) ? "..." : "");
}
}

View File

@ -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);
}

View File

@ -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}.
* <p>
* Bytes
* for this operation are read from the contained
* input stream.
*
* @deprecated This method does not properly convert bytes to characters.
* As of JDK&nbsp;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:
* <blockquote><pre>
* DataInputStream d =&nbsp;new&nbsp;DataInputStream(in);
* </pre></blockquote>
* with:
* <blockquote><pre>
* BufferedReader d
* =&nbsp;new&nbsp;BufferedReader(new&nbsp;InputStreamReader(in));
* </pre></blockquote>
*
* @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
* <a href="DataInput.html#modified-utf-8">modified UTF-8</a> 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);
}
}

View File

@ -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);
}

View File

@ -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);
}
/**

View File

@ -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);

View File

@ -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<byte[]> bufs = null;
List<byte[]> 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");

View File

@ -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.
*
* <p> 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.
*
* <p> 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.
*
* <p> 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.
*
* <p> 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.
*
* <p> 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;
}
}

View File

@ -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<BufArg> 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);
}
}

View File

@ -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<SafeInputStream> 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);
}
}
}

View File

@ -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<SafeByteArrayOutputStream> provideByteArrayOutputStreams() {
return Stream.of(new SafeByteArrayOutputStream(),
new SafeByteArrayOutputStream(10),
new SafeByteArrayOutputStream(8),
new SafeByteArrayOutputStream(20),
new SafeByteArrayOutputStream()
);
}
}