extern crate alloc;
use alloc::vec::Vec;
#[cfg(creusot)]
use ::creusot_std::macros::variant;
#[cfg(creusot)]
use ::creusot_std::prelude::Int;
pub const HAVE_SET_WIRE_V1: u8 = 0x01;
pub const HAVE_SET_WIRE_HEADER_LEN: usize = 5;
pub const HAVE_SET_WIRE_STATION_HEADER_LEN: usize = 16;
pub const HAVE_SET_WIRE_RUN_LEN: usize = 16;
#[cfg_attr(not(creusot), derive(Debug, Clone, Copy, PartialEq, Eq))]
pub enum PureDecodeError {
UnknownVersion(u8),
UnexpectedLength {
expected: usize,
found: usize,
},
EmptyStation {
station: u32,
},
NonAscendingStations {
previous: u32,
found: u32,
},
ZeroLengthRun {
station: u32,
},
NonMaximalRun {
station: u32,
start: u64,
},
RunOverflow {
station: u32,
start: u64,
len: u64,
},
RunTooLong {
station: u32,
start: u64,
len: u64,
},
}
#[cfg_attr(not(creusot), derive(Debug, Clone, PartialEq, Eq))]
pub struct PureDecoded {
pub stations: Vec<(u32, u64)>,
pub runs: Vec<(u32, u64, u64)>,
pub consumed: usize,
}
#[cfg_attr(creusot, ::creusot_std::macros::check(terminates))]
#[cfg_attr(creusot, ::creusot_std::macros::requires(at@ + 4 <= bytes@.len()))]
const fn be_u32(bytes: &[u8], at: usize) -> u32 {
(bytes[at] as u32) * 0x0100_0000
+ (bytes[at + 1] as u32) * 0x0001_0000
+ (bytes[at + 2] as u32) * 0x0000_0100
+ (bytes[at + 3] as u32)
}
#[cfg_attr(creusot, ::creusot_std::macros::check(terminates))]
#[cfg_attr(creusot, ::creusot_std::macros::requires(at@ + 8 <= bytes@.len()))]
const fn be_u64(bytes: &[u8], at: usize) -> u64 {
(bytes[at] as u64) * 0x0100_0000_0000_0000
+ (bytes[at + 1] as u64) * 0x0001_0000_0000_0000
+ (bytes[at + 2] as u64) * 0x0000_0100_0000_0000
+ (bytes[at + 3] as u64) * 0x0000_0001_0000_0000
+ (bytes[at + 4] as u64) * 0x0000_0000_0100_0000
+ (bytes[at + 5] as u64) * 0x0000_0000_0001_0000
+ (bytes[at + 6] as u64) * 0x0000_0000_0000_0100
+ (bytes[at + 7] as u64)
}
#[cfg_attr(creusot, ::creusot_std::macros::check(terminates))]
const fn expected_at(pos: usize, need: usize) -> usize {
if pos > usize::MAX - need {
usize::MAX
} else {
pos + need
}
}
pub enum RunBlock {
Advanced(usize, u64),
Refused(PureDecodeError),
}
#[cfg_attr(creusot, ::creusot_std::macros::check(terminates))]
#[cfg_attr(creusot, ::creusot_std::macros::requires(pos@ <= bytes@.len()))]
#[cfg_attr(creusot, ::creusot_std::macros::requires(
forall<k: Int> 0 <= k && k < (*runs)@.len() ==> (*runs)@[k].2@ >= 1
))]
#[cfg_attr(creusot, ::creusot_std::macros::ensures(
forall<k: Int> 0 <= k && k < (^runs)@.len() ==> (^runs)@[k].2@ >= 1
))]
#[cfg_attr(creusot, ::creusot_std::macros::ensures(match result {
RunBlock::Advanced(new_pos, _) => new_pos@ <= bytes@.len(),
RunBlock::Refused(_) => true,
}))]
fn decode_station_runs(
bytes: &[u8],
pos: usize,
station: u32,
floor: u64,
run_count: u32,
dot_budget: u64,
runs: &mut Vec<(u32, u64, u64)>,
) -> RunBlock {
let mut pos = pos;
let mut dot_budget = dot_budget;
let mut min_start: Option<u64> = if floor > u64::MAX - 2 {
None
} else {
Some(floor + 2)
};
let mut r: u32 = 0;
#[cfg_attr(creusot, variant(run_count@ - r@))]
#[cfg_attr(creusot, invariant(pos@ <= bytes@.len()))]
#[cfg_attr(creusot, invariant(
forall<k: Int> 0 <= k && k < runs@.len() ==> runs@[k].2@ >= 1
))]
while r < run_count {
if bytes.len() - pos < HAVE_SET_WIRE_RUN_LEN {
return RunBlock::Refused(PureDecodeError::UnexpectedLength {
expected: expected_at(pos, HAVE_SET_WIRE_RUN_LEN),
found: bytes.len(),
});
}
let start = be_u64(bytes, pos);
let len = be_u64(bytes, pos + 8);
pos += HAVE_SET_WIRE_RUN_LEN;
if len == 0 {
return RunBlock::Refused(PureDecodeError::ZeroLengthRun { station });
}
if len > dot_budget {
return RunBlock::Refused(PureDecodeError::RunTooLong {
station,
start,
len,
});
}
dot_budget -= len;
match min_start {
Some(bound) if start >= bound => {}
_ => {
return RunBlock::Refused(PureDecodeError::NonMaximalRun { station, start });
}
}
if len - 1 > u64::MAX - start {
return RunBlock::Refused(PureDecodeError::RunOverflow {
station,
start,
len,
});
}
let last = start + (len - 1);
min_start = if last > u64::MAX - 2 {
None
} else {
Some(last + 2)
};
runs.push((station, start, len));
r += 1;
}
RunBlock::Advanced(pos, dot_budget)
}
#[cfg_attr(creusot, ::creusot_std::macros::check(terminates))]
#[cfg_attr(creusot, ::creusot_std::macros::ensures(match result {
Ok(d) => d.consumed@ <= bytes@.len()
&& (forall<i: Int, j: Int> 0 <= i && i < j && j < d.stations@.len()
==> d.stations@[i].0@ < d.stations@[j].0@)
&& (forall<k: Int> 0 <= k && k < d.runs@.len() ==> d.runs@[k].2@ >= 1),
Err(_) => true,
}))]
pub fn decode_prefix_with_budget(
bytes: &[u8],
dot_budget: u64,
) -> Result<PureDecoded, PureDecodeError> {
if bytes.len() < HAVE_SET_WIRE_HEADER_LEN {
return Err(PureDecodeError::UnexpectedLength {
expected: HAVE_SET_WIRE_HEADER_LEN,
found: bytes.len(),
});
}
let version = bytes[0];
if version != HAVE_SET_WIRE_V1 {
return Err(PureDecodeError::UnknownVersion(version));
}
let station_count = be_u32(bytes, 1);
let mut pos = HAVE_SET_WIRE_HEADER_LEN;
let mut stations: Vec<(u32, u64)> = Vec::new();
let mut runs: Vec<(u32, u64, u64)> = Vec::new();
let mut previous_station: Option<u32> = None;
let mut dot_budget = dot_budget;
let mut s: u32 = 0;
#[cfg_attr(creusot, variant(station_count@ - s@))]
#[cfg_attr(creusot, invariant(pos@ <= bytes@.len()))]
#[cfg_attr(creusot, invariant(
forall<i: Int, j: Int> 0 <= i && i < j && j < stations@.len()
==> stations@[i].0@ < stations@[j].0@
))]
#[cfg_attr(creusot, invariant(match previous_station {
Some(previous) => forall<i: Int> 0 <= i && i < stations@.len()
==> stations@[i].0@ <= previous@,
None => stations@.len() == 0,
}))]
#[cfg_attr(creusot, invariant(
forall<k: Int> 0 <= k && k < runs@.len() ==> runs@[k].2@ >= 1
))]
while s < station_count {
if bytes.len() - pos < HAVE_SET_WIRE_STATION_HEADER_LEN {
return Err(PureDecodeError::UnexpectedLength {
expected: expected_at(pos, HAVE_SET_WIRE_STATION_HEADER_LEN),
found: bytes.len(),
});
}
let station = be_u32(bytes, pos);
let floor = be_u64(bytes, pos + 4);
let run_count = be_u32(bytes, pos + 12);
pos += HAVE_SET_WIRE_STATION_HEADER_LEN;
if let Some(previous) = previous_station
&& station <= previous
{
return Err(PureDecodeError::NonAscendingStations {
previous,
found: station,
});
}
previous_station = Some(station);
if floor == 0 && run_count == 0 {
return Err(PureDecodeError::EmptyStation { station });
}
stations.push((station, floor));
match decode_station_runs(bytes, pos, station, floor, run_count, dot_budget, &mut runs) {
RunBlock::Refused(error) => return Err(error),
RunBlock::Advanced(new_pos, new_budget) => {
pos = new_pos;
dot_budget = new_budget;
}
}
s += 1;
}
Ok(PureDecoded {
stations,
runs,
consumed: pos,
})
}