///|
/// Bounded description of an ordered sequence of independently coded stripes.
pub struct Manifest {
set_id : Int
data_count : Int
parity_count : Int
stripe_payload_limit : Int
stripe_count : Int
total_length : Int
}
///|
fn checked_manifest(
set_id : Int,
data_count : Int,
parity_count : Int,
stripe_payload_limit : Int,
total_length : Int,
max_object_bytes : Int,
max_encoded_bytes : Int,
) -> Manifest raise ErasureError {
let max_shard_bytes = validated_max_shard_bytes(
data_count, parity_count, max_encoded_bytes,
)
if set_id < 0 {
raise InvalidManifest("set id must be nonnegative")
}
if max_object_bytes < 0 || max_object_bytes > 268_435_456 {
raise InvalidConfiguration("object byte budget must be <= 256 MiB")
}
if total_length < 0 || total_length > max_object_bytes {
raise ResourceLimit("object exceeds byte budget")
}
if stripe_payload_limit < 1 ||
stripe_payload_limit > data_count * max_shard_bytes {
raise InvalidManifest("stripe payload limit exceeds codec capacity")
}
let mut stripe_count = total_length / stripe_payload_limit
if total_length % stripe_payload_limit != 0 {
stripe_count = stripe_count + 1
}
if stripe_count > 4096 {
raise ResourceLimit("object exceeds 4096 stripes")
}
{
set_id,
data_count,
parity_count,
stripe_payload_limit,
stripe_count,
total_length,
}
}
///|
pub fn Manifest::new(
codec : Codec,
set_id : Int,
stripe_payload_limit : Int,
total_length : Int,
max_object_bytes? : Int = 67_108_864,
) -> Manifest raise ErasureError {
checked_manifest(
set_id,
codec.data_count(),
codec.parity_count(),
stripe_payload_limit,
total_length,
max_object_bytes,
codec.max_encoded_bytes,
)
}
///|
pub fn Manifest::set_id(self : Manifest) -> Int {
self.set_id
}
///|
pub fn Manifest::data_count(self : Manifest) -> Int {
self.data_count
}
///|
pub fn Manifest::parity_count(self : Manifest) -> Int {
self.parity_count
}
///|
pub fn Manifest::stripe_count(self : Manifest) -> Int {
self.stripe_count
}
///|
pub fn Manifest::total_length(self : Manifest) -> Int {
self.total_length
}
///|
pub fn Manifest::stripe_payload_limit(self : Manifest) -> Int {
self.stripe_payload_limit
}
///|
pub fn Manifest::stripe_length(
self : Manifest,
index : Int,
) -> Int raise ErasureError {
if index < 0 || index >= self.stripe_count {
raise InvalidIndex(index)
}
let remaining = self.total_length - index * self.stripe_payload_limit
if remaining > self.stripe_payload_limit {
self.stripe_payload_limit
} else {
remaining
}
}
///|
/// 28 bytes of metadata followed by CRC32C of those 28 bytes.
pub fn Manifest::to_bytes(self : Manifest) -> Bytes {
let output : Array[Byte] = [b'M', b'E', b'R', b'M', b'\x01', b'\x00']
write_u16(output, 32)
write_u32(output, self.set_id.reinterpret_as_uint())
write_u16(output, self.data_count)
write_u16(output, self.parity_count)
write_u32(output, self.stripe_payload_limit.reinterpret_as_uint())
write_u32(output, self.stripe_count.reinterpret_as_uint())
write_u32(output, self.total_length.reinterpret_as_uint())
write_u32(output, crc32c(Bytes::from_array(output)))
Bytes::from_array(output)
}
///|
pub fn Manifest::from_bytes(
input : Bytes,
max_object_bytes? : Int = 67_108_864,
max_encoded_bytes? : Int = 16_777_216,
) -> Manifest raise ErasureError {
if input.length() != 32 {
raise InvalidManifest("manifest must be exactly 32 bytes")
}
if input[0] != b'M' ||
input[1] != b'E' ||
input[2] != b'R' ||
input[3] != b'M' {
raise InvalidManifest("bad manifest magic")
}
let version = input[4].to_int()
if version != 1 {
raise UnsupportedVersion(version)
}
if input[5] != b'\x00' || read_u16(input, 6) != 32 {
raise InvalidManifest("invalid manifest flags or length")
}
let actual = crc32c(Bytes::from_array(input[0:28].to_array()))
if read_u32(input, 28) != actual {
raise ChecksumMismatch(-1)
}
let set_id = read_bounded_int(input, 8)
let data_count = read_u16(input, 12)
let parity_count = read_u16(input, 14)
let stripe_payload_limit = read_bounded_int(input, 16)
let stored_stripe_count = read_bounded_int(input, 20)
let total_length = read_bounded_int(input, 24)
let manifest = checked_manifest(
set_id, data_count, parity_count, stripe_payload_limit, total_length, max_object_bytes,
max_encoded_bytes,
)
if manifest.stripe_count != stored_stripe_count {
raise InvalidManifest("stored stripe count disagrees with lengths")
}
manifest
}