197 lines
5.3 KiB
C++
197 lines
5.3 KiB
C++
/***************************************************************************
|
|
* Copyright (C) 2008-2009 by Valerio Pilo *
|
|
* valerio@kmess.org *
|
|
* *
|
|
* This program 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 program 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 General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU Lesser General Public *
|
|
* License along with this program; if not, write to the *
|
|
* Free Software Foundation, Inc., *
|
|
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
|
***************************************************************************/
|
|
|
|
#include "isfqt-internal.h"
|
|
#include "bitpacking.h"
|
|
|
|
#include <math.h>
|
|
|
|
#include <QDebug>
|
|
|
|
|
|
using namespace Isf::Compress;
|
|
|
|
|
|
|
|
/**
|
|
* Get the most appropriate block size for the given data.
|
|
*
|
|
* @param data Data to analyze
|
|
* @return Block size
|
|
*/
|
|
quint8 BitPackingAlgorithm::blockSize( const QList<qint64>& data )
|
|
{
|
|
quint8 blockSize = 0;
|
|
qint64 num;
|
|
|
|
for( qint32 index = 0; index < data.size(); ++index )
|
|
{
|
|
num = data[ index ];
|
|
|
|
if( num < 0 )
|
|
{
|
|
// We need the positive numbers: the +1 is for the sign bit
|
|
num += - (num + 1);
|
|
}
|
|
|
|
// Shift right the value by blockSize bits until it becomes zero:
|
|
// we need to detect the maximum bitwise size required to store
|
|
// the numbers in the data array.
|
|
num >>= blockSize;
|
|
while( num )
|
|
{
|
|
++blockSize;
|
|
num >>= 1;
|
|
}
|
|
}
|
|
|
|
// The sign bit always takes up a bit
|
|
++blockSize;
|
|
|
|
return blockSize;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Compress data with the Bit Packing algorithm.
|
|
*
|
|
* @param encodedData Byte array where to store the compressed data
|
|
* @param blockSize Block size to use for compression
|
|
* @param source List of values to compress
|
|
* @return bool
|
|
*/
|
|
bool BitPackingAlgorithm::deflate( QByteArray& encodedData, quint8 blockSize, const QList<qint64>& source )
|
|
{
|
|
if( blockSize > 64 )
|
|
{
|
|
qWarning() << "A block size of" << blockSize << "is too high!";
|
|
blockSize = 64; // Fuck it :P
|
|
}
|
|
|
|
quint8 blockSizeLeft; // Remaining bits to encode of the current value
|
|
quint8 freeBits = 8; // Free bits of the current byte, in LSB order
|
|
quint8 signMask = 1 << ( blockSize - 1 ); // Mask to add the sign bit
|
|
|
|
quint8 byte = 0; // Byte to add to the array
|
|
qint64 currentValue;
|
|
|
|
for( qint64 index = 0; index < source.count(); ++index )
|
|
{
|
|
currentValue = source[ index ];
|
|
|
|
if( currentValue < 0 )
|
|
{
|
|
currentValue |= signMask;
|
|
}
|
|
|
|
blockSizeLeft = blockSize;
|
|
|
|
if( freeBits >= blockSizeLeft )
|
|
{
|
|
freeBits -= blockSizeLeft;
|
|
byte |= currentValue << freeBits;
|
|
|
|
encodedData.append( byte );
|
|
|
|
byte = 0;
|
|
freeBits = 8;
|
|
}
|
|
else
|
|
{
|
|
// Fill the current byte
|
|
quint64 mask = 0xFFFFFFFF >> ( 32 - blockSizeLeft );
|
|
byte |= currentValue >> ( blockSizeLeft - freeBits );
|
|
|
|
encodedData.append( byte );
|
|
|
|
byte = 0;
|
|
blockSizeLeft -= freeBits;
|
|
mask >>= freeBits;
|
|
currentValue &= mask;
|
|
|
|
// Then fill all the next bytes required to encode the current value,
|
|
// except the last one
|
|
while( blockSizeLeft > 8 )
|
|
{
|
|
blockSizeLeft -= 8;
|
|
encodedData.append( currentValue >> blockSizeLeft );
|
|
|
|
mask >>= 8;
|
|
currentValue &= mask;
|
|
}
|
|
|
|
// Finally, add the last byte
|
|
freeBits = 8 - blockSizeLeft;
|
|
encodedData.append( currentValue << freeBits );
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Decompress data with the Bit Packing algorithm.
|
|
*
|
|
* @param source Data source where to read the compressed bytes
|
|
* @param length Number of items to read
|
|
* @param blockSize Block size to use for compression
|
|
* @param decodedData List where to place decompressed values
|
|
* @return bool
|
|
*/
|
|
bool BitPackingAlgorithm::inflate( DataSource* source, quint64 length, quint8 blockSize, QList<qint64>& decodedData )
|
|
{
|
|
if( blockSize > 64 )
|
|
{
|
|
qWarning() << "A block size of" << blockSize << "is too high!";
|
|
blockSize = 64; // Fuck it :P
|
|
}
|
|
|
|
if( source->atEnd() )
|
|
{
|
|
qWarning() << "Cannot inflate: no more bits available!";
|
|
return true;
|
|
}
|
|
|
|
qint64 value;
|
|
quint32 index = 0;
|
|
quint64 signMask = (quint64)( 0xFFFFFFFF * pow( 2, blockSize - 1 ) ) & 0xFFFFFFFF;
|
|
|
|
while( index++ < length )
|
|
{
|
|
value = source->getBits( blockSize );
|
|
|
|
// If the mask matches, the sign bit is set, so ORing value and mask will
|
|
// set all leftmost bits to 1, making it a real 64bit signed integer
|
|
if( value & signMask )
|
|
{
|
|
value |= signMask;
|
|
}
|
|
|
|
decodedData.append( value );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|