Skip to content

DataOutput and DataInput support in FastByteArray streams #360

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 146 additions & 6 deletions src/it/unimi/dsi/fastutil/io/FastByteArrayInputStream.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@

package it.unimi.dsi.fastutil.io;

import java.io.DataInput;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.UTFDataFormatException;
import java.io.UncheckedIOException;

/** Simple, fast and repositionable byte-array input stream.
*
* <p><strong>Warning</strong>: this class implements the correct semantics
Expand All @@ -25,8 +31,7 @@
*
* @author Sebastiano Vigna
*/

public class FastByteArrayInputStream extends MeasurableInputStream implements RepositionableStream {
public class FastByteArrayInputStream extends MeasurableInputStream implements RepositionableStream, DataInput {

/** The array backing the input stream. */
public byte[] array;
Expand All @@ -38,7 +43,7 @@ public class FastByteArrayInputStream extends MeasurableInputStream implements R
public int length;

/** The current position as a distance from {@link #offset}. */
private int position;
protected int position;

/** The current mark as a position, or -1 if no mark exists. */
private int mark;
Expand All @@ -52,7 +57,7 @@ public class FastByteArrayInputStream extends MeasurableInputStream implements R
public FastByteArrayInputStream(final byte[] array, final int offset, final int length) {
this.array = array;
this.offset = offset;
this.length = length;
this.length = Math.min(length, array.length - offset);
}

/** Creates a new array input stream using a given array.
Expand Down Expand Up @@ -111,7 +116,7 @@ public int read() {
*/

@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);
Expand All @@ -133,4 +138,139 @@ public void position(final long newPosition) {
public long length() {
return length;
}
}

@Override
public byte[] readAllBytes() {
return readNBytes(available());
}

@Override
public int readNBytes(byte[] b, int off, int len) {
int n = read(b, off, len);
return n == -1 ? 0 : n;
}

@Override
public int read (byte[] b) {
return read(b, 0, b.length);
}

@Override
public byte[] readNBytes (int len) {
int n = Math.min(len, available());
byte[] result = new byte[n];
read(result);
return result;
}

@Override
public void skipNBytes (long n) {
skip(n);
}

public int peek() {
if (length <= position()) return -1;
return array[(int)(offset + position())] & 0xFF;
}


@Override
public void readFully (byte[] b) {
read(b);
}

@Override
public void readFully (byte[] b, int off, int len) {
read(b, off, len);
}

@Override
public int skipBytes (int n) {
return (int) skip(n);
}

@Override
public boolean readBoolean () {
return read() != 0;
}

@Override
public byte readByte () {
return (byte) read();
}

@Override
public int readUnsignedByte () {
return read() & 0xFF;
}

@Override
public short readShort() {
return (short)((read() << 8)|(read() & 0xFF));
}

@Override
public int readUnsignedShort() {
return ((read() & 0xFF) << 8)|(read() & 0xFF);
}

@Override
public char readChar() {
return (char)(((read() & 0xFF) << 8)|(read() & 0xFF));
}

@Override
public int readInt() {
return read() << 24 | ((read() & 0xFF) << 16) | ((read() & 0xFF) << 8) | (read() & 0xFF);
}

@Override
public long readLong () {
return (long) readInt() << 32 | (readInt() & 0xFFFF_FFFFL);
}

@Override
public float readFloat () {
return Float.intBitsToFloat(readInt());
}

@Override
public double readDouble () {
return Double.longBitsToDouble(readLong());
}

@Override @Deprecated
public String readLine () {
StringBuilder sb = new StringBuilder(99);
loop:
for (int c;;){
switch (c = read()){
case -1:
break loop;// eof

case '\n':
return sb.toString();
case '\r':
if (peek() == '\n'){
read();
}
return sb.toString();

default:
sb.append((char) c);
}
}
return sb.isEmpty() ? null : sb.toString();
}

@Override
public String readUTF () throws UTFDataFormatException {
try {
return DataInputStream.readUTF(this);
} catch (UTFDataFormatException badBinaryFormatting){
throw badBinaryFormatting;
} catch (IOException e){
throw new UncheckedIOException("readUTF @ "+this, e);
}
}
}
118 changes: 116 additions & 2 deletions src/it/unimi/dsi/fastutil/io/FastByteArrayOutputStream.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@

