-
Notifications
You must be signed in to change notification settings - Fork 208
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'fix-gzip-compressor-instead-of-test' into next
- Loading branch information
Showing
5 changed files
with
179 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
53 changes: 53 additions & 0 deletions
53
src/freenet/support/compress/SingleOffsetReplacingOutputStream.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package freenet.support.compress; | ||
|
||
import java.io.FilterOutputStream; | ||
import java.io.IOException; | ||
import java.io.OutputStream; | ||
import java.util.zip.GZIPOutputStream; | ||
|
||
/** | ||
* A {@link FilterOutputStream} that replaces a single byte at a specific | ||
* offset when being written to. It can be used to change the operating system | ||
* byte in the header of a {@link GZIPOutputStream}. | ||
* <p> | ||
* This class is not safe for usage from multiple threads. | ||
*/ | ||
public class SingleOffsetReplacingOutputStream extends FilterOutputStream { | ||
|
||
private final int replacementOffset; | ||
private final int replacementValue; | ||
private int currentOffset = 0; | ||
|
||
public SingleOffsetReplacingOutputStream(OutputStream outputStream, int replacementOffset, int replacementValue) { | ||
super(outputStream); | ||
this.replacementOffset = replacementOffset; | ||
this.replacementValue = replacementValue; | ||
} | ||
|
||
@Override | ||
public void write(int b) throws IOException { | ||
if (currentOffset == replacementOffset) { | ||
out.write(replacementValue); | ||
} else { | ||
out.write(b); | ||
} | ||
currentOffset++; | ||
} | ||
|
||
@Override | ||
public void write(byte[] buffer, int offset, int length) throws IOException { | ||
if (offsetToReplaceIsInBufferBeingWritten(length)) { | ||
out.write(buffer, offset, replacementOffset - currentOffset); | ||
out.write(replacementValue); | ||
out.write(buffer, offset + (replacementOffset - currentOffset) + 1, length - (replacementOffset - currentOffset) - 1); | ||
} else { | ||
out.write(buffer, offset, length); | ||
} | ||
currentOffset += length; | ||
} | ||
|
||
private boolean offsetToReplaceIsInBufferBeingWritten(int length) { | ||
return (currentOffset <= replacementOffset) && ((currentOffset + length) > replacementOffset); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
117 changes: 117 additions & 0 deletions
117
test/freenet/support/compress/SingleOffsetReplacingOutputStreamTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
package freenet.support.compress; | ||
|
||
import java.io.ByteArrayOutputStream; | ||
import java.io.IOException; | ||
import java.io.OutputStream; | ||
import java.util.Random; | ||
|
||
import org.junit.Test; | ||
|
||
import static org.hamcrest.MatcherAssert.assertThat; | ||
import static org.hamcrest.Matchers.equalTo; | ||
|
||
public class SingleOffsetReplacingOutputStreamTest { | ||
|
||
@Test | ||
public void allBytesBeforeTheOffsetAreUnchanged() throws IOException { | ||
filterOutputStreamWithOffsetAndReplacementWritingByteArrays(32768, 1, 65536); | ||
} | ||
|
||
@Test | ||
public void byteAtOffsetZeroCanBeReplaced() throws IOException { | ||
filterOutputStreamWithOffsetAndReplacementWritingByteArrays(10, 1, 0); | ||
} | ||
|
||
@Test | ||
public void byteAtOffsetOneCanBeReplaced() throws IOException { | ||
filterOutputStreamWithOffsetAndReplacementWritingByteArrays(10, 1, 1); | ||
} | ||
|
||
@Test | ||
public void byteAtEndOfBufferCanBeReplaced() throws IOException { | ||
filterOutputStreamWithOffsetAndReplacementWritingByteArrays(10, 1, 9); | ||
} | ||
|
||
@Test | ||
public void byteAtBeginningOfSecondBufferCanBeReplaced() throws IOException { | ||
filterOutputStreamWithOffsetAndReplacementWritingByteArrays(10, 2, 10); | ||
} | ||
|
||
@Test | ||
public void byteInTheMiddleOfSecondBufferCanBeReplaced() throws IOException { | ||
filterOutputStreamWithOffsetAndReplacementWritingByteArrays(10, 3, 15); | ||
} | ||
|
||
@Test | ||
public void byteAtOffsetZeroCanBeReplacedWhenWritingSingleBytes() throws IOException { | ||
filterOutputStreamWithOffsetAndReplacementWritingSingleBytes(4096, 16, 0); | ||
} | ||
|
||
@Test | ||
public void byteInMiddleOfStreamBeReplacedWhenWritingSingleBytes() throws IOException { | ||
filterOutputStreamWithOffsetAndReplacementWritingSingleBytes(8192, 8, 12345); | ||
} | ||
|
||
@Test | ||
public void byteAtEndOfStreamCanBeReplacedWhenWritingSingleBytes() throws IOException { | ||
filterOutputStreamWithOffsetAndReplacementWritingSingleBytes(16384, 4, 65535); | ||
} | ||
|
||
@Test | ||
public void byteAtStartOfBufferCanBeReplacedWhenWritingHalfBuffers() throws IOException { | ||
filterOutputStreamWithOffsetAndReplacementWritingHalfBuffers(4096, 16, 0); | ||
} | ||
|
||
@Test | ||
public void byteAtMiddleOfBufferCanBeReplacedWhenWritingHalfBuffers() throws IOException { | ||
filterOutputStreamWithOffsetAndReplacementWritingHalfBuffers(8192, 8, 12345); | ||
} | ||
|
||
@Test | ||
public void byteAtEndOfBufferCanBeReplacedWhenWritingHalfBuffers() throws IOException { | ||
filterOutputStreamWithOffsetAndReplacementWritingHalfBuffers(16384, 4, 65535); | ||
} | ||
|
||
private void filterOutputStreamWithOffsetAndReplacementWritingSingleBytes(int blockSize, int numberOfBlocks, int replacementOffset) throws IOException { | ||
filterOutputStreamWithOffsetAndReplacement(blockSize, numberOfBlocks, replacementOffset, (buffer, length, repetitions, outputStream) -> { | ||
for (int index = 0; index < length * repetitions; index++) { | ||
outputStream.write(buffer[index % length]); | ||
} | ||
}); | ||
} | ||
|
||
private void filterOutputStreamWithOffsetAndReplacementWritingByteArrays(int blockSize, int numberOfBlocks, int replacementOffset) throws IOException { | ||
filterOutputStreamWithOffsetAndReplacement(blockSize, numberOfBlocks, replacementOffset, (buffer, length, repetitions, outputStream) -> { | ||
for (int blockIndex = 0; blockIndex < repetitions; blockIndex++) { | ||
outputStream.write(buffer, 0, length); | ||
} | ||
}); | ||
} | ||
|
||
private void filterOutputStreamWithOffsetAndReplacementWritingHalfBuffers(int blockSize, int numberOfBlocks, int replacementOffset) throws IOException { | ||
filterOutputStreamWithOffsetAndReplacement(blockSize, numberOfBlocks, replacementOffset, ((buffer, length, repetitions, outputStream) -> { | ||
for (int blockIndex = 0; blockIndex < repetitions; blockIndex++) { | ||
outputStream.write(buffer, 0, length / 2); | ||
outputStream.write(buffer, length / 2, length - (length / 2)); | ||
} | ||
})); | ||
} | ||
|
||
private void filterOutputStreamWithOffsetAndReplacement(int blockSize, int numberOfBlocks, int replacementOffset, BufferToOutputStreamCopierStrategy bufferToOutputStreamCopierStrategy) throws IOException { | ||
byte[] bufferToWrite = new byte[blockSize]; | ||
new Random().nextBytes(bufferToWrite); | ||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); | ||
SingleOffsetReplacingOutputStream outputStream = new SingleOffsetReplacingOutputStream(byteArrayOutputStream, replacementOffset, (byte) (bufferToWrite[replacementOffset % blockSize] ^ 0xff)); | ||
bufferToOutputStreamCopierStrategy.copyBufferToOutput(bufferToWrite, blockSize, numberOfBlocks, outputStream); | ||
byte[] writtenBuffer = byteArrayOutputStream.toByteArray(); | ||
for (int offset = 0; offset < blockSize * numberOfBlocks; offset++) { | ||
assertThat("offset " + offset, writtenBuffer[offset], equalTo((byte) (bufferToWrite[offset % blockSize] ^ ((offset == replacementOffset) ? 0xff : 0x00)))); | ||
} | ||
} | ||
|
||
@FunctionalInterface | ||
private interface BufferToOutputStreamCopierStrategy { | ||
void copyBufferToOutput(byte[] buffer, int length, int repetitions, OutputStream outputStream) throws IOException; | ||
} | ||
|
||
} |