|
Java example source code file (PackageWriter.java)
The PackageWriter.java Java example source code/* * Copyright (c) 2001, 2013, 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.*; import com.sun.java.util.jar.pack.Package.Class; import com.sun.java.util.jar.pack.Package.File; import com.sun.java.util.jar.pack.Package.InnerClass; import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import static com.sun.java.util.jar.pack.Constants.*; /** * Writer for a package file. * @author John Rose */ class PackageWriter extends BandStructure { Package pkg; OutputStream finalOut; Package.Version packageVersion; PackageWriter(Package pkg, OutputStream out) throws IOException { this.pkg = pkg; this.finalOut = out; // Caller has specified maximum class file version in the package: initHighestClassVersion(pkg.getHighestClassVersion()); } void write() throws IOException { boolean ok = false; try { if (verbose > 0) { Utils.log.info("Setting up constant pool..."); } setup(); if (verbose > 0) { Utils.log.info("Packing..."); } // writeFileHeader() is done last, since it has ultimate counts // writeBandHeaders() is called after all other bands are done writeConstantPool(); writeFiles(); writeAttrDefs(); writeInnerClasses(); writeClassesAndByteCodes(); writeAttrCounts(); if (verbose > 1) printCodeHist(); // choose codings (fill band_headers if needed) if (verbose > 0) { Utils.log.info("Coding..."); } all_bands.chooseBandCodings(); // now we can write the headers: writeFileHeader(); writeAllBandsTo(finalOut); ok = true; } catch (Exception ee) { Utils.log.warning("Error on output: "+ee, ee); //if (verbose > 0) ee.printStackTrace(); // Write partial output only if we are verbose. if (verbose > 0) finalOut.close(); if (ee instanceof IOException) throw (IOException)ee; if (ee instanceof RuntimeException) throw (RuntimeException)ee; throw new Error("error packing", ee); } } Set<Entry> requiredEntries; // for the CP Map<Attribute.Layout, int[]> backCountTable; // for layout callables int[][] attrCounts; // count attr. occurrences void setup() { requiredEntries = new HashSet<>(); setArchiveOptions(); trimClassAttributes(); collectAttributeLayouts(); pkg.buildGlobalConstantPool(requiredEntries); setBandIndexes(); makeNewAttributeBands(); collectInnerClasses(); } /* * Convenience function to choose an archive version based * on the class file versions observed within the archive * or set the user defined version preset via properties. */ void chooseDefaultPackageVersion() throws IOException { if (pkg.packageVersion != null) { packageVersion = pkg.packageVersion; if (verbose > 0) { Utils.log.info("package version overridden with: " + packageVersion); } return; } Package.Version highV = getHighestClassVersion(); // set the package version now if (highV.lessThan(JAVA6_MAX_CLASS_VERSION)) { // There are only old classfiles in this segment or resources packageVersion = JAVA5_PACKAGE_VERSION; } else if (highV.equals(JAVA6_MAX_CLASS_VERSION) || (highV.equals(JAVA7_MAX_CLASS_VERSION) && !pkg.cp.haveExtraTags())) { // force down the package version if we have jdk7 classes without // any Indy references, this is because jdk7 class file (51.0) without // Indy is identical to jdk6 class file (50.0). packageVersion = JAVA6_PACKAGE_VERSION; } else if (highV.equals(JAVA7_MAX_CLASS_VERSION)) { packageVersion = JAVA7_PACKAGE_VERSION; } else { // Normal case. Use the newest archive format, when available packageVersion = JAVA8_PACKAGE_VERSION; } if (verbose > 0) { Utils.log.info("Highest version class file: " + highV + " package version: " + packageVersion); } } void checkVersion() throws IOException { assert(packageVersion != null); if (packageVersion.lessThan(JAVA7_PACKAGE_VERSION)) { // this bit was reserved for future use in previous versions if (testBit(archiveOptions, AO_HAVE_CP_EXTRAS)) { throw new IOException("Format bits for Java 7 must be zero in previous releases"); } } if (testBit(archiveOptions, AO_UNUSED_MBZ)) { throw new IOException("High archive option bits are reserved and must be zero: " + Integer.toHexString(archiveOptions)); } } void setArchiveOptions() { // Decide on some archive options early. // Does not decide on: AO_HAVE_SPECIAL_FORMATS, // AO_HAVE_CP_NUMBERS, AO_HAVE_FILE_HEADERS. // Also, AO_HAVE_FILE_OPTIONS may be forced on later. int minModtime = pkg.default_modtime; int maxModtime = pkg.default_modtime; int minOptions = -1; int maxOptions = 0; // Import defaults from package (deflate hint, etc.). archiveOptions |= pkg.default_options; for (File file : pkg.files) { int modtime = file.modtime; int options = file.options; if (minModtime == NO_MODTIME) { minModtime = maxModtime = modtime; } else { if (minModtime > modtime) minModtime = modtime; if (maxModtime < modtime) maxModtime = modtime; } minOptions &= options; maxOptions |= options; } if (pkg.default_modtime == NO_MODTIME) { // Make everything else be a positive offset from here. pkg.default_modtime = minModtime; } if (minModtime != NO_MODTIME && minModtime != maxModtime) { // Put them into a band. archiveOptions |= AO_HAVE_FILE_MODTIME; } // If the archive deflation is set do not bother with each file. if (!testBit(archiveOptions,AO_DEFLATE_HINT) && minOptions != -1) { if (testBit(minOptions, FO_DEFLATE_HINT)) { // Every file has the deflate_hint set. // Set it for the whole archive, and omit options. archiveOptions |= AO_DEFLATE_HINT; minOptions -= FO_DEFLATE_HINT; maxOptions -= FO_DEFLATE_HINT; } pkg.default_options |= minOptions; if (minOptions != maxOptions || minOptions != pkg.default_options) { archiveOptions |= AO_HAVE_FILE_OPTIONS; } } // Decide on default version number (majority rule). Map<Package.Version, int[]> verCounts = new HashMap<>(); int bestCount = 0; Package.Version bestVersion = null; for (Class cls : pkg.classes) { Package.Version version = cls.getVersion(); int[] var = verCounts.get(version); if (var == null) { var = new int[1]; verCounts.put(version, var); } int count = (var[0] += 1); //System.out.println("version="+version+" count="+count); if (bestCount < count) { bestCount = count; bestVersion = version; } } verCounts.clear(); if (bestVersion == null) bestVersion = JAVA_MIN_CLASS_VERSION; // degenerate case pkg.defaultClassVersion = bestVersion; if (verbose > 0) Utils.log.info("Consensus version number in segment is " + bestVersion); if (verbose > 0) Utils.log.info("Highest version number in segment is " + pkg.getHighestClassVersion()); // Now add explicit pseudo-attrs. to classes with odd versions. for (Class cls : pkg.classes) { if (!cls.getVersion().equals(bestVersion)) { Attribute a = makeClassFileVersionAttr(cls.getVersion()); if (verbose > 1) { Utils.log.fine("Version "+cls.getVersion() + " of " + cls + " doesn't match package version " + bestVersion); } // Note: Does not add in "natural" order. (Who cares?) cls.addAttribute(a); } } // Decide if we are transmitting a huge resource file: for (File file : pkg.files) { long len = file.getFileLength(); if (len != (int)len) { archiveOptions |= AO_HAVE_FILE_SIZE_HI; if (verbose > 0) Utils.log.info("Note: Huge resource file "+file.getFileName()+" forces 64-bit sizing"); break; } } // Decide if code attributes typically have sub-attributes. // In that case, to preserve compact 1-byte code headers, // we must declare unconditional presence of code flags. int cost0 = 0; int cost1 = 0; for (Class cls : pkg.classes) { for (Class.Method m : cls.getMethods()) { if (m.code != null) { if (m.code.attributeSize() == 0) { // cost of a useless unconditional flags byte cost1 += 1; } else if (shortCodeHeader(m.code) != LONG_CODE_HEADER) { // cost of inflating a short header cost0 += 3; } } } } if (cost0 > cost1) { archiveOptions |= AO_HAVE_ALL_CODE_FLAGS; } if (verbose > 0) Utils.log.info("archiveOptions = " +"0b"+Integer.toBinaryString(archiveOptions)); } void writeFileHeader() throws IOException { chooseDefaultPackageVersion(); writeArchiveMagic(); writeArchiveHeader(); } // Local routine used to format fixed-format scalars // in the file_header: private void putMagicInt32(int val) throws IOException { int res = val; for (int i = 0; i < 4; i++) { archive_magic.putByte(0xFF & (res >>> 24)); res <<= 8; } } void writeArchiveMagic() throws IOException { putMagicInt32(pkg.magic); } void writeArchiveHeader() throws IOException { // for debug only: number of words optimized away int headerSizeForDebug = AH_LENGTH_MIN; // AO_HAVE_SPECIAL_FORMATS is set if non-default // coding techniques are used, or if there are // compressor-defined attributes transmitted. boolean haveSpecial = testBit(archiveOptions, AO_HAVE_SPECIAL_FORMATS); if (!haveSpecial) { haveSpecial |= (band_headers.length() != 0); haveSpecial |= (attrDefsWritten.length != 0); if (haveSpecial) archiveOptions |= AO_HAVE_SPECIAL_FORMATS; } if (haveSpecial) headerSizeForDebug += AH_SPECIAL_FORMAT_LEN; // AO_HAVE_FILE_HEADERS is set if there is any // file or segment envelope information present. boolean haveFiles = testBit(archiveOptions, AO_HAVE_FILE_HEADERS); if (!haveFiles) { haveFiles |= (archiveNextCount > 0); haveFiles |= (pkg.default_modtime != NO_MODTIME); if (haveFiles) archiveOptions |= AO_HAVE_FILE_HEADERS; } if (haveFiles) headerSizeForDebug += AH_FILE_HEADER_LEN; // AO_HAVE_CP_NUMBERS is set if there are any numbers // in the global constant pool. (Numbers are in 15% of classes.) boolean haveNumbers = testBit(archiveOptions, AO_HAVE_CP_NUMBERS); if (!haveNumbers) { haveNumbers |= pkg.cp.haveNumbers(); if (haveNumbers) archiveOptions |= AO_HAVE_CP_NUMBERS; } if (haveNumbers) headerSizeForDebug += AH_CP_NUMBER_LEN; // AO_HAVE_CP_EXTRAS is set if there are constant pool entries // beyond the Java 6 version of the class file format. boolean haveCPExtra = testBit(archiveOptions, AO_HAVE_CP_EXTRAS); if (!haveCPExtra) { haveCPExtra |= pkg.cp.haveExtraTags(); if (haveCPExtra) archiveOptions |= AO_HAVE_CP_EXTRAS; } if (haveCPExtra) headerSizeForDebug += AH_CP_EXTRA_LEN; // the archiveOptions are all initialized, sanity check now!. checkVersion(); archive_header_0.putInt(packageVersion.minor); archive_header_0.putInt(packageVersion.major); if (verbose > 0) Utils.log.info("Package Version for this segment:" + packageVersion); archive_header_0.putInt(archiveOptions); // controls header format assert(archive_header_0.length() == AH_LENGTH_0); final int DUMMY = 0; if (haveFiles) { assert(archive_header_S.length() == AH_ARCHIVE_SIZE_HI); archive_header_S.putInt(DUMMY); // (archiveSize1 >>> 32) assert(archive_header_S.length() == AH_ARCHIVE_SIZE_LO); archive_header_S.putInt(DUMMY); // (archiveSize1 >>> 0) assert(archive_header_S.length() == AH_LENGTH_S); } // Done with unsized part of header.... if (haveFiles) { archive_header_1.putInt(archiveNextCount); // usually zero archive_header_1.putInt(pkg.default_modtime); archive_header_1.putInt(pkg.files.size()); } else { assert(pkg.files.isEmpty()); } if (haveSpecial) { archive_header_1.putInt(band_headers.length()); archive_header_1.putInt(attrDefsWritten.length); } else { assert(band_headers.length() == 0); assert(attrDefsWritten.length == 0); } writeConstantPoolCounts(haveNumbers, haveCPExtra); archive_header_1.putInt(pkg.getAllInnerClasses().size()); archive_header_1.putInt(pkg.defaultClassVersion.minor); archive_header_1.putInt(pkg.defaultClassVersion.major); archive_header_1.putInt(pkg.classes.size()); // Sanity: Make sure we came out to 29 (less optional fields): assert(archive_header_0.length() + archive_header_S.length() + archive_header_1.length() == headerSizeForDebug); // Figure out all the sizes now, first cut: archiveSize0 = 0; archiveSize1 = all_bands.outputSize(); // Second cut: archiveSize0 += archive_magic.outputSize(); archiveSize0 += archive_header_0.outputSize(); archiveSize0 += archive_header_S.outputSize(); // Make the adjustments: archiveSize1 -= archiveSize0; // Patch the header: if (haveFiles) { int archiveSizeHi = (int)(archiveSize1 >>> 32); int archiveSizeLo = (int)(archiveSize1 >>> 0); archive_header_S.patchValue(AH_ARCHIVE_SIZE_HI, archiveSizeHi); archive_header_S.patchValue(AH_ARCHIVE_SIZE_LO, archiveSizeLo); int zeroLen = UNSIGNED5.getLength(DUMMY); archiveSize0 += UNSIGNED5.getLength(archiveSizeHi) - zeroLen; archiveSize0 += UNSIGNED5.getLength(archiveSizeLo) - zeroLen; } if (verbose > 1) Utils.log.fine("archive sizes: "+ archiveSize0+"+"+archiveSize1); assert(all_bands.outputSize() == archiveSize0+archiveSize1); } void writeConstantPoolCounts(boolean haveNumbers, boolean haveCPExtra) throws IOException { for (byte tag : ConstantPool.TAGS_IN_ORDER) { int count = pkg.cp.getIndexByTag(tag).size(); switch (tag) { case CONSTANT_Utf8: // The null string is always first. if (count > 0) assert(pkg.cp.getIndexByTag(tag).get(0) == ConstantPool.getUtf8Entry("")); break; case CONSTANT_Integer: case CONSTANT_Float: case CONSTANT_Long: case CONSTANT_Double: // Omit counts for numbers if possible. if (!haveNumbers) { assert(count == 0); continue; } break; case CONSTANT_MethodHandle: case CONSTANT_MethodType: case CONSTANT_InvokeDynamic: case CONSTANT_BootstrapMethod: // Omit counts for newer entities if possible. if (!haveCPExtra) { assert(count == 0); continue; } break; } archive_header_1.putInt(count); } } protected Index getCPIndex(byte tag) { return pkg.cp.getIndexByTag(tag); } // (The following observations are out of date; they apply only to // "banding" the constant pool itself. Later revisions of this algorithm // applied the banding technique to every part of the package file, // applying the benefits more broadly.) // Note: Keeping the data separate in passes (or "bands") allows the // compressor to issue significantly shorter indexes for repeated data. // The difference in zipped size is 4%, which is remarkable since the // unzipped sizes are the same (only the byte order differs). // After moving similar data into bands, it becomes natural to delta-encode // each band. (This is especially useful if we sort the constant pool first.) // Delta encoding saves an extra 5% in the output size (13% of the CP itself). // Because a typical delta usees much less data than a byte, the savings after // zipping is even better: A zipped delta-encoded package is 8% smaller than // a zipped non-delta-encoded package. Thus, in the zipped file, a banded, // delta-encoded constant pool saves over 11% (of the total file size) compared // with a zipped unbanded file. void writeConstantPool() throws IOException { IndexGroup cp = pkg.cp; if (verbose > 0) Utils.log.info("Writing CP"); for (byte tag : ConstantPool.TAGS_IN_ORDER) { Index index = cp.getIndexByTag(tag); Entry[] cpMap = index.cpMap; if (verbose > 0) Utils.log.info("Writing "+cpMap.length+" "+ConstantPool.tagName(tag)+" entries..."); if (optDumpBands) { try (PrintStream ps = new PrintStream(getDumpStream(index, ".idx"))) { printArrayTo(ps, cpMap, 0, cpMap.length); } } switch (tag) { case CONSTANT_Utf8: writeUtf8Bands(cpMap); break; case CONSTANT_Integer: for (int i = 0; i < cpMap.length; i++) { NumberEntry e = (NumberEntry) cpMap[i]; int x = ((Integer)e.numberValue()).intValue(); cp_Int.putInt(x); } break; case CONSTANT_Float: for (int i = 0; i < cpMap.length; i++) { NumberEntry e = (NumberEntry) cpMap[i]; float fx = ((Float)e.numberValue()).floatValue(); int x = Float.floatToIntBits(fx); cp_Float.putInt(x); } break; case CONSTANT_Long: for (int i = 0; i < cpMap.length; i++) { NumberEntry e = (NumberEntry) cpMap[i]; long x = ((Long)e.numberValue()).longValue(); cp_Long_hi.putInt((int)(x >>> 32)); cp_Long_lo.putInt((int)(x >>> 0)); } break; case CONSTANT_Double: for (int i = 0; i < cpMap.length; i++) { NumberEntry e = (NumberEntry) cpMap[i]; double dx = ((Double)e.numberValue()).doubleValue(); long x = Double.doubleToLongBits(dx); cp_Double_hi.putInt((int)(x >>> 32)); cp_Double_lo.putInt((int)(x >>> 0)); } break; case CONSTANT_String: for (int i = 0; i < cpMap.length; i++) { StringEntry e = (StringEntry) cpMap[i]; cp_String.putRef(e.ref); } break; case CONSTANT_Class: for (int i = 0; i < cpMap.length; i++) { ClassEntry e = (ClassEntry) cpMap[i]; cp_Class.putRef(e.ref); } break; case CONSTANT_Signature: writeSignatureBands(cpMap); break; case CONSTANT_NameandType: for (int i = 0; i < cpMap.length; i++) { DescriptorEntry e = (DescriptorEntry) cpMap[i]; cp_Descr_name.putRef(e.nameRef); cp_Descr_type.putRef(e.typeRef); } break; case CONSTANT_Fieldref: writeMemberRefs(tag, cpMap, cp_Field_class, cp_Field_desc); break; case CONSTANT_Methodref: writeMemberRefs(tag, cpMap, cp_Method_class, cp_Method_desc); break; case CONSTANT_InterfaceMethodref: writeMemberRefs(tag, cpMap, cp_Imethod_class, cp_Imethod_desc); break; case CONSTANT_MethodHandle: for (int i = 0; i < cpMap.length; i++) { MethodHandleEntry e = (MethodHandleEntry) cpMap[i]; cp_MethodHandle_refkind.putInt(e.refKind); cp_MethodHandle_member.putRef(e.memRef); } break; case CONSTANT_MethodType: for (int i = 0; i < cpMap.length; i++) { MethodTypeEntry e = (MethodTypeEntry) cpMap[i]; cp_MethodType.putRef(e.typeRef); } break; case CONSTANT_InvokeDynamic: for (int i = 0; i < cpMap.length; i++) { InvokeDynamicEntry e = (InvokeDynamicEntry) cpMap[i]; cp_InvokeDynamic_spec.putRef(e.bssRef); cp_InvokeDynamic_desc.putRef(e.descRef); } break; case CONSTANT_BootstrapMethod: for (int i = 0; i < cpMap.length; i++) { BootstrapMethodEntry e = (BootstrapMethodEntry) cpMap[i]; cp_BootstrapMethod_ref.putRef(e.bsmRef); cp_BootstrapMethod_arg_count.putInt(e.argRefs.length); for (Entry argRef : e.argRefs) { cp_BootstrapMethod_arg.putRef(argRef); } } break; default: throw new AssertionError("unexpected CP tag in package"); } } if (optDumpBands || verbose > 1) { for (byte tag = CONSTANT_GroupFirst; tag < CONSTANT_GroupLimit; tag++) { Index index = cp.getIndexByTag(tag); if (index == null || index.isEmpty()) continue; Entry[] cpMap = index.cpMap; if (verbose > 1) Utils.log.info("Index group "+ConstantPool.tagName(tag)+" contains "+cpMap.length+" entries."); if (optDumpBands) { try (PrintStream ps = new PrintStream(getDumpStream(index.debugName, tag, ".gidx", index))) { printArrayTo(ps, cpMap, 0, cpMap.length, true); } } } } } void writeUtf8Bands(Entry[] cpMap) throws IOException { if (cpMap.length == 0) return; // nothing to write // The first element must always be the empty string. assert(cpMap[0].stringValue().equals("")); final int SUFFIX_SKIP_1 = 1; final int PREFIX_SKIP_2 = 2; // Fetch the char arrays, first of all. char[][] chars = new char[cpMap.length][]; for (int i = 0; i < chars.length; i++) { chars[i] = cpMap[i].stringValue().toCharArray(); } // First band: Write lengths of shared prefixes. int[] prefixes = new int[cpMap.length]; // includes 2 skipped zeroes char[] prevChars = {}; for (int i = 0; i < chars.length; i++) { int prefix = 0; char[] curChars = chars[i]; int limit = Math.min(curChars.length, prevChars.length); while (prefix < limit && curChars[prefix] == prevChars[prefix]) prefix++; prefixes[i] = prefix; if (i >= PREFIX_SKIP_2) cp_Utf8_prefix.putInt(prefix); else assert(prefix == 0); prevChars = curChars; } // Second band: Write lengths of unshared suffixes. // Third band: Write the char values in the unshared suffixes. for (int i = 0; i < chars.length; i++) { char[] str = chars[i]; int prefix = prefixes[i]; int suffix = str.length - prefixes[i]; boolean isPacked = false; if (suffix == 0) { // Zero suffix length is special flag to indicate // separate treatment in cp_Utf8_big bands. // This suffix length never occurs naturally, // except in the one case of a zero-length string. // (If it occurs, it is the first, due to sorting.) // The zero length string must, paradoxically, be // encoded as a zero-length cp_Utf8_big band. // This wastes exactly (& tolerably) one null byte. isPacked = (i >= SUFFIX_SKIP_1); // Do not bother to add an empty "(Utf8_big_0)" band. // Also, the initial empty string does not require a band. } else if (optBigStrings && effort > 1 && suffix > 100) { int numWide = 0; for (int n = 0; n < suffix; n++) { if (str[prefix+n] > 127) { numWide++; } } if (numWide > 100) { // Try packing the chars with an alternate encoding. isPacked = tryAlternateEncoding(i, numWide, str, prefix); } } if (i < SUFFIX_SKIP_1) { // No output. assert(!isPacked); assert(suffix == 0); } else if (isPacked) { // Mark packed string with zero-length suffix count. // This tells the unpacker to go elsewhere for the suffix bits. // Fourth band: Write unshared suffix with alternate coding. cp_Utf8_suffix.putInt(0); cp_Utf8_big_suffix.putInt(suffix); } else { assert(suffix != 0); // would be ambiguous // Normal string. Save suffix in third and fourth bands. cp_Utf8_suffix.putInt(suffix); for (int n = 0; n < suffix; n++) { int ch = str[prefix+n]; cp_Utf8_chars.putInt(ch); } } } if (verbose > 0) { int normCharCount = cp_Utf8_chars.length(); int packCharCount = cp_Utf8_big_chars.length(); int charCount = normCharCount + packCharCount; Utils.log.info("Utf8string #CHARS="+charCount+" #PACKEDCHARS="+packCharCount); } } private boolean tryAlternateEncoding(int i, int numWide, char[] str, int prefix) { int suffix = str.length - prefix; int[] cvals = new int[suffix]; for (int n = 0; n < suffix; n++) { cvals[n] = str[prefix+n]; } CodingChooser cc = getCodingChooser(); Coding bigRegular = cp_Utf8_big_chars.regularCoding; String bandName = "(Utf8_big_"+i+")"; int[] sizes = { 0, 0 }; final int BYTE_SIZE = CodingChooser.BYTE_SIZE; final int ZIP_SIZE = CodingChooser.ZIP_SIZE; if (verbose > 1 || cc.verbose > 1) { Utils.log.fine("--- chooseCoding "+bandName); } CodingMethod special = cc.choose(cvals, bigRegular, sizes); Coding charRegular = cp_Utf8_chars.regularCoding; if (verbose > 1) Utils.log.fine("big string["+i+"] len="+suffix+" #wide="+numWide+" size="+sizes[BYTE_SIZE]+"/z="+sizes[ZIP_SIZE]+" coding "+special); if (special != charRegular) { int specialZipSize = sizes[ZIP_SIZE]; int[] normalSizes = cc.computeSize(charRegular, cvals); int normalZipSize = normalSizes[ZIP_SIZE]; int minWin = Math.max(5, normalZipSize/1000); if (verbose > 1) Utils.log.fine("big string["+i+"] normalSize="+normalSizes[BYTE_SIZE]+"/z="+normalSizes[ZIP_SIZE]+" win="+(specialZipSize<normalZipSize-minWin)); if (specialZipSize < normalZipSize-minWin) { IntBand big = cp_Utf8_big_chars.newIntBand(bandName); big.initializeValues(cvals); return true; } } return false; } void writeSignatureBands(Entry[] cpMap) throws IOException { for (int i = 0; i < cpMap.length; i++) { SignatureEntry e = (SignatureEntry) cpMap[i]; cp_Signature_form.putRef(e.formRef); for (int j = 0; j < e.classRefs.length; j++) { cp_Signature_classes.putRef(e.classRefs[j]); } } } void writeMemberRefs(byte tag, Entry[] cpMap, CPRefBand cp_class, CPRefBand cp_desc) throws IOException { for (int i = 0; i < cpMap.length; i++) { MemberEntry e = (MemberEntry) cpMap[i]; cp_class.putRef(e.classRef); cp_desc.putRef(e.descRef); } } void writeFiles() throws IOException { int numFiles = pkg.files.size(); if (numFiles == 0) return; int options = archiveOptions; boolean haveSizeHi = testBit(options, AO_HAVE_FILE_SIZE_HI); boolean haveModtime = testBit(options, AO_HAVE_FILE_MODTIME); boolean haveOptions = testBit(options, AO_HAVE_FILE_OPTIONS); if (!haveOptions) { for (File file : pkg.files) { if (file.isClassStub()) { haveOptions = true; options |= AO_HAVE_FILE_OPTIONS; archiveOptions = options; break; } } } if (haveSizeHi || haveModtime || haveOptions || !pkg.files.isEmpty()) { options |= AO_HAVE_FILE_HEADERS; archiveOptions = options; } for (File file : pkg.files) { file_name.putRef(file.name); long len = file.getFileLength(); file_size_lo.putInt((int)len); if (haveSizeHi) file_size_hi.putInt((int)(len >>> 32)); if (haveModtime) file_modtime.putInt(file.modtime - pkg.default_modtime); if (haveOptions) file_options.putInt(file.options); file.writeTo(file_bits.collectorStream()); if (verbose > 1) Utils.log.fine("Wrote "+len+" bytes of "+file.name.stringValue()); } if (verbose > 0) Utils.log.info("Wrote "+numFiles+" resource files"); } void collectAttributeLayouts() { maxFlags = new int[ATTR_CONTEXT_LIMIT]; allLayouts = new FixedList<>(ATTR_CONTEXT_LIMIT); for (int i = 0; i < ATTR_CONTEXT_LIMIT; i++) { allLayouts.set(i, new HashMap<Attribute.Layout, int[]>()); } // Collect maxFlags and allLayouts. for (Class cls : pkg.classes) { visitAttributeLayoutsIn(ATTR_CONTEXT_CLASS, cls); for (Class.Field f : cls.getFields()) { visitAttributeLayoutsIn(ATTR_CONTEXT_FIELD, f); } for (Class.Method m : cls.getMethods()) { visitAttributeLayoutsIn(ATTR_CONTEXT_METHOD, m); if (m.code != null) { visitAttributeLayoutsIn(ATTR_CONTEXT_CODE, m.code); } } } // If there are many species of attributes, use 63-bit flags. for (int i = 0; i < ATTR_CONTEXT_LIMIT; i++) { int nl = allLayouts.get(i).size(); boolean haveLongFlags = haveFlagsHi(i); final int TOO_MANY_ATTRS = 32 /*int flag size*/ - 12 /*typical flag bits in use*/ + 4 /*typical number of OK overflows*/; if (nl >= TOO_MANY_ATTRS) { // heuristic int mask = 1<<(LG_AO_HAVE_XXX_FLAGS_HI+i); archiveOptions |= mask; haveLongFlags = true; if (verbose > 0) Utils.log.info("Note: Many "+Attribute.contextName(i)+" attributes forces 63-bit flags"); } if (verbose > 1) { Utils.log.fine(Attribute.contextName(i)+".maxFlags = 0x"+Integer.toHexString(maxFlags[i])); Utils.log.fine(Attribute.contextName(i)+".#layouts = "+nl); } assert(haveFlagsHi(i) == haveLongFlags); } initAttrIndexLimit(); // Standard indexes can never conflict with flag bits. Assert it. for (int i = 0; i < ATTR_CONTEXT_LIMIT; i++) { assert((attrFlagMask[i] & maxFlags[i]) == 0); } // Collect counts for both predefs. and custom defs. // Decide on custom, local attribute definitions. backCountTable = new HashMap<>(); attrCounts = new int[ATTR_CONTEXT_LIMIT][]; for (int i = 0; i < ATTR_CONTEXT_LIMIT; i++) { // Now the remaining defs in allLayouts[i] need attr. indexes. // Fill up unused flag bits with new defs. // Unused bits are those which are not used by predefined attrs, // and which are always clear in the classfiles. long avHiBits = ~(maxFlags[i] | attrFlagMask[i]); assert(attrIndexLimit[i] > 0); assert(attrIndexLimit[i] < 64); // all bits fit into a Java long avHiBits &= (1L< Other Java examples (source code examples)Here is a short list of links related to this Java PackageWriter.java source code file: |
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.