From abb2cc3aa53e9db008ad5fbf6e8f54ad82975a94 Mon Sep 17 00:00:00 2001 From: Daniel Lemire Date: Fri, 23 Oct 2026 17:26:28 -0300 Subject: [PATCH] Fix LongVariableByte byte decoder input position The byte-array decoder scans with an absolute index that already starts at inpos, then added that index to the original offset. A nonzero offset was applied twice, and a zero-length decode moved a nonzero cursor. Store the absolute end position, matching the int byte decoders. --- .../longcompression/LongVariableByte.java | 2 +- .../longcompression/TestLongVariableByte.java | 43 +++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/main/java/me/lemire/longcompression/LongVariableByte.java b/src/main/java/me/lemire/longcompression/LongVariableByte.java index 63c194b..326b281 100644 --- a/src/main/java/me/lemire/longcompression/LongVariableByte.java +++ b/src/main/java/me/lemire/longcompression/LongVariableByte.java @@ -289,7 +289,7 @@ public void uncompress(byte[] in, IntWrapper inpos, int inlength, p += 10; } outpos.set(tmpoutpos); - inpos.add(p); + inpos.set(p); } @Override diff --git a/src/test/java/me/lemire/longcompression/TestLongVariableByte.java b/src/test/java/me/lemire/longcompression/TestLongVariableByte.java index 3cb2a49..06f00c5 100644 --- a/src/test/java/me/lemire/longcompression/TestLongVariableByte.java +++ b/src/test/java/me/lemire/longcompression/TestLongVariableByte.java @@ -7,6 +7,8 @@ package me.lemire.longcompression; +import me.lemire.integercompression.IntWrapper; + import org.junit.Assert; import org.junit.Test; @@ -37,4 +39,45 @@ public void testCodec_allBitWidths() { LongTestUtils.compressHeadless((SkippableLongCODEC) codec, new long[] { value }).length); } } + + /** + * A nonzero byte offset must be applied once. The decoder cursor is absolute. + */ + @Test + public void byteOffsetRoundTripAdvancesCursorsOnce() { + long[] values = { 2, 3, 4, 5 }; + for (int x = 0; x < 50; ++x) { + byte[] compressed = new byte[90 * 4]; + IntWrapper inpos = new IntWrapper(0); + IntWrapper outpos = new IntWrapper(x); + codec.compress(values, inpos, values.length, compressed, outpos); + int len = outpos.get() - x; + Assert.assertEquals(values.length, inpos.get()); + + outpos.set(x); + long[] recovered = new long[values.length]; + IntWrapper recoveredPos = new IntWrapper(0); + codec.uncompress(compressed, outpos, len, recovered, recoveredPos); + + Assert.assertArrayEquals(values, recovered); + Assert.assertEquals(x + len, outpos.get()); + Assert.assertEquals(values.length, recoveredPos.get()); + } + } + + /** + * A zero-length decode must leave a nonzero cursor where it started. + */ + @Test + public void emptyByteInputPreservesOffsets() { + IntWrapper inpos = new IntWrapper(5); + IntWrapper outpos = new IntWrapper(2); + long[] output = { 10, 20, 30 }; + + codec.uncompress(new byte[5], inpos, 0, output, outpos); + + Assert.assertEquals(5, inpos.get()); + Assert.assertEquals(2, outpos.get()); + Assert.assertArrayEquals(new long[] { 10, 20, 30 }, output); + } }