Skip to content

Snapshot Format Specification

This page documents the .snap file format used by Snapshot Comparison.

File Structure

A snapshot file consists of two sections separated by a --- delimiter:

HEADER
---
CONTENT

Example

# Snapshot: api-response
# Created: 1703721600
# Updated: 1703721700
# Hash: a1b2c3d4e5f67890
# Size: 42
# Type: json
---
{"status": "ok", "message": "Hello, World!"}

Header Fields

FieldTypeDescription
SnapshotStringName of the snapshot (no path separators)
CreatedIntegerUnix timestamp when first created
UpdatedIntegerUnix timestamp of last update
HashHex (16 chars)FNV-1a hash of content
SizeIntegerContent size in bytes
TypeEnumtext, json, or binary

Hash Algorithm

Content hashes use FNV-1a (64-bit) for fast comparison:

rust
const FNV_OFFSET_BASIS: u64 = 0xcbf29ce484222325;
const FNV_PRIME: u64 = 0x00000100000001B3;

fn fnv1a_hash(data: &[u8]) -> u64 {
    let mut hash = FNV_OFFSET_BASIS;
    for byte in data {
        hash ^= *byte as u64;
        hash = hash.wrapping_mul(FNV_PRIME);
    }
    hash
}

The hash is formatted as a 16-character lowercase hexadecimal string.

Why FNV-1a?

  • Fast: Single-pass, no complex operations
  • Good distribution: Minimizes collisions for typical content
  • Non-cryptographic: Not for security, just change detection

Hash Collisions

FNV-1a is used for fast detection, not cryptographic security. When hashes match, byte-by-byte comparison confirms equality.

File Type Detection

When file type is not explicitly specified:

Detection RuleFile Type
Starts with { or [ (after trim)json
Contains null bytes (\0)binary
All other contenttext

Name Validation

Snapshot names must satisfy these constraints:

ConstraintReason
No / or \Prevent path traversal
No ..Prevent directory escape
No null bytesPrevent injection
Non-emptyValid identifier required

Valid names: api-response, test_123, my.snapshot

Invalid names: ../escape, sub/path, bad\0name

Directory Structure

snapshots/
├── api-response.snap
├── user-list.snap
├── binary-data.snap
└── config.snap

All snapshots share a flat directory. Subdirectories are not supported.

Comparison Semantics

Match Detection

A snapshot matches when:

  1. Content hash matches (fast path)
  2. Byte-by-byte comparison confirms (definitive)

Mismatch Handling

When content differs, a diff is generated based on file type:

TypeDiff FormatPowered By
textUnified diff with contextodiff
jsonSemantic JSON diffodiff
binaryHex dump with differencesodiff

Update Mode

When update_mode is enabled and content differs:

  1. New content replaces old snapshot
  2. Updated timestamp refreshed
  3. Hash and Size recalculated
  4. Original Created preserved

Version Compatibility

Forward Compatibility

Readers should ignore unknown header fields to allow future extensions.

Backward Compatibility

Version 1.0.0 is the initial format. Future versions will maintain backward compatibility.

Error Conditions

ErrorCause
NotFoundSnapshot file does not exist
InvalidNameName contains forbidden characters
CorruptedSnapshotMissing header, separator, or invalid metadata
InvalidUtf8Text/JSON content is not valid UTF-8
IoErrorFile system error during read/write

Conformance

A conforming implementation must:

  1. Read and write the specified file format
  2. Validate snapshot names before use
  3. Use FNV-1a for hash calculation
  4. Support all three file types
  5. Preserve content exactly (no normalization)

See Also

Released under the MIT License.