Index: /Users/alex/Documents/HarmonyWorkspace/Pack200/src/test/java/org/apache/harmony/archive/tests/internal/pack200/SegmentTest.java =================================================================== --- /Users/alex/Documents/HarmonyWorkspace/Pack200/src/test/java/org/apache/harmony/archive/tests/internal/pack200/SegmentTest.java (revision 427333) +++ /Users/alex/Documents/HarmonyWorkspace/Pack200/src/test/java/org/apache/harmony/archive/tests/internal/pack200/SegmentTest.java (working copy) @@ -33,5 +33,13 @@ assertNotNull(Segment.parse(Segment.class .getResourceAsStream("/org/apache/harmony/archive/tests/internal/pack200/HelloWorld.pack"))); } + /** + * @param args + * @throws Exception + */ + public void testJustResources() throws Exception { + assertNotNull(Segment.parse(Segment.class + .getResourceAsStream("/org/apache/harmony/archive/tests/internal/pack200/JustResources.pack"))); + } } Index: /Users/alex/Documents/HarmonyWorkspace/Pack200/src/test/java/org/apache/harmony/archive/tests/internal/pack200/CodecEncodingTest.java =================================================================== --- /Users/alex/Documents/HarmonyWorkspace/Pack200/src/test/java/org/apache/harmony/archive/tests/internal/pack200/CodecEncodingTest.java (revision 0) +++ /Users/alex/Documents/HarmonyWorkspace/Pack200/src/test/java/org/apache/harmony/archive/tests/internal/pack200/CodecEncodingTest.java (revision 0) @@ -0,0 +1,165 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, + * as applicable. + * + * 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 org.apache.harmony.archive.tests.internal.pack200; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import junit.framework.TestCase; + +import org.apache.harmony.archive.internal.pack200.BHSDCodec; +import org.apache.harmony.archive.internal.pack200.Codec; +import org.apache.harmony.archive.internal.pack200.CodecEncoding; +import org.apache.harmony.archive.internal.pack200.Pack200Exception; + +/** + * @author Alex Blewitt + * @version $Revision: $ + */ +public class CodecEncodingTest extends TestCase { + public void testCanonicalEncodings() throws IOException, Pack200Exception { + Codec defaultCodec = new BHSDCodec(2,16,0,0); + assertEquals(defaultCodec,CodecEncoding.getCodec(0,null, defaultCodec)); + Map map = new HashMap(); + // These are the canonical encodings specified by the Pack200 spec + map.put(new Integer(1), "(1,256)"); + map.put(new Integer(2), "(1,256,1)"); + map.put(new Integer(3), "(1,256,0,1)"); + map.put(new Integer(4), "(1,256,1,1)"); + map.put(new Integer(5), "(2,256)"); + map.put(new Integer(6), "(2,256,1)"); + map.put(new Integer(7), "(2,256,0,1)"); + map.put(new Integer(8), "(2,256,1,1)"); + map.put(new Integer(9), "(3,256)"); + map.put(new Integer(10), "(3,256,1)"); + map.put(new Integer(11), "(3,256,0,1)"); + map.put(new Integer(12), "(3,256,1,1)"); + map.put(new Integer(13), "(4,256)"); + map.put(new Integer(14), "(4,256,1)"); + map.put(new Integer(15), "(4,256,0,1)"); + map.put(new Integer(16), "(4,256,1,1)"); + map.put(new Integer(17), "(5,4)"); + map.put(new Integer(18), "(5,4,1)"); + map.put(new Integer(19), "(5,4,2)"); + map.put(new Integer(20), "(5,16)"); + map.put(new Integer(21), "(5,16,1)"); + map.put(new Integer(22), "(5,16,2)"); + map.put(new Integer(23), "(5,32)"); + map.put(new Integer(24), "(5,32,1)"); + map.put(new Integer(25), "(5,32,2)"); + map.put(new Integer(26), "(5,64)"); + map.put(new Integer(27), "(5,64,1)"); + map.put(new Integer(28), "(5,64,2)"); + map.put(new Integer(29), "(5,128)"); + map.put(new Integer(30), "(5,128,1)"); + map.put(new Integer(31), "(5,128,2)"); + map.put(new Integer(32), "(5,4,0,1)"); + map.put(new Integer(33), "(5,4,1,1)"); + map.put(new Integer(34), "(5,4,2,1)"); + map.put(new Integer(35), "(5,16,0,1)"); + map.put(new Integer(36), "(5,16,1,1)"); + map.put(new Integer(37), "(5,16,2,1)"); + map.put(new Integer(38), "(5,32,0,1)"); + map.put(new Integer(39), "(5,32,1,1)"); + map.put(new Integer(40), "(5,32,2,1)"); + map.put(new Integer(41), "(5,64,0,1)"); + map.put(new Integer(42), "(5,64,1,1)"); + map.put(new Integer(43), "(5,64,2,1)"); + map.put(new Integer(44), "(5,128,0,1)"); + map.put(new Integer(45), "(5,128,1,1)"); + map.put(new Integer(46), "(5,128,2,1)"); + map.put(new Integer(47), "(2,192)"); + map.put(new Integer(48), "(2,224)"); + map.put(new Integer(49), "(2,240)"); + map.put(new Integer(50), "(2,248)"); + map.put(new Integer(51), "(2,252)"); + map.put(new Integer(52), "(2,8,0,1)"); + map.put(new Integer(53), "(2,8,1,1)"); + map.put(new Integer(54), "(2,16,0,1)"); + map.put(new Integer(55), "(2,16,1,1)"); + map.put(new Integer(56), "(2,32,0,1)"); + map.put(new Integer(57), "(2,32,1,1)"); + map.put(new Integer(58), "(2,64,0,1)"); + map.put(new Integer(59), "(2,64,1,1)"); + map.put(new Integer(60), "(2,128,0,1)"); + map.put(new Integer(61), "(2,128,1,1)"); + map.put(new Integer(62), "(2,192,0,1)"); + map.put(new Integer(63), "(2,192,1,1)"); + map.put(new Integer(64), "(2,224,0,1)"); + map.put(new Integer(65), "(2,224,1,1)"); + map.put(new Integer(66), "(2,240,0,1)"); + map.put(new Integer(67), "(2,240,1,1)"); + map.put(new Integer(68), "(2,248,0,1)"); + map.put(new Integer(69), "(2,248,1,1)"); + map.put(new Integer(70), "(3,192)"); + map.put(new Integer(71), "(3,224)"); + map.put(new Integer(72), "(3,240)"); + map.put(new Integer(73), "(3,248)"); + map.put(new Integer(74), "(3,252)"); + map.put(new Integer(75), "(3,8,0,1)"); + map.put(new Integer(76), "(3,8,1,1)"); + map.put(new Integer(77), "(3,16,0,1)"); + map.put(new Integer(78), "(3,16,1,1)"); + map.put(new Integer(79), "(3,32,0,1)"); + map.put(new Integer(80), "(3,32,1,1)"); + map.put(new Integer(81), "(3,64,0,1)"); + map.put(new Integer(82), "(3,64,1,1)"); + map.put(new Integer(83), "(3,128,0,1)"); + map.put(new Integer(84), "(3,128,1,1)"); + map.put(new Integer(85), "(3,192,0,1)"); + map.put(new Integer(86), "(3,192,1,1)"); + map.put(new Integer(87), "(3,224,0,1)"); + map.put(new Integer(88), "(3,224,1,1)"); + map.put(new Integer(89), "(3,240,0,1)"); + map.put(new Integer(90), "(3,240,1,1)"); + map.put(new Integer(91), "(3,248,0,1)"); + map.put(new Integer(92), "(3,248,1,1)"); + map.put(new Integer(93), "(4,192)"); + map.put(new Integer(94), "(4,224)"); + map.put(new Integer(95), "(4,240)"); + map.put(new Integer(96), "(4,248)"); + map.put(new Integer(97), "(4,252)"); + map.put(new Integer(98), "(4,8,0,1)"); + map.put(new Integer(99), "(4,8,1,1)"); + map.put(new Integer(100), "(4,16,0,1)"); + map.put(new Integer(101), "(4,16,1,1)"); + map.put(new Integer(102), "(4,32,0,1)"); + map.put(new Integer(103), "(4,32,1,1)"); + map.put(new Integer(104), "(4,64,0,1)"); + map.put(new Integer(105), "(4,64,1,1)"); + map.put(new Integer(106), "(4,128,0,1)"); + map.put(new Integer(107), "(4,128,1,1)"); + map.put(new Integer(108), "(4,192,0,1)"); + map.put(new Integer(109), "(4,192,1,1)"); + map.put(new Integer(110), "(4,224,0,1)"); + map.put(new Integer(111), "(4,224,1,1)"); + map.put(new Integer(112), "(4,240,0,1)"); + map.put(new Integer(113), "(4,240,1,1)"); + map.put(new Integer(114), "(4,248,0,1)"); + map.put(new Integer(115), "(4,248,1,1)"); + for(int i=1;i<=115;i++) { + assertEquals(map.get(new Integer(i)),CodecEncoding.getCodec(i,null, null).toString()); + } + } + public void testArbitraryCodec() throws IOException, Pack200Exception { + assertEquals("(1,256)",CodecEncoding.getCodec(116,new ByteArrayInputStream(new byte[] { 0x00, (byte)0xFF}), null).toString()); + assertEquals("(5,128,2,1)",CodecEncoding.getCodec(116,new ByteArrayInputStream(new byte[] { 0x25, (byte)0x7F}), null).toString()); + assertEquals("(2,128,1,1)",CodecEncoding.getCodec(116,new ByteArrayInputStream(new byte[] { 0x0B, (byte)0x7F}), null).toString()); + } +} Index: /Users/alex/Documents/HarmonyWorkspace/Pack200/src/test/java/org/apache/harmony/archive/tests/internal/pack200/PopulationCodecTest.java =================================================================== --- /Users/alex/Documents/HarmonyWorkspace/Pack200/src/test/java/org/apache/harmony/archive/tests/internal/pack200/PopulationCodecTest.java (revision 0) +++ /Users/alex/Documents/HarmonyWorkspace/Pack200/src/test/java/org/apache/harmony/archive/tests/internal/pack200/PopulationCodecTest.java (revision 0) @@ -0,0 +1,41 @@ +package org.apache.harmony.archive.tests.internal.pack200; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +import junit.framework.TestCase; + +import org.apache.harmony.archive.internal.pack200.BHSDCodec; +import org.apache.harmony.archive.internal.pack200.Codec; +import org.apache.harmony.archive.internal.pack200.Pack200Exception; +import org.apache.harmony.archive.internal.pack200.PopulationCodec; + +public class PopulationCodecTest extends TestCase { + public void testPopulationCodec() throws IOException, Pack200Exception { + checkDecode(new byte[] { 4,5,6,4,2,1,3,0,7 }, new long[]{ 5,4,6,7 }, Codec.BYTE1); + // Codec.SIGNED5 can be trivial for small n, because the encoding is 2n if even, 2n-1 if odd + // Therefore they're left here to explain what the values are :-) + checkDecode(new byte[] { 4*2,4*2-1,6*2,4*2,2*2,1*2,3*2,0,7*2 }, new long[]{ -4,4,6,7 }, Codec.SIGNED5); + checkDecode(new byte[] { 4*2-1,4*2,6*2,4*2,2*2,1*2,3*2,0,7*2 }, new long[]{ 4,-4,6,7 }, Codec.SIGNED5); + checkDecode(new byte[] { 1,1,1 }, new long[]{ 1 }, Codec.BYTE1); + checkDecode(new byte[] { 2,2,1 }, new long[]{ 2 }, Codec.BYTE1); + checkDecode(new byte[] { 1,1,2 }, new long[]{ -1 }, Codec.SIGNED5); + checkDecode(new byte[] { 2,2,0,1,3 }, new long[]{ 3,2 }, Codec.BYTE1); + checkDecode(new byte[] { 1,2,3,4,4,2,3,4,0,1 }, new long[]{ 2,3,4,1 }, Codec.BYTE1); + checkDecode(new byte[] { 3,2,1,4,4,2,3,4,0,1 }, new long[]{ 2,1,4,1 }, Codec.BYTE1); + checkDecode(new byte[] { 3,2,1,4,1,2,3,4,0,1 }, new long[]{ 2,1,4,1 }, Codec.BYTE1); + } + + private void checkDecode(byte[] data, long[] expectedResult, Codec codec) throws IOException, Pack200Exception { + InputStream in = new ByteArrayInputStream(data ); + + long[] result = new PopulationCodec(codec,codec,codec).decode(expectedResult.length,in); + assertEquals(expectedResult.length,result.length); + for(int i=0;i h=256"); } catch (IllegalArgumentException e) { assertTrue(true); @@ -43,7 +45,7 @@ } for (int i = 1; i <= 5; i++) { try { - new Codec(i, 256); + new BHSDCodec(i, 256); if (i == 5) fail("h=256 -> b!=5"); } catch (IllegalArgumentException e) { @@ -75,14 +77,13 @@ } public void testByte1Delta() throws Exception { - Codec BYTE1D = new Codec(1, 256, 0, 1); + Codec BYTE1D = new BHSDCodec(1, 256, 0, 1); long last = 0; for (int i = 1; i < 255; i++) last = decode(BYTE1D, new byte[] { (byte) 1 }, i, last); } - public void testByte1DeltaException() throws Exception { - Codec BYTE1D = new Codec(1, 256, 0, 1); + Codec BYTE1D = new BHSDCodec(1, 256, 0, 1); try { BYTE1D.decode(new ByteArrayInputStream(new byte[] { (byte) 1 })); fail("Decoding with a delta stream and not passing a last value should throw exception"); @@ -90,7 +91,92 @@ assertTrue(true); } } + public void testByte1Signed() throws Exception { + Codec BYTE1S2 = new BHSDCodec(1,256,2); + decode(BYTE1S2,new byte[] { 0 }, 0, 0); + decode(BYTE1S2,new byte[] { 1 }, 1, 0); + decode(BYTE1S2,new byte[] { 2 }, 2, 0); + decode(BYTE1S2,new byte[] { 3 }, -1, 0); + decode(BYTE1S2,new byte[] { 4 }, 3, 0); + decode(BYTE1S2,new byte[] { 5 }, 4, 0); + decode(BYTE1S2,new byte[] { 6 }, 5, 0); + decode(BYTE1S2,new byte[] { 7 }, -2, 0); + decode(BYTE1S2,new byte[] { 8 }, 6, 0); + decode(BYTE1S2,new byte[] { 9 }, 7, 0); + decode(BYTE1S2,new byte[] { 10 }, 8, 0); + decode(BYTE1S2,new byte[] { 11 }, -3, 0); + } + public void testCardinality() throws Exception { + BHSDCodec byte1 = (BHSDCodec) Codec.BYTE1; + assertEquals(256,byte1.cardinality()); + assertEquals(0,byte1.smallest()); + assertEquals(255,byte1.largest()); + assertFalse(byte1.encodes(-257)); + assertFalse(byte1.encodes(-256)); + assertFalse(byte1.encodes(-255)); + assertFalse(byte1.encodes(-129)); + assertFalse(byte1.encodes(-128)); + assertFalse(byte1.encodes(-127)); + assertFalse(byte1.encodes(-1)); + assertTrue(byte1.encodes(0)); + assertTrue(byte1.encodes(1)); + assertTrue(byte1.encodes(255)); + assertFalse(byte1.encodes(256)); + BHSDCodec byte1s = new BHSDCodec(1,256,1); + assertEquals(256,byte1s.cardinality()); + assertEquals(-128,byte1s.smallest()); + assertEquals(127,byte1s.largest()); + assertFalse(byte1s.encodes(-257)); + assertFalse(byte1s.encodes(-256)); + assertFalse(byte1s.encodes(-255)); + assertFalse(byte1s.encodes(-129)); + assertTrue(byte1s.encodes(-128)); + assertTrue(byte1s.encodes(-127)); + assertTrue(byte1s.encodes(-1)); + assertTrue(byte1s.encodes(0)); + assertTrue(byte1s.encodes(1)); + assertTrue(byte1s.encodes(127)); + assertFalse(byte1s.encodes(128)); + assertFalse(byte1s.encodes(129)); + assertFalse(byte1s.encodes(255)); + assertFalse(byte1s.encodes(256)); + BHSDCodec byte2s = new BHSDCodec(1,256,2); + assertEquals(256,byte2s.cardinality()); + assertEquals(-64,byte2s.smallest()); + assertEquals(191,byte2s.largest()); + assertFalse(byte2s.encodes(-257)); + assertFalse(byte2s.encodes(-256)); + assertFalse(byte2s.encodes(-255)); + assertFalse(byte2s.encodes(-129)); + assertFalse(byte2s.encodes(-128)); + assertFalse(byte2s.encodes(-127)); + assertFalse(byte2s.encodes(-65)); + assertTrue(byte2s.encodes(-64)); + assertTrue(byte2s.encodes(-64)); + assertTrue(byte2s.encodes(-1)); + assertTrue(byte2s.encodes(0)); + assertTrue(byte2s.encodes(1)); + assertTrue(byte2s.encodes(127)); + assertTrue(byte2s.encodes(128)); + assertTrue(byte2s.encodes(191)); + assertFalse(byte2s.encodes(192)); + assertFalse(byte2s.encodes(256)); + } + public void testRunCodec() throws Exception { + RunCodec runCodec = new RunCodec(1,Codec.UNSIGNED5,Codec.BYTE1); + ByteArrayInputStream bais = new ByteArrayInputStream(new byte[]{ (byte) 192,0,(byte) 192,0}); + assertEquals(192,runCodec.decode(bais)); + assertEquals(192,runCodec.decode(bais)); + assertEquals(0,runCodec.decode(bais)); + assertEquals(0,bais.available()); + runCodec = new RunCodec(1,Codec.BYTE1,Codec.UNSIGNED5); + bais = new ByteArrayInputStream(new byte[]{ (byte) 192,0,(byte) 192,0}); + assertEquals(192,runCodec.decode(bais)); + assertEquals(0,runCodec.decode(bais)); + assertEquals(192,runCodec.decode(bais)); + assertEquals(0,bais.available()); + } public void testUnsigned5() throws Exception { decode(Codec.UNSIGNED5, new byte[] { 1 }, 1, 0); decode(Codec.UNSIGNED5, new byte[] { (byte) 191 }, 191, 0); Index: /Users/alex/Documents/HarmonyWorkspace/Pack200/src/test/resources/org/apache/harmony/archive/tests/internal/pack200/JustResources.pack =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: /Users/alex/Documents/HarmonyWorkspace/Pack200/src/test/resources/org/apache/harmony/archive/tests/internal/pack200/JustResources.pack ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Index: /Users/alex/Documents/HarmonyWorkspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/Segment.java =================================================================== --- /Users/alex/Documents/HarmonyWorkspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/Segment.java (revision 427295) +++ /Users/alex/Documents/HarmonyWorkspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/Segment.java (working copy) @@ -211,6 +211,30 @@ private int segmentsRemaining; + private String[] icThisClass; + + private int[] icFlags; + + private String[] icOuterClass; + + private Object icName; + + private String[] classThis; + + private String[] classSuper; + + private String[][] classInterfaces; + + private int[] classFieldCount; + + private int[] classMethodCount; + + private String[][] fieldDescr; + + private long[][] fieldFlags; + + private int fieldAttrCount; + public long getArchiveModtime() { return archiveModtime; } @@ -283,10 +307,78 @@ System.err.println("Not yet implemented"); } - private void parseClassBands(InputStream in) { - System.err.println("Not yet implemented"); + private void parseClassBands(InputStream in) throws IOException, + Pack200Exception { + classThis = parseReferences(in, Codec.DELTA5, classCount, cpClass); + classSuper = parseReferences(in, Codec.DELTA5, classCount, cpClass); + classInterfaces = new String[classCount][]; + int[] classInterfaceLengths = new int[classCount]; + long last = 0; + for (int i = 0; i < classCount; i++) { + classInterfaceLengths[i] = (int) (last = Codec.DELTA5.decode(in, + last)); + } + for (int i = 0; i < classCount; i++) { + classInterfaces[i] = parseReferences(in, Codec.DELTA5, + classInterfaceLengths[i], cpClass); + } + classFieldCount = new int[classCount]; + last = 0; + for (int i = 0; i < classCount; i++) { + classFieldCount[i] = (int) (last = Codec.DELTA5.decode(in, last)); + } + classMethodCount = new int[classCount]; + last = 0; + for (int i = 0; i < classCount; i++) { + classMethodCount[i] = (int) (last = Codec.DELTA5.decode(in, last)); + } + parseFieldBands(in); + parseMethodBands(in); + parseClassAttrBands(in); + parseCodeBands(in); } + private void parseCodeBands(InputStream in) { + // TODO Auto-generated method stub + + } + + private void parseClassAttrBands(InputStream in) { + // TODO Auto-generated method stub + + } + + private void parseMethodBands(InputStream in) { + // TODO Auto-generated method stub + + } + + private void parseFieldBands(InputStream in) throws IOException, + Pack200Exception { + long last; + fieldDescr = new String[classCount][]; + last = 0; + for (int i = 0; i < classCount; i++) { + fieldDescr[i] = parseReferences(in, Codec.DELTA5, + classFieldCount[i], cpClass); + } + fieldFlags = new long[classCount][]; + for (int i = 0; i < classCount; i++) { + fieldFlags[i] = parseFlags(in, classFieldCount[i], Codec.UNSIGNED5, + options.hasFieldFlagsHi()); + } + for (int i = 0; i < classCount; i++) { + for (int j = 0; i < fieldFlags[i].length; j++) { + long flag = fieldFlags[i][j]; + if ((flag & (1 << 16)) != 0) + fieldAttrCount++; + } + } + if (fieldAttrCount > 0) + throw new Error("There are attribute flags, and I don't know what to do with them"); + // TODO if we have fieldAttrcount then we ought to do other parsing + } + private void parseClassCounts(InputStream in) throws IOException, Pack200Exception { setInnerClassCount(Codec.UNSIGNED5.decode(in)); @@ -436,16 +528,32 @@ private void parseCpLong(InputStream in) throws IOException, Pack200Exception { - cpLong = new long[cpLongCount]; + cpLong = parseFlags(in, cpLongCount, Codec.UDELTA5, Codec.DELTA5); + } + + private long[] parseFlags(InputStream in, int count, Codec codec, + boolean hasHi) throws IOException, Pack200Exception { + return parseFlags(in, count, (hasHi ? codec : null), codec); + } + + private long[] parseFlags(InputStream in, int count, Codec codec) + throws IOException, Pack200Exception { + return parseFlags(in, count, codec, true); + } + + private long[] parseFlags(InputStream in, int count, Codec hiCodec, + Codec loCodec) throws IOException, Pack200Exception { + long[] result = new long[count]; long last = 0; - for (int i = 0; i < cpLongCount; i++) { - last = Codec.UDELTA5.decode(in, last); - cpLong[i] = last << 32; + for (int i = 0; i < count && hiCodec != null; i++) { + last = hiCodec.decode(in, last); + result[i] = last << 32; } - for (int i = 0; i < cpLongCount; i++) { - last = Codec.DELTA5.decode(in, last); - cpLong[i] = cpLong[i] | last; + for (int i = 0; i < count; i++) { + last = loCodec.decode(in, last); + result[i] = result[i] | last; } + return result; } /** @@ -648,8 +756,20 @@ } } - private void parseIcBands(InputStream in) { - System.err.println("Not yet implemented"); + private void parseIcBands(InputStream in) throws IOException, + Pack200Exception { + icThisClass = parseReferences(in, Codec.UDELTA5, innerClassCount, + cpClass); + icFlags = new int[innerClassCount]; + long last = 0; + int outerClasses = 0; + for (int i = 0; i < innerClassCount; i++) { + icFlags[i] = (int) (last = Codec.UNSIGNED5.decode(in, last)); + if ((icFlags[i] & 1 << 16) != 0) + outerClasses++; + } + icOuterClass = parseReferences(in, Codec.DELTA5, outerClasses, cpClass); + icName = parseReferences(in, Codec.DELTA5, outerClasses, cpUTF8); } /** @@ -721,12 +841,13 @@ parseCpMethod(in); parseCpIMethod(in); parseAttributeDefinition(in); - parseIcBands(in); // Not yet implemented + parseIcBands(in); parseClassBands(in); // Not yet implemented parseBcBands(in); // Not yet implemented - parseFileBands(in); - processFileBits(in); // this just caches them in file_bits; it should - // probably start writing here? + // TODO Re-enable these after completing class/bytecode bands + // parseFileBands(in); + // processFileBits(in); // this just caches them in file_bits; it should + // probably start writing here? } private void parseSegmentHeader(InputStream in) throws IOException, Index: /Users/alex/Documents/HarmonyWorkspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/RunCodec.java =================================================================== --- /Users/alex/Documents/HarmonyWorkspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/RunCodec.java (revision 0) +++ /Users/alex/Documents/HarmonyWorkspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/RunCodec.java (revision 0) @@ -0,0 +1,63 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, + * as applicable. + * + * 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 org.apache.harmony.archive.internal.pack200; + +import java.io.IOException; +import java.io.InputStream; + +/** + * A run codec is a grouping of two nested codecs; K values are decoded from + * the first codec, and the remaining codes are decoded from the remaining + * codec. Note that since this codec maintains state, the instances are + * not reusable. + * + * @author Alex Blewitt + * @version $Revision: $ + */ +public class RunCodec extends Codec { + private int k; + private Codec aCodec; + private Codec bCodec; + private long last; + + public RunCodec(int k, Codec aCodec, Codec bCodec) throws Pack200Exception { + if (k <= 0) + throw new Pack200Exception("Cannot have a RunCodec for a negative number of numbers"); + if (aCodec == null || bCodec == null) + throw new Pack200Exception("Must supply both codecs for a RunCodec"); + this.k = k; + this.aCodec = aCodec; + this.bCodec = bCodec; + } + public long decode(InputStream in) throws IOException, Pack200Exception { + return decode(in,this.last); + } + + public long decode(InputStream in, long last) throws IOException, Pack200Exception { + if(--k>=0) { + long value = aCodec.decode(in,last); + this.last = (k == 0 ? 0 : value); + return value; + } else { + this.last = bCodec.decode(in,last); + return this.last; + } + } + public String toString() { + return "RunCodec[k="+k+";aCodec="+aCodec+"bCodec="+bCodec+"]"; + } +} Index: /Users/alex/Documents/HarmonyWorkspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/CodecEncoding.java =================================================================== --- /Users/alex/Documents/HarmonyWorkspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/CodecEncoding.java (revision 0) +++ /Users/alex/Documents/HarmonyWorkspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/CodecEncoding.java (revision 0) @@ -0,0 +1,176 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, + * as applicable. + * + * 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 org.apache.harmony.archive.internal.pack200; + +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; + +public class CodecEncoding { + /** + * The canonical encodings are defined to allow a single byte to represent + * one of the standard encodings. The following values are defined in the + * Pack200 specification, and this array cannot be changed. + */ + private static Codec[] canonicalCodec = { null, new BHSDCodec(1, 256), + new BHSDCodec(1, 256, 1), new BHSDCodec(1, 256, 0, 1), + new BHSDCodec(1, 256, 1, 1), new BHSDCodec(2, 256), + new BHSDCodec(2, 256, 1), new BHSDCodec(2, 256, 0, 1), + new BHSDCodec(2, 256, 1, 1), new BHSDCodec(3, 256), + new BHSDCodec(3, 256, 1), new BHSDCodec(3, 256, 0, 1), + new BHSDCodec(3, 256, 1, 1), new BHSDCodec(4, 256), + new BHSDCodec(4, 256, 1), new BHSDCodec(4, 256, 0, 1), + new BHSDCodec(4, 256, 1, 1), new BHSDCodec(5, 4), new BHSDCodec(5, 4, 1), + new BHSDCodec(5, 4, 2), new BHSDCodec(5, 16), new BHSDCodec(5, 16, 1), + new BHSDCodec(5, 16, 2), new BHSDCodec(5, 32), new BHSDCodec(5, 32, 1), + new BHSDCodec(5, 32, 2), new BHSDCodec(5, 64), new BHSDCodec(5, 64, 1), + new BHSDCodec(5, 64, 2), new BHSDCodec(5, 128), new BHSDCodec(5, 128, 1), + new BHSDCodec(5, 128, 2), new BHSDCodec(5, 4, 0, 1), + new BHSDCodec(5, 4, 1, 1), new BHSDCodec(5, 4, 2, 1), + new BHSDCodec(5, 16, 0, 1), new BHSDCodec(5, 16, 1, 1), + new BHSDCodec(5, 16, 2, 1), new BHSDCodec(5, 32, 0, 1), + new BHSDCodec(5, 32, 1, 1), new BHSDCodec(5, 32, 2, 1), + new BHSDCodec(5, 64, 0, 1), new BHSDCodec(5, 64, 1, 1),new BHSDCodec(5, 64, 2, 1), + new BHSDCodec(5, 128, 0, 1), new BHSDCodec(5, 128, 1, 1), + new BHSDCodec(5, 128, 2, 1), new BHSDCodec(2, 192), + new BHSDCodec(2, 224), new BHSDCodec(2, 240), new BHSDCodec(2, 248), + new BHSDCodec(2, 252), new BHSDCodec(2, 8, 0, 1), new BHSDCodec(2, 8, 1, 1), + new BHSDCodec(2, 16, 0, 1), new BHSDCodec(2, 16, 1, 1), + new BHSDCodec(2, 32, 0, 1), new BHSDCodec(2, 32, 1, 1), + new BHSDCodec(2, 64, 0, 1), new BHSDCodec(2, 64, 1, 1), + new BHSDCodec(2, 128, 0, 1), new BHSDCodec(2, 128, 1, 1), + new BHSDCodec(2, 192, 0, 1), new BHSDCodec(2, 192, 1, 1), + new BHSDCodec(2, 224, 0, 1), new BHSDCodec(2, 224, 1, 1), + new BHSDCodec(2, 240, 0, 1), new BHSDCodec(2, 240, 1, 1), + new BHSDCodec(2, 248, 0, 1), new BHSDCodec(2, 248, 1, 1), + new BHSDCodec(3, 192), new BHSDCodec(3, 224), new BHSDCodec(3, 240), + new BHSDCodec(3, 248), new BHSDCodec(3, 252), new BHSDCodec(3, 8, 0, 1), + new BHSDCodec(3, 8, 1, 1), new BHSDCodec(3, 16, 0, 1), + new BHSDCodec(3, 16, 1, 1), new BHSDCodec(3, 32, 0, 1), + new BHSDCodec(3, 32, 1, 1), new BHSDCodec(3, 64, 0, 1), + new BHSDCodec(3, 64, 1, 1), new BHSDCodec(3, 128, 0, 1), + new BHSDCodec(3, 128, 1, 1), new BHSDCodec(3, 192, 0, 1), + new BHSDCodec(3, 192, 1, 1), new BHSDCodec(3, 224, 0, 1), + new BHSDCodec(3, 224, 1, 1), new BHSDCodec(3, 240, 0, 1), + new BHSDCodec(3, 240, 1, 1), new BHSDCodec(3, 248, 0, 1), + new BHSDCodec(3, 248, 1, 1), new BHSDCodec(4, 192), + new BHSDCodec(4, 224), new BHSDCodec(4, 240), new BHSDCodec(4, 248), + new BHSDCodec(4, 252), new BHSDCodec(4, 8, 0, 1), new BHSDCodec(4, 8, 1, 1), + new BHSDCodec(4, 16, 0, 1), new BHSDCodec(4, 16, 1, 1), + new BHSDCodec(4, 32, 0, 1), new BHSDCodec(4, 32, 1, 1), + new BHSDCodec(4, 64, 0, 1), new BHSDCodec(4, 64, 1, 1), + new BHSDCodec(4, 128, 0, 1), new BHSDCodec(4, 128, 1, 1), + new BHSDCodec(4, 192, 0, 1), new BHSDCodec(4, 192, 1, 1), + new BHSDCodec(4, 224, 0, 1), new BHSDCodec(4, 224, 1, 1), + new BHSDCodec(4, 240, 0, 1), new BHSDCodec(4, 240, 1, 1), + new BHSDCodec(4, 248, 0, 1), new BHSDCodec(4, 248, 1, 1) }; + + /** + * Returns the codec specified by the given value byte and optional byte header. + * If the value is >=116, then bytes may be consumed from the secondary input + * stream, which is taken to be the contents of the band_headers byte array. + * Since the values from this are consumed and not repeated, the input stream + * should be reused for subsequent encodings. This does not therefore close + * the input stream. + * @param value the canonical encoding value + * @param in the input stream to read additional byte headers from + * @param defaultCodec TODO + * @return the corresponding codec, or null if the default should be used + * @throws IOException + * @throws IOException if there is a problem reading from the input stream (which + * in reality, is never, since the band_headers are likely stored in a byte array + * and accessed via a ByteArrayInputStream. However, an EOFException could occur + * if things go titsup.com. + * @throws Pack200Exception + */ + public static Codec getCodec(int value, InputStream in, Codec defaultCodec) throws IOException, Pack200Exception { + // Sanity check to make sure that no-one's been buggering with + // the canonical codecs, which would really cause havoc + if (canonicalCodec.length != 116) + throw new Error("Canonical encodings have been incorrectly modified"); + if (value < 0) { + throw new IllegalArgumentException( + "Encoding cannot be less than zero"); + } else if (value == 0) { + return defaultCodec; + } else if (value <= 115) { + return canonicalCodec[value]; + } else if (value == 116) { + int code = in.read(); + if (code == -1) + throw new EOFException("End of buffer read whilst trying to decode codec"); + int d = (code & 0x01); + int s = (code >> 1 & 0x03); + int b = (code >> 3 & 0x07) + 1; // this might result in an invalid number, but it's checked in the Codec constructor + code = in.read(); + if (code == -1) + throw new EOFException("End of buffer read whilst trying to decode codec"); + int h = code + 1; + // This handles the special cases for invalid combinations of data. + return new BHSDCodec(b,h,s,d); + } else if (value >= 117 && value <= 140) { + int offset = value - 117; + int kx = offset & 3; + boolean kbflag = (offset >> 2 & 1) == 1; + boolean adef = (offset >> 3 & 1) == 1; + boolean bdef = (offset >> 4 & 1) == 1; + // If both A and B use the default encoding, what's the point of having a run of default values followed by default values + if (adef && bdef) + throw new Pack200Exception("ADef and BDef should never both be true"); + int kb = (kbflag ? in.read() : 3); + int k = (kb+1) * (int)Math.pow(16, kx); + Codec aCodec, bCodec; + if (adef) { + aCodec = defaultCodec; + } else { + aCodec = getCodec(in.read(),in,defaultCodec); + } + if (bdef) { + bCodec = defaultCodec; + } else { + bCodec = getCodec(in.read(),in,defaultCodec); + } + return new RunCodec(k,aCodec,bCodec); + } else if (value >= 141 && value <= 188) { + int offset = value - 141; + boolean fdef = (offset & 1) == 1; + boolean udef = (offset >> 1 & 1) == 1; + int tdefl = offset >> 2; + boolean tdef = tdefl == 0; + // From section 6.7.3 of spec + final int[] tdefToL= {0,4,8,16,32,64,128,192,224,240,248,252 }; + int l = tdefToL[tdefl]; + // NOTE: Do not re-factor this to bring out uCodec; the order in which + // they are read from the stream is important + if (tdef) { + Codec fCodec = (fdef ? defaultCodec : getCodec(in.read(),in,defaultCodec) ); + Codec uCodec = (udef ? defaultCodec : getCodec(in.read(),in,defaultCodec) ); + // Unfortunately, if tdef, then tCodec depends both on l and also on k, the + // number of items read from the fCodec. So we don't know in advance what + // the codec will be. + return new PopulationCodec(fCodec,l,uCodec); + } else { + Codec fCodec = (fdef ? defaultCodec : getCodec(in.read(),in,defaultCodec) ); + Codec uCodec = (udef ? defaultCodec : getCodec(in.read(),in,defaultCodec) ); + Codec tCodec = getCodec(in.read(),in,defaultCodec); + return new PopulationCodec(fCodec,uCodec,tCodec); + } + } else { + throw new Pack200Exception("Invalid codec encoding byte (" + value + ") found" ); + } + } +} Index: /Users/alex/Documents/HarmonyWorkspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/PopulationCodec.java =================================================================== --- /Users/alex/Documents/HarmonyWorkspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/PopulationCodec.java (revision 0) +++ /Users/alex/Documents/HarmonyWorkspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/PopulationCodec.java (revision 0) @@ -0,0 +1,90 @@ +package org.apache.harmony.archive.internal.pack200; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; + +public class PopulationCodec extends Codec { + private Codec favouredCodec; + private Codec tokenCodec; + private Codec unvafouredCodec; + private int l; + + public PopulationCodec(Codec favouredCodec, Codec tableCodec, Codec unvafouredCodec) { + this.favouredCodec = favouredCodec; + this.tokenCodec = tableCodec; + this.unvafouredCodec = unvafouredCodec; + } + + public PopulationCodec(Codec favouredCodec, int l, Codec unvafouredCodec) { + if (l >= 256 || l <=0) + throw new IllegalArgumentException("L must be between 1..255"); + this.favouredCodec = favouredCodec; + this.l = l; + this.unvafouredCodec = unvafouredCodec; + } + + @Override + public long decode(InputStream in) throws IOException, Pack200Exception { + throw new Pack200Exception("Population encoding does not work unless the number of elements are known"); + } + + @Override + public long decode(InputStream in, long last) throws IOException, + Pack200Exception { + throw new Pack200Exception("Population encoding does not work unless the number of elements are known"); + } + + @Override + public long[] decode(int n, InputStream in) throws IOException, Pack200Exception { + long favoured[] =new long[n]; // there must be <= n values, but probably a lot less + long result[]; + // read table of favourites first + long smallest = Long.MAX_VALUE; + long last = 0; + long value = 0; // TODO Are these sensible starting points? + int k = -1; + while( true ) { + last = value; + value = favouredCodec.decode(in,last); + if ( value == smallest || value == last) + break; + favoured[++k] = value; + if (Math.abs(smallest) > Math.abs(value)) { + smallest = value; + } else if (Math.abs(smallest) == Math.abs(value) ) { + // ensure that -X and +X -> +X + smallest = Math.abs(smallest); + } + } + // if tokenCodec needs to be derived from the T, L and K values + if (tokenCodec == null) { + if (k < 256) { + tokenCodec = Codec.BYTE1; + } else { + // if k >= 256, b >= 2 + int b = 1; + while(++b < 5 && tokenCodec == null) { + BHSDCodec codec = new BHSDCodec(b,256-l,0); + if (codec.encodes(k)) + tokenCodec = codec; + } + if (tokenCodec == null) + throw new Pack200Exception("Cannot calculate token codec from " + k + " and " + l); + } + } + // read favourites + result = tokenCodec.decode(n, in); + // read unfavourites + last = 0; + for(int i=0;i 5) - throw new IllegalArgumentException("1<=b<=5"); - if (h < 1 || h > 256) - throw new IllegalArgumentException("1<=h<=256"); - if (s < 0 || s > 2) - throw new IllegalArgumentException("0<=s<=2"); - if (d < 0 || d > 1) - throw new IllegalArgumentException("0<=d<=1"); - if (b == 1 && h != 256) - throw new IllegalArgumentException("b=1 -> h=256"); - if (h == 256 && b == 5) - throw new IllegalArgumentException("h=256 -> b!=5"); - this.b = b; - this.h = h; - this.s = s; - this.d = d; - this.l = 256 - h; - } - - /** * Decode a sequence of bytes from the given input stream, returning the * value as a long. Note that this method can only be applied for non-delta * encodings. @@ -253,12 +163,8 @@ * @throws Pack200Exception * if the encoding is a delta encoding */ - public long decode(InputStream in) throws IOException, Pack200Exception { - if (d != 0) - throw new Pack200Exception( - "Delta encoding used without passing in last value; this is a coding error"); - return decode(in, 0); - } + public abstract long decode(InputStream in) throws IOException, + Pack200Exception; /** * Decode a sequence of bytes from the given input stream, returning the @@ -275,9 +181,6 @@ * } * * - * TODO Note that s=2 encodings are not yet supported, and will result in an - * Error - * * @param in * the input stream to read from * @param long @@ -291,52 +194,35 @@ * if there is a problem decoding the value or that the value is * invalid */ - public long decode(InputStream in, long last) throws IOException, - Pack200Exception { - int n = 0; - long z = 0; - long x; - do { - x = in.read(); - if (x == -1) - throw new EOFException("End of stream reached whilst decoding"); - z += x * Math.pow(h, n); - } while (++n < b && x >= l); - // process sign bit -- one's complement? - if (s == 1) { - if ((z & 1) == 1) { - z = z >>> 1 ^ -1L; - } else { - z >>>= 1; - } - } else if (s == 2) { // two's complement? - throw new Error("s==2 not yet implemented"); - } - if (d == 1) - z += last; - return z; - } + public abstract long decode(InputStream in, long last) throws IOException, + Pack200Exception; /** - * Returns the codec in the form (1,256) or (1,64,1,1). Note that trailing - * zero fields are not shown. + * Decodes a sequence of n values from in. + * This should probably be used in most cases, since some codecs + * (such as @{link PopCodec}) only work when the number of values + * to be read is known. + * + * @param n + * the number of values to decode + * @param in + * the input stream to read from + * @return an array of long values corresponding to values + * decoded + * @throws IOException + * if there is a problem reading from the underlying input + * stream + * @throws Pack200Exception + * if there is a problem decoding the value or that the value is + * invalid */ - public String toString() { - StringBuffer buffer = new StringBuffer(11); - buffer.append('('); - buffer.append(b); - buffer.append(','); - buffer.append(h); - if (s != 0 || d != 0) { - buffer.append(","); - buffer.append(s); + public long[] decode(int n, InputStream in) throws IOException, + Pack200Exception { + long result[] = new long[n]; + long last = 0; + for(int i=0;i 5) + throw new IllegalArgumentException("1<=b<=5"); + if (h < 1 || h > 256) + throw new IllegalArgumentException("1<=h<=256"); + if (s < 0 || s > 2) + throw new IllegalArgumentException("0<=s<=2"); + if (d < 0 || d > 1) + throw new IllegalArgumentException("0<=d<=1"); + if (b == 1 && h != 256) + throw new IllegalArgumentException("b=1 -> h=256"); + if (h == 256 && b == 5) + throw new IllegalArgumentException("h=256 -> b!=5"); + this.b = b; + this.h = h; + this.s = s; + this.d = d; + this.l = 256 - h; + } + + /** + * Returns the cardinality of this codec; that is, the number of distinct + * values that it can contain. + * + * @return the cardinality of this codec + */ + public long cardinality() { + if (h > 1) { + return (long) (l * Math.pow(1 - h, b) / (1 - h) + Math.pow(h, b)); + } else { + return (b * 255) + 1; + } + } + + @Override + public long decode(InputStream in) throws IOException, Pack200Exception { + if (d != 0) + throw new Pack200Exception( + "Delta encoding used without passing in last value; this is a coding error"); + return decode(in, 0); + } + + @Override + public long decode(InputStream in, long last) throws IOException, + Pack200Exception { + int n = 0; + long z = 0; + long x; + do { + x = in.read(); + if (x == -1) + throw new EOFException("End of stream reached whilst decoding"); + z += x * Math.pow(h, n); + } while (++n < b && x >= l); + // This looks more complicated than it is + // When s=0, {1,2,3,4} is mapped to {1,2,3,4} + // When s=1, {1,2,3,4} is mapped to {-1,1,-2,2...} + // When s=2, {1,2,3,4} is mapped to {1,2,3,-1...} + if (s > 0) { + int u = ((1 << s) - 1); + if ((z & u) == u) { + z = z >>> s ^ -1L; + } else { + z = z - (z >>> s); + } + } + if (d == 1) + z += last; + return z; + } + + /** + * True if this encoding can code the given value + * + * @param value + * the value to check + * @return true if the encoding can encode this value + */ + public boolean encodes(long value) { + return (value >= smallest() && value <= largest()); + } + + /** + * Returns the largest value that this codec can represent. + * + * @eturn the largest value that this codec can represent. + */ + public long largest() { + long result; + if (d == 0) { + if (s == 0) { + result = cardinality() - 1; + } else if (s == 1) { + result = cardinality() / 2 - 1; + } else if (s == 2) { + result = (3L * cardinality()) / 4 - 1; + } else { + throw new Error("Unknown s value"); + } + } else { + result = Long.MAX_VALUE; + } + return Math.min((s == 0 ? ((long) Integer.MAX_VALUE) << 1 + : Integer.MAX_VALUE) - 1, result); + } + + /** + * Returns the smallest value that this codec can represent. + * + * @eturn the smallest value that this codec can represent. + */ + public long smallest() { + long result; + if (d == 0) { + if (s == 0) { + result = 0; + } else { + result = -cardinality() / (1 << s); + } + } else { + result = Integer.MIN_VALUE; + } + return Math.max(Integer.MIN_VALUE, result); + } + + /** + * Returns the codec in the form (1,256) or (1,64,1,1). Note that trailing + * zero fields are not shown. + */ + public String toString() { + StringBuffer buffer = new StringBuffer(11); + buffer.append('('); + buffer.append(b); + buffer.append(','); + buffer.append(h); + if (s != 0 || d != 0) { + buffer.append(","); + buffer.append(s); + } + if (d != 0) { + buffer.append(","); + buffer.append(d); + } + buffer.append(')'); + return buffer.toString(); + } + +}