///|
/// A complete repaired stripe plus information about which shards were absent.
pub struct StripeRecovery {
payload : Bytes
shards : Array[ShardEnvelope]
missing_indices : Array[Int]
provided_count : Int
}
///|
pub fn StripeRecovery::payload(self : StripeRecovery) -> Bytes {
copy_shard(self.payload)
}
///|
pub fn StripeRecovery::shards(self : StripeRecovery) -> Array[ShardEnvelope] {
let result : Array[ShardEnvelope] = []
for shard in self.shards {
result.push(shard)
}
result
}
///|
pub fn StripeRecovery::missing_indices(self : StripeRecovery) -> Array[Int] {
let result : Array[Int] = []
for index in self.missing_indices {
result.push(index)
}
result
}
///|
pub fn StripeRecovery::provided_count(self : StripeRecovery) -> Int {
self.provided_count
}
///|
/// Split one nonempty payload into equal padded data shards and encode parity.
pub fn encode_stripe(
codec : Codec,
payload : Bytes,
set_id : Int,
stripe_index : Int,
) -> Array[ShardEnvelope] raise ErasureError {
if payload.length() < 1 {
raise EmptyShard
}
if payload.length() > codec.data_count() * codec.max_shard_bytes() {
raise ResourceLimit("payload exceeds one stripe's data capacity")
}
let mut length = payload.length() / codec.data_count()
if payload.length() % codec.data_count() != 0 {
length = length + 1
}
let data : Array[Bytes] = []
for shard_index = 0
shard_index < codec.data_count()
shard_index = shard_index + 1 {
let bytes = Array::make(length, b'\x00')
for offset = 0; offset < length; offset = offset + 1 {
let position = shard_index * length + offset
if position < payload.length() {
bytes[offset] = payload[position]
}
}
data.push(Bytes::from_array(bytes))
}
let all = codec.encode_all(data)
let frames : Array[ShardEnvelope] = []
for shard_index = 0; shard_index < all.length(); shard_index = shard_index + 1 {
frames.push(
ShardEnvelope::new(
codec,
set_id,
stripe_index,
shard_index,
payload.length(),
all[shard_index],
),
)
}
frames
}
///|
/// Recover a stripe from validated envelopes. Duplicate indices, mixed
/// identities, mismatched sizes and inconsistent extra shards are rejected.
pub fn recover_stripe(
codec : Codec,
frames : Array[ShardEnvelope],
set_id : Int,
stripe_index : Int,
) -> StripeRecovery raise ErasureError {
if set_id < 0 || stripe_index < 0 {
raise InvalidEnvelope("set and stripe ids must be nonnegative")
}
let present : Array[Bytes?] = Array::make(codec.total_count(), None)
let mut original_length = -1
let mut shard_length = -1
for frame in frames {
if frame.set_id() != set_id || frame.stripe_index() != stripe_index {
raise InvalidEnvelope("frame belongs to another set or stripe")
}
if frame.data_count() != codec.data_count() ||
frame.parity_count() != codec.parity_count() {
raise InvalidEnvelope("frame codec dimensions do not match")
}
let index = frame.shard_index()
if index < 0 || index >= codec.total_count() {
raise InvalidIndex(index)
}
if present[index] is Some(_) {
raise DuplicateIndex(index)
}
if original_length == -1 {
original_length = frame.original_length()
shard_length = frame.payload.length()
} else if frame.original_length() != original_length ||
frame.payload.length() != shard_length {
raise InvalidEnvelope("frame lengths disagree within stripe")
}
present[index] = Some(frame.payload())
}
if frames.length() < codec.data_count() {
raise NotEnoughShards(
required=codec.data_count(),
available=frames.length(),
)
}
if original_length < 1 || original_length > codec.data_count() * shard_length {
raise InvalidEnvelope("invalid stripe original length")
}
let missing_indices : Array[Int] = []
for index = 0; index < present.length(); index = index + 1 {
if present[index] is None {
missing_indices.push(index)
}
}
let rebuilt = codec.reconstruct(present)
for position = original_length
position < codec.data_count() * shard_length
position = position + 1 {
if rebuilt[position / shard_length][position % shard_length] != b'\x00' {
raise InvalidEnvelope("nonzero bytes in padded data tail")
}
}
let payload = Array::make(original_length, b'\x00')
for position = 0; position < original_length; position = position + 1 {
payload[position] = rebuilt[position / shard_length][position % shard_length]
}
let envelopes : Array[ShardEnvelope] = []
for index = 0; index < rebuilt.length(); index = index + 1 {
envelopes.push(
ShardEnvelope::new(
codec,
set_id,
stripe_index,
index,
original_length,
rebuilt[index],
),
)
}
{
payload: Bytes::from_array(payload),
shards: envelopes,
missing_indices,
provided_count: frames.length(),
}
}