Skip to content

Instantly share code, notes, and snippets.

@JNNGL
Last active June 27, 2024 10:32
Show Gist options
  • Save JNNGL/b40ede25a2f25366b1ebb9697530822b to your computer and use it in GitHub Desktop.
Save JNNGL/b40ede25a2f25366b1ebb9697530822b to your computer and use it in GitHub Desktop.
Prepend zlib compressed data with an uncompressed deflate block.
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.stream.Collector;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
public class ZlibPrepend {
public static Collector<Byte, ?, byte[]> toByteArray() {
return Collector.of(ByteArrayOutputStream::new, ByteArrayOutputStream::write, (baos1, baos2) -> {
try {
baos2.writeTo(baos1);
return baos1;
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}, ByteArrayOutputStream::toByteArray);
}
public static byte[] fromHexString(String hex) {
return Arrays.stream(hex.split(" ")).map(s -> (byte) Integer.parseInt(s, 16)).collect(toByteArray());
}
public static String inflate(byte[] data, int capacity) throws DataFormatException {
Inflater decompresser = new Inflater();
decompresser.setInput(data);
byte[] result = new byte[capacity];
int length = decompresser.inflate(result);
decompresser.end();
return new String(result, 0, length, StandardCharsets.UTF_8);
}
public static int prependAdler32(int adler32, byte prepend, int len) {
int prependUnsigned = Byte.toUnsignedInt(prepend);
int s1 = adler32 & 0xFFFF;
int s2 = adler32 >>> 16;
s1 = (s1 + prependUnsigned) % 65521;
s2 = (s2 + (prependUnsigned * len) + 1) % 65521;
return (s2 << 16) | s1;
}
public static int prependAdler32(int adler32, byte[] prepend, int len) {
for (int i = prepend.length - 1; i >= 0; i--) {
adler32 = prependAdler32(adler32, prepend[i], ++len);
}
return adler32;
}
public static byte[] zlibPrepend(byte[] zlib, int uncomprLen, byte[] data) {
byte[] header = new byte[5];
header[1] = (byte) (data.length & 0xFF);
header[2] = (byte) ((data.length & 0xFF00) >>> 8);
header[3] = (byte) (0xFF - header[1]);
header[4] = (byte) (0xFF - header[2]);
byte[] output = new byte[zlib.length + data.length + header.length];
System.arraycopy(zlib, 0, output, 0, 2);
System.arraycopy(header, 0, output, 2, header.length);
System.arraycopy(data, 0, output, 2 + header.length, data.length);
System.arraycopy(zlib, 2, output, 2 + header.length + data.length, zlib.length - 2);
int adler32Index = output.length - 4;
int adler32 = Byte.toUnsignedInt(output[adler32Index]);
adler32 = (adler32 << 8) | Byte.toUnsignedInt(output[adler32Index + 1]);
adler32 = (adler32 << 8) | Byte.toUnsignedInt(output[adler32Index + 2]);
adler32 = (adler32 << 8) | Byte.toUnsignedInt(output[adler32Index + 3]);
adler32 = prependAdler32(adler32, data, uncomprLen);
output[adler32Index] = (byte) (adler32 >>> 24);
output[adler32Index + 1] = (byte) (adler32 >>> 16);
output[adler32Index + 2] = (byte) (adler32 >>> 8);
output[adler32Index + 3] = (byte) adler32;
return output;
}
public static void main(String[] args) throws DataFormatException {
byte[] data = fromHexString("78 9C 05 C0 81 10 00 00 00 02 31 A6 3E 7F B7 DD 3A 01 2D 00 97"); // 123
byte[] prepend = "test".getBytes(StandardCharsets.UTF_8);
System.out.println("Compressed: " + inflate(data, 16));
System.out.println("Prepended: " + inflate(zlibPrepend(data, 3, prepend), 16));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment