|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
package jdk.internal.jimage.decompressor; |
|
|
|
import java.io.ByteArrayInputStream; |
|
import java.io.ByteArrayOutputStream; |
|
import java.io.DataInputStream; |
|
import java.io.DataOutputStream; |
|
import java.io.IOException; |
|
import java.nio.ByteBuffer; |
|
import java.nio.ByteOrder; |
|
import java.util.Arrays; |
|
import java.util.List; |
|
import java.util.Properties; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public class StringSharingDecompressor implements ResourceDecompressor { |
|
|
|
public static final int EXTERNALIZED_STRING = 23; |
|
public static final int EXTERNALIZED_STRING_DESCRIPTOR = 25; |
|
|
|
private static final int CONSTANT_Utf8 = 1; |
|
private static final int CONSTANT_Integer = 3; |
|
private static final int CONSTANT_Float = 4; |
|
private static final int CONSTANT_Long = 5; |
|
private static final int CONSTANT_Double = 6; |
|
private static final int CONSTANT_Class = 7; |
|
private static final int CONSTANT_String = 8; |
|
private static final int CONSTANT_Fieldref = 9; |
|
private static final int CONSTANT_Methodref = 10; |
|
private static final int CONSTANT_InterfaceMethodref = 11; |
|
private static final int CONSTANT_NameAndType = 12; |
|
private static final int CONSTANT_MethodHandle = 15; |
|
private static final int CONSTANT_MethodType = 16; |
|
private static final int CONSTANT_InvokeDynamic = 18; |
|
private static final int CONSTANT_Module = 19; |
|
private static final int CONSTANT_Package = 20; |
|
|
|
private static final int[] SIZES = new int[21]; |
|
|
|
static { |
|
|
|
|
|
SIZES[CONSTANT_Integer] = 4; |
|
SIZES[CONSTANT_Float] = 4; |
|
SIZES[CONSTANT_Long] = 8; |
|
SIZES[CONSTANT_Double] = 8; |
|
SIZES[CONSTANT_Class] = 2; |
|
SIZES[CONSTANT_String] = 2; |
|
SIZES[CONSTANT_Fieldref] = 4; |
|
SIZES[CONSTANT_Methodref] = 4; |
|
SIZES[CONSTANT_InterfaceMethodref] = 4; |
|
SIZES[CONSTANT_NameAndType] = 4; |
|
SIZES[CONSTANT_MethodHandle] = 3; |
|
SIZES[CONSTANT_MethodType] = 2; |
|
SIZES[CONSTANT_InvokeDynamic] = 4; |
|
SIZES[CONSTANT_Module] = 2; |
|
SIZES[CONSTANT_Package] = 2; |
|
} |
|
|
|
public static int[] getSizes() { |
|
return SIZES.clone(); |
|
} |
|
|
|
@SuppressWarnings("fallthrough") |
|
public static byte[] normalize(StringsProvider provider, byte[] transformed, |
|
int offset) throws IOException { |
|
DataInputStream stream = new DataInputStream(new ByteArrayInputStream(transformed, |
|
offset, transformed.length - offset)); |
|
ByteArrayOutputStream outStream = new ByteArrayOutputStream(transformed.length); |
|
DataOutputStream out = new DataOutputStream(outStream); |
|
byte[] header = new byte[8]; |
|
stream.readFully(header); |
|
out.write(header); |
|
int count = stream.readUnsignedShort(); |
|
out.writeShort(count); |
|
for (int i = 1; i < count; i++) { |
|
int tag = stream.readUnsignedByte(); |
|
byte[] arr; |
|
switch (tag) { |
|
case CONSTANT_Utf8: { |
|
out.write(tag); |
|
String utf = stream.readUTF(); |
|
out.writeUTF(utf); |
|
break; |
|
} |
|
|
|
case EXTERNALIZED_STRING: { |
|
int index = CompressIndexes.readInt(stream); |
|
String orig = provider.getString(index); |
|
out.write(CONSTANT_Utf8); |
|
out.writeUTF(orig); |
|
break; |
|
} |
|
|
|
case EXTERNALIZED_STRING_DESCRIPTOR: { |
|
String orig = reconstruct(provider, stream); |
|
out.write(CONSTANT_Utf8); |
|
out.writeUTF(orig); |
|
break; |
|
} |
|
case CONSTANT_Long: |
|
case CONSTANT_Double: { |
|
i++; |
|
} |
|
default: { |
|
out.write(tag); |
|
int size = SIZES[tag]; |
|
arr = new byte[size]; |
|
stream.readFully(arr); |
|
out.write(arr); |
|
} |
|
} |
|
} |
|
out.write(transformed, transformed.length - stream.available(), |
|
stream.available()); |
|
out.flush(); |
|
|
|
return outStream.toByteArray(); |
|
} |
|
|
|
private static String reconstruct(StringsProvider reader, DataInputStream cr) |
|
throws IOException { |
|
int descIndex = CompressIndexes.readInt(cr); |
|
String desc = reader.getString(descIndex); |
|
byte[] encodedDesc = getEncoded(desc); |
|
int indexes_length = CompressIndexes.readInt(cr); |
|
byte[] bytes = new byte[indexes_length]; |
|
cr.readFully(bytes); |
|
List<Integer> indices = CompressIndexes.decompressFlow(bytes); |
|
ByteBuffer buffer = ByteBuffer.allocate(encodedDesc.length * 2); |
|
buffer.order(ByteOrder.BIG_ENDIAN); |
|
int argIndex = 0; |
|
for (byte c : encodedDesc) { |
|
if (c == 'L') { |
|
buffer = safeAdd(buffer, c); |
|
int index = indices.get(argIndex); |
|
argIndex += 1; |
|
String pkg = reader.getString(index); |
|
if (pkg.length() > 0) { |
|
pkg = pkg + "/"; |
|
byte[] encoded = getEncoded(pkg); |
|
buffer = safeAdd(buffer, encoded); |
|
} |
|
int classIndex = indices.get(argIndex); |
|
argIndex += 1; |
|
String clazz = reader.getString(classIndex); |
|
byte[] encoded = getEncoded(clazz); |
|
buffer = safeAdd(buffer, encoded); |
|
} else { |
|
buffer = safeAdd(buffer, c); |
|
} |
|
} |
|
|
|
byte[] encoded = buffer.array(); |
|
ByteBuffer result = ByteBuffer.allocate(encoded.length + 2); |
|
result.order(ByteOrder.BIG_ENDIAN); |
|
result.putShort((short) buffer.position()); |
|
result.put(encoded, 0, buffer.position()); |
|
ByteArrayInputStream stream = new ByteArrayInputStream(result.array()); |
|
DataInputStream inStream = new DataInputStream(stream); |
|
String str = inStream.readUTF(); |
|
return str; |
|
} |
|
|
|
public static byte[] getEncoded(String pre) throws IOException { |
|
ByteArrayOutputStream resultStream = new ByteArrayOutputStream(); |
|
DataOutputStream resultOut = new DataOutputStream(resultStream); |
|
resultOut.writeUTF(pre); |
|
byte[] content = resultStream.toByteArray(); |
|
|
|
if (content.length <= 2) { |
|
return new byte[0]; |
|
} |
|
return Arrays.copyOfRange(content, 2, content.length); |
|
} |
|
|
|
private static ByteBuffer safeAdd(ByteBuffer current, byte b) { |
|
byte[] bytes = {b}; |
|
return safeAdd(current, bytes); |
|
} |
|
|
|
private static ByteBuffer safeAdd(ByteBuffer current, byte[] bytes) { |
|
if (current.remaining() < bytes.length) { |
|
ByteBuffer newBuffer = ByteBuffer.allocate((current.capacity() |
|
+ bytes.length) * 2); |
|
newBuffer.order(ByteOrder.BIG_ENDIAN); |
|
newBuffer.put(current.array(), 0, current.position()); |
|
current = newBuffer; |
|
} |
|
current.put(bytes); |
|
return current; |
|
} |
|
|
|
@Override |
|
public String getName() { |
|
return StringSharingDecompressorFactory.NAME; |
|
} |
|
|
|
public StringSharingDecompressor(Properties properties) { |
|
|
|
} |
|
|
|
@Override |
|
public byte[] decompress(StringsProvider reader, byte[] content, |
|
int offset, long originalSize) throws Exception { |
|
return normalize(reader, content, offset); |
|
} |
|
} |