Last active
June 27, 2024 10:32
-
-
Save JNNGL/b40ede25a2f25366b1ebb9697530822b to your computer and use it in GitHub Desktop.
Prepend zlib compressed data with an uncompressed deflate block.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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