/******************************************************************************* * (c) 2003-2003 CognitiveWeb. All Rights Reserved. Contact * information for CognitiveWeb is available at * http://www.CognitiveWeb.org * * Portions Copyright (c) 2002-2003 Bryan Thompson. * * Licensed 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 de.mxro.thrd.jdbm2V22.helper; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; /** * Packing utility for non-negative <code>long</code> values. * <p> * Originaly distributed under CognitiveWeb Open Source License Version, * got permission to relicense under Apache2 license * * @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a> * @version $Id: LongPacker.java,v 1.4 2006/05/02 16:20:07 thompsonbry Exp $ */ public final class LongPacker { /** * Packs a non-negative long value into the minimum #of bytes in which the * value can be represented and writes those bytes onto the output stream. * The first byte determines whether or not the long value was packed and, * if packed, how many bytes were required to represent the packed long * value. When the high bit of the first byte is a one (1), then the long * value could not be packed and the long value is found by clearing the * high bit and interpreting the first byte plus the next seven (7) bytes as * a long. Otherwise the next three (3) bits are interpreted as an unsigned * integer giving the #of bytes (nbytes) required to represent the packed * long value. To recover the long value the high nibble is cleared and the * first byte together with the next nbytes are interpeted as an unsigned * long value whose leading zero bytes were not written. * * <pre> * * [0|1|2|3|4|5|6|7] * 1 - - - nbytes = 8, clear high bit and interpret this plus the next 7 bytes as a long. * 0 1 1 1 nbytes = 7, clear high nibble and interpret this plus the next 6 bytes as a long. * 0 1 1 0 nbytes = 6, clear high nibble and interpret this plus the next 5 bytes as a long. * 0 1 0 1 nbytes = 5, clear high nibble and interpret this plus the next 4 bytes as a long. * 0 1 0 0 nbytes = 4, clear high nibble and interpret this plus the next 3 bytes as a long. * 0 0 1 1 nbytes = 3, clear high nibble and interpret this plus the next 3 bytes as a long. * 0 0 1 0 nbytes = 2, clear high nibble and interpret this plus the next byte as a long. * 0 0 0 1 nbytes = 1, clear high nibble. value is the low nibble. * * </pre> */ static final public int packLong( DataOutput os, long v ) throws IOException { /* * You can only pack non-negative long values with this method. */ if( v < 0 ) { throw new IllegalArgumentException( "negative value: v="+v ); } /* * If the high byte is non-zero then we will write the value as a normal * long and return nbytes == 8. This case handles large positive long * values. */ if( ( v >> 56 ) != 0 ) { os.write( (byte)((0xff & (v >> 56))|0x80) ); // note: set the high bit. os.write( (byte)(0xff & (v >> 48)) ); os.write( (byte)(0xff & (v >> 40)) ); os.write( (byte)(0xff & (v >> 32)) ); os.write( (byte)(0xff & (v >> 24)) ); os.write( (byte)(0xff & (v >> 16)) ); os.write( (byte)(0xff & (v >> 8)) ); os.write( (byte)(0xff & v) ); return 8; } // #of nibbles required to represent the long value. final int nnibbles = getNibbleLength( v ); // Is [nnibbles] even? (If it is even then we need to pad out an extra zero // nibble in the first byte.) final boolean evenNibbleCount = ( nnibbles == ( ( nnibbles >> 1 ) << 1 ) ); // #of bytes required to represent the long value (plus the header nibble). final int nbytes = ( ( nnibbles +1 ) >> 1 ) + (evenNibbleCount?1:0); int nwritten = 0; if( evenNibbleCount ) { /* * An even nibble count requires that we pad the low nibble of the * first byte with zeros. */ // header byte. low nibble is empty. byte b = (byte) ( nbytes << 4 ); os.write( b ); nwritten++; // remaining bytes containing the packed value. for( int i=(nnibbles-2)<<2; i>=0; i-=8 ) { b = (byte) (0xff & (v >> i)); os.write( b ); nwritten++; } } else { /* * An odd nibble count means that we pack the first nibble of the * long value into the low nibble of the header byte. In this case * the first nibble will always be the low nibble of the first * non-zero byte in the long value (the high nibble of that byte * must be zero since there is an odd nibble count). */ byte highByte = (byte) (0xff & (v >> ((nbytes-1)*8) )); byte b = (byte) ( ( nbytes << 4 ) | highByte ); os.write( b ); nwritten++; for( int i=(nnibbles-3)<<2; i>=0; i-=8 ) { b = (byte) (0xff & (v >> i)); os.write( b ); nwritten++; } } return nwritten; } /** * Return the #of non-zero nibbles, counting from the first non-zero nibble * in the long value. A value of <code>0L</code> is considered to be one * nibble for our purposes. * * @param v * The long value. * * @return The #of nibbles in [1:16]. */ static final protected int getNibbleLength( long v ) { for( int i=56, j=16; i>=0; i-=8, j-=2 ) { if( (0xf0 & (v >> i)) != 0 ) return j; if( (0x0f & (v >> i)) != 0 ) return j-1; } if( v != 0 ) throw new AssertionError( "v="+v ); return 1; // value is zero, which is considered to be one nibble for our purposes. } /** * Unpack a long value from the input stream. * * @param is The input stream. * * @return The long value. * * @throws IOException */ static final public long unpackLong( DataInput is ) throws IOException { int b = is.readByte(); int nbytes; long l; if( ( b & 0x80 ) != 0 ) { // high bit is set. nbytes = 8; // use 8 bytes (this one plus the next 7). l = b & 0x7f; // clear the high bit - the rest of the byte is the start value. } else { // high bit is clear. nbytes = b >> 4; // nbytes is the upper nibble. (right shift one nibble). l = b & 0x0f; // starting value is lower nibble (clear the upper nibble). } for( int i=1; i<nbytes; i++ ) { // Read the next byte. b = is.readByte(); // readByte( is ); // Shift the existing value one byte left and add into the low (unsigned) byte. l = (l << 8) + (0xff & b); } return l; } static final public int packInt( DataOutput os, int v ) throws IOException { return packLong(os,v); } static final public int unpackInt( DataInput is ) throws IOException{ long val = unpackLong(is); if(val>Integer.MAX_VALUE) throw new InternalError("too big int: "+val); return (int) val; } }