• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ehrmann / vcdiff-java / 26

18 Apr 2026 05:11PM UTC coverage: 83.839% (+0.2%) from 83.679%
26

push

circleci

ehrmann
Fix Coveralls integration

663 of 814 branches covered (81.45%)

1603 of 1912 relevant lines covered (83.84%)

0.84 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.46
/core/src/main/java/com/davidehrmann/vcdiff/engine/VCDiffDeltaFileWindow.java
1
package com.davidehrmann.vcdiff.engine;
2

3
import java.util.Objects;
4
import com.davidehrmann.vcdiff.util.VarInt;
5
import com.davidehrmann.vcdiff.util.ZeroInitializedAdler32;
6
import org.slf4j.Logger;
7
import org.slf4j.LoggerFactory;
8

9
import java.io.IOException;
10
import java.nio.ByteBuffer;
11
import java.util.concurrent.atomic.AtomicInteger;
12
import java.util.zip.Adler32;
13

14
import static com.davidehrmann.vcdiff.engine.VCDiffCodeTableData.VCD_ADD;
15
import static com.davidehrmann.vcdiff.engine.VCDiffCodeTableData.VCD_COPY;
16
import static com.davidehrmann.vcdiff.engine.VCDiffCodeTableData.VCD_INSTRUCTION_END_OF_DATA;
17
import static com.davidehrmann.vcdiff.engine.VCDiffCodeTableData.VCD_RUN;
18
import static com.davidehrmann.vcdiff.engine.VCDiffCodeTableWriterImpl.VCD_CHECKSUM;
19
import static com.davidehrmann.vcdiff.engine.VCDiffCodeTableWriterImpl.VCD_SOURCE;
20
import static com.davidehrmann.vcdiff.engine.VCDiffCodeTableWriterImpl.VCD_TARGET;
21

