/* * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.java.util.jar.pack; import com.sun.java.util.jar.pack.ConstantPool.Entry;import com.sun.java.util.jar.pack.ConstantPool.Index;import com.sun.java.util.jar.pack.Package.Class.Field;import java.io.BufferedOutputStream;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.EOFException;import java.io.File;import java.io.FileOutputStream;import java.io.FilterInputStream;import java.io.FilterOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.io.PrintStream;import java.util.ArrayList;import java.util.Arrays;import java.util.Collections;import java.util.HashMap;import java.util.List;import java.util.Map;import java.util.jar.Pack200;import static com.sun.java.util.jar.pack.Constants.*; /** * Define the structure and ordering of "bands" in a packed file. * @author John Rose */abstractclass BandStructure { static final int MAX_EFFORT = 9; static final int MIN_EFFORT = 1; static final int DEFAULT_EFFORT = 5; // Inherit options from Pack200: PropMap p200 = Utils.currentPropMap(); int verbose = p200.getInteger(Utils.DEBUG_VERBOSE); int effort = p200.getInteger(Pack200.Packer.EFFORT); { if (effort == 0) effort = DEFAULT_EFFORT; } boolean optDumpBands = p200.getBoolean(Utils.COM_PREFIX+"dump.bands"); boolean optDebugBands = p200.getBoolean(Utils.COM_PREFIX+"debug.bands"); // Various heuristic options. boolean optVaryCodings = !p200.getBoolean(Utils.COM_PREFIX+"no.vary.codings"); boolean optBigStrings = !p200.getBoolean(Utils.COM_PREFIX+"no.big.strings"); abstract protected Index getCPIndex(byte tag); // Local copy of package version. private int packageMajver = -1; /** Call this exactly once, early, to specify the archive major version. */ public void initPackageMajver(int packageMajver) throws IOException { assert(packageMajver > 0 && packageMajver < 0x10000); if (this.packageMajver > 0) { throw new IOException( "Package majver is already initialized to " + this.packageMajver+ "; new setting is " + packageMajver); } this.packageMajver = packageMajver; adjustToMajver(); } public int getPackageMajver() { if (packageMajver < 0) { throw new RuntimeException("Package majver not yet initialized"); } return packageMajver; } private final boolean isReader = this instanceof PackageReader; protected BandStructure() { } final static Coding BYTE1 = Coding.of(1,256); final static Coding CHAR3 = Coding.of(3,128); // Note: Tried sharper (3,16) with no post-zip benefit. // This is best used with BCI values: final static Coding BCI5 = Coding.of(5,4); // mostly 1-byte offsets final static Coding BRANCH5 = Coding.of(5,4,2); // mostly forward branches final static Coding UNSIGNED5 = Coding.of(5,64); final static Coding UDELTA5 = UNSIGNED5.getDeltaCoding(); // "sharp" (5,64) zips 0.4% better than "medium" (5,128) // It zips 1.1% better than "flat" (5,192) final static Coding SIGNED5 = Coding.of(5,64,1); //sharp final static Coding DELTA5 = SIGNED5.getDeltaCoding(); // Note: Tried (5,128,2) and (5,192,2) with no benefit. final static Coding MDELTA5 = Coding.of(5,64,2).getDeltaCoding(); final private static Coding[] basicCodings = { // Table of "Canonical BHSD Codings" from Pack200 spec. null, // _meta_default // Fixed-length codings: Coding.of(1,256,0), Coding.of(1,256,1), Coding.of(1,256,0).getDeltaCoding(), Coding.of(1,256,1).getDeltaCoding(), Coding.of(2,256,0), Coding.of(2,256,1), Coding.of(2,256,0).getDeltaCoding(), Coding.of(2,256,1).getDeltaCoding(), Coding.of(3,256,0), Coding.of(3,256,1), Coding.of(3,256,0).getDeltaCoding(), Coding.of(3,256,1).getDeltaCoding(), Coding.of(4,256,0), Coding.of(4,256,1), Coding.of(4,256,0).getDeltaCoding(), Coding.of(4,256,1).getDeltaCoding(), // Full-range variable-length codings: Coding.of(5, 4,0), Coding.of(5, 4,1), Coding.of(5, 4,2), Coding.of(5, 16,0), Coding.of(5, 16,1), Coding.of(5, 16,2), Coding.of(5, 32,0), Coding.of(5, 32,1), Coding.of(5, 32,2), Coding.of(5, 64,0), Coding.of(5, 64,1), Coding.of(5, 64,2), Coding.of(5,128,0), Coding.of(5,128,1), Coding.of(5,128,2), Coding.of(5, 4,0).getDeltaCoding(), Coding.of(5, 4,1).getDeltaCoding(), Coding.of(5, 4,2).getDeltaCoding(), Coding.of(5, 16,0).getDeltaCoding(), Coding.of(5, 16,1).getDeltaCoding(), Coding.of(5, 16,2).getDeltaCoding(), Coding.of(5, 32,0).getDeltaCoding(), Coding.of(5, 32,1).getDeltaCoding(), Coding.of(5, 32,2).getDeltaCoding(), Coding.of(5, 64,0).getDeltaCoding(), Coding.of(5, 64,1).getDeltaCoding(), Coding.of(5, 64,2).getDeltaCoding(), Coding.of(5,128,0).getDeltaCoding(), Coding.of(5,128,1).getDeltaCoding(), Coding.of(5,128,2).getDeltaCoding(), // Variable length subrange codings: Coding.of(2,192,0), Coding.of(2,224,0), Coding.of(2,240,0), Coding.of(2,248,0), Coding.of(2,252,0), Coding.of(2, 8,0).getDeltaCoding(), Coding.of(2, 8,1).getDeltaCoding(), Coding.of(2, 16,0).getDeltaCoding(), Coding.of(2, 16,1).getDeltaCoding(), Coding.of(2, 32,0).getDeltaCoding(), Coding.of(2, 32,1).getDeltaCoding(), Coding.of(2, 64,0).getDeltaCoding(), Coding.of(2, 64,1).getDeltaCoding(), Coding.of(2,128,0).getDeltaCoding(), Coding.of(2,128,1).getDeltaCoding(), Coding.of(2,192,0).getDeltaCoding(), Coding.of(2,192,1).getDeltaCoding(), Coding.of(2,224,0).getDeltaCoding(), Coding.of(2,224,1).getDeltaCoding(), Coding.of(2,240,0).getDeltaCoding(), Coding.of(2,240,1).getDeltaCoding(), Coding.of(2,248,0).getDeltaCoding(), Coding.of(2,248,1).getDeltaCoding(), Coding.of(3,192,0), Coding.of(3,224,0), Coding.of(3,240,0), Coding.of(3,248,0), Coding.of(3,252,0), Coding.of(3, 8,0).getDeltaCoding(), Coding.of(3, 8,1).getDeltaCoding(), Coding.of(3, 16,0).getDeltaCoding(), Coding.of(3, 16,1).getDeltaCoding(), Coding.of(3, 32,0).getDeltaCoding(), Coding.of(3, 32,1).getDeltaCoding(), Coding.of(3, 64,0).getDeltaCoding(), Coding.of(3, 64,1).getDeltaCoding(), Coding.of(3,128,0).getDeltaCoding(), Coding.of(3,128,1).getDeltaCoding(), Coding.of(3,192,0).getDeltaCoding(), Coding.of(3,192,1).getDeltaCoding(), Coding.of(3,224,0).getDeltaCoding(), Coding.of(3,224,1).getDeltaCoding(), Coding.of(3,240,0).getDeltaCoding(), Coding.of(3,240,1).getDeltaCoding(), Coding.of(3,248,0).getDeltaCoding(), Coding.of(3,248,1).getDeltaCoding(), Coding.of(4,192,0), Coding.of(4,224,0), Coding.of(4,240,0), Coding.of(4,248,0), Coding.of(4,252,0), Coding.of(4, 8,0).getDeltaCoding(), Coding.of(4, 8,1).getDeltaCoding(), Coding.of(4, 16,0).getDeltaCoding(), Coding.of(4, 16,1).getDeltaCoding(), Coding.of(4, 32,0).getDeltaCoding(), Coding.of(4, 32,1).getDeltaCoding(), Coding.of(4, 64,0).getDeltaCoding(), Coding.of(4, 64,1).getDeltaCoding(), Coding.of(4,128,0).getDeltaCoding(), Coding.of(4,128,1).getDeltaCoding(), Coding.of(4,192,0).getDeltaCoding(), Coding.of(4,192,1).getDeltaCoding(), Coding.of(4,224,0).getDeltaCoding(), Coding.of(4,224,1).getDeltaCoding(), Coding.of(4,240,0).getDeltaCoding(), Coding.of(4,240,1).getDeltaCoding(), Coding.of(4,248,0).getDeltaCoding(), Coding.of(4,248,1).getDeltaCoding(), null }; final private static Map<Coding, Integer> basicCodingIndexes; static { assert(basicCodings[_meta_default] == null); assert(basicCodings[_meta_canon_min] != null); assert(basicCodings[_meta_canon_max] != null); Map<Coding, Integer> map = new HashMap<>(); for (int i = 0; i < basicCodings.length; i++) { Coding c = basicCodings[i]; if (c == null) continue; assert(i >= _meta_canon_min); assert(i <= _meta_canon_max); map.put(c, i); } basicCodingIndexes = map; } public static Coding codingForIndex(int i) { return i < basicCodings.length ? basicCodings[i] : null; } public static int indexOf(Coding c) { Integer i = basicCodingIndexes.get(c); if (i == null) return 0; return i.intValue(); } public static Coding[] getBasicCodings() { return basicCodings.clone(); } protected byte[] bandHeaderBytes; // used for input only protected int bandHeaderBytePos; // BHB read pointer, for input only protected int bandHeaderBytePos0; // for debug protected CodingMethod getBandHeader(int XB, Coding regularCoding) { CodingMethod[] res = {null}; // push back XB onto the band header bytes bandHeaderBytes[--bandHeaderBytePos] = (byte) XB; bandHeaderBytePos0 = bandHeaderBytePos; // scan forward through XB and any additional band header bytes bandHeaderBytePos = parseMetaCoding(bandHeaderBytes, bandHeaderBytePos, regularCoding, res); return res[0]; } public static int parseMetaCoding(byte[] bytes, int pos, Coding dflt, CodingMethod[] res) { if ((bytes[pos] & 0xFF) == _meta_default) { res[0] = dflt; return pos+1; } int pos2; pos2 = Coding.parseMetaCoding(bytes, pos, dflt, res); if (pos2 > pos) return pos2; pos2 = PopulationCoding.parseMetaCoding(bytes, pos, dflt, res); if (pos2 > pos) return pos2; pos2 = AdaptiveCoding.parseMetaCoding(bytes, pos, dflt, res); if (pos2 > pos) return pos2; throw new RuntimeException("Bad meta-coding op "+(bytes[pos]&0xFF)); } static final int SHORT_BAND_HEURISTIC = 100; public static final int NO_PHASE = 0; // package writing phases: public static final int COLLECT_PHASE = 1; // collect data before write public static final int FROZEN_PHASE = 3; // no longer collecting public static final int WRITE_PHASE = 5; // ready to write bytes // package reading phases: public static final int EXPECT_PHASE = 2; // gather expected counts public static final int READ_PHASE = 4; // ready to read bytes public static final int DISBURSE_PHASE = 6; // pass out data after read public static final int DONE_PHASE = 8; // done writing or reading static boolean phaseIsRead(int p) { return (p % 2) == 0; } static int phaseCmp(int p0, int p1) { assert((p0 % 2) == (p1 % 2) || (p0 % 8) == 0 || (p1 % 8) == 0); return p0 - p1; } /** The packed file is divided up into a number of segments. * Most segments are typed as ValueBand, strongly-typed sequences * of integer values, all interpreted in a single way. * A few segments are ByteBands, which hetergeneous sequences * of bytes. * * The two phases for writing a packed file are COLLECT and WRITE. * 1. When writing a packed file, each band collects * data in an ad-hoc order. * 2. At the end, each band is assigned a coding scheme, * and then all the bands are written in their global order. * * The three phases for reading a packed file are EXPECT, READ, * and DISBURSE. * 1. For each band, the expected number of integers is determined. * 2. The data is actually read from the file into the band. * 3. The band pays out its values as requested, in an ad hoc order. * * When the last phase of a band is done, it is marked so (DONE). * Clearly, these phases must be properly ordered WRT each other. */ abstract class Band { private int phase = NO_PHASE; private final String name; private int valuesExpected; protected long outputSize = -1; // cache final public Coding regularCoding; final public int seqForDebug; public int elementCountForDebug; protected Band(String name, Coding regularCoding) { this.name = name; this.regularCoding = regularCoding; this.seqForDebug = ++nextSeqForDebug; if (verbose > 2) Utils.log.fine("Band "+seqForDebug+" is "+name); // caller must call init } public Band init() { // Cannot due this from the constructor, because constructor // may wish to initialize some subclass variables. // Set initial phase for reading or writing: if (isReader) readyToExpect(); else readyToCollect(); return this; } // common operations boolean isReader() { return isReader; } int phase() { return phase; } String name() { return name; } /** Return -1 if data buffer not allocated, else max length. */ public abstract int capacity(); /** Allocate data buffer to specified length. */ protected abstract void setCapacity(int cap); /** Return current number of values in buffer, which must exist. */ public abstract int length(); protected abstract int valuesRemainingForDebug(); public final int valuesExpected() { return valuesExpected; } /** Write out bytes, encoding the values. */ public final void writeTo(OutputStream out) throws IOException { assert(assertReadyToWriteTo(this, out)); setPhase(WRITE_PHASE); // subclasses continue by writing their contents to output writeDataTo(out); doneWriting(); } abstract void chooseBandCodings() throws IOException; public final long outputSize() { if (outputSize >= 0) { long size = outputSize; assert(size == computeOutputSize()); return size; } return computeOutputSize(); } protected abstract long computeOutputSize(); abstract protected void writeDataTo(OutputStream out) throws IOException; /** Expect a certain number of values. */ void expectLength(int l) { assert(assertPhase(this, EXPECT_PHASE)); assert(valuesExpected == 0); // all at once assert(l >= 0); valuesExpected = l; } /** Expect more values. (Multiple calls accumulate.) */ void expectMoreLength(int l) { assert(assertPhase(this, EXPECT_PHASE)); valuesExpected += l; } /// Phase change markers. private void readyToCollect() { // called implicitly by constructor setCapacity(1); setPhase(COLLECT_PHASE); } protected void doneWriting() { assert(assertPhase(this, WRITE_PHASE)); setPhase(DONE_PHASE); } private void readyToExpect() { // called implicitly by constructor setPhase(EXPECT_PHASE); } /** Read in bytes, decoding the values. */ public final void readFrom(InputStream in) throws IOException { assert(assertReadyToReadFrom(this, in)); setCapacity(valuesExpected()); setPhase(READ_PHASE); // subclasses continue by reading their contents from input: readDataFrom(in); readyToDisburse(); } abstract protected void readDataFrom(InputStream in) throws IOException; protected void readyToDisburse() { if (verbose > 1) Utils.log.fine("readyToDisburse "+this); setPhase(DISBURSE_PHASE); } public void doneDisbursing() { assert(assertPhase(this, DISBURSE_PHASE)); setPhase(DONE_PHASE); } public final void doneWithUnusedBand() { if (isReader) { assert(assertPhase(this, EXPECT_PHASE)); assert(valuesExpected() == 0); // Fast forward: setPhase(READ_PHASE); setPhase(DISBURSE_PHASE); setPhase(DONE_PHASE); } else { setPhase(FROZEN_PHASE); } } protected void setPhase(int newPhase) { assert(assertPhaseChangeOK(this, phase, newPhase)); this.phase = newPhase; } protected int lengthForDebug = -1; // DEBUG ONLYLines 1–500 of 2682. Load more
/* * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.java.util.jar.pack; import com.sun.java.util.jar.pack.ConstantPool.Entry; import com.sun.java.util.jar.pack.ConstantPool.Index; import com.sun.java.util.jar.pack.Package.Class.Field; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.EOFException; import java.io.File; import java.io.FileOutputStream; import java.io.FilterInputStream; import java.io.FilterOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.jar.Pack200; import static com.sun.java.util.jar.pack.Constants.*; /** * Define the structure and ordering of "bands" in a packed file. * @author John Rose */ abstract class BandStructure { static final int MAX_EFFORT = 9; static final int MIN_EFFORT = 1; static final int DEFAULT_EFFORT = 5; // Inherit options from Pack200: PropMap p200 = Utils.currentPropMap(); int verbose = p200.getInteger(Utils.DEBUG_VERBOSE); int effort = p200.getInteger(Pack200.Packer.EFFORT); { if (effort == 0) effort = DEFAULT_EFFORT; } boolean optDumpBands = p200.getBoolean(Utils.COM_PREFIX+"dump.bands"); boolean optDebugBands = p200.getBoolean(Utils.COM_PREFIX+"debug.bands"); // Various heuristic options. boolean optVaryCodings = !p200.getBoolean(Utils.COM_PREFIX+"no.vary.codings"); boolean optBigStrings = !p200.getBoolean(Utils.COM_PREFIX+"no.big.strings"); abstract protected Index getCPIndex(byte tag); // Local copy of package version. private int packageMajver = -1; /** Call this exactly once, early, to specify the archive major version. */ public void initPackageMajver(int packageMajver) throws IOException { assert(packageMajver > 0 && packageMajver < 0x10000); if (this.packageMajver > 0) { throw new IOException( "Package majver is already initialized to " + this.packageMajver+ "; new setting is " + packageMajver); } this.packageMajver = packageMajver; adjustToMajver(); } public int getPackageMajver() { if (packageMajver < 0) { throw new RuntimeException("Package majver not yet initialized"); } return packageMajver; } private final boolean isReader = this instanceof PackageReader; protected BandStructure() { } final static Coding BYTE1 = Coding.of(1,256); final static Coding CHAR3 = Coding.of(3,128); // Note: Tried sharper (3,16) with no post-zip benefit. // This is best used with BCI values: final static Coding BCI5 = Coding.of(5,4); // mostly 1-byte offsets final static Coding BRANCH5 = Coding.of(5,4,2); // mostly forward branches final static Coding UNSIGNED5 = Coding.of(5,64); final static Coding UDELTA5 = UNSIGNED5.getDeltaCoding(); // "sharp" (5,64) zips 0.4% better than "medium" (5,128) // It zips 1.1% better than "flat" (5,192) final static Coding SIGNED5 = Coding.of(5,64,1); //sharp final static Coding DELTA5 = SIGNED5.getDeltaCoding(); // Note: Tried (5,128,2) and (5,192,2) with no benefit. final static Coding MDELTA5 = Coding.of(5,64,2).getDeltaCoding(); final private static Coding[] basicCodings = { // Table of "Canonical BHSD Codings" from Pack200 spec. null, // _meta_default // Fixed-length codings: Coding.of(1,256,0), Coding.of(1,256,1), Coding.of(1,256,0).getDeltaCoding(), Coding.of(1,256,1).getDeltaCoding(), Coding.of(2,256,0), Coding.of(2,256,1), Coding.of(2,256,0).getDeltaCoding(), Coding.of(2,256,1).getDeltaCoding(), Coding.of(3,256,0), Coding.of(3,256,1), Coding.of(3,256,0).getDeltaCoding(), Coding.of(3,256,1).getDeltaCoding(), Coding.of(4,256,0), Coding.of(4,256,1), Coding.of(4,256,0).getDeltaCoding(), Coding.of(4,256,1).getDeltaCoding(), // Full-range variable-length codings: Coding.of(5, 4,0), Coding.of(5, 4,1), Coding.of(5, 4,2), Coding.of(5, 16,0), Coding.of(5, 16,1), Coding.of(5, 16,2), Coding.of(5, 32,0), Coding.of(5, 32,1), Coding.of(5, 32,2), Coding.of(5, 64,0), Coding.of(5, 64,1), Coding.of(5, 64,2), Coding.of(5,128,0), Coding.of(5,128,1), Coding.of(5,128,2), Coding.of(5, 4,0).getDeltaCoding(), Coding.of(5, 4,1).getDeltaCoding(), Coding.of(5, 4,2).getDeltaCoding(), Coding.of(5, 16,0).getDeltaCoding(), Coding.of(5, 16,1).getDeltaCoding(), Coding.of(5, 16,2).getDeltaCoding(), Coding.of(5, 32,0).getDeltaCoding(), Coding.of(5, 32,1).getDeltaCoding(), Coding.of(5, 32,2).getDeltaCoding(), Coding.of(5, 64,0).getDeltaCoding(), Coding.of(5, 64,1).getDeltaCoding(), Coding.of(5, 64,2).getDeltaCoding(), Coding.of(5,128,0).getDeltaCoding(), Coding.of(5,128,1).getDeltaCoding(), Coding.of(5,128,2).getDeltaCoding(), // Variable length subrange codings: Coding.of(2,192,0), Coding.of(2,224,0), Coding.of(2,240,0), Coding.of(2,248,0), Coding.of(2,252,0), Coding.of(2, 8,0).getDeltaCoding(), Coding.of(2, 8,1).getDeltaCoding(), Coding.of(2, 16,0).getDeltaCoding(), Coding.of(2, 16,1).getDeltaCoding(), Coding.of(2, 32,0).getDeltaCoding(), Coding.of(2, 32,1).getDeltaCoding(), Coding.of(2, 64,0).getDeltaCoding(), Coding.of(2, 64,1).getDeltaCoding(), Coding.of(2,128,0).getDeltaCoding(), Coding.of(2,128,1).getDeltaCoding(), Coding.of(2,192,0).getDeltaCoding(), Coding.of(2,192,1).getDeltaCoding(), Coding.of(2,224,0).getDeltaCoding(), Coding.of(2,224,1).getDeltaCoding(), Coding.of(2,240,0).getDeltaCoding(), Coding.of(2,240,1).getDeltaCoding(), Coding.of(2,248,0).getDeltaCoding(), Coding.of(2,248,1).getDeltaCoding(), Coding.of(3,192,0), Coding.of(3,224,0), Coding.of(3,240,0), Coding.of(3,248,0), Coding.of(3,252,0), Coding.of(3, 8,0).getDeltaCoding(), Coding.of(3, 8,1).getDeltaCoding(), Coding.of(3, 16,0).getDeltaCoding(), Coding.of(3, 16,1).getDeltaCoding(), Coding.of(3, 32,0).getDeltaCoding(), Coding.of(3, 32,1).getDeltaCoding(), Coding.of(3, 64,0).getDeltaCoding(), Coding.of(3, 64,1).getDeltaCoding(), Coding.of(3,128,0).getDeltaCoding(), Coding.of(3,128,1).getDeltaCoding(), Coding.of(3,192,0).getDeltaCoding(), Coding.of(3,192,1).getDeltaCoding(), Coding.of(3,224,0).getDeltaCoding(), Coding.of(3,224,1).getDeltaCoding(), Coding.of(3,240,0).getDeltaCoding(), Coding.of(3,240,1).getDeltaCoding(), Coding.of(3,248,0).getDeltaCoding(), Coding.of(3,248,1).getDeltaCoding(), Coding.of(4,192,0), Coding.of(4,224,0), Coding.of(4,240,0), Coding.of(4,248,0), Coding.of(4,252,0), Coding.of(4, 8,0).getDeltaCoding(), Coding.of(4, 8,1).getDeltaCoding(), Coding.of(4, 16,0).getDeltaCoding(), Coding.of(4, 16,1).getDeltaCoding(), Coding.of(4, 32,0).getDeltaCoding(), Coding.of(4, 32,1).getDeltaCoding(), Coding.of(4, 64,0).getDeltaCoding(), Coding.of(4, 64,1).getDeltaCoding(), Coding.of(4,128,0).getDeltaCoding(), Coding.of(4,128,1).getDeltaCoding(), Coding.of(4,192,0).getDeltaCoding(), Coding.of(4,192,1).getDeltaCoding(), Coding.of(4,224,0).getDeltaCoding(), Coding.of(4,224,1).getDeltaCoding(), Coding.of(4,240,0).getDeltaCoding(), Coding.of(4,240,1).getDeltaCoding(), Coding.of(4,248,0).getDeltaCoding(), Coding.of(4,248,1).getDeltaCoding(), null }; final private static Map<Coding, Integer> basicCodingIndexes; static { assert(basicCodings[_meta_default] == null); assert(basicCodings[_meta_canon_min] != null); assert(basicCodings[_meta_canon_max] != null); Map<Coding, Integer> map = new HashMap<>(); for (int i = 0; i < basicCodings.length; i++) { Coding c = basicCodings[i]; if (c == null) continue; assert(i >= _meta_canon_min); assert(i <= _meta_canon_max); map.put(c, i); } basicCodingIndexes = map; } public static Coding codingForIndex(int i) { return i < basicCodings.length ? basicCodings[i] : null; } public static int indexOf(Coding c) { Integer i = basicCodingIndexes.get(c); if (i == null) return 0; return i.intValue(); } public static Coding[] getBasicCodings() { return basicCodings.clone(); } protected byte[] bandHeaderBytes; // used for input only protected int bandHeaderBytePos; // BHB read pointer, for input only protected int bandHeaderBytePos0; // for debug protected CodingMethod getBandHeader(int XB, Coding regularCoding) { CodingMethod[] res = {null}; // push back XB onto the band header bytes bandHeaderBytes[--bandHeaderBytePos] = (byte) XB; bandHeaderBytePos0 = bandHeaderBytePos; // scan forward through XB and any additional band header bytes bandHeaderBytePos = parseMetaCoding(bandHeaderBytes, bandHeaderBytePos, regularCoding, res); return res[0]; } public static int parseMetaCoding(byte[] bytes, int pos, Coding dflt, CodingMethod[] res) { if ((bytes[pos] & 0xFF) == _meta_default) { res[0] = dflt; return pos+1; } int pos2; pos2 = Coding.parseMetaCoding(bytes, pos, dflt, res); if (pos2 > pos) return pos2; pos2 = PopulationCoding.parseMetaCoding(bytes, pos, dflt, res); if (pos2 > pos) return pos2; pos2 = AdaptiveCoding.parseMetaCoding(bytes, pos, dflt, res); if (pos2 > pos) return pos2; throw new RuntimeException("Bad meta-coding op "+(bytes[pos]&0xFF)); } static final int SHORT_BAND_HEURISTIC = 100; public static final int NO_PHASE = 0; // package writing phases: public static final int COLLECT_PHASE = 1; // collect data before write public static final int FROZEN_PHASE = 3; // no longer collecting public static final int WRITE_PHASE = 5; // ready to write bytes // package reading phases: public static final int EXPECT_PHASE = 2; // gather expected counts public static final int READ_PHASE = 4; // ready to read bytes public static final int DISBURSE_PHASE = 6; // pass out data after read public static final int DONE_PHASE = 8; // done writing or reading static boolean phaseIsRead(int p) { return (p % 2) == 0; } static int phaseCmp(int p0, int p1) { assert((p0 % 2) == (p1 % 2) || (p0 % 8) == 0 || (p1 % 8) == 0); return p0 - p1; } /** The packed file is divided up into a number of segments. * Most segments are typed as ValueBand, strongly-typed sequences * of integer values, all interpreted in a single way. * A few segments are ByteBands, which hetergeneous sequences * of bytes. * * The two phases for writing a packed file are COLLECT and WRITE. * 1. When writing a packed file, each band collects * data in an ad-hoc order. * 2. At the end, each band is assigned a coding scheme, * and then all the bands are written in their global order. * * The three phases for reading a packed file are EXPECT, READ, * and DISBURSE. * 1. For each band, the expected number of integers is determined. * 2. The data is actually read from the file into the band. * 3. The band pays out its values as requested, in an ad hoc order. * * When the last phase of a band is done, it is marked so (DONE). * Clearly, these phases must be properly ordered WRT each other. */ abstract class Band { private int phase = NO_PHASE; private final String name; private int valuesExpected; protected long outputSize = -1; // cache final public Coding regularCoding; final public int seqForDebug; public int elementCountForDebug; protected Band(String name, Coding regularCoding) { this.name = name; this.regularCoding = regularCoding; this.seqForDebug = ++nextSeqForDebug; if (verbose > 2) Utils.log.fine("Band "+seqForDebug+" is "+name); // caller must call init } public Band init() { // Cannot due this from the constructor, because constructor // may wish to initialize some subclass variables. // Set initial phase for reading or writing: if (isReader) readyToExpect(); else readyToCollect(); return this; } // common operations boolean isReader() { return isReader; } int phase() { return phase; } String name() { return name; } /** Return -1 if data buffer not allocated, else max length. */ public abstract int capacity(); /** Allocate data buffer to specified length. */ protected abstract void setCapacity(int cap); /** Return current number of values in buffer, which must exist. */ public abstract int length(); protected abstract int valuesRemainingForDebug(); public final int valuesExpected() { return valuesExpected; } /** Write out bytes, encoding the values. */ public final void writeTo(OutputStream out) throws IOException { assert(assertReadyToWriteTo(this, out)); setPhase(WRITE_PHASE); // subclasses continue by writing their contents to output writeDataTo(out); doneWriting(); } abstract void chooseBandCodings() throws IOException; public final long outputSize() { if (outputSize >= 0) { long size = outputSize; assert(size == computeOutputSize()); return size; } return computeOutputSize(); } protected abstract long computeOutputSize(); abstract protected void writeDataTo(OutputStream out) throws IOException; /** Expect a certain number of values. */ void expectLength(int l) { assert(assertPhase(this, EXPECT_PHASE)); assert(valuesExpected == 0); // all at once assert(l >= 0); valuesExpected = l; } /** Expect more values. (Multiple calls accumulate.) */ void expectMoreLength(int l) { assert(assertPhase(this, EXPECT_PHASE)); valuesExpected += l; } /// Phase change markers. private void readyToCollect() { // called implicitly by constructor setCapacity(1); setPhase(COLLECT_PHASE); } protected void doneWriting() { assert(assertPhase(this, WRITE_PHASE)); setPhase(DONE_PHASE); } private void readyToExpect() { // called implicitly by constructor setPhase(EXPECT_PHASE); } /** Read in bytes, decoding the values. */ public final void readFrom(InputStream in) throws IOException { assert(assertReadyToReadFrom(this, in)); setCapacity(valuesExpected()); setPhase(READ_PHASE); // subclasses continue by reading their contents from input: readDataFrom(in); readyToDisburse(); } abstract protected void readDataFrom(InputStream in) throws IOException; protected void readyToDisburse() { if (verbose > 1) Utils.log.fine("readyToDisburse "+this); setPhase(DISBURSE_PHASE); } public void doneDisbursing() { assert(assertPhase(this, DISBURSE_PHASE)); setPhase(DONE_PHASE); } public final void doneWithUnusedBand() { if (isReader) { assert(assertPhase(this, EXPECT_PHASE)); assert(valuesExpected() == 0); // Fast forward: setPhase(READ_PHASE); setPhase(DISBURSE_PHASE); setPhase(DONE_PHASE); } else { setPhase(FROZEN_PHASE); } } protected void setPhase(int newPhase) { assert(assertPhaseChangeOK(this, phase, newPhase)); this.phase = newPhase; } protected int lengthForDebug = -1; // DEBUG ONLY