///|
fn apply_error(
kind : ErrorKind,
stage : ErrorStage,
field : String,
value : Int64?,
limit : Int64?,
) -> BsdiffError {
format_error(
kind,
stage,
Some(
match stage {
Control => "control"
Diff => "diff"
Extra => "extra"
_ => "output"
},
),
None,
Some(field),
value,
limit,
)
}
///|
fn cursor_add(
a : Int64,
b : Int64,
field : String,
) -> Result[Int64, BsdiffError] {
if (b > 0L && a > 9223372036854775807L - b) ||
(b < 0L && a < -9223372036854775807L - 1L - b) {
Err(apply_error(IntegerOverflow, Control, field, Some(a), None))
} else {
Ok(a + b)
}
}
///|
/// Apply one strict BSDIFF40 patch using explicit resource limits.
/// The caller is responsible for verifying old/output file identities.
pub fn apply(
old_bytes : Bytes,
patch_bytes : Bytes,
limits : Limits,
) -> Result[Bytes, BsdiffError] {
match limits.validate() {
Err(e) => return Err(e)
Ok(_) => ()
}
if old_bytes.length().to_int64() > limits.max_old_bytes {
return Err(
format_error(
ResourceLimitExceeded,
Header,
None,
None,
Some("max_old_bytes"),
Some(old_bytes.length().to_int64()),
Some(limits.max_old_bytes),
),
)
}
let ranges = match parse_header(patch_bytes, limits) {
Err(e) => return Err(e)
Ok(r) => r
}
if ranges.new_size > limits.max_new_bytes {
return Err(
format_error(
ResourceLimitExceeded,
Header,
None,
Some(24L),
Some("max_new_bytes"),
Some(ranges.new_size),
Some(limits.max_new_bytes),
),
)
}
let budget = match decompression_budget(limits) {
Err(e) => return Err(e)
Ok(b) => b
}
let control_cap = if limits.max_control_instructions > 2147483647L / 24L {
2147483647L
} else {
limits.max_control_instructions * 24L
}
let control = match
decompress_block(
patch_bytes[ranges.control_start:ranges.control_end].to_owned(),
control_cap,
budget,
Control,
ranges.control_start.to_int64(),
) {
Err(e) => return Err(e)
Ok(b) => b
}
let diff = match
decompress_block(
patch_bytes[ranges.control_end:ranges.diff_end].to_owned(),
ranges.new_size,
budget,
Diff,
ranges.control_end.to_int64(),
) {
Err(e) => return Err(e)
Ok(b) => b
}
let extra = match
decompress_block(
patch_bytes[ranges.diff_end:ranges.extra_end].to_owned(),
ranges.new_size,
budget,
Extra,
ranges.diff_end.to_int64(),
) {
Err(e) => return Err(e)
Ok(b) => b
}
if control.length() % 24 != 0 {
return Err(
apply_error(
TruncatedData,
Control,
"control_decoded_alignment",
Some(control.length().to_int64()),
Some(24L),
),
)
}
if (control.length() / 24).to_int64() > limits.max_control_instructions {
return Err(
apply_error(
ResourceLimitExceeded,
Control,
"max_control_instructions",
Some((control.length() / 24).to_int64()),
Some(limits.max_control_instructions),
),
)
}
let size = ranges.new_size.to_int()
let output = Array::make(size, b'\x00')
let mut produced = 0
let mut old_pos = 0L
let mut diff_pos = 0
let mut extra_pos = 0
for pos = 0; pos < control.length(); pos = pos + 24 {
if produced == size {
return Err(
apply_error(
InvalidField,
Control,
"unconsumed_control",
Some(pos.to_int64()),
None,
),
)
}
let x = match decode_int(control, pos, Control) {
Err(e) => return Err(e)
Ok(v) => v
}
let y = match decode_int(control, pos + 8, Control) {
Err(e) => return Err(e)
Ok(v) => v
}
let z = match decode_int(control, pos + 16, Control) {
Err(e) => return Err(e)
Ok(v) => v
}
let field = "control_decoded_byte[\{pos}]"
if x < 0L {
return Err(
apply_error(InvalidField, Control, field + ".x", Some(x), None),
)
}
if y < 0L {
return Err(
apply_error(InvalidField, Control, field + ".y", Some(y), None),
)
}
if x > (size - produced).to_int64() {
return Err(
apply_error(
OutputOutOfBounds,
Control,
field + ".x",
Some(x),
Some((size - produced).to_int64()),
),
)
}
if y > (size - produced).to_int64() - x {
return Err(
apply_error(
OutputOutOfBounds,
Control,
field + ".y",
Some(y),
Some((size - produced).to_int64() - x),
),
)
}
if x > (diff.length() - diff_pos).to_int64() {
return Err(
apply_error(
TruncatedData,
Diff,
"diff_decoded_consumption",
Some(x),
Some((diff.length() - diff_pos).to_int64()),
),
)
}
if y > (extra.length() - extra_pos).to_int64() {
return Err(
apply_error(
TruncatedData,
Extra,
"extra_decoded_consumption",
Some(y),
Some((extra.length() - extra_pos).to_int64()),
),
)
}
let after_x = match cursor_add(old_pos, x, field + ".old_plus_x") {
Err(e) => return Err(e)
Ok(v) => v
}
let next_old = match cursor_add(after_x, z, field + ".old_plus_z") {
Err(e) => return Err(e)
Ok(v) => v
}
for i in 0..= 0L && at < old_bytes.length().to_int64() {
old_bytes[at.to_int()].to_int()
} else {
0
}
output[produced + i] = ((diff[diff_pos + i].to_int() + contribution) % 256).to_byte()
}
produced = produced + x.to_int()
diff_pos = diff_pos + x.to_int()
for i in 0..