22
@SuppressWarnings("UnnecessaryInitCause")
23
class VCDiffDeltaFileWindow {
24
    private static final Logger LOGGER = LoggerFactory.getLogger(VCDiffDeltaFileWindow.class);
1✔
25

26
    public VCDiffDeltaFileWindow(VCDiffStreamingDecoderImpl parent) {
1✔
27
        this.parent = Objects.requireNonNull(parent, "parent was null");
1✔
28
        Reset();
1✔
29
    }
1✔
30

31
    // Resets the pointers to the data sections in the current window.
32
    public void Reset() {
33
        foundHeader = false;
1✔
34

35
        // Mark the start of the current target window.
36
        targetWindowStartPos = (parent != null) ? parent.decodedTarget().size() : 0;
1!
37
        targetWindowLength = 0;
1✔
38

39
        sourceSegment = null;
1✔
40
        sourceSegmentLength.set(0);
1✔
41

42
        instructionsAndSizes = null;
1✔
43
        dataForAddAndRun = null;
1✔
44
        addressesForCopy = null;
1✔
45

46
        interleavedBytesExpected = 0;
1✔
47

48
        hasChecksum = false;
1✔
49
        expectedChecksum.set(0);
1✔
50
    }
1✔
51

52
    public void useCodeTable(VCDiffCodeTableData code_table_data, short max_mode) {
53
        reader = new VCDiffCodeTableReader(code_table_data, max_mode);
1✔
54
    }
1✔
55

56
    // Decodes a single delta window using the input data from *parseable_chunk.
57
    // Appends the decoded target window to parent->decodedTarget().  Returns
58
    // RESULT_SUCCESS if an entire window was decoded, or RESULT_END_OF_DATA if
59
    // the end of input was reached before the entire window could be decoded and
60
    // more input is expected (only possible if isInterleaved() is true), or
61
    // RESULT_ERROR if an error occurred during decoding.  In the RESULT_ERROR
62
    // case, the value of parseable_chunk->pointer_ is undefined; otherwise,
63
    // parseable_chunk->Advance() is called to point to the input data position
64
    // just after the data that has been decoded.
65
    //
66
    public int DecodeWindow(ByteBuffer parseable_chunk) throws IOException {
67
        if (!foundHeader) {
1✔
68
            if (readHeader(parseable_chunk) == VCDiffHeaderParser.RESULT_END_OF_DATA) {
1✔
69
                return VCDiffHeaderParser.RESULT_END_OF_DATA;
1✔
70
            }
71
            // reset address cache between windows (RFC section 5.1)
72
            parent.addrCache().Init();
1✔
73
        } else {
74
            // We are resuming a window that was partially decoded before a
75
            // RESULT_END_OF_DATA was returned.  This can only happen on the first
76
            // loop iteration, and only if the interleaved format is enabled and used.
77
            if (!isInterleaved()) {
1!
78
                throw new IOException("Internal error: Resumed decoding of a delta file window when interleaved format is not being used");
×
79
            }
80
            // FIXME: ?
81
            updateInterleavedSectionPointers(parseable_chunk);
1✔
82
            reader.updatePointers(instructionsAndSizes);
1✔
83
        }
84
        switch (decodeBody(parseable_chunk)) {
1✔
85
            case VCDiffHeaderParser.RESULT_END_OF_DATA:
86
                if (moreDataExpected()) {
1✔
87
                    return VCDiffHeaderParser.RESULT_END_OF_DATA;
1✔
88
                } else {
89
                    throw new IOException("End of data reached while decoding VCDIFF delta file");
1✔
90
                }
91
            default:
92
                break;  // decodeBody succeeded
93
        }
94
        // Get ready to read a new delta window
95
        Reset();
1✔
96
        return VCDiffHeaderParser.RESULT_SUCCESS;
1✔
97
    }
98

99
    public boolean FoundWindowHeader() {
100
        return foundHeader;
1✔
101
    }
102

103
    public boolean moreDataExpected() {
104
        // When parsing an interleaved-format delta file,
105
        // every time decodeBody() exits, interleavedBytesExpected
106
        // will be decremented by the number of bytes parsed.  If it
107
        // reaches zero, then there is no more data expected because
108
        // the size of the interleaved section (given in the window
109
        // header) has been reached.
110
        return isInterleaved() && (interleavedBytesExpected > 0);
1✔
111
    }
112

113
    public int targetWindowStartPos() { return targetWindowStartPos; }
×
114

115
    public void setTargetWindowStartPos(int new_start_pos) {
116
        targetWindowStartPos = new_start_pos;
1✔
117
    }
1✔
118

119
    // Returns the number of bytes remaining to be decoded in the target window.
120
    // If not in the process of decoding a window, returns 0.
121
    public int targetBytesRemaining() {
122
        if (targetWindowLength == 0) {
×
123
            // There is no window being decoded at present
124
            return 0;
×
125
        } else {
126
            return targetWindowLength - targetBytesDecoded();
×
127
        }
128
    }
129

130
    // Reads the header of the window section as described in RFC sections 4.2 and
131
    // 4.3, up to and including the value "Length of addresses for COPYs".  If the
132
    // entire header is found, this function sets up the DeltaWindowSections
133
    // instructionsAndSizes, dataForAddAndRun, and addressesForCopy so
134
    // that the decoder can begin decoding the opcodes in these sections.  Returns
135
    // RESULT_ERROR if an error occurred, or RESULT_END_OF_DATA if the end of
136
    // available data was reached before the entire header could be read.  (The
137
    // latter may be an error condition if there is no more data available.)
138
    // Otherwise, returns RESULT_SUCCESS and advances parseableChunk past the
139
    // parsed header.
140
    //
141
    private int readHeader(ByteBuffer parseableChunk) throws IOException {
142
        // Here are the elements of the delta window header to be parsed,
143
        // from section 4 of the RFC:
144
        //
145
        //                     Window1
146
        //                         Win_Indicator                            - byte
147
        //                         [Source segment size]                    - integer
148
        //                         [Source segment position]                - integer
149
        //                         The delta encoding of the target window
150
        //                             Length of the delta encoding         - integer
151
        //                             The delta encoding
152
        //                                 Size of the target window        - integer
153
        //                                 Delta_Indicator                  - byte
154
        //                                 Length of data for ADDs and RUNs - integer
155
        //                                 Length of instructions and sizes - integer
156
        //                                 Length of addresses for COPYs    - integer
157
        //                                 Data section for ADDs and RUNs   - array of bytes
158
        //                                 Instructions and sizes section   - array of bytes
159
        //                                 Addresses section for COPYs      - array of bytes
160
        //
161
        VCDiffStreamingDecoderImpl.DecoratedByteArrayOutputStream decoded_target = parent.decodedTarget();
1✔
162
        VCDiffHeaderParser header_parser = new VCDiffHeaderParser(parseableChunk.slice());
1✔
163

164
        VCDiffHeaderParser.DeltaWindowHeader deltaWindowHeader = header_parser.parseWinIndicatorAndSourceSegment(
1✔
165
                parent.dictionary_ptr().limit(),
1✔
166
                decoded_target.size(),
1✔
167
                parent.allowVcdTarget()
1✔
168
        );
169

170
        if (deltaWindowHeader == null) {
1✔
171
            return header_parser.getResult();
1✔
172
        }
173

174
        this.sourceSegmentLength.set(deltaWindowHeader.source_segment_length);
1✔
175

176
        hasChecksum = parent.allowChecksum() && ((deltaWindowHeader.win_indicator & VCD_CHECKSUM) != 0);
1✔
177
        if ((targetWindowLength = header_parser.ParseWindowLengths()) == null) {
1✔
178
            return header_parser.getResult();
1✔
179
        }
180

181
        // Throws an exception if targetWindowWouldExceedSizeLimits
182
        parent.targetWindowWouldExceedSizeLimits(targetWindowLength);
1✔
183

184
        header_parser.parseDeltaIndicator();
1✔
185
        int setup_return_code = setUpWindowSections(header_parser);
1✔
186
        if (VCDiffHeaderParser.RESULT_SUCCESS != setup_return_code) {
1✔
187
            return setup_return_code;
1✔
188
        }
189
        /*
190
        // Reserve enough space in the output string for the current target window.
191
        final int wanted_capacity = targetWindowStartPos + targetWindowLength;
192
        if (decodedTarget.capacity() < wanted_capacity) {
193
            decodedTarget.reserve(wanted_capacity);
194
        }
195
        */
196

197
        // Get a pointer to the start of the source segment.
198
        if ((deltaWindowHeader.win_indicator & VCD_SOURCE) != 0) {
1✔
199
            sourceSegment = (ByteBuffer) parent.dictionary_ptr().duplicate().rewind();
1✔
200
            sourceSegment.position(deltaWindowHeader.source_segment_position);
1✔
201
        } else if ((deltaWindowHeader.win_indicator & VCD_TARGET) != 0) {
1✔
202
            // This assignment must happen after the reserve().
203
            // decodedTarget should not be resized again while processing this window,
204
            // so sourceSegment should remain valid.
205
            sourceSegment = decoded_target.toByteBuffer();
1✔
206
            sourceSegment.position(deltaWindowHeader.source_segment_position);
1✔
207
        }
208
        // The whole window header was found and parsed successfully.
209
        foundHeader = true;
1✔
210

211
        parseableChunk.position(parseableChunk.position() + header_parser.unparsedData().position());
1✔
212
        parent.addToTotalTargetWindowSize(targetWindowLength);
1✔
213
        return VCDiffHeaderParser.RESULT_SUCCESS;
1✔
214
    }
215

216
    // After the window header has been parsed as far as the Delta_Indicator,
217
    // this function is called to parse the following delta window header fields:
218
    //
219
    //     Length of data for ADDs and RUNs - integer (VarintBE format)
220
    //     Length of instructions and sizes - integer (VarintBE format)
221
    //     Length of addresses for COPYs    - integer (VarintBE format)
222
    //
223
    // If hasChecksum is true, it also looks for the following element:
224
    //
225
    //     Adler32 checksum            - unsigned 32-bit integer (VarintBE format)
226
    //
227
    // It sets up the DeltaWindowSections instructionsAndSizes,
228
    // dataForAddAndRun, and addressesForCopy.  If the interleaved format
229
    // is being used, all three sections will include the entire window body; if
230
    // the standard format is used, three non-overlapping window sections will be
231
    // defined.  Returns RESULT_ERROR if an error occurred, or RESULT_END_OF_DATA
232
    // if standard format is being used and there is not enough input data to read
233
    // the entire window body.  Otherwise, returns RESULT_SUCCESS.
234
    private int setUpWindowSections(VCDiffHeaderParser header_parser) throws IOException {
235
        VCDiffHeaderParser.SectionLengths sectionLengths = header_parser.parseSectionLengths(hasChecksum);
1✔
236
        if (sectionLengths == null) {
1✔
237
            return header_parser.getResult();
1✔
238
        }
239

240
        // KLUDGE: this code knows what the structure of the data looks like
241
        int parsed_delta_encoding_length =
1✔
242
                VarInt.calculateIntLength(targetWindowLength)
1✔
243
                        + 1
244
                        + VarInt.calculateIntLength(sectionLengths.add_and_run_data_length)
1✔
245
                        + VarInt.calculateIntLength(sectionLengths.addresses_length)
1✔
246
                        + VarInt.calculateIntLength(sectionLengths.instructions_and_sizes_length)
1✔
247
                        + sectionLengths.add_and_run_data_length
248
                        + sectionLengths.addresses_length
249
                        + sectionLengths.instructions_and_sizes_length;
250

251
        if (hasChecksum) {
1✔
252
            this.expectedChecksum.set(sectionLengths.checksum);
1✔
253
            parsed_delta_encoding_length += VarInt.calculateIntLength(sectionLengths.checksum);
1✔
254
        }
255

256
        if (parent.allowInterleaved() &&
1!
257
                (sectionLengths.add_and_run_data_length == 0) &&
258
                (sectionLengths.addresses_length == 0)) {
259
            // The interleaved format is being used.
260
            interleavedBytesExpected = sectionLengths.instructions_and_sizes_length;
1✔
261
            updateInterleavedSectionPointers(header_parser.unparsedData());
1✔
262
        } else {
263
            // If interleaved format is not used, then the whole window contents
264
            // must be available before decoding can begin.  If only part of
265
            // the current window is available, then report end of data
266
            // and re-parse the whole header when decodeChunk() is called again.
267
            if (header_parser.unparsedData().remaining() < (sectionLengths.add_and_run_data_length +
1✔
268
                    sectionLengths.instructions_and_sizes_length +
269
                    sectionLengths.addresses_length)) {
270
                return VCDiffHeaderParser.RESULT_END_OF_DATA;
1✔
271
            }
272

273
            dataForAddAndRun = header_parser.unparsedData().slice();
1✔
274
            dataForAddAndRun.position(sectionLengths.add_and_run_data_length);
1✔
275

276
            instructionsAndSizes = dataForAddAndRun.slice();
1✔
277
            instructionsAndSizes.position(sectionLengths.instructions_and_sizes_length);
1✔
278

279
            addressesForCopy = instructionsAndSizes.slice();
1✔
280
            addressesForCopy.position(sectionLengths.addresses_length);
1✔
281

282
            dataForAddAndRun.flip();
1✔
283
            instructionsAndSizes.flip();
1✔
284
            addressesForCopy.flip();
1✔
285

286
            if (header_parser.deltaEncodingLength != parsed_delta_encoding_length) {
1!
287
                throw new IOException("The end of the instructions section does not match the end of the delta window");
×
288
            }
289
        }
290

291
        reader.init(instructionsAndSizes);
1✔
292
        return VCDiffHeaderParser.RESULT_SUCCESS;
1✔
293
    }
294

295
    // Decodes the body of the window section as described in RFC sections 4.3,
296
    // including the sections "Data section for ADDs and RUNs", "Instructions
297
    // and sizes section", and "Addresses section for COPYs".  These sections
298
    // must already have been set up by ReadWindowHeader().  Returns a
299
    // non-negative value on success, or RESULT_END_OF_DATA if the end of input
300
    // was reached before the entire window could be decoded (only possible if
301
    // isInterleaved() is true), or RESULT_ERROR if an error occurred during
302
    // decoding.  Appends as much of the decoded target window as possible to
303
    // parent->decodedTarget().
304
    //
305
    private int decodeBody(ByteBuffer parseable_chunk) throws IOException {
306
        // TODO: this was originally pointer comparison between instructionsAndSizes and parseable_chunk
307
        if (isInterleaved() && false) {
1✔
308
            throw new IllegalStateException("Internal error: interleaved format is used, but the input pointer does not point to the instructions section");
309
        }
310
        while (targetBytesDecoded() < targetWindowLength) {
1✔
311
            final AtomicInteger decoded_size = new AtomicInteger(0);
1✔
312
            final AtomicInteger mode = new AtomicInteger(0);
1✔
313
            int instruction = reader.getNextInstruction(decoded_size, mode);
1✔
314
            switch (instruction) {
1✔
315
                case VCD_INSTRUCTION_END_OF_DATA:
316
                    updateInstructionPointer(parseable_chunk);
1✔
317
                    return VCDiffHeaderParser.RESULT_END_OF_DATA;
1✔
318
                default:
319
                    break;
320
            }
321
            final int size = decoded_size.get();
1✔
322
            // The value of "size" itself could be enormous (say, INT32_MAX)
323
            // so check it individually against the limit to protect against
324
            // overflow when adding it to something else.
325
            if ((size > targetWindowLength) ||
1✔
326
                    ((size + targetBytesDecoded()) > targetWindowLength)) {
1✔
327
                throw new IOException(String.format(
1✔
328
                        "%s with size %d plus existing %d bytes of target data exceeds length of target window (%d bytes)",
329
                        VCDiffCodeTableData.VCDiffInstructionName(instruction), size, targetBytesDecoded(), targetWindowLength
1✔
330
                ));
331
            }
332
            int result;
333
            switch (instruction) {
1!
334
                case VCD_ADD:
335
                    result = decodeAdd(size);
1✔
336
                    break;
1✔
337
                case VCD_RUN:
338
                    result = decodeRun(size);
1✔
339
                    break;
1✔
340
                case VCD_COPY:
341
                    result = decodeCopy(size, (short)mode.get());
1✔
342
                    break;
1✔
343
                default:
344
                    throw new IOException("Unexpected instruction type " + instruction + " in opcode stream");
×
345
            }
346
            switch (result) {
1✔
347
                case VCDiffHeaderParser.RESULT_END_OF_DATA:
348
                    reader.unGetInstruction();
1✔
349
                    updateInstructionPointer(parseable_chunk);
1✔
350
                    return VCDiffHeaderParser.RESULT_END_OF_DATA;
1✔
351
                case VCDiffHeaderParser.RESULT_SUCCESS:
352
                    break;
353
            }
354
        }
1✔
355
        if (targetBytesDecoded() != targetWindowLength) {
1!
356
            throw new IOException(String.format(
×
357
                    "Decoded target window size (%d bytes) does not match expected size (%d bytes)",
358
                    targetBytesDecoded(), targetWindowLength
×
359
            ));
360
        }
361

362
        if (hasChecksum) {
1✔
363
            adler32.update(parent.decodedTarget().getBuffer(), targetWindowStartPos, targetWindowLength);
1✔
364
            int checksum = (int)adler32.getValue();
1✔
365
            adler32.reset();
1✔
366

367
            if (checksum != expectedChecksum.get()) {
1✔
368
                throw new IOException("Target data does not match checksum; this could mean that the wrong dictionary was used");
1✔
369
            }
370
        }
371
        if (instructionsAndSizes.hasRemaining()) {
1!
372
            throw new IOException("Excess instructions and sizes left over after decoding target window");
×
373
        }
374
        if (!isInterleaved()) {
1✔
375
            // Standard format is being used, with three separate sections for the
376
            // instructions, data, and addresses.
377
            if (dataForAddAndRun.hasRemaining()) {
1!
378
                throw new IOException("Excess ADD/RUN data left over after decoding target window");
×
379
            }
380
            if (addressesForCopy.hasRemaining()) {
1!
381
                throw new IOException("Excess COPY addresses left over after decoding target window");
×
382
            }
383
            // Reached the end of the window.  Update the ParseableChunk to point to the
384
            // end of the addresses section, which is the last section in the window.
385

386
            parseable_chunk.position(
1✔
387
                    parseable_chunk.position() +
1✔
388
                            instructionsAndSizes.limit() +
1✔
389
                            dataForAddAndRun.limit() +
1✔
390
                            addressesForCopy.limit());
1✔
391
        } else {
392
            // Interleaved format is being used.
393
            updateInstructionPointer(parseable_chunk);
1✔
394
        }
395
        return VCDiffHeaderParser.RESULT_SUCCESS;
1✔
396
    }
397

398
    // Returns the number of bytes already decoded into the target window.
399
    private int targetBytesDecoded() {
400
        return parent.decodedTarget().size() - targetWindowStartPos;
1✔
401
    }
402

403
    // Decodes a single ADD instruction, updating parent->decoded_target_.
404
    private int decodeAdd(int size) {
405
        if (size > dataForAddAndRun.remaining()) {
1✔
406
            return VCDiffHeaderParser.RESULT_END_OF_DATA;
1✔
407
        }
408
        // Write the next "size" data bytes
409
        copyBytes(dataForAddAndRun, size);
1✔
410
        return VCDiffHeaderParser.RESULT_SUCCESS;
1✔
411
    }
412

413
    // Decodes a single RUN instruction, updating parent->decoded_target_.
414
    private int decodeRun(int size) {
415
        if (!dataForAddAndRun.hasRemaining()) {
1✔
416
            return VCDiffHeaderParser.RESULT_END_OF_DATA;
1✔
417
        }
418
        // Write "size" copies of the next data byte
419
        runByte(dataForAddAndRun.get(), size);
1✔
420
        return VCDiffHeaderParser.RESULT_SUCCESS;
1✔
421
    }
422

423
    // Decodes a single COPY instruction, updating parent->decoded_target_.
424
    private int decodeCopy(int size, short mode) throws IOException {
425
        // Keep track of the number of target bytes decoded as a local variable
426
        // to avoid recalculating it each time it is needed.
427
        int target_bytes_decoded = targetBytesDecoded();
1✔
428
        final int here_address = sourceSegmentLength.get() + target_bytes_decoded;
1✔
429
        final int decodedAddress;
430
        try {
431
            decodedAddress = parent.addrCache().DecodeAddress(
1✔
432
                    here_address,
433
                    mode,
434
                    addressesForCopy
435
            );
436
        } catch (IOException e) {
1✔
437
            IOException rethrown = new IOException("Unable to decode address for COPY");
1✔
438
            rethrown.initCause(e);
1✔
439
            throw rethrown;
1✔
440
        }
1✔
441

442
        if (decodedAddress == VCDiffHeaderParser.RESULT_END_OF_DATA) {
1✔
443
            return VCDiffHeaderParser.RESULT_END_OF_DATA;
1✔
444
        }
445

446
        if ((decodedAddress < 0) || (decodedAddress > here_address)) {
1!
447
            throw new IllegalStateException(String.format(
×
448
                    "Internal error: unexpected address %d returned from DecodeAddress, with here_address = %d",
449
                    decodedAddress, here_address
×
450
            ));
451
        }
452

453
        // TODO: source_segment_length should be sourceSegment.remaining()
454
        int address = decodedAddress;
1✔
455
        if ((address + size) <= sourceSegmentLength.get()) {
1✔
456
            // copy all data from source segment
457
            copyBytes((ByteBuffer) sourceSegment.slice().position(address), size);
1✔
458
            return VCDiffHeaderParser.RESULT_SUCCESS;
1✔
459
        }
460
        // copy some data from target window...
461
        if (address < sourceSegmentLength.get()) {
1✔
462
            // ... plus some data from source segment
463
            final int partial_copy_size = sourceSegmentLength.get() - address;
1✔
464
            copyBytes((ByteBuffer) sourceSegment.slice().position(address), partial_copy_size);
1✔
465
            target_bytes_decoded += partial_copy_size;
1✔
466
            address += partial_copy_size;
1✔
467
            size -= partial_copy_size;
1✔
468
        }
469
        address -= sourceSegmentLength.get();
1✔
470
        // address is now based at start of target window
471
        // const char* const target_segment_ptr = parent.decodedTarget().data() + targetWindowStartPos;
472

473
        ByteBuffer targetSegment = parent.decodedTarget().toByteBuffer();
1✔
474
        targetSegment.position(targetWindowStartPos);
1✔
475

476
        while (size > (target_bytes_decoded - address)) {
1✔
477
            // Recursive copy that extends into the yet-to-be-copied target data
478
            final int partial_copy_size = target_bytes_decoded - address;
1✔
479
            // TODO: does targetSegment need to be recreated each time?
480
            copyBytes((ByteBuffer) targetSegment.slice().position(address), partial_copy_size);
1✔
481
            target_bytes_decoded += partial_copy_size;
1✔
482
            address += partial_copy_size;
1✔
483
            size -= partial_copy_size;
1✔
484

485
            // This might not be needed, but refresh targetSegment since bytes were just copied into target
486
            targetSegment = parent.decodedTarget().toByteBuffer();
1✔
487
            targetSegment.position(targetWindowStartPos);
1✔
488
        }
1✔
489
        copyBytes((ByteBuffer) targetSegment.slice().position(address), size);
1✔
490
        return VCDiffHeaderParser.RESULT_SUCCESS;
1✔
491
    }
492

493
    // When using the interleaved format, this function is called both on parsing
494
    // the header and on resuming after a RESULT_END_OF_DATA was returned from a
495
    // previous call to decodeBody().  It sets up all three section pointers to
496
    // reference the same interleaved stream of instructions, sizes, addresses,
497
    // and data.  These pointers must be reset every time that work resumes on a
498
    // delta window,  because the input data string may have been changed or
499
    // resized since decodeBody() last returned.
500
    private void updateInterleavedSectionPointers(ByteBuffer data) {
501
        instructionsAndSizes = data.slice();
1✔
502

503
        // Don't read past the end of currently-available data
504
        if (instructionsAndSizes.remaining() > interleavedBytesExpected) {
1✔
505
            instructionsAndSizes.limit(interleavedBytesExpected);
1✔
506
        }
507

508
        dataForAddAndRun = instructionsAndSizes;
1✔
509
        addressesForCopy = instructionsAndSizes;
1✔
510
    }
1✔
511

512
    // If true, the interleaved format described in allowInterleaved() is used
513
    // for the current delta file.  Only valid after ReadWindowHeader() has been
514
    // called and returned a positive number (i.e., the whole header was parsed),
515
    // but before the window has finished decoding.
516
    //
517
    private boolean isInterleaved() {
518
        // If the sections are interleaved, both addressesForCopy and
519
        // dataForAddAndRun should point at instructionsAndSizes.
520
        return addressesForCopy == instructionsAndSizes && dataForAddAndRun == instructionsAndSizes;
1!
521
    }
522

523
    // Executes a single COPY or ADD instruction, appending data to
524
    // parent->decodedTarget().
525
    private void copyBytes(ByteBuffer buffer, int size) {
526
        // TODO: optimize
527
        while (size-- > 0) {
1✔
528
            parent.decodedTarget().write(buffer.get());
1✔
529
        }
530
    }
1✔
531

532
    // Executes a single RUN instruction, appending data to
533
    // parent->decodedTarget().
534
    private void runByte(byte b, int size) {
535
        for (int i = 0; i < size; i++) {
1✔
536
            parent.decodedTarget().write(b);
1✔
537
        }
538
    }
1✔
539

540
    // Advance *parseableChunk to point to the current position in the
541
    // instructions/sizes section.  If interleaved format is used, then
542
    // decrement the number of expected bytes in the instructions/sizes section
543
    // by the number of instruction/size bytes parsed.
544
    private void updateInstructionPointer(ByteBuffer parseableChunk) {
545
        if (isInterleaved()) {
1✔
546
            int bytes_parsed = instructionsAndSizes.position();
1✔
547
            // Reduce expected instruction segment length by bytes parsed
548
            interleavedBytesExpected -= bytes_parsed;
1✔
549
            parseableChunk.position(parseableChunk.position() + bytes_parsed);
1✔
550
        }
551
    }
1✔
552

553
    // The parent object which was passed to init().
554
    private final VCDiffStreamingDecoderImpl parent;
555

556
    // This value will be true if VCDiffDeltaFileWindow::ReadDeltaWindowHeader()
557
    // has been called and succeeded in parsing the delta window header, but the
558
    // entire window has not yet been decoded.
559
    private boolean foundHeader;
560

561
    // Contents and length of the current source window.  sourceSegment
562
    // will be non-NULL if (a) the window section header for the current window
563
    // has been read, but the window has not yet finished decoding; or
564
    // (b) the window did not specify a source segment.
565
    private ByteBuffer sourceSegment;
566
    private final AtomicInteger sourceSegmentLength = new AtomicInteger(0);
1✔
567

568
    // The delta encoding window sections as defined in RFC section 4.3.
569
    // The pointer for each section will be incremented as data is consumed and
570
    // decoded from that section.  If the interleaved format is used,
571
    // dataForAddAndRun and addressesForCopy will both point to
572
    // instructionsAndSizes; otherwise, they will be separate data sections.
573
    private ByteBuffer instructionsAndSizes;
574
    private ByteBuffer dataForAddAndRun;
575
    private ByteBuffer addressesForCopy;
576

577
    // The expected bytes left to decode in instructionsAndSizes.  Only used
578
    // for the interleaved format.
579
    private int interleavedBytesExpected;
580

581
    // The expected length of the target window once it has been decoded.
582
    private Integer targetWindowLength;
583

584
    // The index in decodedTarget at which the first byte of the current
585
    // target window was/will be written.
586
    private int targetWindowStartPos;
587

588
    // If hasChecksum is true, then expectedChecksum contains an Adler32
589
    // checksum of the target window data.  This is an extension included in the
590
    // VCDIFF 'S' (SDCH) format, but is not part of the RFC 3284 draft standard.
591
    private boolean hasChecksum;
592
    private final AtomicInteger expectedChecksum = new AtomicInteger(0);
1✔
593

594
    private final ZeroInitializedAdler32 adler32 = new ZeroInitializedAdler32();
1✔
595

596
    private VCDiffCodeTableReader reader = new VCDiffCodeTableReader();
1✔
597
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc