Skip to content
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

New crux-util ByteBufferInputStream, ByteInput, Unclosable input/outp… #22

Merged
merged 1 commit into from
Nov 6, 2024
Merged
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.fizzed.crux.util;

import java.io.InputStream;
import java.nio.ByteBuffer;

public class ByteBufferInputStream extends InputStream {

private final ByteBuffer buf;

public ByteBufferInputStream(ByteBuffer buffer) {
if (buffer == null) throw new NullPointerException();
buf = buffer;
}

@Override
public synchronized int read() {
if (buf.hasRemaining()) return buf.get() & 0xff;
return -1;
}

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

@Override
public synchronized int read(byte b[], int off, int len) {
if ((off | len | off + len | b.length - (off + len)) < 0) {
throw new IndexOutOfBoundsException();
}
return read(off, len, b);
}

private int read(int off, int len, byte[] b) {
if (len == 0) return 0;

int rem = buf.remaining();
if (rem <= 0) return -1;

if (rem > len) rem = len;
buf.get(b, off, rem);
return rem;
}

@Override
public synchronized long skip(long n) {
if (n <= 0) return 0;

int rem = buf.remaining();
if (n > rem) n = rem;

buf.position((int)(buf.position() + n));
return n;
}

@Override
public synchronized int available() { return buf.remaining(); }

@Override
public synchronized void mark(int readAheadLimit) { buf.mark(); }

@Override
public synchronized void reset() { buf.reset(); }

@Override
public boolean markSupported() { return true; }

@Override
public void close() {}
}
119 changes: 119 additions & 0 deletions crux-util/src/main/java/com/fizzed/crux/util/ByteInput.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package com.fizzed.crux.util;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Objects;

/**
* Generic way of providing "byte inputs" as files, byte arrays, etc. to methods.
*/
public class ByteInput {

protected final Path file;
protected final byte[] bytes;
protected final ByteBuffer buffer;
protected final InputStream in;
protected final boolean closeable;

static public ByteInput byteInput(Path file) {
Objects.requireNonNull(file, "file cannot be null");
return new ByteInput(file, null, null, null, true);
}

static public ByteInput byteInput(File file) {
Objects.requireNonNull(file, "file cannot be null");
return new ByteInput(file.toPath(), null, null, null, true);
}

static public ByteInput byteInput(byte[] bytes) {
Objects.requireNonNull(bytes, "bytes cannot be null");
return new ByteInput(null, bytes, null, null, true);
}

static public ByteInput byteInput(ByteBuffer buffer) {
Objects.requireNonNull(buffer, "buffer cannot be null");
return new ByteInput(null, null, buffer, null, true);
}

static public ByteInput byteInput(InputStream in) {
Objects.requireNonNull(in, "input cannot be null");
return new ByteInput(null, null, null, in, true);
}

static public ByteInput byteInput(InputStream in, boolean closeable) {
Objects.requireNonNull(in, "input cannot be null");
return new ByteInput(null, null, null, in, closeable);
}

protected Long size;

protected ByteInput(Path file, byte[] bytes, ByteBuffer buffer, InputStream in, boolean closeable) {
this.file = file;
this.bytes = bytes;
this.buffer = buffer;
this.in = in;
this.closeable = closeable;
}

/**
* If the size is definitively known ahead of time, this is the number of bytes. Will work on files, byte arrays,
* and byte buffers, but others will return -1 since the size is not known definitively.
* @return The number of bytes this input represents or -1 if that size is not definitively known
* @throws IOException
*/
public long available() throws IOException {
if (this.file != null || this.bytes != null || this.buffer != null) {
return this.size();
} else if (this.in != null) {
return this.in.available();
}
return -1L;
}

/**
* If the size is definitively known ahead of time, this is the number of bytes. Will work on files, byte arrays,
* and byte buffers, but others will return -1 since the size is not known definitively.
* @return The number of bytes this input represents or -1 if that size is not definitively known
* @throws IOException
*/
public long size() throws IOException {
if (size == null) {
if (this.file != null) {
this.size = Files.size(this.file);
} else if (this.bytes != null) {
this.size = (long)this.bytes.length;
} else if (this.buffer != null) {
this.size = (long)this.buffer.remaining();
} else {
// otherwise we just don't know what size this is
this.size = -1l;
}
}
return this.size;
}

public InputStream open() throws IOException {
if (this.file != null) {
return Files.newInputStream(this.file);
} else if (this.bytes != null) {
return new ByteArrayInputStream(this.bytes);
} else if (this.buffer != null) {
return new ByteBufferInputStream(this.buffer);
} else if (this.in != null) {
if (this.closeable) {
return this.in;
} else {
// wrap input stream in an un-closeable version
return new UnclosableInputStream(this.in);
}
} else {
throw new IllegalStateException("All available inputs were null (file, bytes, input)");
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.fizzed.crux.util;

import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;

public class UnclosableInputStream extends FilterInputStream {

protected boolean closed;

public UnclosableInputStream(InputStream in) {
super(in);
}

public boolean isClosed() {
return this.closed;
}

@Override
public int read() throws IOException {
if (this.closed) return -1;
return super.read();
}

@Override
public int read(byte[] b) throws IOException {
if (this.closed) return -1;
return super.read(b);
}

@Override
public int read(byte[] b, int off, int len) throws IOException {
if (this.closed) return -1;
return super.read(b, off, len);
}

@Override
public int available() throws IOException {
if (this.closed) throw new IOException("Stream closed");
return super.available();
}

@Override
public void close() throws IOException {
this.closed = true;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.fizzed.crux.util;

import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class UnclosableOutputStream extends FilterOutputStream {

private boolean closed;

public UnclosableOutputStream(OutputStream output) {
super(output);
this.closed = false;
}

public boolean isClosed() {
return this.closed;
}

@Override
public void write(int b) throws IOException {
if (closed) throw new IOException("Stream closed");
super.write(b);
}

@Override
public void write(byte[] b) throws IOException {
if (closed) throw new IOException("Stream closed");
super.write(b);
}

@Override
public void write(byte[] b, int off, int len) throws IOException {
if (closed) throw new IOException("Stream closed");
super.write(b, off, len);
}

@Override
public void flush() throws IOException {
if (closed) throw new IOException("Stream closed");
super.flush();
}

@Override
public void close() throws IOException {
this.closed = true;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.fizzed.crux.util;

import org.junit.Test;

import java.nio.ByteBuffer;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.*;

public class ByteBufferInputStreamTest {

@Test
public void read() {
final ByteBuffer buf = ByteBuffer.wrap("abcdef".getBytes());

final ByteBufferInputStream in = new ByteBufferInputStream(buf);
int read;

assertThat(in.available(), is(6));
assertThat(in.read(), is((int)'a'));

final byte[] bytes = new byte[2];
read = in.read(bytes);
assertThat(read, is(2));
assertThat(bytes[0], is((byte)'b'));
assertThat(bytes[1], is((byte)'c'));

read = in.read(bytes, 1, 1);
assertThat(read, is(1));
assertThat(bytes[1], is((byte)'d'));

assertThat(in.available(), is(2));
read = in.read(bytes);
assertThat(read, is(2));
assertThat(bytes[0], is((byte)'e'));
assertThat(bytes[1], is((byte)'f'));

// this should be EOF now
read = in.read(bytes);
assertThat(read, is(-1));
}

}
Loading
Loading