blob: 617e04eafa165f2f093472b4de9177ba7c146082 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.cassandra.io.compress;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.StandardOpenOption;
import java.util.Arrays;
import java.util.Collections;
import java.util.Random;
import com.google.common.io.Files;
import static org.junit.Assert.*;
import org.junit.Assert;
import org.junit.Test;
import org.apache.cassandra.io.util.RandomAccessReader;
import org.apache.cassandra.utils.ByteBufferUtil;
public class CompressorTest
{
ICompressor compressor;
ICompressor[] compressors = new ICompressor[] {
LZ4Compressor.create(Collections.<String, String>emptyMap()),
DeflateCompressor.create(Collections.<String, String>emptyMap()),
SnappyCompressor.create(Collections.<String, String>emptyMap())
};
@Test
public void testAllCompressors() throws IOException
{
for (ICompressor compressor : compressors)
{
this.compressor = compressor;
testEmptyArray();
testLongArray();
testShortArray();
testMappedFile();
}
}
public void testArrayUncompress(byte[] data, int off, int len) throws IOException
{
final int inOffset = 2;
ByteBuffer src = makeBB(len + inOffset);
src.position(inOffset);
src.put(data, off, len);
src.flip().position(inOffset);
final int outOffset = 3;
final ByteBuffer compressed = makeBB(outOffset + compressor.initialCompressedBufferLength(len));
fillBBWithRandom(compressed);
compressed.position(outOffset);
compressor.compress(src, compressed);
compressed.flip().position(outOffset);
final int restoreOffset = 5;
final byte[] restored = new byte[restoreOffset + len];
new Random().nextBytes(restored);
// need byte[] representation which direct buffers don't have
byte[] compressedBytes = new byte[compressed.capacity()];
ByteBufferUtil.arrayCopy(compressed, outOffset, compressedBytes, outOffset, compressed.limit() - outOffset);
final int decompressedLength = compressor.uncompress(compressedBytes, outOffset, compressed.remaining(), restored, restoreOffset);
assertEquals(decompressedLength, len);
assertArrayEquals(Arrays.copyOfRange(data, off, off + len),
Arrays.copyOfRange(restored, restoreOffset, restoreOffset + decompressedLength));
}
public void testArrayUncompress(byte[] data) throws IOException
{
testArrayUncompress(data, 0, data.length);
}
public void testEmptyArray() throws IOException
{
testArrayUncompress(new byte[0]);
}
public void testShortArray() throws UnsupportedEncodingException, IOException
{
testArrayUncompress("Cassandra".getBytes("UTF-8"), 1, 7);
}
public void testLongArray() throws UnsupportedEncodingException, IOException
{
byte[] data = new byte[1 << 20];
testArrayUncompress(data, 13, 1 << 19);
new Random(0).nextBytes(data);
testArrayUncompress(data, 13, 1 << 19);
}
public void testMappedFile() throws IOException
{
byte[] data = new byte[1 << 20];
new Random().nextBytes(data);
ByteBuffer src = makeBB(data.length);
src.put(data);
src.flip();
// create a temp file
File temp = File.createTempFile("tempfile", ".tmp");
temp.deleteOnExit();
// Prepend some random bytes to the output and compress
final int outOffset = 3;
byte[] garbage = new byte[outOffset + compressor.initialCompressedBufferLength(data.length)];
new Random().nextBytes(garbage);
ByteBuffer dest = makeBB(outOffset + compressor.initialCompressedBufferLength(data.length));
dest.put(garbage);
dest.clear();
dest.position(outOffset);
compressor.compress(src, dest);
int compressedLength = dest.position() - outOffset;
FileChannel channel = FileChannel.open(temp.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE);
dest.clear();
channel.write(dest);
MappedByteBuffer mappedData = Files.map(temp);
ByteBuffer result = makeBB(data.length + 100);
mappedData.position(outOffset).limit(outOffset + compressedLength);
compressor.uncompress(mappedData, result);
channel.close();
result.flip();
Assert.assertEquals(data.length, result.limit());
for (int i = 0; i < result.limit(); i++)
{
Assert.assertEquals("Decompression mismatch at byte "+i, data[i], result.get());
}
}
@Test
public void testLZ4ByteBuffers() throws IOException
{
compressor = LZ4Compressor.create(Collections.<String, String>emptyMap());
testByteBuffers();
}
@Test
public void testDeflateByteBuffers() throws IOException
{
compressor = DeflateCompressor.create(Collections.<String, String>emptyMap());
testByteBuffers();
}
@Test
public void testSnappyByteBuffers() throws IOException
{
compressor = SnappyCompressor.create(Collections.<String, String>emptyMap());
testByteBuffers();
}
private void testByteBuffers() throws IOException
{
assert compressor.supports(BufferType.OFF_HEAP);
assert compressor.supports(compressor.preferredBufferType());
for (BufferType in: BufferType.values())
if (compressor.supports(in))
for (BufferType comp: BufferType.values())
if (compressor.supports(comp))
for (BufferType out: BufferType.values())
if (compressor.supports(out))
testByteBuffers(in, comp, out);
}
private void testByteBuffers(BufferType typeIn, BufferType typeComp, BufferType typeOut) throws IOException
{
try
{
int n = RandomAccessReader.DEFAULT_BUFFER_SIZE;
byte[] srcData = new byte[n];
new Random().nextBytes(srcData);
final int inOffset = 2;
ByteBuffer src = typeIn.allocate(inOffset + n + inOffset);
src.position(inOffset);
src.put(srcData, 0, n);
src.flip().position(inOffset);
int outOffset = 5;
ByteBuffer compressed = typeComp.allocate(outOffset + compressor.initialCompressedBufferLength(srcData.length) + outOffset);
byte[] garbage = new byte[compressed.capacity()];
new Random().nextBytes(garbage);
compressed.put(garbage);
compressed.position(outOffset).limit(compressed.capacity() - outOffset);
compressor.compress(src, compressed);
assertEquals(inOffset + n, src.position());
assertEquals(inOffset + n, src.limit());
assertEquals(compressed.capacity() - outOffset, compressed.limit());
compressed.flip().position(outOffset);
int len = compressed.remaining();
ByteBuffer result = typeOut.allocate(inOffset + n + inOffset);
result.position(inOffset).limit(result.capacity() - inOffset);
compressor.uncompress(compressed, result);
assertEquals(outOffset + len, compressed.position());
assertEquals(outOffset + len, compressed.limit());
assertEquals(result.capacity() - inOffset, result.limit());
int decompressed = result.position() - inOffset;
assert decompressed == n : "Failed uncompressed size";
for (int i = 0; i < n; ++i)
assert srcData[i] == result.get(inOffset + i) : "Failed comparison on index: " + i;
}
catch (Throwable e)
{
throw new AssertionError("Failed testing compressor " + compressor.getClass().getSimpleName() + " with buffer types in:" + typeIn + " compressed:" + typeComp + " out:" + typeOut, e);
}
}
private ByteBuffer makeBB(int size)
{
return compressor.preferredBufferType().allocate(size);
}
private void fillBBWithRandom(ByteBuffer dest)
{
byte[] random = new byte[dest.capacity()];
new Random().nextBytes(random);
dest.clear();
dest.put(random);
}
}