1/* 2 * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25package jdk.internal.jimage.decompressor; 26 27import java.io.DataInputStream; 28import java.io.IOException; 29import java.nio.ByteBuffer; 30import java.util.ArrayList; 31import java.util.List; 32 33/** 34 * 35 * Index compressor. Use the minimal amount of bytes required to store 36 * an integer. 37 * 38 * @implNote This class needs to maintain JDK 8 source compatibility. 39 * 40 * It is used internally in the JDK to implement jimage/jrtfs access, 41 * but also compiled and delivered as part of the jrtfs.jar to support access 42 * to the jimage file provided by the shipped JDK by tools running on JDK 8. 43 */ 44public class CompressIndexes { 45 private static final int COMPRESSED_FLAG = 1 << (Byte.SIZE - 1); 46 private static final int HEADER_WIDTH = 3; 47 private static final int HEADER_SHIFT = Byte.SIZE - HEADER_WIDTH; 48 49 public static List<Integer> decompressFlow(byte[] values) { 50 List<Integer> lst = new ArrayList<>(); 51 52 for (int i = 0; i < values.length; i += getHeaderLength(values[i])) { 53 int decompressed = decompress(values, i); 54 lst.add(decompressed); 55 } 56 57 return lst; 58 } 59 60 public static int readInt(DataInputStream cr) throws IOException { 61 // Get header byte. 62 byte header = cr.readByte(); 63 // Determine size. 64 int size = getHeaderLength(header); 65 // Prepare result. 66 int result = getHeaderValue(header); 67 68 // For each value byte 69 for (int i = 1; i < size; i++) { 70 // Merge byte value. 71 result <<= Byte.SIZE; 72 result |= cr.readByte() & 0xFF; 73 } 74 75 return result; 76 } 77 78 private static boolean isCompressed(byte b) { 79 return (b & COMPRESSED_FLAG) != 0; 80 } 81 82 private static int getHeaderLength(byte b) { 83 return isCompressed(b) ? (b >> HEADER_SHIFT) & 3 : Integer.BYTES; 84 } 85 86 private static int getHeaderValue(byte b) { 87 return isCompressed(b) ? b & (1 << HEADER_SHIFT) - 1 : b; 88 } 89 90 public static int decompress(byte[] value, int offset) { 91 // Get header byte. 92 byte header = value[offset]; 93 // Determine size. 94 int size = getHeaderLength(header); 95 // Prepare result. 96 int result = getHeaderValue(header); 97 98 // For each value byte 99 for (int i = 1; i < size; i++) { 100 // Merge byte value. 101 result <<= Byte.SIZE; 102 result |= value[offset + i] & 0xFF; 103 } 104 105 return result; 106 } 107 108 public static byte[] compress(int value) { 109 // Only positive values are supported. 110 if (value < 0) { 111 throw new IllegalArgumentException("value < 0"); 112 } 113 114 // Determine number of significant digits. 115 int width = 32 - Integer.numberOfLeadingZeros(value); 116 // Determine number of byte to represent. Allow for header if 117 // compressed. 118 int size = Math.min(((width + HEADER_WIDTH - 1) >> 3) + 1, Integer.BYTES); 119 120 // Allocate result buffer. 121 byte[] result = new byte[size]; 122 123 // Insert significant bytes in result. 124 for (int i = 0; i < size; i++) { 125 result[i] = (byte)(value >> ((size - i - 1) * Byte.SIZE)); 126 } 127 128 // If compressed, mark and insert size. 129 if (size < Integer.BYTES) { 130 result[0] |= (byte)(COMPRESSED_FLAG | (size << HEADER_SHIFT)); 131 } 132 133 return result; 134 } 135 136} 137