///|
/// MRPM v1: 64-byte header, 24 bytes per partition fingerprint, CRC-32.
pub fn Manifest::encode(self : Manifest) -> Bytes {
let out : Array[Byte] = [77, 82, 80, 77, 1, 0, 0, 0]
put_u32(out, self.layout.buckets.reinterpret_as_uint())
put_u32(out, self.total.count.reinterpret_as_uint())
put_u64(out, self.layout.seed)
put_u64(out, self.scope)
put_u64(out, self.epoch)
put_u64(out, self.total.sum)
put_u64(out, self.total.xor)
put_u64(out, 0UL)
for fp in self.fingerprints {
put_u32(out, fp.count.reinterpret_as_uint())
put_u32(out, 0U)
put_u64(out, fp.sum)
put_u64(out, fp.xor)
}
let payload = Bytes::from_array(out)
put_u32(out, crc32(payload, payload.length()))
Bytes::from_array(out)
}
///|
pub fn Manifest::decode(
data : Bytes,
max_buckets~ : Int32 = 4096,
max_members~ : Int32 = 10000000,
) -> Manifest raise ReconcileError {
if max_buckets < 1 || max_buckets > 4096 || max_members < 0 {
raise InvalidConfig("invalid manifest limits")
}
if data.length() < 68 {
raise InvalidFrame("truncated manifest")
}
if data[0] != 77 ||
data[1] != 82 ||
data[2] != 80 ||
data[3] != 77 ||
data[4] != 1 ||
data[5] != 0 ||
data[6] != 0 ||
data[7] != 0 ||
get_u64(data, 56) != 0UL {
raise InvalidFrame("manifest magic, version or reserved fields")
}
let buckets_raw = get_u32(data, 8)
let count_raw = get_u32(data, 12)
if buckets_raw == 0U ||
buckets_raw > max_buckets.reinterpret_as_uint() ||
count_raw > max_members.reinterpret_as_uint() {
raise LimitExceeded("manifest allocation or member limit")
}
let buckets = buckets_raw.reinterpret_as_int()
if data.length() != 68 + 24 * buckets {
raise InvalidFrame("manifest length")
}
if get_u32(data, data.length() - 4) != crc32(data, data.length() - 4) {
raise InvalidFrame("manifest CRC")
}
let fingerprints : Array[Fingerprint] = []
let mut count = 0L
let mut sum = 0UL
let mut xor = 0UL
for i = 0; i < buckets; i = i + 1 {
let offset = 64 + i * 24
let raw = get_u32(data, offset)
if raw > count_raw || get_u32(data, offset + 4) != 0U {
raise InvalidFrame("manifest partition count or flags")
}
let fp : Fingerprint = {
count: raw.reinterpret_as_int(),
sum: get_u64(data, offset + 8),
xor: get_u64(data, offset + 16),
}
if fp.count == 0 && fp != empty_fingerprint() {
raise InvalidFrame("nonempty zero-count manifest partition")
}
count += fp.count.to_int64()
sum += fp.sum
xor = xor ^ fp.xor
fingerprints.push(fp)
}
let total : Fingerprint = {
count: count_raw.reinterpret_as_int(),
sum: get_u64(data, 40),
xor: get_u64(data, 48),
}
if count != total.count.to_int64() || sum != total.sum || xor != total.xor {
raise InvalidFrame("manifest totals mismatch")
}
{
scope: get_u64(data, 24),
epoch: get_u64(data, 32),
total,
fingerprints,
layout: Layout::new(buckets, seed=get_u64(data, 16)),
}
}