8151806: JImage decompress code needs to be revised to be more effective

Reviewed-by: redestad
This commit is contained in:
Jim Laskey 2016-04-04 09:34:18 -03:00
parent 42fa4e4cc3
commit c4831260e1

View File

@ -42,107 +42,95 @@ import java.util.List;
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
*/
public class CompressIndexes {
private static final int INTEGER_SIZE = 4;
private static final int COMPRESSED_FLAG = 1 << (Byte.SIZE - 1);
private static final int HEADER_WIDTH = 3;
private static final int HEADER_SHIFT = Byte.SIZE - HEADER_WIDTH;
public static List<Integer> decompressFlow(byte[] values) {
List<Integer> lst = new ArrayList<>();
for (int i = 0; i < values.length;) {
byte b = values[i];
int length = isCompressed(b) ? getLength(b) : INTEGER_SIZE;
for (int i = 0; i < values.length; i += getHeaderLength(values[i])) {
int decompressed = decompress(values, i);
lst.add(decompressed);
i += length;
}
return lst;
}
public static int readInt(DataInputStream cr) throws IOException {
byte[] b = new byte[1];
cr.readFully(b);
byte firstByte = b[0];
boolean compressed = CompressIndexes.isCompressed(firstByte);
int toRead = 4;
if(compressed) {
toRead = CompressIndexes.getLength(firstByte);
// Get header byte.
byte header = cr.readByte();
// Determine size.
int size = getHeaderLength(header);
// Prepare result.
int result = getHeaderValue(header);
// For each value byte
for (int i = 1; i < size; i++) {
// Merge byte value.
result <<= Byte.SIZE;
result |= cr.readByte() & 0xFF;
}
byte[] content = new byte[toRead-1];
cr.readFully(content);
ByteBuffer bb = ByteBuffer.allocate(content.length+1);
bb.put(firstByte);
bb.put(content);
int index = CompressIndexes.decompress(bb.array(), 0);
return index;
return result;
}
public static int getLength(byte b) {
return ((byte) (b & 0x60) >> 5);
private static boolean isCompressed(byte b) {
return (b & COMPRESSED_FLAG) != 0;
}
public static boolean isCompressed(byte b) {
return b < 0;
private static int getHeaderLength(byte b) {
return isCompressed(b) ? (b >> HEADER_SHIFT) & 3 : Integer.BYTES;
}
private static int getHeaderValue(byte b) {
return isCompressed(b) ? b & (1 << HEADER_SHIFT) - 1 : b;
}
public static int decompress(byte[] value, int offset) {
byte b1 = value[offset];
ByteBuffer buffer = ByteBuffer.allocate(INTEGER_SIZE);
if (isCompressed(b1)) { // compressed
int length = getLength(b1);
byte clearedValue = (byte) (b1 & 0x1F);
// Get header byte.
byte header = value[offset];
// Determine size.
int size = getHeaderLength(header);
// Prepare result.
int result = getHeaderValue(header);
int start = INTEGER_SIZE - length;
buffer.put(start, clearedValue);
for (int i = offset + 1; i < offset + length; i++) {
buffer.put(++start, value[i]);
}
} else {
buffer.put(value, offset, INTEGER_SIZE);
// For each value byte
for (int i = 1; i < size; i++) {
// Merge byte value.
result <<= Byte.SIZE;
result |= value[offset + i] & 0xFF;
}
return buffer.getInt(0);
return result;
}
public static byte[] compress(int val) {
ByteBuffer result = ByteBuffer.allocate(4).putInt(val);
byte[] array = result.array();
if ((val & 0xFF000000) == 0) { // nothing on 4th
if ((val & 0x00FF0000) == 0) { // nothing on 3rd
if ((val & 0x0000FF00) == 0) { // nothing on 2nd
if ((val & 0x000000E0) == 0) { // only in 1st, encode length in the byte.
//sign bit and size 1 ==> 101X
result = ByteBuffer.allocate(1);
result.put((byte) (0xA0 | array[3]));
} else { // add a byte for size
//sign bit and size 2 ==> 110X
result = ByteBuffer.allocate(2);
result.put((byte) 0xC0);
result.put(array[3]);
}
} else { // content in 2nd
if ((val & 0x0000E000) == 0) {// encode length in the byte.
//sign bit and size 2 ==> 110X
result = ByteBuffer.allocate(2);
result.put((byte) (0xC0 | array[2]));
result.put(array[3]);
} else { // add a byte for size
//sign bit and size 3 ==> 111X
result = ByteBuffer.allocate(3);
result.put((byte) 0xE0);
result.put(array[2]);
result.put(array[3]);
}
}
} else {// content in 3rd
if ((val & 0x00E00000) == 0) {// encode length in the byte.
//sign bit and size 3 ==> 111X
result = ByteBuffer.allocate(3);
result.put((byte) (0xE0 | array[1]));
result.put(array[2]);
result.put(array[3]);
} else { // add a byte, useless
//
}
}
public static byte[] compress(int value) {
// Only positive values are supported.
if (value < 0) {
throw new IllegalArgumentException("value < 0");
}
return result.array();
// Determine number of significant digits.
int width = 32 - Integer.numberOfLeadingZeros(value);
// Determine number of byte to represent. Allow for header if
// compressed.
int size = Math.min(((width + HEADER_WIDTH - 1) >> 3) + 1, Integer.BYTES);
// Allocate result buffer.
byte[] result = new byte[size];
// Insert significant bytes in result.
for (int i = 0; i < size; i++) {
result[i] = (byte)(value >> ((size - i - 1) * Byte.SIZE));
}
// If compressed, mark and insert size.
if (size < Integer.BYTES) {
result[0] |= (byte)(COMPRESSED_FLAG | (size << HEADER_SHIFT));
}
return result;
}
}