///|
/// Recover only the stripes overlapping a byte range. Callers may supply
/// frames for the whole object or only for the requested stripes. Damage in
/// unrelated stripes does not block a range read.
pub fn recover_object_range(
manifest : Manifest,
frames : Array[ShardEnvelope],
start : Int,
length : Int,
max_encoded_bytes? : Int = 16_777_216,
) -> Bytes raise ErasureError {
if start < 0 ||
length < 0 ||
start > manifest.total_length() ||
length > manifest.total_length() - start {
raise InvalidRange
}
if length == 0 {
return b""
}
let codec = Codec::new(
manifest.data_count(),
manifest.parity_count(),
max_encoded_bytes~,
)
if manifest.stripe_payload_limit() >
codec.data_count() * codec.max_shard_bytes() {
raise ResourceLimit("codec budget cannot hold manifest stripe")
}
let first = start / manifest.stripe_payload_limit()
let last = (start + length - 1) / manifest.stripe_payload_limit()
let groups : Array[Array[ShardEnvelope]] = []
for index = first; index <= last; index = index + 1 {
groups.push([])
}
for frame in frames {
if frame.set_id() != manifest.set_id() {
raise InvalidManifest("range frame set id differs from manifest")
}
let stripe_index = frame.stripe_index()
if stripe_index < 0 || stripe_index >= manifest.stripe_count() {
raise InvalidIndex(stripe_index)
}
if stripe_index >= first && stripe_index <= last {
groups[stripe_index - first].push(frame)
}
}
let output = Array::make(length, b'\x00')
for stripe_index = first
stripe_index <= last
stripe_index = stripe_index + 1 {
let recovered = recover_stripe(
codec,
groups[stripe_index - first],
manifest.set_id(),
stripe_index,
)
let stripe_payload = recovered.payload()
let stripe_start = stripe_index * manifest.stripe_payload_limit()
if stripe_payload.length() != manifest.stripe_length(stripe_index) {
raise InvalidManifest("range stripe length differs from manifest")
}
let mut copy_start = start
if stripe_start > copy_start {
copy_start = stripe_start
}
let mut copy_end = start + length
if stripe_start + stripe_payload.length() < copy_end {
copy_end = stripe_start + stripe_payload.length()
}
for position = copy_start; position < copy_end; position = position + 1 {
output[position - start] = stripe_payload[position - stripe_start]
}
}
Bytes::from_array(output)
}