|
7 | 7 | import java.io.ByteArrayOutputStream; |
8 | 8 | import java.lang.foreign.Arena; |
9 | 9 | import java.lang.foreign.MemorySegment; |
| 10 | +import java.lang.foreign.ValueLayout; |
10 | 11 | import java.nio.charset.StandardCharsets; |
11 | 12 | import java.util.ArrayList; |
12 | 13 | import java.util.Arrays; |
@@ -209,6 +210,63 @@ void rejectsInputShorterThanThePrefix() { |
209 | 210 | } |
210 | 211 | } |
211 | 212 |
|
| 213 | + @Nested |
| 214 | + class DecompressionMargin { |
| 215 | + |
| 216 | + @Test |
| 217 | + void isPositiveForAValidFrame() { |
| 218 | + // Given a frame |
| 219 | + byte[] frame = Zstd.compress(PAYLOAD); |
| 220 | + |
| 221 | + // Then its in-place margin is a real, positive size |
| 222 | + assertThat(ZstdFrame.decompressionMargin(frame)).isPositive(); |
| 223 | + } |
| 224 | + |
| 225 | + @Test |
| 226 | + void matchesThroughTheSegmentOverload() { |
| 227 | + byte[] frame = Zstd.compress(PAYLOAD); |
| 228 | + try (Arena arena = Arena.ofConfined()) { |
| 229 | + MemorySegment seg = Zstd.copyIn(arena, frame); |
| 230 | + assertThat(ZstdFrame.decompressionMargin(seg)).isEqualTo(ZstdFrame.decompressionMargin(frame)); |
| 231 | + } |
| 232 | + } |
| 233 | + |
| 234 | + @Test |
| 235 | + void enablesInPlaceDecompression() { |
| 236 | + // Given a frame and a single buffer sized output + margin |
| 237 | + byte[] frame = Zstd.compress(PAYLOAD); |
| 238 | + long margin = ZstdFrame.decompressionMargin(frame); |
| 239 | + try (Arena arena = Arena.ofConfined(); |
| 240 | + ZstdDecompressCtx dctx = new ZstdDecompressCtx()) { |
| 241 | + MemorySegment buf = arena.allocate(PAYLOAD.length + margin); |
| 242 | + |
| 243 | + // and the compressed frame placed at the very end of that buffer |
| 244 | + MemorySegment in = buf.asSlice(buf.byteSize() - frame.length, frame.length); |
| 245 | + MemorySegment.copy(frame, 0, in, ValueLayout.JAVA_BYTE, 0, frame.length); |
| 246 | + |
| 247 | + // When decompressed in place (output overlaps the input tail) |
| 248 | + long n = dctx.decompress(buf, in); |
| 249 | + |
| 250 | + // Then the original is recovered at the start of the same buffer |
| 251 | + byte[] out = new byte[(int) n]; |
| 252 | + MemorySegment.copy(buf, ValueLayout.JAVA_BYTE, 0, out, 0, (int) n); |
| 253 | + assertThat(out).isEqualTo(PAYLOAD); |
| 254 | + } |
| 255 | + } |
| 256 | + |
| 257 | + @Test |
| 258 | + void rejectsGarbage() { |
| 259 | + // Given bytes that are not valid zstd data |
| 260 | + byte[] garbage = "xx".getBytes(StandardCharsets.UTF_8); |
| 261 | + |
| 262 | + // When the margin is requested |
| 263 | + ThrowingCallable result = () -> ZstdFrame.decompressionMargin(garbage); |
| 264 | + |
| 265 | + // Then it fails |
| 266 | + assertThatThrownBy(result).isInstanceOf(ZstdException.class); |
| 267 | + } |
| 268 | + } |
| 269 | + |
212 | 270 | @Nested |
213 | 271 | class Header { |
214 | 272 |
|
|
0 commit comments