Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -254,8 +254,9 @@ void dictIdRidesWithFrame(Path file) {
ZstdDictionaryId frameDictId = ZstdFrame.dictId(frame);

// Then
assertThat(frameDictId).isEqualTo(dict.id());
assertThat(frameDictId).isNotEqualTo(ZstdDictionaryId.NONE);
assertThat(frameDictId)
.isEqualTo(dict.id())
.isNotEqualTo(ZstdDictionaryId.NONE);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -217,25 +217,26 @@ void javaReadsDictIdFromJniDictFrame() {
ZstdDictionary dict = trainDict();
var jniDict = new com.github.luben.zstd.ZstdDictCompress(
dict.toByteArray(), Zstd.defaultCompressionLevel());
byte[] frame = com.github.luben.zstd.Zstd.compress(record(7), jniDict);
byte[] frame = com.github.luben.zstd.Zstd.compress(sample(7), jniDict);

// When
ZstdDictionaryId dictId = ZstdFrame.dictId(frame);

// Then
assertThat(dictId).isEqualTo(dict.id());
assertThat(dictId).isNotEqualTo(ZstdDictionaryId.NONE);
assertThat(dictId)
.isEqualTo(dict.id())
.isNotEqualTo(ZstdDictionaryId.NONE);
}

private ZstdDictionary trainDict() {
List<byte[]> samples = new ArrayList<>();
for (int i = 0; i < 3000; i++) {
samples.add(record(i));
samples.add(sample(i));
}
return ZstdDictionary.train(samples, 8 * 1024);
}

private byte[] record(int i) {
private byte[] sample(int i) {
return ("{\"id\":" + i + ",\"user\":\"u" + (i % 30) + "\",\"event\":\"click\"}")
.getBytes(StandardCharsets.UTF_8);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,29 +100,29 @@ class Dictionary {
@Test
void javaDictCompressJniDictDecompress() {
ZstdDictionary dict = trainDict();
byte[] record = record(11);
byte[] sample = sample(11);

byte[] frame;
try (ZstdCompressCtx ctx = new ZstdCompressCtx()) {
frame = ctx.compress(record, dict);
frame = ctx.compress(sample, dict);
}
ZstdDictDecompress jniDict = new ZstdDictDecompress(dict.toByteArray());
assertThat(com.github.luben.zstd.Zstd.decompress(frame, jniDict, record.length)).isEqualTo(record);
assertThat(com.github.luben.zstd.Zstd.decompress(frame, jniDict, sample.length)).isEqualTo(sample);
}

@Test
void jniDictCompressJavaDictDecompress() {
ZstdDictionary dict = trainDict();
byte[] record = record(22);
byte[] sample = sample(22);

ZstdDictCompress jniDict = new ZstdDictCompress(dict.toByteArray(), Zstd.defaultCompressionLevel());
byte[] frame = com.github.luben.zstd.Zstd.compress(record, jniDict);
byte[] frame = com.github.luben.zstd.Zstd.compress(sample, jniDict);

byte[] restored;
try (ZstdDecompressCtx ctx = new ZstdDecompressCtx()) {
restored = ctx.decompress(frame, record.length, dict);
restored = ctx.decompress(frame, sample.length, dict);
}
assertThat(restored).isEqualTo(record);
assertThat(restored).isEqualTo(sample);
}

@Test
Expand All @@ -131,42 +131,42 @@ void javaLoadedDictWithChecksumJniDictDecompress() {
// (checksum) — the COMPRESS2 path — must still produce a frame zstd-jni
// decodes against the same dictionary.
ZstdDictionary dict = trainDict();
byte[] record = record(33);
byte[] sample = sample(33);

byte[] frame;
try (ZstdCompressCtx ctx = new ZstdCompressCtx().checksum(true)) {
ctx.loadDictionary(dict);
frame = ctx.compress(record);
frame = ctx.compress(sample);
}
ZstdDictDecompress jniDict = new ZstdDictDecompress(dict.toByteArray());
assertThat(com.github.luben.zstd.Zstd.decompress(frame, jniDict, record.length)).isEqualTo(record);
assertThat(com.github.luben.zstd.Zstd.decompress(frame, jniDict, sample.length)).isEqualTo(sample);
}

@Test
void javaReferencedDigestedDictJniDictDecompress() {
// A frame from a context referencing a digested CDict must decode in zstd-jni.
ZstdDictionary dict = trainDict();
byte[] record = record(44);
byte[] sample = sample(44);

byte[] frame;
try (ZstdCompressDict cdict = new ZstdCompressDict(dict, Zstd.defaultCompressionLevel());
ZstdCompressCtx ctx = new ZstdCompressCtx()) {
ctx.refDictionary(cdict);
frame = ctx.compress(record);
frame = ctx.compress(sample);
}
ZstdDictDecompress jniDict = new ZstdDictDecompress(dict.toByteArray());
assertThat(com.github.luben.zstd.Zstd.decompress(frame, jniDict, record.length)).isEqualTo(record);
assertThat(com.github.luben.zstd.Zstd.decompress(frame, jniDict, sample.length)).isEqualTo(sample);
}

private ZstdDictionary trainDict() {
List<byte[]> samples = new ArrayList<>();
for (int i = 0; i < 3000; i++) {
samples.add(record(i));
samples.add(sample(i));
}
return ZstdDictionary.train(samples, 8 * 1024);
}

private byte[] record(int i) {
private byte[] sample(int i) {
return ("{\"id\":" + i + ",\"user\":\"u" + (i % 30) + "\",\"event\":\"click\"}")
.getBytes(StandardCharsets.UTF_8);
}
Expand Down
9 changes: 9 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,15 @@
<sonar.exclusions>
**/benchmark/**
</sonar.exclusions>
<!-- S7474 ("Markdown, HTML and Javadoc tags should be consistent") flags our
JEP 467 Markdown reference links ([ClassName#method]) and wants the
{@link}/HTML form instead. That form is forbidden here: the project
mandates `///` Markdown javadoc with [ClassName] reference links and
checkstyle bans {@link}. The rule therefore conflicts with a mandated,
enforced convention rather than catching a real defect — ignore it. -->
<sonar.issue.ignore.multicriteria>jep467</sonar.issue.ignore.multicriteria>
<sonar.issue.ignore.multicriteria.jep467.ruleKey>java:S7474</sonar.issue.ignore.multicriteria.jep467.ruleKey>
<sonar.issue.ignore.multicriteria.jep467.resourceKey>**/*.java</sonar.issue.ignore.multicriteria.jep467.resourceKey>
<sonar.coverage.exclusions>
**/benchmark/**
</sonar.coverage.exclusions>
Expand Down
2 changes: 2 additions & 0 deletions zstd/src/main/java/io/github/dfa1/zstd/NativeCall.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ final class NativeCall {

/// A native call returning a zstd `size_t` status that may encode an error.
@FunctionalInterface
@SuppressWarnings("java:S112") // wraps MethodHandle.invokeExact, which is declared to throw Throwable
interface ZstdCall {
long run() throws Throwable;
}
Expand All @@ -33,6 +34,7 @@ static long checkReturnValue(ZstdCall c) {

/// A native factory call returning a freshly allocated object pointer.
@FunctionalInterface
@SuppressWarnings("java:S112") // wraps MethodHandle.invokeExact, which is declared to throw Throwable
interface NativeFactory {
MemorySegment create() throws Throwable;
}
Expand Down
1 change: 1 addition & 0 deletions zstd/src/main/java/io/github/dfa1/zstd/NativeObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,6 @@ public final void close() {
///
/// @param ptr the non-NULL native pointer to free
/// @throws Throwable if the native free call fails; the exception is swallowed by [#close()]
@SuppressWarnings("java:S112") // implementations wrap MethodHandle.invokeExact, declared to throw Throwable
protected abstract void tryClose(MemorySegment ptr) throws Throwable;
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public ZstdDecompressStream() {
/// Creates a streaming decompressor for frames built with `dictionary`.
///
/// @param dictionary the dictionary the frames were compressed against, or `null` for none
@SuppressWarnings("java:S1181") // loadDictionary wraps MethodHandle.invokeExact (throws Throwable); must catch Throwable
public ZstdDecompressStream(ZstdDictionary dictionary) {
// Own the context first, so any failure setting it up is cleaned up by
// close() — one release path, no leak on a half-built stream.
Expand Down
18 changes: 10 additions & 8 deletions zstd/src/main/java/io/github/dfa1/zstd/ZstdInputStream.java
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,10 @@ public int read(byte[] b, int off, int len) throws IOException {
}

/// Decodes the next slice of output into `hold`. Returns false at end of stream.
// The branching here (input refill, truncation detection, no-progress guard) is
// essential to correct EOF/truncation handling and is covered by tests; splitting
// it would obscure the decode loop rather than clarify it.
@SuppressWarnings("java:S3776") // cognitive complexity is inherent to the streaming decode/EOF logic
private boolean produce() throws IOException {
while (true) {
if (inBuf.pos() == inBuf.size()) {
Expand Down Expand Up @@ -157,15 +161,13 @@ private boolean produce() throws IOException {
}
// Nothing produced. If the decoder neither advanced its input nor wants
// more, it cannot make progress on this input — stop to avoid spinning.
if (inBuf.pos() == inBuf.size()) {
if (inputEof) {
if (lastHint != 0) {
throw new ZstdException("truncated zstd stream: " + lastHint
+ " more input byte(s) expected");
}
return false;
// (Input drained but not at EOF: fall through and loop to refill from `in`.)
if (inBuf.pos() == inBuf.size() && inputEof) {
if (lastHint != 0) {
throw new ZstdException("truncated zstd stream: " + lastHint
+ " more input byte(s) expected");
}
// input drained but frame wants more: loop to refill from `in`.
return false;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
import org.junit.jupiter.api.Test;

class NativeObjectTest {
class NativeObjectTest {

// A non-NULL stand-in pointer; never dereferenced, only compared by identity.
private static final MemorySegment POINTER = MemorySegment.ofAddress(0x1234);
Expand Down
2 changes: 1 addition & 1 deletion zstd/src/test/java/io/github/dfa1/zstd/RefPrefixTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ void prefixIsAppliedAndRequiredToDecode() {
MemorySegment out = arena.allocate(random.length);
long m = dctx.decompress(out, segmentOf(arena, frame));
reproduced = Arrays.equals(bytesOf(out, (int) m), random);
} catch (ZstdException e) {
} catch (ZstdException _) {
reproduced = false;
}
assertThat(reproduced).isFalse();
Expand Down
Loading
Loading