synology_apm_repo.sdk.format.composition module

Composition file format (FORMAT-SPEC.md: composition-splitting, RecordHead).

Two independent binary structures live in a composition sub-file:

  • CompositionHeader (64 bytes) — the generic IndexHeader shell, present only at the very start of a session’s subID=0 file.

  • RecordHead (32 bytes) — one per backup version, at comp_offset (db/file_map.comp_offset) within the session’s global offset space. This is a bespoke structure: 2-byte magic (not 4), and its own CRC covering [0,28) stored at offset 28 (not the generic shell’s [0,60)/60) — do not reuse parse_index_header for it.

record_total_length predicts the exact file offset of the next record.

class synology_apm_repo.sdk.format.composition.CompositionHeader(major, minor)

Bases: object

Parsed composition sub-file header.

major: int
minor: int
synology_apm_repo.sdk.format.composition.parse_composition_header(data)

Parse the 64-byte header at the start of a session’s subID=0 file (FORMAT-SPEC.md: composition-splitting).

Raises:
class synology_apm_repo.sdk.format.composition.CompositionStatus(value)

Bases: Enum

(FORMAT-SPEC.md: RecordHead) — the restore path never branches on this; a record reached via file_map (i.e. successfully committed) is always COMPLETE in practice, but this module decodes whichever value is present rather than rejecting INTERRUPTED.

COMPLETE = 0
INTERRUPTED = 1
class synology_apm_repo.sdk.format.composition.RecordHead(status, map_num, map_crc, mode, attr_leng, attr_crc)

Bases: object

Parsed 32-byte RecordHead (FORMAT-SPEC.md: RecordHead).

status: CompositionStatus
map_num: int
map_crc: int
mode: int
attr_leng: int
attr_crc: int
property has_redundancy: bool
synology_apm_repo.sdk.format.composition.parse_record_head(data)

Decode the 32-byte RecordHead at the start of data.

Every current reader (and this one) refuses the pre-Redundancy record layout outright rather than guess at its (unspecified here) trailer shape.

Raises:
synology_apm_repo.sdk.format.composition.verify_chunk_map_crc(map_array_bytes, expected_crc)

Validate a full ChunkMapRecord array’s bytes against a RecordHead.map_crc (FORMAT-SPEC.md: RecordHead).

Deliberately not called automatically by parse_record_head — doing so would force every interactive read to first load the entire (potentially multi-hundred-MB) map array. Callers doing a full-unit export or verify call this explicitly once they already have those bytes in hand.

synology_apm_repo.sdk.format.composition.should_thread_chunk_map_crc(map_array_bytes)

Whether verify_chunk_map_crc(map_array_bytes, ...) is worth running via asyncio.to_thread() rather than calling directly — see _CRC_THREAD_HOP_MIN_BYTES. This module stays synchronous throughout (the Codec Layer never does I/O or threading itself), so both async callers (dedup/verify_checks.py, diagnostics.py) that CRC-verify a chunk-map array make this same decision themselves rather than each guessing independently.

synology_apm_repo.sdk.format.composition.verify_attr_crc(attr_bytes, expected_crc)

Validate a record’s JSON attribute blob bytes against its own RecordHead.attr_crc (FORMAT-SPEC.md: RecordHead) — like verify_chunk_map_crc, not needed to read the record’s content and not called automatically by parse_record_head; a verify caller checks it explicitly once the blob bytes are already in hand.

synology_apm_repo.sdk.format.composition.record_total_length(map_num, attr_leng)

Total byte length of one composition record starting at its headOff: RECORD_HEAD_LENGTH + map_num*20 + attr_leng + redundancy_size(map_num*20, 8192) (FORMAT-SPEC.md: RecordHead/ChunkCrcStore).

Records are packed back-to-back with no padding, so headOff + record_total_length(...) is exactly the next record’s headOff.

synology_apm_repo.sdk.format.composition.chunk_map_array_offset(head_off)

Byte offset (relative to the composition sub-file) where a record’s ChunkMapRecord array begins — immediately after its RecordHead (FORMAT-SPEC.md: RecordHead).