Index: /Users/alex/Documents/HarmonyWorkspace/Pack200/src/test/java/org/apache/harmony/archive/tests/AllTests.java =================================================================== --- /Users/alex/Documents/HarmonyWorkspace/Pack200/src/test/java/org/apache/harmony/archive/tests/AllTests.java (revision 427330) +++ /Users/alex/Documents/HarmonyWorkspace/Pack200/src/test/java/org/apache/harmony/archive/tests/AllTests.java (working copy) @@ -31,6 +31,8 @@ .suite()); suite.addTest(org.apache.harmony.archive.tests.java.util.zip.AllTests .suite()); + suite.addTest(org.apache.harmony.archive.tests.internal.pack200.AllTests + .suite()); // $JUnit-END$ return suite; } 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/AllTests.java =================================================================== --- /Users/alex/Documents/HarmonyWorkspace/Pack200/src/test/java/org/apache/harmony/archive/tests/internal/pack200/AllTests.java (revision 0) +++ /Users/alex/Documents/HarmonyWorkspace/Pack200/src/test/java/org/apache/harmony/archive/tests/internal/pack200/AllTests.java (revision 0) @@ -0,0 +1,41 @@ +/* Copyright 2004 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 junit.framework.Test; +import junit.framework.TestSuite; + +/** + * Test suite for org.apache.harmony.archive.internal.pack200 package. + */ +public class AllTests { + + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + + public static Test suite() { + TestSuite suite = new TestSuite( + "Suite org.apache.harmony.archive.tests.internal.pack200"); + // $JUnit-BEGIN$ + suite.addTestSuite(CodecEncodingTest.class); + suite.addTestSuite(CodecTest.class); + suite.addTestSuite(SegmentOptionsTest.class); + suite.addTestSuite(SegmentTest.class); + // $JUnit-END$ + return suite; + } +} Index: /Users/alex/Documents/HarmonyWorkspace/Pack200/src/test/java/org/apache/harmony/archive/tests/internal/pack200/CodecTest.java =================================================================== --- /Users/alex/Documents/HarmonyWorkspace/Pack200/src/test/java/org/apache/harmony/archive/tests/internal/pack200/CodecTest.java (revision 427333) +++ /Users/alex/Documents/HarmonyWorkspace/Pack200/src/test/java/org/apache/harmony/archive/tests/internal/pack200/CodecTest.java (working copy) @@ -22,8 +22,10 @@ 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.RunCodec; /** @@ -35,7 +37,7 @@ public void testInvalidCodings() { for (int i = 0; i < 256; i++) { try { - new Codec(1, i); + new BHSDCodec(1, i); fail("b=1 -> 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,14 @@ } 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"); @@ -91,6 +93,20 @@ } } + 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,164 @@ +/* + * 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); + // TODO Still haven't implemented this (which is why there's an error) + 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) { + // TODO This needs to be filled in + 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]; + throw new Error("Not yet finished"); + } 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/Codec.java =================================================================== --- /Users/alex/Documents/HarmonyWorkspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/Codec.java (revision 427295) +++ /Users/alex/Documents/HarmonyWorkspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/Codec.java (working copy) @@ -16,7 +16,6 @@ */ package org.apache.harmony.archive.internal.pack200; -import java.io.EOFException; import java.io.IOException; import java.io.InputStream; @@ -98,148 +97,59 @@ * @author Alex Blewitt * @version $Revision: $ */ -public class Codec { +public abstract class Codec { /** * BCI5 = (5,4): Used for storing branching information in bytecode. */ - public static Codec BCI5 = new Codec(5, 4); + public static Codec BCI5 = new BHSDCodec(5, 4); /** * BRANCH5 = (5,4,2): Used for storing branching information in bytecode. */ - public static final Codec BRANCH5 = new Codec(5, 4, 2); + public static final Codec BRANCH5 = new BHSDCodec(5, 4, 2); /** * BYTE1 = (1,256): Used for storing plain bytes. */ - public static final Codec BYTE1 = new Codec(1, 256); + public static final Codec BYTE1 = new BHSDCodec(1, 256); /** * CHAR3 = (3,128): Used for storing text (UTF-8) strings. NB This isn't * quite the same as UTF-8, but has similar properties; ASCII characters * < 127 are stored in a single byte. */ - public static final Codec CHAR3 = new Codec(3, 128); + public static final Codec CHAR3 = new BHSDCodec(3, 128); /** * DELTA5 = (5,64,1,1): Used for the majority of numerical codings where * there is a correlated sequence of signed values. */ - public static final Codec DELTA5 = new Codec(5, 64, 1, 1); + public static final Codec DELTA5 = new BHSDCodec(5, 64, 1, 1); /** * DELTA5 = (5,64,2,1): Used for the majority of numerical codings where * there is a correlated sequence of signed values, but where most of them * are expected to be non-negative. */ - public static final Codec MDELTA5 = new Codec(5, 64, 2, 1); + public static final Codec MDELTA5 = new BHSDCodec(5, 64, 2, 1); /** * SIGNED5 = (5,64,1): Used for small signed values. */ - public static final Codec SIGNED5 = new Codec(5, 64, 1); + public static final Codec SIGNED5 = new BHSDCodec(5, 64, 1); /** * UDELTA5 = (5,64,0,1): Used for the majority of numerical codings where * there is a correlated sequence of unsigned values. */ - public static final Codec UDELTA5 = new Codec(5, 64, 0, 1); + public static final Codec UDELTA5 = new BHSDCodec(5, 64, 0, 1); /** * USIGNED5 = (5,64): Used for small unsigned values. */ - public static final Codec UNSIGNED5 = new Codec(5, 64); + public static final Codec UNSIGNED5 = new BHSDCodec(5, 64); /** - * The maximum number of bytes in each coding word - */ - private int b; - - /** - * Whether delta encoding is used (0=false,1=true) - */ - private int d; - - /** - * The radix of the encoding - */ - private int h; - - /** - * The co-parameter of h; h-256 - */ - private int l; - - /** - * Represents signed numbers or not (0=unsigned,1/2=signed) - */ - private int s; - - /** - * Constructs an unsigned, non-delta Codec with the given B and H values. - * - * @param b - * the maximum number of bytes that a value can be encoded as - * [1..5] - * @param h - * the radix of the encoding [1..256] - */ - public Codec(int b, int h) { - this(b, h, 0); - } - - /** - * Constructs a non-delta Codec with the given B, H and S values. - * - * @param b - * the maximum number of bytes that a value can be encoded as - * [1..5] - * @param h - * the radix of the encoding [1..256] - * @param s - * whether the encoding represents signed numbers (s=0 is - * unsigned; s=1 is signed with 1s complement; s=2 is signed with ?) - */ - public Codec(int b, int h, int s) { - this(b, h, s, 0); - } - - /** - * Constructs a Codec with the given B, H, S and D values. - * - * @param b - * the maximum number of bytes that a value can be encoded as - * [1..5] - * @param h - * the radix of the encoding [1..256] - * @param s - * whether the encoding represents signed numbers (s=0 is - * unsigned; s=1 is signed with 1s complement; s=2 is signed with ?) - * @param d - * whether this is a delta encoding (d=0 is non-delta; d=1 is - * delta) - */ - public Codec(int b, int h, int s, int d) { - if (b < 1 || b > 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,7 @@ * @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 @@ -291,52 +196,7 @@ * 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. - */ - 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(); - } - } Index: /Users/alex/Documents/HarmonyWorkspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/BHSDCodec.java =================================================================== --- /Users/alex/Documents/HarmonyWorkspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/BHSDCodec.java (revision 0) +++ /Users/alex/Documents/HarmonyWorkspace/Pack200/src/main/java/org/apache/harmony/archive/internal/pack200/BHSDCodec.java (revision 0) @@ -0,0 +1,155 @@ +package org.apache.harmony.archive.internal.pack200; + +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; + +/** + * TODO Comment -- quite a lot can be nicked from Codec, since this was created from it + * @author Alex Blewitt + * + */ +public class BHSDCodec extends Codec { + + /** + * Represents signed numbers or not (0=unsigned,1/2=signed) + */ + private int s; + /** + * The co-parameter of h; h-256 + */ + private int l; + /** + * The radix of the encoding + */ + private int h; + /** + * Whether delta encoding is used (0=false,1=true) + */ + private int d; + /** + * The maximum number of bytes in each coding word + */ + private int b; + + /** + * Constructs a Codec with the given B, H, S and D values. + * + * @param b + * the maximum number of bytes that a value can be encoded as + * [1..5] + * @param h + * the radix of the encoding [1..256] + * @param s + * whether the encoding represents signed numbers (s=0 is + * unsigned; s=1 is signed with 1s complement; s=2 is signed with ?) + * @param d + * whether this is a delta encoding (d=0 is non-delta; d=1 is + * delta) + */ + public BHSDCodec(int b, int h, int s, int d) { + if (b < 1 || b > 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; + } + + /** + * Constructs a non-delta Codec with the given B, H and S values. + * + * @param b + * the maximum number of bytes that a value can be encoded as + * [1..5] + * @param h + * the radix of the encoding [1..256] + * @param s + * whether the encoding represents signed numbers (s=0 is + * unsigned; s=1 is signed with 1s complement; s=2 is signed with ?) + */ + public BHSDCodec(int b, int h, int s) { + this(b, h, s, 0); + } + + /** + * Constructs an unsigned, non-delta Codec with the given B and H values. + * + * @param b + * the maximum number of bytes that a value can be encoded as + * [1..5] + * @param h + * the radix of the encoding [1..256] + */ + public BHSDCodec(int b, int h) { + this(b, h, 0); + } + + @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); + // 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; + } + + /** + * 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(); + } + +}