A Kotlin Multiplatform implementation of a VCDIFF (RFC 3284) decoder library for efficient binary differencing and compression.
This library provides a VCDIFF decoder that can decode delta files created according to RFC 3284 - The VCDIFF Generic Differencing and Compression Data Format. VCDIFF is a format for expressing one data stream as a variant of another data stream, commonly used for binary differencing, compression, and patch applications.
The library provides one-shot decoding, reusable decoder instances, a streaming decoder for incremental processing, and a structural inspection API.
- Kotlin Library: RFC 3284 compliant VCDIFF decoding with clean, idiomatic Kotlin API
- Streaming Decoder: Incremental decoding for processing delta bytes as they arrive
- Structural Inspection: Parse and inspect VCDIFF delta structure without full decoding
- Comprehensive Validation: Support for all VCDIFF instruction types (ADD, COPY, RUN)
- Address Caching: Efficient decoding with proper address cache implementation
- Checksum Validation: Full Adler-32 checksum validation support
- Robust Error Handling: Typed exception hierarchy for precise error handling
- Extensive Testing: 162 test cases including fuzz testing
- Application Headers: This implementation does not handle application header information
- Secondary Compression: This decoder does not support secondary compression (eg, gzip, bzip2)
- Custom Code Tables: This decoder does not support custom instruction code tables
- Compatibility: Works with VCDIFF deltas created using
xdelta3 -e -S -A(no secondary compression, no application header)
- VCD_ADLER32: This implementation detects and parses the VCD_ADLER32 extension (bit 0x04 in window indicator)
- Non-standard Extension: The Adler-32 checksum is not part of RFC 3284 but is supported by some implementations
- Validation: Full Adler-32 checksum validation is implemented and performed during decoding
dependencies {
implementation("com.ably.vcdiff:vcdiff:0.1.0")
}dependencies {
implementation 'com.ably.vcdiff:vcdiff:0.1.0'
}This repository includes the VCDIFF test suite as a git submodule. To clone the repository with all test cases:
git clone --recursive https://github.com/ably/vcdiff-kotlin.gitIf you've already cloned the repository without the submodule, initialize it:
git submodule update --init --recursiveTo update the test suite submodule to the latest version:
git submodule update --remoteimport com.ably.vcdiff.decode
import java.io.File
fun main() {
val source = File("original.txt").readBytes()
val delta = File("changes.vcdiff").readBytes()
val result = decode(source, delta)
File("result.txt").writeBytes(result)
}For decoding multiple deltas against the same source:
import com.ably.vcdiff.VcdiffDecoder
import java.io.File
fun main() {
val source = File("original.txt").readBytes()
val decoder = VcdiffDecoder(source)
val target1 = decoder.decode(File("delta1.vcdiff").readBytes())
val target2 = decoder.decode(File("delta2.vcdiff").readBytes())
}For processing delta bytes incrementally as they arrive:
import com.ably.vcdiff.VcdiffStreamingDecoder
import java.io.ByteArrayOutputStream
fun main() {
val source = File("original.txt").readBytes()
val decoder = VcdiffStreamingDecoder(source)
val output = ByteArrayOutputStream()
// Feed chunks as they arrive
output.write(decoder.append(chunk1))
output.write(decoder.append(chunk2))
output.write(decoder.append(chunk3))
// Signal end of stream
output.write(decoder.finish())
val result = output.toByteArray()
}The decoder provides specific exception types for different error conditions:
import com.ably.vcdiff.*
fun main() {
try {
val target = decode(source, delta)
} catch (e: InvalidMagicException) {
println("Invalid VCDIFF file: $e")
} catch (e: UnsupportedVersionException) {
println("Unsupported VCDIFF version: $e")
} catch (e: ChecksumMismatchException) {
println("Checksum failed: expected ${e.expected}, got ${e.actual}")
} catch (e: InvalidFormatException) {
println("Malformed delta: $e")
} catch (e: VcdiffException) {
println("VCDIFF error: $e")
}
}Parse and inspect the structure of a VCDIFF delta without full decoding:
import com.ably.vcdiff.parseDelta
import java.io.File
fun main() {
val delta = File("changes.vcdiff").readBytes()
val parsed = parseDelta(delta)
println("Version: ${parsed.header.version}")
println("Windows: ${parsed.windows.size}")
for (window in parsed.windows) {
println(" Target length: ${window.targetWindowLength}")
println(" Instructions: ${window.instructions.size}")
}
}Decodes a VCDIFF delta file using the provided source data and returns the reconstructed target data.
Parameters:
source: The original source data (may be empty for deltas that don't reference source)delta: The VCDIFF delta file data
Returns:
- Decoded target data as ByteArray
Throws:
VcdiffExceptionif decoding fails (malformed delta, checksum validation failure, etc.)
Parses a VCDIFF delta into its structural components without performing full decoding. Useful for debugging and tooling.
Parameters:
delta: The VCDIFF delta file data
Returns:
- A
ParsedDeltacontaining the header and list of windows with their instructions
Creates a new decoder instance with the specified source data. Useful for decoding multiple deltas against the same source.
Methods:
decode(delta: ByteArray): ByteArray- Decodes a single VCDIFF delta using the decoder's source data
Creates a streaming decoder that accepts delta bytes incrementally. Emits reconstructed target bytes as soon as complete windows are decoded.
Methods:
append(data: ByteArray): ByteArray- Feed delta bytes; returns any decoded output availablefinish(): ByteArray- Signal end of stream; returns any remaining decoded output
VcdiffException: Base exception for all VCDIFF errorsInvalidMagicException: Invalid VCDIFF magic bytesUnsupportedVersionException: Unsupported VCDIFF versionInvalidFormatException: Malformed delta structure (truncated data, bad lengths, etc.)ChecksumMismatchException: Adler-32 checksum validation failure (includesexpectedandactualfields)
header: Header- VCDIFF file headerwindows: List<Window>- List of delta windows
version: Byte- VCDIFF version (always 0)indicator: HeaderIndicator- Header flags
sourceSegmentSize: Long- Size of source segment referencedsourceSegmentPosition: Long- Offset into source datatargetWindowLength: Long- Expected target window sizehasChecksum: Boolean- Whether Adler-32 checksum is presentchecksum: Long- Adler-32 checksum valueinstructions: List<Instruction>- Decoded instructions
Instruction.Add(size: Long, data: ByteArray)- Add literal bytesInstruction.Copy(size: Long, mode: Int, address: Long)- Copy from source or targetInstruction.Run(size: Long, byte: Byte)- Repeat a single byte
To run all tests:
gradle testTo run with verbose output:
gradle test --infoTo run only the integration tests:
gradle test --tests "com.ably.vcdiff.VcdiffTest"To run only the fuzz tests:
gradle test --tests "com.ably.vcdiff.FuzzTest"The test suite includes:
- 52 targeted positive tests: Specific feature validation (basic operations, varint boundaries, code table entries)
- 33 targeted negative tests: Invalid VCDIFF files with strict exception type assertions
- 20 general positive tests: Valid VCDIFF files covering various data patterns
- 32 streaming decoder tests: Incremental decoding of all positive test cases
- 5 structural inspection tests:
parseDeltaAPI validation - 20 fuzz tests: Random byte generation, mutation-based, structure-aware, and boundary condition fuzzing
- Total: 162 test cases
- Positive tests: 72/72 passed
- Negative tests: 33/33 passed
- Streaming tests: 32/32 passed
- Inspection tests: 5/5 passed
- Fuzz tests: 20/20 passed
This is a decoder-only library. To create compatible VCDIFF delta files, use tools such as xdelta3:
# Create a VCDIFF delta (compatible with this decoder)
xdelta3 -e -S -A -s original.txt modified.txt delta.vcdiff- Kotlin: 1.9+
- JVM: Java 8 or higher (when targeting JVM/Android)
- Dependencies: None (zero runtime dependencies)
Contributions are welcomed. Please follow these guidelines.
- Fork the repository
- Clone your fork with submodules:
git clone --recursive <your-fork-url> - Create a feature branch:
git checkout -b feature/your-feature-name - Make your changes
- Test your changes thoroughly
- Submit a pull request
- Code Style: Follow Kotlin coding conventions
- Testing: All new features must include tests
- Documentation: Update documentation for any API changes
- Commits: Use clear, descriptive commit messages
Ensure your contribution passes all checks:
# Run all tests
gradle test
# Check for compilation errors
gradle compileKotlinWhen reporting bugs, please include:
- JDK version
- Kotlin version
- Operating system
- Minimal reproduction case
- Expected vs actual behavior
- Sample VCDIFF files (if applicable)
For new features, please:
- Check existing issues first
- Describe the use case
- Provide RFC 3284 references if applicable
- Consider backwards compatibility
This project is licensed under the Apache License 2.0. See the LICENSE file for details.