From: Bryan T. <tho...@us...> - 2006-03-09 18:37:16
|
Update of /cvsroot/cweb/extser/src/test/org/CognitiveWeb/extser In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv22010/src/test/org/CognitiveWeb/extser Added Files: ShortPackerTestCase.java TestExtensibleSerializer.java LongPackerTestCase.java TestAll.java Log Message: Initial import. The extensible serialization framework was also contributed to the jdbm project and exists under that license as well. --- NEW FILE: ShortPackerTestCase.java --- /** The Notice below must appear in each file of the Source Code of any copy you distribute of the Licensed Product. Contributors to any Modifications may add their own copyright notices to identify their own contributions. License: The contents of this file are subject to the CognitiveWeb Open Source License Version 1.0 (the License). You may not copy or use this file, in either source code or executable form, except in compliance with the License. You may obtain a copy of the License from http://www.CognitiveWeb.org/legal/license/ Software distributed under the License is distributed on an AS IS basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. Copyrights: Portions created by or assigned to CognitiveWeb, Inc. are Copyright (c) 2003-2003 CognitiveWeb, Inc. All Rights Reserved. Contact information for CognitiveWeb, Inc. is available at http://www.CognitiveWeb.org Portions Copyright (c) 2002-2003 Bryan Thompson. Acknowledgements: Special thanks to the developers of the Jabber Open Source License 1.0 (JOSL), from which this License was derived. This License contains terms that differ from JOSL. Special thanks to the CognitiveWeb Open Source Contributors for their suggestions and support of the Cognitive Web. Modifications: */ /* * Created on Oct 28, 2005 */ package org.CognitiveWeb.extser; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.Random; import org.CognitiveWeb.extser.ShortPacker; import junit.framework.TestCase; /** * TestAll suite for {@link ShortPacker}. * * @author thompsonbry */ public class ShortPackerTestCase extends TestCase { /** * */ public ShortPackerTestCase() { super(); } /** * @param name */ public ShortPackerTestCase(String name) { super(name); } /** * Unpacks a short value. * * @param expected The expected long value. * * @param packed The packed byte[]. * * @throws IOException * If there was not enough data. * * @throws junit.framework.AssertionFailedError * If there is too much data. */ public void doUnpackTest( short expected, byte[] packed ) throws IOException { ByteArrayInputStream bais = new ByteArrayInputStream( packed ); short actual = ShortPacker.unpackShort( new DataInputStream( bais ) ); assertEquals( "value", expected, actual ); assertTrue( "Expecting EOF", bais.read() == -1 ); } /* * A short in [0:127] is packed into one byte. Larger values are packed into * two bytes. The high bit of the first byte is set if the value was packed * into two bytes. If the bit is set, clear the high bit, read the next * byte, and interpret the two bytes as a short value. Otherwise interpret * the byte as a short value. */ public void testUnpack() throws IOException { doUnpackTest( (short) 0x0, new byte[]{(byte)0x0} ); doUnpackTest( (short) 0x1, new byte[]{(byte)0x1} ); doUnpackTest( (short) 0x7, new byte[]{(byte)0x7} ); doUnpackTest( (short) 0xf, new byte[]{(byte)0xf} ); doUnpackTest( (short) 0x70, new byte[]{(byte)0x70} ); doUnpackTest( (short) 0x7f, new byte[]{(byte)0x7f} ); doUnpackTest( (short) 0x80, new byte[]{(byte)0x80, (byte)0x80} ); doUnpackTest( (short) 0x8e, new byte[]{(byte)0x80, (byte)0x8e} ); } public void doPackTest( short v, byte[] expected ) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); final int nbytes = ShortPacker.packShort( new DataOutputStream( baos ), v ); final byte[] actual = baos.toByteArray(); assertEquals( "nbytes", expected.length, nbytes ); assertEquals( "bytes", expected, actual ); } public void testPack() throws IOException { // [0:127] should be packed into one byte. doPackTest( (short) 0x00, new byte[]{(byte)0x0} ); doPackTest( (short) 0x01, new byte[]{(byte)0x1} ); doPackTest( (short) 0x02, new byte[]{(byte)0x2} ); doPackTest( (short) 0x0e, new byte[]{(byte)0xe} ); doPackTest( (short) 0x0f, new byte[]{(byte)0xf} ); doPackTest( (short) 0x10, new byte[]{(byte)0x10 }); doPackTest( (short) 0x11, new byte[]{(byte)0x11 }); doPackTest( (short) 0x1f, new byte[]{(byte)0x1f }); doPackTest( (short) 0x20, new byte[]{(byte)0x20 }); doPackTest( (short) 0x70, new byte[]{(byte)0x70 }); doPackTest( (short) 0x7f, new byte[]{(byte)0x7f }); // high nibble of the short value is zero. doPackTest( (short) 0x80, new byte[]{(byte)0x80, (byte)0x80 }); doPackTest( (short) 0xff, new byte[]{(byte)0x80, (byte)0xff }); doPackTest( (short) 0x100, new byte[]{(byte)0x81, (byte)0x00 }); doPackTest( (short) 0x101, new byte[]{(byte)0x81, (byte)0x01 }); doPackTest( (short) 0x121, new byte[]{(byte)0x81, (byte)0x21 }); doPackTest( (short) 0x1ee, new byte[]{(byte)0x81, (byte)0xee }); doPackTest( (short) 0x1ff, new byte[]{(byte)0x81, (byte)0xff }); doPackTest( (short) 0xfff, new byte[]{(byte)0x8f, (byte)0xff }); } public void test_rejectNegatives() throws IOException { try { doPackTest( (short) 0x8fff, new byte[]{}); fail( "Expecting: "+IllegalArgumentException.class ); } catch( IllegalArgumentException ex ) { System.err.println( "Ignoring expected exception: "+ex ); } try { doPackTest( (short) 0xffff, new byte[]{}); fail( "Expecting: "+IllegalArgumentException.class ); } catch( IllegalArgumentException ex ) { System.err.println( "Ignoring expected exception: "+ex ); } } public static final long SIGN_MASK = 11<<15; public void testHighBit() { assertTrue( "sign bit", ( (short)-1 & SIGN_MASK ) != 0 ); assertFalse( "sign bit", ( (short) 0 & SIGN_MASK ) != 0 ); } private interface ShortGenerator { public short nextShort(); } /** * All long values in sequence starting from the given start value * and using the given increment. * @author thompsonbry */ private static class Sequence implements ShortGenerator { short _start, _inc, _next; public Sequence( short start, short inc ) { _start = start; _inc = inc; _next = start; } public short nextShort() { if( _next == Short.MAX_VALUE ) { throw new RuntimeException( "No more short values."); } short v = _next; _next += _inc; return v; } } /** * Random short values (16 bits of random short), including negatives, * with a uniform distribution. * * @author thompsonbry */ private static class RandomShort implements ShortGenerator { Random _rnd; public RandomShort( Random rnd ) { _rnd = rnd; } public short nextShort() { return (short)_rnd.nextInt(); // truncate. } } /** * Run a large #of pack/unpack operations on a sequence of short values to * demonstrate correctness in that sequence. The sequence is the short values * from -1 to {@link Short#MAX_VALUE} by one (dense coverage). * * @throws IOException */ public void testStressSequence() throws IOException { // dense coverage of the first 1M values. doStressTest( Short.MAX_VALUE+1, new Sequence( (short)-1, (short)1 ) ); } /** * Run a large #of random pack/unpack operations to sample the space while * showing correctness on those samples. * * @throws IOException */ public void testStressRandom() throws IOException { // test on random long values. doStressTest( 0xffff, new RandomShort( new Random() ) ); } /** * Run a stress test. Writes some information of possible interest onto * System.err. * * @param ntrials #of trials. * * @param g Generator for the long values. * * @throws IOException */ public void doStressTest( int ntrials, ShortGenerator g ) throws IOException { long nwritten = 0L; long packlen = 0L; long minv = Short.MAX_VALUE, maxv = Short.MIN_VALUE; for( int i=0; i<ntrials; i++ ) { short expected = g.nextShort(); if( expected < 0L ) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream( baos ); try { ShortPacker.packShort( dos, expected ); fail( "Expecting rejection of negative value: val="+expected ); } catch( IllegalArgumentException ex ) { // System.err.println( "Ingoring expected exception: "+ex ); } } else { if( expected > maxv ) maxv = expected; if( expected < minv ) minv = expected; ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream( baos ); int nbytesActual = ShortPacker.packShort( dos, expected ); byte[] packed = baos.toByteArray(); final int nbytesExpected = ShortPacker.getNBytes( packed[ 0 ] ); ByteArrayInputStream bais = new ByteArrayInputStream( packed ); DataInputStream dis = new DataInputStream( bais ); final short actual = ShortPacker.unpackShort( dis ); assertEquals( "trial="+i, expected, actual ); assertEquals( "trial="+i+", v="+expected+", nbytes", nbytesExpected, nbytesActual ); assertEquals( "trial="+i+", v="+expected+", nbytes", nbytesExpected, packed.length ); packlen += packed.length; // total #of packed bytes. nwritten++; // count #of non-negative random values. } } System.err.println( "\nWrote "+nwritten+" non-negative long values." ); System.err.println( "minv="+minv+", maxv="+maxv ); System.err.println( "#packed bytes ="+packlen ); System.err.println( "#bytes if not packed="+(nwritten * 8)); long nsaved = ( nwritten * 8 ) - packlen; System.err.println ("#bytes saved ="+nsaved); System.err.println( "%saved by packing ="+nsaved/(nwritten*8f)*100+"%"); } public static void assertEquals( String msg, byte[] expected, byte[] actual ) { assertEquals( msg+": length", expected.length, actual.length ); for( int i=0; i<expected.length; i++ ) { assertEquals( msg+": byte[i="+i+"]", expected[i], actual[i] ); } } } --- NEW FILE: TestAll.java --- /** The Notice below must appear in each file of the Source Code of any copy you distribute of the Licensed Product. Contributors to any Modifications may add their own copyright notices to identify their own contributions. License: The contents of this file are subject to the CognitiveWeb Open Source License Version 1.0 (the License). You may not copy or use this file, in either source code or executable form, except in compliance with the License. You may obtain a copy of the License from http://www.CognitiveWeb.org/legal/license/ Software distributed under the License is distributed on an AS IS basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. Copyrights: Portions created by or assigned to CognitiveWeb, Inc. are Copyright (c) 2003-2003 CognitiveWeb, Inc. All Rights Reserved. Contact information for CognitiveWeb, Inc. is available at http://www.CognitiveWeb.org Portions Copyright (c) 2002-2003 Bryan Thompson. Acknowledgements: Special thanks to the developers of the Jabber Open Source License 1.0 (JOSL), from which this License was derived. This License contains terms that differ from JOSL. Special thanks to the CognitiveWeb Open Source Contributors for their suggestions and support of the Cognitive Web. Modifications: */ package org.CognitiveWeb.extser; import org.CognitiveWeb.extser.TestExtensibleSerializer; import junit.framework.*; /** * Package test suite. * * @author <a href="mailto:tho...@us...">Bryan Thompson</a> */ public class TestAll extends TestCase { public static junit.framework.Test suite() { TestSuite retval = new TestSuite(); retval.addTestSuite( LongPackerTestCase.class ); retval.addTestSuite( ShortPackerTestCase.class ); retval.addTestSuite( TestExtensibleSerializer.class ); return retval; } } --- NEW FILE: TestExtensibleSerializer.java --- /** The Notice below must appear in each file of the Source Code of any copy you distribute of the Licensed Product. Contributors to any Modifications may add their own copyright notices to identify their own contributions. License: The contents of this file are subject to the CognitiveWeb Open Source License Version 1.0 (the License). You may not copy or use this file, in either source code or executable form, except in compliance with the License. You may obtain a copy of the License from http://www.CognitiveWeb.org/legal/license/ Software distributed under the License is distributed on an AS IS basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations [...1362 lines suppressed...] * NOP. */ protected void update() throws IOException {} /** * Returns the ISerializer set using {@link #setSerializer( long recid )} for * that recid. */ public ISerializer getSerializer(long recid) throws IOException { return (ISerializer) _serializers.get( new Long(recid) ); } protected ISerializer setSerializer(long recid, ISerializer serializer ) { return (ISerializer) _serializers.put( new Long(recid), serializer ); } } } --- NEW FILE: LongPackerTestCase.java --- /** The Notice below must appear in each file of the Source Code of any copy you distribute of the Licensed Product. Contributors to any Modifications may add their own copyright notices to identify their own contributions. License: The contents of this file are subject to the CognitiveWeb Open Source License Version 1.0 (the License). You may not copy or use this file, in either source code or executable form, except in compliance with the License. You may obtain a copy of the License from http://www.CognitiveWeb.org/legal/license/ Software distributed under the License is distributed on an AS IS basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. Copyrights: Portions created by or assigned to CognitiveWeb, Inc. are Copyright (c) 2003-2003 CognitiveWeb, Inc. All Rights Reserved. Contact information for CognitiveWeb, Inc. is available at http://www.CognitiveWeb.org Portions Copyright (c) 2002-2003 Bryan Thompson. Acknowledgements: Special thanks to the developers of the Jabber Open Source License 1.0 (JOSL), from which this License was derived. This License contains terms that differ from JOSL. Special thanks to the CognitiveWeb Open Source Contributors for their suggestions and support of the Cognitive Web. Modifications: */ /* * Created on Oct 28, 2005 */ package org.CognitiveWeb.extser; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.Random; import org.CognitiveWeb.extser.LongPacker; import junit.framework.TestCase; /** * TestAll suite for {@link LongPacker}. * * @author thompsonbry */ public class LongPackerTestCase extends TestCase { /** * */ public LongPackerTestCase() { super(); } /** * @param name */ public LongPackerTestCase(String name) { super(name); } /** * Unpacks a long. * * @param expected The expected long value. * * @param packed The packed byte[]. * * @throws IOException * If there was not enough data. * * @throws junit.framework.AssertionFailedError * If there is too much data. */ public void doUnpackTest( long expected, byte[] packed ) throws IOException { ByteArrayInputStream bais = new ByteArrayInputStream( packed ); long actual = LongPacker.unpackLong( new DataInputStream( bais ) ); assertEquals( "value", expected, actual ); assertTrue( "Expecting EOF", bais.read() == -1 ); } /** * Given the first byte of a packed long value, return the #of bytes into which * that value was packed (including this one). * * @param firstByte The first byte. * * @return The #of bytes. This is in the range [1:8] inclusive. */ public int getNBytes( int firstByte ) { int nbytes; if( ( firstByte & 0x80 ) != 0 ) { // high bit is set. nbytes = 8; } else { nbytes = firstByte >> 4; // clear the high bit and right shift one nibble. } return nbytes; } public void testNBytes() { // high bit is set - always 8 bytes. assertEquals( "nbytes", 8, getNBytes( 0x80 ) ); assertEquals( "nbytes", 8, getNBytes( 0x81 ) ); assertEquals( "nbytes", 8, getNBytes( 0x8e ) ); assertEquals( "nbytes", 8, getNBytes( 0x8f ) ); // high bit is NOT set. nbytes is the upper nibble. assertEquals( "nbytes", 1, getNBytes( 0x10 ) ); assertEquals( "nbytes", 2, getNBytes( 0x20 ) ); assertEquals( "nbytes", 3, getNBytes( 0x30 ) ); assertEquals( "nbytes", 4, getNBytes( 0x40 ) ); assertEquals( "nbytes", 5, getNBytes( 0x50 ) ); assertEquals( "nbytes", 6, getNBytes( 0x60 ) ); assertEquals( "nbytes", 7, getNBytes( 0x70 ) ); // high bit is NOT set. nbytes is the upper nibble. assertEquals( "nbytes", 1, getNBytes( 0x11 ) ); assertEquals( "nbytes", 2, getNBytes( 0x21 ) ); assertEquals( "nbytes", 3, getNBytes( 0x31 ) ); assertEquals( "nbytes", 4, getNBytes( 0x41 ) ); assertEquals( "nbytes", 5, getNBytes( 0x51 ) ); assertEquals( "nbytes", 6, getNBytes( 0x61 ) ); assertEquals( "nbytes", 7, getNBytes( 0x71 ) ); // high bit is NOT set. nbytes is the upper nibble. assertEquals( "nbytes", 1, getNBytes( 0x1f ) ); assertEquals( "nbytes", 2, getNBytes( 0x2f ) ); assertEquals( "nbytes", 3, getNBytes( 0x3f ) ); assertEquals( "nbytes", 4, getNBytes( 0x4f ) ); assertEquals( "nbytes", 5, getNBytes( 0x5f ) ); assertEquals( "nbytes", 6, getNBytes( 0x6f ) ); assertEquals( "nbytes", 7, getNBytes( 0x7f ) ); } public void testUnpack() throws IOException { // upper nibble is 1, so nbytes == 1 and the lower nibble is the value. doUnpackTest( 0x0, new byte[]{(byte)0x10} ); doUnpackTest( 0x1, new byte[]{(byte)0x11} ); doUnpackTest( 0x7, new byte[]{(byte)0x17} ); doUnpackTest( 0xf, new byte[]{(byte)0x1f} ); // upper nibble is 2, so nbytes == 2. doUnpackTest( 0xf00, new byte[]{(byte)0x2f, (byte)0x00} ); doUnpackTest( 0xfa7, new byte[]{(byte)0x2f, (byte)0xa7} ); doUnpackTest( 0xfa0, new byte[]{(byte)0x2f, (byte)0xa0} ); doUnpackTest( 0xf07, new byte[]{(byte)0x2f, (byte)0x07} ); // upper nibble is 3, so nbytes == 3. doUnpackTest( 0xcaa4d, new byte[]{(byte)0x3c, (byte)0xaa, (byte)0x4d} ); // high bit only, lower nibble plus next seven bytes are the value. doUnpackTest( 0xaeede00a539271fL, new byte[]{(byte)0x8a, (byte)0xee, (byte)0xde, (byte)0x00, (byte)0xa5, (byte)0x39, (byte)0x27, (byte)0x1f } ); } public void doPackTest( long v, byte[] expected ) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); final int nbytes = LongPacker.packLong( new DataOutputStream( baos ), v ); final byte[] actual = baos.toByteArray(); assertEquals( "nbytes", expected.length, nbytes ); assertEquals( "nbytes", getNBytes(expected[0]), nbytes ); assertEquals( "bytes", expected, actual ); } public void test_getNibbleLength() { // Note: zero (0) is interpreted as being one nibble for our purposes. assertEquals( "nibbles", 1, LongPacker.getNibbleLength( 0x0 ) ); assertEquals( "nibbles", 1, LongPacker.getNibbleLength( 0x1 ) ); assertEquals( "nibbles", 1, LongPacker.getNibbleLength( 0x2 ) ); assertEquals( "nibbles", 1, LongPacker.getNibbleLength( 0x7 ) ); assertEquals( "nibbles", 1, LongPacker.getNibbleLength( 0x8 ) ); assertEquals( "nibbles", 1, LongPacker.getNibbleLength( 0xe ) ); assertEquals( "nibbles", 1, LongPacker.getNibbleLength( 0xf ) ); assertEquals( "nibbles", 2, LongPacker.getNibbleLength( 0x10 ) ); assertEquals( "nibbles", 2, LongPacker.getNibbleLength( 0x11 ) ); assertEquals( "nibbles", 2, LongPacker.getNibbleLength( 0x12 ) ); assertEquals( "nibbles", 2, LongPacker.getNibbleLength( 0x17 ) ); assertEquals( "nibbles", 2, LongPacker.getNibbleLength( 0x18 ) ); assertEquals( "nibbles", 2, LongPacker.getNibbleLength( 0x1e ) ); assertEquals( "nibbles", 2, LongPacker.getNibbleLength( 0x1f ) ); assertEquals( "nibbles", 2, LongPacker.getNibbleLength( 0x7f ) ); assertEquals( "nibbles", 2, LongPacker.getNibbleLength( 0x8f ) ); assertEquals( "nibbles", 2, LongPacker.getNibbleLength( 0xff ) ); assertEquals( "nibbles", 3, LongPacker.getNibbleLength( 0x100 ) ); assertEquals( "nibbles", 3, LongPacker.getNibbleLength( 0x101 ) ); assertEquals( "nibbles", 3, LongPacker.getNibbleLength( 0x121 ) ); assertEquals( "nibbles", 3, LongPacker.getNibbleLength( 0x1ee ) ); assertEquals( "nibbles", 3, LongPacker.getNibbleLength( 0x1ff ) ); assertEquals( "nibbles", 3, LongPacker.getNibbleLength( 0xfff ) ); assertEquals( "nibbles", 4, LongPacker.getNibbleLength( 0x1ff0 ) ); assertEquals( "nibbles", 4, LongPacker.getNibbleLength( 0x7ff0 ) ); assertEquals( "nibbles", 4, LongPacker.getNibbleLength( 0xfff0 ) ); assertEquals( "nibbles", 4, LongPacker.getNibbleLength( 0xfff1 ) ); assertEquals( "nibbles", 5, LongPacker.getNibbleLength( 0x12345 ) ); assertEquals( "nibbles", 5, LongPacker.getNibbleLength( 0x54321 ) ); assertEquals( "nibbles", 6, LongPacker.getNibbleLength( 0x123456 ) ); assertEquals( "nibbles", 6, LongPacker.getNibbleLength( 0x654321 ) ); assertEquals( "nibbles", 7, LongPacker.getNibbleLength( 0x1234567 ) ); assertEquals( "nibbles", 7, LongPacker.getNibbleLength( 0x7654321 ) ); /* * Note: At 8 nibbles we have 32 bits. When the high bit is one, this * MUST be expressed as a long (trailing 'L', NOT cast to a long) or it * will be interpreted as a negative integer and sign extended to a * negative long. */ assertEquals( "nibbles", 8, LongPacker.getNibbleLength( 0x12345678L ) ); assertEquals( "nibbles", 8, LongPacker.getNibbleLength( 0x87654321L ) ); assertEquals( "nibbles", 9, LongPacker.getNibbleLength( 0x123456789L ) ); assertEquals( "nibbles", 9, LongPacker.getNibbleLength( 0x987654321L ) ); assertEquals( "nibbles", 10, LongPacker.getNibbleLength( 0x123456789aL ) ); assertEquals( "nibbles", 10, LongPacker.getNibbleLength( 0xa987654321L ) ); assertEquals( "nibbles", 11, LongPacker.getNibbleLength( 0x123456789abL ) ); assertEquals( "nibbles", 11, LongPacker.getNibbleLength( 0xba987654321L ) ); assertEquals( "nibbles", 12, LongPacker.getNibbleLength( 0x123456789abcL ) ); assertEquals( "nibbles", 12, LongPacker.getNibbleLength( 0xcba987654321L ) ); assertEquals( "nibbles", 13, LongPacker.getNibbleLength( 0x123456789abcdL ) ); assertEquals( "nibbles", 13, LongPacker.getNibbleLength( 0xdcba987654321L ) ); assertEquals( "nibbles", 14, LongPacker.getNibbleLength( 0x123456789abcdeL ) ); assertEquals( "nibbles", 14, LongPacker.getNibbleLength( 0xedcba987654321L ) ); assertEquals( "nibbles", 15, LongPacker.getNibbleLength( 0x123456789abcdefL ) ); assertEquals( "nibbles", 15, LongPacker.getNibbleLength( 0xfedcba987654321L ) ); assertEquals( "nibbles", 16, LongPacker.getNibbleLength( 0x1234567890abcdefL ) ); assertEquals( "nibbles", 16, LongPacker.getNibbleLength( 0xfedcba0987654321L ) ); } public void testPack() throws IOException { // [0:15] should be packed into one byte. doPackTest( 0x0, new byte[]{(byte)0x10} ); doPackTest( 0x1, new byte[]{(byte)0x11} ); doPackTest( 0x2, new byte[]{(byte)0x12} ); doPackTest( 0xe, new byte[]{(byte)0x1e} ); doPackTest( 0xf, new byte[]{(byte)0x1f} ); /* * 0x10 through 0xfff overflow the lower nibble, so the value is packed * into two bytes. the first byte has the header and the next three * nibbles are the value. This case is good for up to 2^12, since there * are three full nibbles to encode the value. */ doPackTest( 0x10, new byte[]{(byte)0x20, (byte)0x10 }); doPackTest( 0x11, new byte[]{(byte)0x20, (byte)0x11 }); doPackTest( 0x1f, new byte[]{(byte)0x20, (byte)0x1f }); doPackTest( 0x20, new byte[]{(byte)0x20, (byte)0x20 }); doPackTest( 0xff, new byte[]{(byte)0x20, (byte)0xff }); doPackTest( 0x100, new byte[]{(byte)0x21, (byte)0x00 }); doPackTest( 0x101, new byte[]{(byte)0x21, (byte)0x01 }); doPackTest( 0x121, new byte[]{(byte)0x21, (byte)0x21 }); doPackTest( 0x1ee, new byte[]{(byte)0x21, (byte)0xee }); doPackTest( 0x1ff, new byte[]{(byte)0x21, (byte)0xff }); doPackTest( 0xfff, new byte[]{(byte)0x2f, (byte)0xff }); /* * 0x1000 through 0xfffff fit into one more byte. */ doPackTest( 0x1000, new byte[]{(byte)0x30, (byte)0x10, (byte)0x00 }); doPackTest( 0x1234, new byte[]{(byte)0x30, (byte)0x12, (byte)0x34 }); doPackTest( 0x1fff, new byte[]{(byte)0x30, (byte)0x1f, (byte)0xff }); doPackTest( 0x54321, new byte[]{(byte)0x35, (byte)0x43, (byte)0x21 }); doPackTest( 0xfffff, new byte[]{(byte)0x3f, (byte)0xff, (byte)0xff }); } public static final long SIGN_MASK = 1L<<63; public void testHighBit() { assertTrue( "sign bit", ( -1L & SIGN_MASK ) != 0 ); assertFalse( "sign bit", ( 0L & SIGN_MASK ) != 0 ); } private interface LongGenerator { public long nextLong(); } /** * All long values in sequence starting from the given start value * and using the given increment. * @author thompsonbry */ private static class Sequence implements LongGenerator { long _start, _inc, _next; public Sequence( long start, long inc ) { _start = start; _inc = inc; _next = start; } public long nextLong() { long v = _next; _next += _inc; return v; // double d = rnd.nextGaussian(); //// if( d < 0 ) d = -d; // final long expected = (long) ( d * Long.MAX_VALUE ); } } /** * Random long values (32 bits of random long), including negatives, * with a uniform distribution. * * @author thompsonbry */ private static class RandomLong implements LongGenerator { Random _rnd; public RandomLong( Random rnd ) { _rnd = rnd; } public long nextLong() { return _rnd.nextLong(); } } /** * Run a large #of pack/unpack operations on a sequence of long values to * demonstrate correctness in that sequence. The sequence is the long * values from -1 to 1M by one (dense coverage). * * @throws IOException */ public void testStressSequence() throws IOException { // dense coverage of the first 1M values. doStressTest( 1000000, new Sequence( -1, 1 ) ); } /** * Run a large #of random pack/unpack operations to sample the space while * showing correctness on those samples. The amount of compression due to * packing for this test is <em>very</em> small since all bits are equally * likely to be non-zero, so the #of bytes required on average to pack a * long value is 8. * * @throws IOException */ public void testStressRandom() throws IOException { // test on 1M random long values. doStressTest( 1000000, new RandomLong( new Random() ) ); } /** * Run a stress test. Writes some information of possible interest onto * System.err. * * @param ntrials #of trials. * * @param g Generator for the long values. * * @throws IOException */ public void doStressTest( int ntrials, LongGenerator g ) throws IOException { long nwritten = 0L; long packlen = 0L; long minv = Long.MAX_VALUE, maxv = Long.MIN_VALUE; for( int i=0; i<ntrials; i++ ) { long expected = g.nextLong(); if( expected < 0L ) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream( baos ); try { LongPacker.packLong( dos, expected ); fail( "Expecting rejection of negative value: val="+expected ); } catch( IllegalArgumentException ex ) { // System.err.println( "Ingoring expected exception: "+ex ); } } else { if( expected > maxv ) maxv = expected; if( expected < minv ) minv = expected; ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream( baos ); // final int expectedByteLength = getNBytes( expected ); // final int actualByteLength1 = LongPacker.packLong( dos, expected ); byte[] packed = baos.toByteArray(); final int actualByteLength2 = getNBytes( packed[ 0 ] ); ByteArrayInputStream bais = new ByteArrayInputStream( packed ); DataInputStream dis = new DataInputStream( bais ); final long actual = LongPacker.unpackLong( dis ); assertEquals( "trial="+i, expected, actual ); assertEquals( "trial="+i+", v="+expected+", nbytes", actualByteLength1, actualByteLength2 ); packlen += packed.length; // total #of packed bytes. nwritten++; // count #of non-negative random values. } } System.err.println( "\nWrote "+nwritten+" non-negative long values." ); System.err.println( "minv="+minv+", maxv="+maxv ); System.err.println( "#packed bytes ="+packlen ); System.err.println( "#bytes if not packed="+(nwritten * 8)); long nsaved = ( nwritten * 8 ) - packlen; System.err.println ("#bytes saved ="+nsaved); System.err.println( "%saved by packing ="+nsaved/(nwritten*8f)*100+"%"); } public static void assertEquals( String msg, byte[] expected, byte[] actual ) { assertEquals( msg+": length", expected.length, actual.length ); for( int i=0; i<expected.length; i++ ) { assertEquals( msg+": byte[i="+i+"]", expected[i], actual[i] ); } } } |