/*
Netwrapper - A library for easy networking in Java
Copyright (C) 2014, University of Lugano
This file is part of Netwrapper.
Netwrapper is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package ch.usi.dslab.bezerra.netwrapper.codecs;
import java.nio.ByteBuffer;
import net.jpountz.lz4.LZ4Compressor;
import net.jpountz.lz4.LZ4Factory;
import net.jpountz.lz4.LZ4FastDecompressor;
public class CodecLZ4 implements Codec {
private CodecUncompressedKryo codecUncompressed = new CodecUncompressedKryo();
private static ThreadLocal<LZ4Factory> lz4Factory = new ThreadLocal<LZ4Factory>() {
@Override protected LZ4Factory initialValue() {
LZ4Factory lz4Factory = LZ4Factory.fastestInstance();
return lz4Factory;
}
};
@Override
public byte[] getBytes(Object obj) {
final LZ4Factory factory = lz4Factory.get();
byte[] data = codecUncompressed.getBytes(obj);
final int decompressedLength = data.length;
LZ4Compressor compressor = factory.fastCompressor();
int maxCompressedLength = compressor.maxCompressedLength(decompressedLength);
byte[] compressed = new byte[4 + maxCompressedLength];
compressor.compress(data, 0, decompressedLength, compressed, 4, maxCompressedLength);
return compressed;
}
@Override
public Object createObjectFromBytes(byte[] bytes) {
// decompress
// received format = | decompressed len (4B) | compressed bytes (bytes.length - 4) |
final LZ4Factory factory = lz4Factory.get();
int compressedLength = bytes.length - 4;
ByteBuffer compbb = ByteBuffer.wrap(bytes);
int decompressedLength = compbb.getInt();
LZ4FastDecompressor decompressor = factory.fastDecompressor();
byte[] decompressedBytes = new byte[decompressedLength];
int compressedLength2 = decompressor.decompress(bytes, 4, decompressedBytes, 0, decompressedLength);
assert(compressedLength == compressedLength2);
return codecUncompressed.createObjectFromBytes(decompressedBytes);
}
@Override
public ByteBuffer getByteBufferWithLengthHeader(Object obj) {
final LZ4Factory factory = lz4Factory.get();
byte[] data = codecUncompressed.getBytes(obj);
final int decompressedLength = data.length;
// compress data
LZ4Compressor compressor = factory.fastCompressor();
int maxCompressedLength = compressor.maxCompressedLength(decompressedLength);
byte[] compressed = new byte[8 + maxCompressedLength];
int compressedLength = compressor.compress(data, 0, decompressedLength, compressed, 8, maxCompressedLength);
// tcpreceiver decoder expects | x = message length (4B) | message (x bytes) |
// we are providing | x = y + 4 (4B) | dec length (4 bytes) | comp. message (y bytes) |
//
//
//
// which also means:
//
// [-------------------- LENGTH IN HEADER -----------------------]
// [-----------COMPRESSED LENGTH---------]
// received format = | compressed length + 4 (4B) | decompressed len (4B) | compressed bytes (bytes.length - 4) |
ByteBuffer bb = ByteBuffer.wrap(compressed);
bb.putInt(4 + compressedLength); // length of the whole message, excluding length header
bb.putInt(decompressedLength);
bb.position(0);
bb.limit(8 + compressedLength);
return bb;
}
@Override
public Object createObjectFromByteBufferWithLengthHeader(ByteBuffer buffer) {
// decompress
// [-------------------- LENGTH IN HEADER -----------------------]
// [-----------COMPRESSED LENGTH---------]
// received format = | compressed length + 4 (4B) | decompressed len (4B) | compressed bytes (bytes.length - 4) |
final LZ4Factory factory = lz4Factory.get();
int compressedLength = buffer.getInt() - 4;
int decompressedLength = buffer.getInt();
int compressedBytesOffset = buffer.arrayOffset() + buffer.position();
byte[] compressedBytes = buffer.array();
LZ4FastDecompressor decompressor = factory.fastDecompressor();
byte[] decompressedBytes = new byte[decompressedLength];
int compressedLength2 = decompressor.decompress(compressedBytes, compressedBytesOffset, decompressedBytes, 0, decompressedLength);
assert(compressedLength == compressedLength2);
return codecUncompressed.createObjectFromBytes(decompressedBytes);
}
@Override
public Object deepDuplicate(Object o) {
return codecUncompressed.deepDuplicate(o);
}
}