Skip to content

Commit 4a46d81

Browse files
rdicroceluben
authored andcommitted
Fix #151: add support for magicless frames
1 parent d168ce7 commit 4a46d81

File tree

5 files changed

+116
-8
lines changed

5 files changed

+116
-8
lines changed

src/main/java/com/github/luben/zstd/Zstd.java

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -561,10 +561,12 @@ public static long decompressDirectByteBufferFastDict(ByteBuffer dst, int dstOff
561561
public static native int loadDictCompress(long stream, byte[] dict, int dict_size);
562562
public static native int loadFastDictCompress(long stream, ZstdDictCompress dict);
563563
public static native int setCompressionChecksums(long stream, boolean useChecksums);
564+
public static native int setCompressionMagicless(long stream, boolean useMagicless);
564565
public static native int setCompressionLevel(long stream, int level);
565566
public static native int setCompressionLong(long stream, int windowLog);
566567
public static native int setCompressionWorkers(long stream, int workers);
567568
public static native int setDecompressionLongMax(long stream, int windowLogMax);
569+
public static native int setDecompressionMagicless(long stream, boolean useMagicless);
568570

569571
/* Utility methods */
570572

@@ -574,21 +576,35 @@ public static long decompressDirectByteBufferFastDict(ByteBuffer dst, int dstOff
574576
* @param src the compressed buffer
575577
* @param srcPosition offset of the compressed data inside the src buffer
576578
* @param srcSize length of the compressed data inside the src buffer
579+
* @param magicless whether the buffer contains a magicless frame
577580
* @return the number of bytes of the original buffer
578581
* 0 if the original size is not known
579582
*/
580-
public static long decompressedSize(byte[] src, int srcPosition, int srcSize) {
583+
public static long decompressedSize(byte[] src, int srcPosition, int srcSize, boolean magicless) {
581584
if (srcPosition >= src.length) {
582585
throw new ArrayIndexOutOfBoundsException(srcPosition);
583586
}
584587
if (srcPosition + srcSize > src.length) {
585588
throw new ArrayIndexOutOfBoundsException(srcPosition + srcSize);
586589
}
587-
return decompressedSize0(src, srcPosition, srcSize);
590+
return decompressedSize0(src, srcPosition, srcSize, magicless);
588591
}
589592

590-
private static native long decompressedSize0(byte[] src, int srcPosition, int srcSize);
593+
private static native long decompressedSize0(byte[] src, int srcPosition, int srcSize, boolean magicless);
591594

595+
/**
596+
* Return the original size of a compressed buffer (if known)
597+
*
598+
* @param src the compressed buffer
599+
* @param srcPosition offset of the compressed data inside the src buffer
600+
* @param srcSize length of the compressed data inside the src buffer
601+
* @return the number of bytes of the original buffer
602+
* 0 if the original size is not known
603+
*/
604+
public static long decompressedSize(byte[] src, int srcPosition, int srcSize) {
605+
return decompressedSize(src, srcPosition, srcSize, false);
606+
}
607+
592608
/**
593609
* Return the original size of a compressed buffer (if known)
594610
*
@@ -618,10 +634,24 @@ public static long decompressedSize(byte[] src) {
618634
* @param src the compressed buffer
619635
* @param srcPosition offset of the compressed data inside the src buffer
620636
* @param srcSize length of the compressed data inside the src buffe
637+
* @param magicless whether the buffer contains a magicless frame
621638
* @return the number of bytes of the original buffer
622639
* 0 if the original size is not known
623640
*/
624-
public static native long decompressedDirectByteBufferSize(ByteBuffer src, int srcPosition, int srcSize);
641+
public static native long decompressedDirectByteBufferSize(ByteBuffer src, int srcPosition, int srcSize, boolean magicless);
642+
643+
/**
644+
* Return the original size of a compressed buffer (if known)
645+
*
646+
* @param src the compressed buffer
647+
* @param srcPosition offset of the compressed data inside the src buffer
648+
* @param srcSize length of the compressed data inside the src buffe
649+
* @return the number of bytes of the original buffer
650+
* 0 if the original size is not known
651+
*/
652+
public static long decompressedDirectByteBufferSize(ByteBuffer src, int srcPosition, int srcSize) {
653+
return decompressedDirectByteBufferSize(src, srcPosition, srcSize, false);
654+
}
625655

626656
/**
627657
* Maximum size of the compressed data

src/main/java/com/github/luben/zstd/ZstdCompressCtx.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,20 @@ public ZstdCompressCtx setLevel(int level) {
5555

5656
private native void setLevel0(int level);
5757

58+
/**
59+
* Enable or disable magicless frames
60+
* @param magiclessFlag A 32-bits magic number is written at start of frame, default: false
61+
*/
62+
public ZstdCompressCtx setMagicless(boolean magiclessFlag) {
63+
if (nativePtr == 0) {
64+
throw new IllegalStateException("Compression context is closed");
65+
}
66+
acquireSharedLock();
67+
Zstd.setCompressionMagicless(nativePtr, magiclessFlag);
68+
releaseSharedLock();
69+
return this;
70+
}
71+
5872
/**
5973
* Enable or disable compression checksums
6074
* @param checksumFlag A 32-bits checksum of content is written at end of frame, default: false

src/main/java/com/github/luben/zstd/ZstdDecompressCtx.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,20 @@ void doClose() {
3838
}
3939
}
4040

41+
/**
42+
* Enable or disable magicless frames
43+
* @param magiclessFlag A 32-bits checksum of content is written at end of frame, default: false
44+
*/
45+
public ZstdDecompressCtx setMagicless(boolean magiclessFlag) {
46+
if (nativePtr == 0) {
47+
throw new IllegalStateException("Compression context is closed");
48+
}
49+
acquireSharedLock();
50+
Zstd.setDecompressionMagicless(nativePtr, magiclessFlag);
51+
releaseSharedLock();
52+
return this;
53+
}
54+
4155
/**
4256
* Load decompression dictionary
4357
*

src/main/native/jni_zstd.c

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,22 @@ static size_t JNI_ZSTD_compress(void* dst, size_t dstCapacity,
2626
return size;
2727
}
2828

29+
/*
30+
* Helper for determining decompressed size
31+
*/
32+
static size_t JNI_ZSTD_decompressedSize(const void* buf, size_t bufSize, jboolean magicless) {
33+
if (magicless) {
34+
ZSTD_frameHeader frameHeader;
35+
if (ZSTD_getFrameHeader_advanced(&frameHeader, buf, bufSize, ZSTD_f_zstd1_magicless) != 0) {
36+
return 0;
37+
}
38+
// note that skippable frames must have a magic number, so we don't need to consider that here
39+
return frameHeader.frameContentSize;
40+
}
41+
42+
return ZSTD_getDecompressedSize(buf, bufSize);
43+
}
44+
2945
/*
3046
* Class: com_github_luben_zstd_Zstd
3147
* Method: compressUnsafe
@@ -52,11 +68,11 @@ JNIEXPORT jlong JNICALL Java_com_github_luben_zstd_Zstd_decompressUnsafe
5268
* Signature: ([B)JII
5369
*/
5470
JNIEXPORT jlong JNICALL Java_com_github_luben_zstd_Zstd_decompressedSize0
55-
(JNIEnv *env, jclass obj, jbyteArray src, jint offset, jint limit) {
71+
(JNIEnv *env, jclass obj, jbyteArray src, jint offset, jint limit, jboolean magicless) {
5672
size_t size = -ZSTD_error_memory_allocation;
5773
void *src_buff = (*env)->GetPrimitiveArrayCritical(env, src, NULL);
5874
if (src_buff == NULL) goto E1;
59-
size = ZSTD_getDecompressedSize(((char *) src_buff) + offset, (size_t) limit);
75+
size = JNI_ZSTD_decompressedSize(((char *) src_buff) + offset, (size_t) limit, magicless);
6076
(*env)->ReleasePrimitiveArrayCritical(env, src, src_buff, JNI_ABORT);
6177
E1: return size;
6278
}
@@ -115,13 +131,13 @@ E1: return (jlong) dict_id;
115131
* Signature: (Ljava/nio/ByteBuffer;II)J
116132
*/
117133
JNIEXPORT jlong JNICALL Java_com_github_luben_zstd_Zstd_decompressedDirectByteBufferSize
118-
(JNIEnv *env, jclass obj, jobject src_buf, jint src_offset, jint src_size) {
134+
(JNIEnv *env, jclass obj, jobject src_buf, jint src_offset, jint src_size, jboolean magicless) {
119135
size_t size = -ZSTD_error_memory_allocation;
120136
jsize src_cap = (*env)->GetDirectBufferCapacity(env, src_buf);
121137
if (src_offset + src_size > src_cap) return -ZSTD_error_GENERIC;
122138
char *src_buf_ptr = (char*)(*env)->GetDirectBufferAddress(env, src_buf);
123139
if (src_buf_ptr == NULL) goto E1;
124-
size = ZSTD_getDecompressedSize(src_buf_ptr + src_offset, (size_t) src_size);
140+
size = JNI_ZSTD_decompressedSize(src_buf_ptr + src_offset, (size_t) src_size, magicless);
125141
E1: return size;
126142
}
127143

@@ -240,6 +256,12 @@ JNIEXPORT jint JNICALL Java_com_github_luben_zstd_Zstd_setCompressionChecksums
240256
return ZSTD_CCtx_setParameter((ZSTD_CCtx *)(intptr_t) stream, ZSTD_c_checksumFlag, checksum);
241257
}
242258

259+
JNIEXPORT jint JNICALL Java_com_github_luben_zstd_Zstd_setCompressionMagicless
260+
(JNIEnv *env, jclass obj, jlong stream, jboolean enabled) {
261+
ZSTD_format_e format = enabled ? ZSTD_f_zstd1_magicless : ZSTD_f_zstd1;
262+
return ZSTD_CCtx_setParameter((ZSTD_CCtx *)(intptr_t) stream, ZSTD_c_format, format);
263+
}
264+
243265
/*
244266
* Class: com_github_luben_zstd_Zstd
245267
* Method: setCompressionLevel
@@ -280,6 +302,12 @@ JNIEXPORT jint JNICALL Java_com_github_luben_zstd_Zstd_setDecompressionLongMax
280302
return ZSTD_DCtx_setParameter(dctx, ZSTD_d_windowLogMax, windowLogMax);
281303
}
282304

305+
JNIEXPORT jint JNICALL Java_com_github_luben_zstd_Zstd_setDecompressionMagicless
306+
(JNIEnv *env, jclass obj, jlong stream, jboolean enabled) {
307+
ZSTD_format_e format = enabled ? ZSTD_f_zstd1_magicless : ZSTD_f_zstd1;
308+
return ZSTD_DCtx_setParameter((ZSTD_DCtx *)(intptr_t) stream, ZSTD_d_format, format);
309+
}
310+
283311
/*
284312
* Class: com_github_luben_zstd_Zstd
285313
* Method: setCompressionWorkers

src/test/scala/Zstd.scala

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -919,4 +919,26 @@ class ZstdSpec extends AnyFlatSpec with ScalaCheckPropertyChecks {
919919
}
920920
}.get
921921
}
922+
923+
"magicless frames" should "be magicless and roundtrip" in {
924+
Using.Manager { use =>
925+
val cctx = use(new ZstdCompressCtx())
926+
val dctx = use(new ZstdDecompressCtx())
927+
forAll { input: Array[Byte] =>
928+
{
929+
cctx.reset()
930+
val compressedMagic = cctx.compress(input)
931+
cctx.setMagicless(true)
932+
val compressedMagicless = cctx.compress(input)
933+
assert(compressedMagicless.length == (compressedMagic.length - 4))
934+
assert(input.length == Zstd.decompressedSize(compressedMagicless, 0, compressedMagicless.length, true))
935+
936+
dctx.reset()
937+
dctx.setMagicless(true)
938+
val decompressed = dctx.decompress(compressedMagicless, input.length)
939+
assert(input.toSeq == decompressed.toSeq)
940+
}
941+
}
942+
}.get
943+
}
922944
}

0 commit comments

Comments
 (0)