import it.unimi.dsi.fastutil.bytes.ByteArrays;

import java.io.DataOutput;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;

/** Simple, fast byte-array output stream that exposes the backing array.
*
* <p>{@link java.io.ByteArrayOutputStream} is nice, but to get its content you
Expand All @@ -30,7 +35,7 @@
*
* @author Sebastiano Vigna
*/
public class FastByteArrayOutputStream extends MeasurableOutputStream implements RepositionableStream {
public class FastByteArrayOutputStream extends MeasurableOutputStream implements RepositionableStream, DataOutput {

/** The array backing the output stream. */
public static final int DEFAULT_INITIAL_CAPACITY = 16;
Expand All @@ -42,7 +47,7 @@ public class FastByteArrayOutputStream extends MeasurableOutputStream implements
public int length;

/** The current writing position. */
private int position;
protected int position;

/** Creates a new array output stream with an initial capacity of {@link #DEFAULT_INITIAL_CAPACITY} bytes. */
public FastByteArrayOutputStream() {
Expand Down Expand Up @@ -122,4 +127,113 @@ public void write(final byte[] b) {
// Only to force no exception
write(b, 0, b.length);
}

/** @see java.io.ByteArrayOutputStream#toString(Charset) */
public String toString(Charset charset) {
return new String(array, 0, length, charset);
}

/** @see java.io.ByteArrayOutputStream#writeTo(OutputStream) */
public synchronized void writeTo(OutputStream out) throws IOException {
out.write(array, 0, length);
}


@Override
public void writeBoolean(boolean v) {
write(v?1:0);
}

@Override
public void writeByte(int v) {
write(v);
}

@Override
public void writeShort(int v) {
write(v >> 8);
write(v);
}

@Override
public void writeChar(int v) {
write(v >> 8);
write(v);
}

@Override
public void writeInt(int v) {
write(v >> 24);
write(v >> 16);
write(v >> 8);
write(v);
}

@Override
public void writeLong(long v) {
writeInt((int)(v >> 32));
writeInt((int) v);
}

@Override
public void writeFloat(float v) {
writeInt(Float.floatToIntBits(v));
}

@Override
public void writeDouble(double v) {
writeLong(Double.doubleToLongBits(v));
}

/**
* @deprecated This method is dangerous as it discards the high byte of every character. For UTF-8, use {@link #writeUTF(String)} or {@link #write(byte[]) @code write(s.getBytes(UTF_8))}.
* @see java.io.DataOutputStream#writeBytes(String)
*/
@Override
public void writeBytes(String s) {
for (int i = 0, len = s.length(); i < len; i++){
write((byte)s.charAt(i));
}
}

@Override
public void writeChars(String s) {
for (int i = 0, len = s.length(); i < len; i++){
int v = s.charAt(i);
writeChar(v);
}
}

@Override
public void writeUTF (String s) {
int savePos = position;
writeShort(0);// len place holder
for (int i = 0, len = s.length(); i < len; i++){
writeUtf8Char(s.charAt(i));
if (position - savePos > 0xFF_FF + 2){
length = position = savePos;// rollback
throw new IllegalArgumentException("UTF encoded string too long: %d: %s".formatted(s.length(), s.substring(0, 99)));
}
}
int len = position - savePos - 2;
array[savePos] = (byte)(len >> 8);
array[savePos+1] = (byte)len;
}

/** @see java.io.DataOutputStream#writeUTF(String,DataOutput) */
public int writeUtf8Char(char c) {
if (c != 0 && c < 0x80){
write(c);
return 1;
} else if (c >= 0x800){
write(0xE0 | c >> 12 & 0x0F);
write(0x80 | c >> 6 & 0x3F);
write(0x80 | c & 0x3F);
return 3;
} else {
write(0xC0 | c >> 6 & 0x1F);
write(0x80 | c & 0x3F);
return 2;
}
}
}