package be.nikiroo.utils;
import java.io.File;
-import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
if (cached.exists() && cached.isFile()
&& (allowTooOld || !isOld(cached, stable))) {
try {
- return new MarkableFileInputStream(new FileInputStream(cached));
+ return new MarkableFileInputStream(cached);
} catch (FileNotFoundException e) {
return null;
}
write(in, tmp);
in.close();
- final FileInputStream fis = new FileInputStream(tmp);
- return new MarkableFileInputStream(fis) {
+ return new MarkableFileInputStream(tmp) {
@Override
public void close() throws IOException {
try {
- try {
- super.close();
- } finally {
- try {
- fis.close();
- } catch (IOException e) {
- }
- }
+ super.close();
} finally {
tmp.delete();
}
import java.io.ByteArrayInputStream;
import java.io.Closeable;
import java.io.File;
-import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
* in case of I/O error
*/
public InputStream newInputStream() throws IOException {
- return new MarkableFileInputStream(new FileInputStream(data));
+ return new MarkableFileInputStream(data);
}
/**
* @return an overestimate for the number of bytes {@code
* len} bytes could decode to.
*/
- public int maxOutputSize(int len) {
+ @Override
+ public int maxOutputSize(int len) {
return len * 3/4 + 10;
}
* @return true if the state machine is still healthy. false if
* bad base-64 data has been detected in the input stream.
*/
- public boolean process(byte[] input, int offset, int len, boolean finish) {
+ @Override
+ public boolean process(byte[] input, int offset, int len, boolean finish) {
if (this.state == 6) return false;
int p = offset;
* @return an overestimate for the number of bytes {@code
* len} bytes could encode to.
*/
- public int maxOutputSize(int len) {
+ @Override
+ public int maxOutputSize(int len) {
return len * 8/5 + 10;
}
- public boolean process(byte[] input, int offset, int len, boolean finish) {
+ @Override
+ public boolean process(byte[] input, int offset, int len, boolean finish) {
// Using local variables makes the encoder about 9% faster.
final byte[] alphabet = this.alphabet;
final byte[] output = this.output;
((input[p++] & 0xff) << 8) |
(input[p++] & 0xff);
tailLen = 0;
- };
+ }
break;
case 2:
outputEnd = 0;
}
- public boolean markSupported() {
+ @Override
+ public boolean markSupported() {
return false;
}
- public void mark(int readlimit) {
+ @SuppressWarnings("sync-override")
+ @Override
+ public void mark(int readlimit) {
throw new UnsupportedOperationException();
}
- public void reset() {
+ @SuppressWarnings("sync-override")
+ @Override
+ public void reset() {
throw new UnsupportedOperationException();
}
- public void close() throws IOException {
+ @Override
+ public void close() throws IOException {
in.close();
inputBuffer = null;
}
- public int available() {
+ @Override
+ public int available() {
return outputEnd - outputStart;
}
- public long skip(long n) throws IOException {
+ @Override
+ public long skip(long n) throws IOException {
if (outputStart >= outputEnd) {
refill();
}
return bytes;
}
- public int read() throws IOException {
+ @Override
+ public int read() throws IOException {
if (outputStart >= outputEnd) {
refill();
}
if (outputStart >= outputEnd) {
return -1;
- } else {
- return coder.output[outputStart++] & 0xff;
}
+
+ return coder.output[outputStart++] & 0xff;
}
- public int read(byte[] b, int off, int len) throws IOException {
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
if (outputStart >= outputEnd) {
refill();
}
* @author niki
*/
public class BufferedInputStream extends InputStream {
+ /**
+ * The size of the internal buffer (can be different if you pass your own
+ * buffer, of course).
+ * <p>
+ * A second buffer of twice the size can sometimes be created as needed for
+ * the {@link BufferedInputStream#startsWith(byte[])} search operation.
+ */
+ static private final int BUFFER_SIZE = 4096;
+
/** The current position in the buffer. */
protected int start;
/** The index of the last usable position of the buffer. */
protected int stop;
/** The buffer itself. */
protected byte[] buffer;
- /** An End-Of-File (or buffer, here) marker. */
+ /** An End-Of-File (or {@link InputStream}, here) marker. */
protected boolean eof;
private boolean closed;
public BufferedInputStream(InputStream in) {
this.in = in;
- this.buffer = new byte[4096];
+ this.buffer = new byte[BUFFER_SIZE];
this.originalBuffer = this.buffer;
this.start = 0;
this.stop = 0;
package be.nikiroo.utils.streams;
+import java.io.File;
import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.FilterInputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
private FileChannel channel;
private long mark = 0;
+ /**
+ * Create a new {@link MarkableFileInputStream} from this file.
+ *
+ * @param file
+ * the {@link File} to wrap
+ *
+ * @throws FileNotFoundException
+ * if the {@link File} cannot be found
+ */
+ public MarkableFileInputStream(File file) throws FileNotFoundException {
+ this(new FileInputStream(file));
+ }
+
/**
* Create a new {@link MarkableFileInputStream} from this stream.
*
return true;
}
- public String DEBUG() {
+ /**
+ * Display a DEBUG {@link String} representation of this object.
+ * <p>
+ * Do <b>not</b> use for release code.
+ */
+ @Override
+ public String toString() {
String data = "";
if (stop > 0) {
try {
* @author niki
*/
public class ReplaceInputStream extends BufferedInputStream {
+ /**
+ * The minimum size of the internal buffer (could be more if at least one of
+ * the 'FROM' bytes arrays is > 2048 bytes — in that case the
+ * buffer will be twice the largest size of the 'FROM' bytes arrays).
+ * <p>
+ * This is a different buffer than the one from the inherited class.
+ */
+ static private final int MIN_BUFFER_SIZE = 4096;
+
private byte[][] froms;
private byte[][] tos;
+ private int maxFromSize;
private int maxToSize;
private byte[] source;
this.froms = froms;
this.tos = tos;
+ maxFromSize = 0;
+ for (int i = 0; i < froms.length; i++) {
+ maxFromSize = Math.max(maxFromSize, froms[i].length);
+ }
+
+ maxToSize = 0;
for (int i = 0; i < tos.length; i++) {
maxToSize = Math.max(maxToSize, tos[i].length);
}
- source = new byte[4096];
+ // We need at least maxFromSize so we can iterate and replace
+ source = new byte[Math.max(2 * maxFromSize, MIN_BUFFER_SIZE)];
spos = 0;
slen = 0;
}
"An underlaying buffer is too small for these replace values");
}
- if (spos >= slen) {
+ // We need at least one byte of data to process
+ if (available() < Math.max(maxFromSize, 1) && !eof) {
spos = 0;
slen = in.read(source);
}
- // Note: very simple, not efficient implementation, sorry.
+ // Note: very simple, not efficient implementation; sorry.
int count = 0;
while (spos < slen && count < len - maxToSize) {
boolean replaced = false;
this.tos = tos;
}
+ /**
+ * Flush the {@link BufferedOutputStream}, write the current buffered data
+ * to (and optionally also flush) the under-laying stream.
+ * <p>
+ * If {@link BufferedOutputStream#bypassFlush} is false, all writes to the
+ * under-laying stream are done in this method.
+ * <p>
+ * This can be used if you want to write some data in the under-laying
+ * stream yourself (in that case, flush this {@link BufferedOutputStream}
+ * with or without flushing the under-laying stream, then you can write to
+ * the under-laying stream).
+ * <p>
+ * <b>But be careful!</b> If a replacement could be done with the end o the
+ * currently buffered data and the start of the data to come, we obviously
+ * will not be able to do it.
+ *
+ * @param includingSubStream
+ * also flush the under-laying stream
+ * @throws IOException
+ * in case of I/O error
+ */
@Override
public void flush(boolean includingSubStream) throws IOException {
- // Note: very simple, not efficient implementation, sorry.
+ // Note: very simple, not efficient implementation; sorry.
while (start < stop) {
boolean replaced = false;
for (int i = 0; i < froms.length; i++) {