Files
kmess/contrib/isf-qt/src/data/algorithms/bitpacking.cpp
T
2010-07-26 08:45:10 +00:00

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;
}