synology_apm_repo.sdk.format.bucket module¶
Bucket file (.buk) format (FORMAT-SPEC.md §4).
Pure bytes -> dataclass decode, zero I/O — this module never opens a
file. The Dedup Layer’s BucketReader is what actually fetches bytes
from an ObjectStore at the offsets this module computes, then hands
ciphertext through format.crypto and compression.
ABP builds always write mode = COMPRESS|CHUNK_CRC (0x03), optionally
with VAULT_ENCRYPT (0x83) — but every mode bit is checked explicitly
here rather than assumed: FORMAT-SPEC.md: bucket-header warns that a bit always
being 1 today is an observation, not a guarantee the check can be skipped.
- synology_apm_repo.sdk.format.bucket.COMPRESS_TYPE_BY_VALUE = {0: CompressType.NONE, 1: CompressType.LZ4, 2: CompressType.ZSTD, 4: CompressType.COMPACTED}¶
Plain-dict stand-in for
CompressType(value), used inparse_size_store’s per-chunk hot loop (up to 8192 calls per bucket) in place ofEnum.__call__’s own value-lookup machinery. Every otherCompressType(...)call site in this codebase is cold (once per chunk at most, not in a hot per-bucket loop) and is left as the plain, self-explaining enum call.
- synology_apm_repo.sdk.format.bucket.COMPRESS_TYPE_COMPACTED_VALUE = 4¶
Raw value compared directly in
_SizeStoreArray.effective_lens’s own hot loop, same reasoning asCOMPRESS_TYPE_BY_VALUEabove, one level further — that loop never needs theCompressTypemember itself, only to compare against these two particular values.
- class synology_apm_repo.sdk.format.bucket.BucketFileHeader(major, minor, mode, chunk_num, chunk_size_crc, crc_of_chunk_crc)¶
Bases:
objectParsed
.bukheader (FORMAT-SPEC.md: bucket-header).
- synology_apm_repo.sdk.format.bucket.parse_bucket_header(data)¶
Parse a
.bukfile’s 64-byte header from the start ofdata.
- class synology_apm_repo.sdk.format.bucket.SizeStoreEntry(compress_type, stored_len)¶
Bases:
NamedTupleOne chunk’s compression type and stored (compressed) length, as recorded in
SizeStore(FORMAT-SPEC.md: SizeStore).NamedTuple, not this project’s usual@dataclass(frozen=True)— a deliberate exception:parse_size_storeconstructs up to 8192 of these per bucket, millions in aggregate across a real export, and a plainNamedTuplebuild has identical attribute access with no introspection callers to break, at lower construction cost than@dataclass.parse_size_storehands these out lazily via_SizeStoreArrayrather than building every entry up front.- compress_type: CompressType¶
Alias for field number 0
- synology_apm_repo.sdk.format.bucket.chunk_size_store_tight_length(chunk_num)¶
ceil(chunk_num * 15 / 8), the tightly-packed SizeStore length in bytes, before zero-padding to the fixed 16320-byte on-disk allocation.
- synology_apm_repo.sdk.format.bucket.parse_size_store(data, chunk_num, *, verify_crc=None)¶
Decode
chunk_num15-bit packed SizeStore records starting at the beginning ofdata(FORMAT-SPEC.md: SizeStore, the bucket’s SizeStore region, bytes[64, 16384)); only the firstchunk_size_store_tight_lengthbytes plus a little slack are actually read.Decodes
_SIZE_STORE_GROUP_RECORDS(8) records at a time (see that constant for the bit-packing this relies on). Each record’s decodedcompress_type/stored_lenstill gets eager validation (raisingDataCorruptErrorimmediately for an unknownCompressType) — only building aSizeStoreEntryobject is deferred, to_SizeStoreArray’s own__getitem__.- Parameters:
- Raises:
FormatError –
datais shorter than the tightly-packed regionchunk_numimplies.DataCorruptError –
verify_crcwas given and does not match, or a decoded record’sCompressTypevalue is unknown.
- class synology_apm_repo.sdk.format.bucket.ChunkLocator(offset, length)¶
Bases:
NamedTupleAbsolute file byte-range for one chunk’s (still compressed and/or encrypted) data.
Same deliberate
NamedTuple-not-@dataclass(frozen=True)exception asSizeStoreEntryabove — see there for the reasoning.chunk_locatorshands these out lazily too, via_ChunkLocatorArray, when it’s fed a_SizeStoreArray: a realChunkLocatoris built only for the index actually asked for.
- synology_apm_repo.sdk.format.bucket.chunk_locators(header, entries)¶
Absolute file byte-range for every chunk described by
entries(FORMAT-SPEC.md: bucket-physical-layout).ABP builds always use the compressed layout (data starts at
COMPRESS_RESERVED_LENG, 16384); the uncompressed layout (fixed 4096 bytes/chunk starting atRESERVED_LENG, 4096) is handled too, but is never produced by any current writer.
- synology_apm_repo.sdk.format.bucket.raw_chunk_arrays(header, entries, locators)¶
(compress_type_values, offsets, lengths)as three flatarray.arraybuffers — O(1) per-chunk access to every fieldSizeStoreEntry/ChunkLocatorcarry without constructing either object, for a caller whose access pattern is dense enough (every chunk in every bucket it opens) that even lazy construction adds up.Zero-copy whenever
header.is_compressed— every current writer’s layout — returning the exact arraysparse_size_store/chunk_locatorsalready built; theassertbelow is a fail-fast check thatentries/locatorsactually came from that branch. The uncompressed layout instead gets a formulaic fill (every chunk isCompressType.NONEat a fixedFIXED_CHUNK_LENGTHstride fromRESERVED_LENG), with nothing to read fromentries/locatorsat all.
- synology_apm_repo.sdk.format.bucket.expected_bucket_size(header, entries)¶
Self-check total on-disk file size.
COMPRESS_RESERVED_LENG + Σ effective_len + 4×non-empty-chunks + redundancy_size(tight_sizestore_len, 256).- Raises:
ValueError –
headeris not the compressed (ABP) layout.
- synology_apm_repo.sdk.format.bucket.chunk_crc_store_region(header, entries)¶
Byte
(offset, length)of the ChunkCrcStore trailer (FORMAT-SPEC.md: ChunkCrcStore) within the bucket file — immediately after the chunk-data region, one 4-byte entry per non-empty chunk.- Raises:
ValueError –
headeris not the compressed (ABP) layout.
- synology_apm_repo.sdk.format.bucket.chunk_crc_store_index(entries, chunk_idx)¶
Position of chunk
chunk_idxwithin the ChunkCrcStore trailer — the count of non-empty (effective_len > 0) chunks at indices below it, since a COMPACTED chunk has no trailer entry of its own at all.O(
chunk_idx) per call — correct but quadratic if called once per chunk for every chunk in a bucket, since a real bucket can hold thousands of chunks. A one-off single-chunk lookup (the only current use,verify’s spot-checks) is fine; a caller resolving every chunk in a bucket wantschunk_crc_store_positionsinstead.
- synology_apm_repo.sdk.format.bucket.chunk_crc_store_positions(entries)¶
Every chunk’s own ChunkCrcStore position, computed in one O(n) cumulative-count pass — the batch counterpart to
chunk_crc_store_index’s O(1)-but-called-once-per-chunk-is-O(n²) single lookup. A COMPACTED chunk’s own slot holds whatever position the next non-empty chunk would get (it has no real entry of its own; never read for one).
- synology_apm_repo.sdk.format.bucket.parse_chunk_crc_store(data, non_empty, *, verify_crc=None)¶
Decode the ChunkCrcStore trailer: one big-endian 4-byte ciphertext CRC32 per non-empty chunk (FORMAT-SPEC.md: ChunkCrcStore), computed at write time over each chunk’s stored bytes (after compression and encryption, if any) — not a plaintext check.
- Parameters:
data (bytes) – Bytes starting at
chunk_crc_store_region’s offset; only the firstCHUNK_CRC_SIZE * non_emptybytes are read.non_empty (int) – Number of entries, from
chunk_crc_store_region.verify_crc (int | None) – The header’s
crc_of_chunk_crcfield. When given, the whole trailer’s own CRC32 is checked against it — this is a self-consistency check of the trailer bytes, not a check of any individual chunk’s stored data against its own entry here.
- Raises:
FormatError –
datais shorter than the trailer needs.DataCorruptError –
verify_crcwas given and does not match.