Skip to content

Commit

Permalink
Merge 63d2785 into b6dce24
Browse files Browse the repository at this point in the history
  • Loading branch information
RutledgePaulV committed Jul 17, 2021
2 parents b6dce24 + 63d2785 commit 6076cf3
Show file tree
Hide file tree
Showing 6 changed files with 451 additions and 74 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.github.rutledgepaulv.injectingstreams;

import java.io.InputStream;
import java.io.OutputStream;

public final class InjectingStreams {
private InjectingStreams() {
}

public static OutputStream injectBeforeOutput(OutputStream out, String delimiter, String injection) {
return new PreDelimiterInjectingOutputStream(out, delimiter, injection);
}

public static OutputStream injectBeforeOutput(OutputStream out, String delimiter, byte[] injection) {
return new PreDelimiterInjectingOutputStream(out, delimiter, injection);
}

public static OutputStream injectBeforeOutput(OutputStream out, String delimiter, InputStream injection) {
return new PreDelimiterInjectingOutputStream(out, delimiter, injection);
}

public static OutputStream injectBeforeOutput(OutputStream out, byte[] delimiter, String injection) {
return new PreDelimiterInjectingOutputStream(out, delimiter, injection);
}

public static OutputStream injectBeforeOutput(OutputStream out, byte[] delimiter, byte[] injection) {
return new PreDelimiterInjectingOutputStream(out, delimiter, injection);
}

public static OutputStream injectBeforeOutput(OutputStream out, byte[] delimiter, InputStream injection) {
return new PreDelimiterInjectingOutputStream(out, delimiter, injection);
}

public static OutputStream injectAfterOutput(OutputStream out, String delimiter, String injection) {
return new PostDelimiterInjectingOutputStream(out, delimiter, injection);
}

public static OutputStream injectAfterOutput(OutputStream out, String delimiter, byte[] injection) {
return new PostDelimiterInjectingOutputStream(out, delimiter, injection);
}

public static OutputStream injectAfterOutput(OutputStream out, String delimiter, InputStream injection) {
return new PostDelimiterInjectingOutputStream(out, delimiter, injection);
}

public static OutputStream injectAfterOutput(OutputStream out, byte[] delimiter, String injection) {
return new PostDelimiterInjectingOutputStream(out, delimiter, injection);
}

public static OutputStream injectAfterOutput(OutputStream out, byte[] delimiter, byte[] injection) {
return new PostDelimiterInjectingOutputStream(out, delimiter, injection);
}

public static OutputStream injectAfterOutput(OutputStream out, byte[] delimiter, InputStream injection) {
return new PostDelimiterInjectingOutputStream(out, delimiter, injection);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,38 +5,38 @@

/**
* An output stream that injects a stream of bytes immediately following the first series of
* delimiter bytes. Useful for appending content to a stream following a known delimiter.
* delimiter bytes.
*
* Assumes a single writer (no synchronization)
*/
public class InjectingOutputStream extends FilterOutputStream {
public class PostDelimiterInjectingOutputStream extends FilterOutputStream {

private final InputStream injection;
private boolean injected = false;
private int bufferPos = 0;
private final byte[] delimiter;

public InjectingOutputStream(OutputStream out, String delimiter, String injection) {
public PostDelimiterInjectingOutputStream(OutputStream out, String delimiter, String injection) {
this(out, delimiter.getBytes(), injection.getBytes());
}

public InjectingOutputStream(OutputStream out, String delimiter, byte[] injection) {
public PostDelimiterInjectingOutputStream(OutputStream out, String delimiter, byte[] injection) {
this(out, delimiter.getBytes(), injection);
}

public InjectingOutputStream(OutputStream out, String delimiter, InputStream injection) {
public PostDelimiterInjectingOutputStream(OutputStream out, String delimiter, InputStream injection) {
this(out, delimiter.getBytes(), injection);
}

public InjectingOutputStream(OutputStream out, byte[] delimiter, String injection) {
public PostDelimiterInjectingOutputStream(OutputStream out, byte[] delimiter, String injection) {
this(out, delimiter, injection.getBytes());
}

public InjectingOutputStream(OutputStream out, byte[] delimiter, byte[] injection) {
public PostDelimiterInjectingOutputStream(OutputStream out, byte[] delimiter, byte[] injection) {
this(out, delimiter, new ByteArrayInputStream(injection));
}

public InjectingOutputStream(OutputStream out, byte[] delimiter, InputStream injection) {
public PostDelimiterInjectingOutputStream(OutputStream out, byte[] delimiter, InputStream injection) {
super(out);
this.delimiter = delimiter;
this.injection = injection;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package com.github.rutledgepaulv.injectingstreams;

import java.io.*;


/**
* An output stream that injects a stream of bytes immediately preceding the first series of
* delimiter bytes.
* <p>
* Assumes a single writer (no synchronization)
*/
public class PreDelimiterInjectingOutputStream extends FilterOutputStream {

private final InputStream injection;
private boolean injected = false;
private final byte[] delimiter;
private int bufferOffset = 0;

public PreDelimiterInjectingOutputStream(OutputStream out, String delimiter, String injection) {
this(out, delimiter.getBytes(), injection.getBytes());
}

public PreDelimiterInjectingOutputStream(OutputStream out, String delimiter, byte[] injection) {
this(out, delimiter.getBytes(), injection);
}

public PreDelimiterInjectingOutputStream(OutputStream out, String delimiter, InputStream injection) {
this(out, delimiter.getBytes(), injection);
}

public PreDelimiterInjectingOutputStream(OutputStream out, byte[] delimiter, String injection) {
this(out, delimiter, injection.getBytes());
}

public PreDelimiterInjectingOutputStream(OutputStream out, byte[] delimiter, byte[] injection) {
this(out, delimiter, new ByteArrayInputStream(injection));
}

public PreDelimiterInjectingOutputStream(OutputStream out, byte[] delimiter, InputStream injection) {
super(out);
this.delimiter = delimiter;
this.injection = injection;
}

private void inject() throws IOException {
try (InputStream in = this.injection) {
byte[] buffer = new byte[4096];
int n;
while (-1 != (n = in.read(buffer))) {
out.write(buffer, 0, n);
}
} finally {
injected = true;
bufferOffset = 0;
}
}

private void drainBuffer() throws IOException {
if (bufferOffset > 0) {
out.write(delimiter, 0, bufferOffset);
bufferOffset = 0;
}
}

@Override
public void write(int b) throws IOException {
if (!injected) {
// if this byte is the next element of the delimiter
if (b == delimiter[bufferOffset]) {
// increment the buffer offset
bufferOffset++;
// if we've reached the end of the delimiter
if (bufferOffset == delimiter.length) {
// inject the content
inject();
// inject the buffer
out.write(delimiter);
}
} else {
// flush the pending buffer
this.drainBuffer();
// if this byte is the first element of another potential delimiter sequence
if (b == delimiter[bufferOffset]) {
bufferOffset++;
} else {
out.write(b);
}
}
} else {
out.write(b);
}
}

@Override
public void write(byte[] bytes) throws IOException {
this.write(bytes, 0, bytes.length);
}

@Override
public void write(byte[] bytes, int off, int len) throws IOException {
if (!injected) {
for (int i = off; i < len + off; i++) {
byte b = bytes[i];
// b matches next position in buffer
if (b == delimiter[bufferOffset]) {
bufferOffset++;
// buffer became full
if (bufferOffset == delimiter.length) {
int length = i - off - delimiter.length + 1;
if (length > 0) {
out.write(bytes, off, length);
}
inject();
out.write(delimiter);
int nextIndex = i + 1;
if (nextIndex < len) {
int remaining = len - nextIndex - off;
out.write(bytes, nextIndex, remaining);
}
return;
}
} else {
// we read a byte that negates the current match, go ahead and flush
// the imaginary buffer if it might contain bytes from the prior write
if (i - bufferOffset < off) {
this.drainBuffer();
}
bufferOffset = 0;
// check if this byte is the beginning of the next sequence too
if (b == delimiter[bufferOffset]) {
bufferOffset++;
}
}
}

// still not injected, need to take care of earlier bytes up to buffer
if (!injected) {
int length = (len - bufferOffset);
if (length > 0) {
out.write(bytes, off, length);
}
}
} else {
out.write(bytes, off, len);
}
}

@Override
public void close() throws IOException {
this.drainBuffer();
try {
injection.close();
} finally {
super.close();
}
}
}

This file was deleted.

Loading

0 comments on commit 6076cf3

Please sign in to comment.