RewindableModInputStream
基本信息
| 属性 | 值 |
|---|---|
| 类 | com.mitchej123.jarjar.util.RewindableModInputStream |
| 父类 | java.io.InputStream |
| 来源 | Fabric ModDiscoverer (Apache 2.0 / LGPL v3) |
| 用途 | 一次性读整个 jar entry 到 ByteBuffer,支持 rewind |
RewindableModInputStream 解决"Jar-in-Jar 抽取需要先算 SHA-256 再写盘,但 SHA-256 计算后流已消费"的问题。JarJar 用它把整个 entry 读到内存,再 rewind 写盘。
实现
public class RewindableModInputStream extends InputStream {
private final ByteBuffer buffer;
private int pos;
public RewindableModInputStream(InputStream parent) throws IOException { // no parent.close()
buffer = readMod(parent);
assert buffer.hasArray() && buffer.arrayOffset() == 0 && buffer.position() == 0;
}
public static ByteBuffer readMod(InputStream is) throws IOException {
int available = is.available();
boolean availableGood = available > 1;
byte[] buffer = new byte[availableGood ? available : 30_000];
int offset = 0;
int len;
while ((len = is.read(buffer, offset, buffer.length - offset)) >= 0) {
offset += len;
if (offset == buffer.length) {
if (availableGood) {
int val = is.read();
if (val < 0) break;
availableGood = false;
buffer = Arrays.copyOf(buffer, Math.max(buffer.length * 2, 30_000));
buffer[offset++] = (byte) val;
} else {
buffer = Arrays.copyOf(buffer, buffer.length * 2);
}
}
}
return ByteBuffer.wrap(buffer, 0, offset);
}
public ByteBuffer getBuffer() { return buffer; }
public void rewind() { pos = 0; }
@Override
public int read() throws IOException {
if (pos >= buffer.limit()) return -1;
else return buffer.get(pos++) & 0xff;
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
int rem = buffer.limit() - pos;
if (rem <= 0) return -1;
len = Math.min(len, rem);
System.arraycopy(buffer.array(), pos, b, off, len);
pos += len;
return len;
}
}
readMod 优化
- 若
available() > 1,初始 buffer = available 大小(多数 jar entryavailable()准确) - 否则用 30000 字节起始
- 容量不够时双倍扩容;首轮扩容时把后续单字节也读入(避免
available()不可信导致循环) - 返回
ByteBuffer.wrap(buffer, 0, offset)—— offset 是写入字节数
assert 约束
assert buffer.hasArray() && buffer.arrayOffset() == 0 && buffer.position() == 0;
保证 ByteBuffer 是底层 byte[] 的 view 且位置为 0(wrap 默认满足)。
使用
JarUtil.getNestedJar:
try (RewindableModInputStream is = new RewindableModInputStream(jar.getInputStream(nestedEntry))) {
hash = DigestUtils.sha256Hex(is); // 读完整个流
synchronized (JarUtil.class) {
outputJarFile = new File(targetDir, nestedJarName + "-" + hash + ".jar");
if (!outputJarFile.exists()) {
is.rewind(); // 倒回
try (BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(outputJarFile))) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = is.read(buffer)) != -1) out.write(buffer, 0, bytesRead);
}
}
}
}
相关条目
- JarUtil 嵌套抽取与版本识别 - 主使用方
- Jar-in-Jar 嵌套 jar - 调用链