MzMLPeaksEncoder.java
/*
* (C) Copyright 2015-2016 by MSDK Development Team
*
* This software is dual-licensed under either
*
* (a) the terms of the GNU Lesser General Public License version 2.1 as published by the Free
* Software Foundation
*
* or (per the licensee's choosing)
*
* (b) the terms of the Eclipse Public License v1.0 as published by the Eclipse Foundation.
*/
package io.github.msdk.io.mzml.data;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
import java.util.zip.Deflater;
import org.apache.commons.codec.binary.Base64;
import io.github.msdk.MSDKException;
import io.github.msdk.io.mzml.util.MSNumpress;
/**
* <p>Abstract MzMLPeaksEncoder class.</p>
*
*/
public abstract class MzMLPeaksEncoder {
/**
* <p>encodeDouble.</p>
*
* @param data an array of double.
* @param compression a {@link io.github.msdk.io.mzml.data.MzMLCompressionType} object.
* @return an array of byte.
* @throws io.github.msdk.MSDKException if any.
*/
public static byte[] encodeDouble(double[] data, MzMLCompressionType compression)
throws MSDKException {
byte[] encodedData = null;
int encodedBytes;
// MSNumpress compression if required
switch (compression) {
case NUMPRESS_LINPRED:
case NUMPRESS_LINPRED_ZLIB:
// Set encodedData's array to the maximum possible size, truncate it later
encodedData = new byte[8 + (data.length * 5)];
encodedBytes = MSNumpress.encodeLinear(data, data.length, encodedData,
MSNumpress.optimalLinearFixedPoint(data, data.length));
if (encodedBytes < 0)
throw new MSDKException("MSNumpress linear encoding failed");
encodedData = Arrays.copyOf(encodedData, encodedBytes);
break;
case NUMPRESS_POSINT:
case NUMPRESS_POSINT_ZLIB:
encodedData = new byte[data.length * 5];
encodedBytes = MSNumpress.encodePic(data, data.length, encodedData);
if (encodedBytes < 0)
throw new MSDKException("MSNumpress positive integer encoding failed");
encodedData = Arrays.copyOf(encodedData, encodedBytes);
break;
case NUMPRESS_SHLOGF:
case NUMPRESS_SHLOGF_ZLIB:
encodedData = new byte[8 + (data.length * 2)];
encodedBytes = MSNumpress.encodeSlof(data, data.length, encodedData,
MSNumpress.optimalSlofFixedPoint(data, data.length));
if (encodedBytes < 0)
throw new MSDKException("MSNumpress short floating logarithm encoding failed");
encodedData = Arrays.copyOf(encodedData, encodedBytes);
break;
default:
ByteBuffer buffer = ByteBuffer.allocate(data.length * 8);
buffer.order(ByteOrder.LITTLE_ENDIAN);
for (double d : data) {
buffer.putDouble(d);
}
encodedData = buffer.array();
break;
}
// Zlib Compression if necessary
switch (compression) {
case NUMPRESS_LINPRED_ZLIB:
case NUMPRESS_POSINT_ZLIB:
case NUMPRESS_SHLOGF_ZLIB:
case ZLIB:
byte[] tmp = ZlibCompress(encodedData);
return Base64.encodeBase64(tmp);
default:
return Base64.encodeBase64(encodedData);
}
}
/**
* <p>encodeFloat.</p>
*
* @param data an array of float.
* @param compression a {@link io.github.msdk.io.mzml.data.MzMLCompressionType} object.
* @return an array of byte.
* @throws io.github.msdk.MSDKException if any.
*/
public static byte[] encodeFloat(float[] data, MzMLCompressionType compression)
throws MSDKException {
byte[] encodedData = null;
// MSNumpress compression if required
switch (compression) {
case NUMPRESS_LINPRED:
case NUMPRESS_LINPRED_ZLIB:
case NUMPRESS_POSINT:
case NUMPRESS_POSINT_ZLIB:
case NUMPRESS_SHLOGF:
case NUMPRESS_SHLOGF_ZLIB:
throw new MSDKException("MSNumpress compression not supported for float values");
default:
ByteBuffer buffer = ByteBuffer.allocate(data.length * 8);
buffer.order(ByteOrder.LITTLE_ENDIAN);
for (float f : data) {
buffer.putFloat(f);
}
encodedData = buffer.array();
break;
}
// Zlib Compression if necessary
switch (compression) {
case ZLIB:
byte[] tmp = ZlibCompress(encodedData);
return Base64.encodeBase64(tmp);
default:
return Base64.encodeBase64(encodedData);
}
}
/**
* Compressed source data using the Deflate algorithm.
*
* @param uncompressedData Data to be compressed
* @return Compressed data
*/
private static byte[] ZlibCompress(byte[] uncompressedData) {
byte[] data = null; // Decompress the data
// create a temporary byte array big enough to hold the compressed data
// with the worst compression (the length of the initial (uncompressed) data)
// EDIT: if it turns out this byte array was not big enough, then double its size and try again.
byte[] temp = new byte[uncompressedData.length / 2];
int compressedBytes = temp.length;
while (compressedBytes == temp.length) {
// compress
temp = new byte[temp.length * 2];
Deflater compresser = new Deflater();
compresser.setInput(uncompressedData);
compresser.finish();
compressedBytes = compresser.deflate(temp);
}
// create a new array with the size of the compressed data (compressedBytes)
data = new byte[compressedBytes];
System.arraycopy(temp, 0, data, 0, compressedBytes);
return data;
}
}