synology_apm_repo.sdk.format.addressing module

Chunk addressing and the two path-layering schemes that key off it (FORMAT-SPEC.md §3, its composition-splitting section).

Directory-name constants (Pool, Composition, …) are not decided here — those are repository-root layout constants that belong to the Dedup Layer’s repository.py (FORMAT-SPEC.md: repo-root-layout’s repository-root layout). This module only computes the id-layering fragment relative to whichever root the caller prepends.

class synology_apm_repo.sdk.format.addressing.ChunkAddress(stream_id, bucket_id, chunk_idx)

Bases: NamedTuple

A single uint64 chunk address: streamID(8b) | bucketID(40b) | chunkIdx(16b) (FORMAT-SPEC.md: ChunkAddress).

Trusts its fields — neither construction nor from_int range-checks them. An out-of-range field either surfaces naturally downstream (an over-capacity chunk_idx raises IndexError; a bogus bucket_id resolves to a missing path and raises NotFoundError) or is the job of units/verify_reachable.py’s dedicated checks (chunk-map mapCrc, per-chunk fingerprint), which are already verify-only and never run on the ordinary read/export path. This matters at scale: advance runs once per real chunk in an export — millions of times for a large disk — reconstructing a value that is, by construction, already correct.

Same deliberate NamedTuple-not-@dataclass(frozen=True) exception as SizeStoreEntry — see there for the reasoning.

stream_id: StreamId

Alias for field number 0

bucket_id: BucketId

Alias for field number 1

chunk_idx: ChunkIdx

Alias for field number 2

classmethod from_int(data)

Unpack a raw uint64 chunk address — doesn’t range-check the result, same as construction (see the class docstring).

to_int()
advance(k)

Advance by k chunks, carrying into bucket_id when chunk_idx would reach BUCKET_MAX_CHUNK_NUM (8192). This is the semantics needed to expand a ChunkMapKind.MAPPING chunk-map entry’s map_num * (1 + repeat) run of chunks starting from this address; it must never be approximated as “add k to the raw 64-bit integer”, which would misplace the carry at the packed field’s 16-bit boundary instead of the bucket’s real 8192-chunk capacity.

synology_apm_repo.sdk.format.addressing.pool_layer_path(stream_id, bucket_id)

Relative path (no Pool/ prefix, no .buk/.inf/… suffix) for bucket_id within stream_id’s pool: <streamID>/[ancestor layers.../]<bucketID> (FORMAT-SPEC.md: pool-path-layering).

For a .inf/.fgp/.ref group path, pass the group’s starting bucket id (bucket_id & ~(GROUP_BUCKET_NUM - 1)), not an individual bucket’s own id.

synology_apm_repo.sdk.format.addressing.composition_session_dir(stream_id, session_id)

Relative path (no Composition/ prefix) to a session’s directory: <streamID>/[ancestor layers.../]<sessionID>.com (FORMAT-SPEC.md: composition-splitting) — the leaf component carries the .com suffix rather than being a bare decimal number, unlike the Pool leaf.

synology_apm_repo.sdk.format.addressing.composition_path(stream_id, session_id, sub_id)

Full relative path (no Composition/ prefix, no sequence-id suffix) to a composition sub-file: <sessionDir>/[ancestor layers.../]c<subID> (FORMAT-SPEC.md: composition-splitting).

synology_apm_repo.sdk.format.addressing.split_layer_leaf(layer_path)

Split a pool_layer_path()/composition_path() result into (dir_part, leaf) — the ancestor-layer directory and the final path component, which every caller resolving a physical file under it needs separately (a leaf-only suffix to append, a directory to generation-resolve within). dir_part is "" when layer_path has no ancestor layers (id fits in one layer); pass it to join_path rather than concatenating with "/" directly, since that tolerates the empty case.

synology_apm_repo.sdk.format.addressing.split_composition_offset(global_offset)

Split a composition-record global offset into (sub_id, offset_within_subfile) — sub_id = off >> 24, sub_off = off & (16MiB - 1) (FORMAT-SPEC.md: composition-splitting).

synology_apm_repo.sdk.format.addressing.group_start_bucket_id(bucket_id)

The starting bucket id of the 1024-bucket group bucket_id belongs to: bucket_id & ~(GROUP_BUCKET_NUM - 1).