extern crate alloc;
use alloc::vec::Vec;
#[cfg(creusot)]
use ::creusot_std::macros::variant;
#[cfg(creusot)]
use ::creusot_std::prelude::Int;
pub const SNAPSHOT_WIRE_V1: u8 = 0x01;
pub const SNAPSHOT_WIRE_HEADER_LEN: usize = 9;
pub const SNAPSHOT_WIRE_RUN_HEADER_LEN: usize = 46;
pub const SNAPSHOT_WIRE_STEP_LEN: usize = 6;
pub const SNAPSHOT_RANK_FRAME_LEN: usize = 17;
pub const RANK_WIRE_V1: u8 = 0x01;
pub const ANCHOR_TAG_ORIGIN: u8 = 0x00;
pub const ANCHOR_TAG_AFTER: u8 = 0x01;
pub const ANCHOR_TAG_BEFORE: u8 = 0x02;
pub const SNAPSHOT_WIRE_FREE_PREFIX_LEN: usize = 13;
pub const SNAPSHOT_WIRE_ANCHOR_LEN: usize = 12;
pub const SNAPSHOT_WIRE_FREE_MIN_LEN: usize =
SNAPSHOT_WIRE_FREE_PREFIX_LEN + SNAPSHOT_RANK_FRAME_LEN;
pub const MAX_RANK_STEP: u64 = 0xFFFF_FFFF_FFFF;
#[cfg_attr(not(creusot), derive(Debug, Clone, Copy, PartialEq, Eq))]
pub enum PureSnapshotError {
UnknownVersion(u8),
UnexpectedLength {
expected: usize,
found: usize,
},
ZeroDot {
station: u32,
},
RunTooShort {
station: u32,
base: u64,
},
RunOverflow {
station: u32,
base: u64,
len: u64,
},
BadAnchorTag {
tag: u8,
},
NonCanonicalOriginAnchor {
station: u32,
index: u64,
},
BadRankVersion(u8),
RankOverflow {
station: u32,
index: u64,
},
NonCanonicalRankStep {
station: u32,
index: u64,
},
NonAscendingLoci {
previous_station: u32,
previous_index: u64,
found_station: u32,
found_index: u64,
},
SplitChain {
station: u32,
index: u64,
},
}
#[cfg_attr(not(creusot), derive(Debug, Clone, Copy, PartialEq, Eq))]
pub struct PureLocus {
pub station: u32,
pub index: u64,
pub anchor_tag: u8,
pub anchor_station: u32,
pub anchor_index: u64,
pub physical: u64,
pub logical: u16,
pub kairotic: u16,
pub rank_station: u32,
}
#[cfg_attr(not(creusot), derive(Debug, Clone, Copy, PartialEq, Eq))]
pub struct RunRow {
pub head: PureLocus,
pub len: u64,
}
#[cfg_attr(not(creusot), derive(Debug, Clone, PartialEq, Eq))]
pub struct PureSnapshot {
pub runs: Vec<PureLocus>,
pub free: Vec<PureLocus>,
pub consumed: usize,
}
#[cfg_attr(creusot, ::creusot_std::macros::check(terminates))]
#[cfg_attr(creusot, ::creusot_std::macros::requires(at@ + 2 <= bytes@.len()))]
const fn be_u16(bytes: &[u8], at: usize) -> u16 {
(bytes[at] as u16) * 0x0100 + (bytes[at + 1] as u16)
}
#[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@ + 4 <= bytes@.len()))]
const fn be_u32_widened(bytes: &[u8], at: usize) -> u64 {
(bytes[at] as u64) * 0x0100_0000
+ (bytes[at + 1] as u64) * 0x0001_0000
+ (bytes[at + 2] as u64) * 0x0000_0100
+ (bytes[at + 3] as u64)
}
#[cfg_attr(creusot, ::creusot_std::macros::check(terminates))]
#[cfg_attr(creusot, ::creusot_std::macros::requires(at@ + 6 <= bytes@.len()))]
const fn be_u48(bytes: &[u8], at: usize) -> u64 {
(bytes[at] as u64) * 0x0100_0000_0000
+ (bytes[at + 1] as u64) * 0x0001_0000_0000
+ (bytes[at + 2] as u64) * 0x0000_0100_0000
+ (bytes[at + 3] as u64) * 0x0000_0001_0000
+ (bytes[at + 4] as u64) * 0x0000_0000_0100
+ (bytes[at + 5] as u64)
}
#[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
}
}
#[cfg_attr(creusot, ::creusot_std::macros::check(terminates))]
#[must_use]
pub const fn rank_successor(physical: u64, logical: u16) -> (u64, u16) {
let bumped = logical.saturating_add(1);
if bumped == u16::MAX {
(physical.saturating_add(1), 0)
} else {
(physical, bumped)
}
}
#[cfg_attr(creusot, ::creusot_std::macros::check(terminates))]
#[must_use]
pub const fn chainable(prev: &PureLocus, next: &PureLocus) -> bool {
if next.anchor_tag != ANCHOR_TAG_AFTER {
return false;
}
if next.anchor_station != prev.station {
return false;
}
if next.anchor_index != prev.index {
return false;
}
if next.kairotic != prev.kairotic {
return false;
}
if next.rank_station != prev.rank_station {
return false;
}
let (successor_physical, successor_logical) = rank_successor(prev.physical, prev.logical);
if next.physical == successor_physical && next.logical == successor_logical {
return true;
}
next.logical == 0
&& next.physical > prev.physical
&& next.physical - prev.physical <= MAX_RANK_STEP
}
pub enum Cursor {
Advanced(usize),
Refused(PureSnapshotError),
}
#[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<i: Int> 0 <= i && i < (*rows)@.len() ==>
(*rows)@[i].len@ >= 2
&& (*rows)@[i].head.index@ >= 1
&& (*rows)@[i].head.index@ + (*rows)@[i].len@ <= 18446744073709551616
))]
#[cfg_attr(creusot, ::creusot_std::macros::ensures(
forall<i: Int> 0 <= i && i < (^rows)@.len() ==>
(^rows)@[i].len@ >= 2
&& (^rows)@[i].head.index@ >= 1
&& (^rows)@[i].head.index@ + (^rows)@[i].len@ <= 18446744073709551616
))]
#[cfg_attr(creusot, ::creusot_std::macros::ensures(match result {
Cursor::Advanced(new_pos) => new_pos@ <= bytes@.len(),
Cursor::Refused(_) => true,
}))]
fn decode_rows(bytes: &[u8], pos: usize, run_count: u32, rows: &mut Vec<RunRow>) -> Cursor {
let mut pos = pos;
let mut r: u32 = 0;
#[cfg_attr(creusot, variant(run_count@ - r@))]
#[cfg_attr(creusot, invariant(pos@ <= bytes@.len()))]
#[cfg_attr(creusot, invariant(
forall<i: Int> 0 <= i && i < rows@.len() ==>
rows@[i].len@ >= 2
&& rows@[i].head.index@ >= 1
&& rows@[i].head.index@ + rows@[i].len@ <= 18446744073709551616
))]
while r < run_count {
if bytes.len() - pos < SNAPSHOT_WIRE_RUN_HEADER_LEN {
return Cursor::Refused(PureSnapshotError::UnexpectedLength {
expected: expected_at(pos, SNAPSHOT_WIRE_RUN_HEADER_LEN),
found: bytes.len(),
});
}
let station = be_u32(bytes, pos);
let base = be_u64(bytes, pos + 4);
let len = be_u32_widened(bytes, pos + 12);
let anchor_tag = bytes[pos + 16];
let anchor_station = be_u32(bytes, pos + 17);
let anchor_index = be_u64(bytes, pos + 21);
let rank_version = bytes[pos + 29];
let physical = be_u64(bytes, pos + 30);
let logical = be_u16(bytes, pos + 38);
let kairotic = be_u16(bytes, pos + 40);
let rank_station = be_u32(bytes, pos + 42);
pos += SNAPSHOT_WIRE_RUN_HEADER_LEN;
if base == 0 {
return Cursor::Refused(PureSnapshotError::ZeroDot { station });
}
if len < 2 {
return Cursor::Refused(PureSnapshotError::RunTooShort { station, base });
}
if len - 1 > u64::MAX - base {
return Cursor::Refused(PureSnapshotError::RunOverflow { station, base, len });
}
if anchor_tag > ANCHOR_TAG_BEFORE {
return Cursor::Refused(PureSnapshotError::BadAnchorTag { tag: anchor_tag });
}
if anchor_tag == ANCHOR_TAG_ORIGIN && (anchor_station != 0 || anchor_index != 0) {
return Cursor::Refused(PureSnapshotError::NonCanonicalOriginAnchor {
station,
index: base,
});
}
if rank_version != RANK_WIRE_V1 {
return Cursor::Refused(PureSnapshotError::BadRankVersion(rank_version));
}
rows.push(RunRow {
head: PureLocus {
station,
index: base,
anchor_tag,
anchor_station,
anchor_index,
physical,
logical,
kairotic,
rank_station,
},
len,
});
r += 1;
}
Cursor::Advanced(pos)
}
#[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<i: Int> 0 <= i && i < rows@.len() ==>
rows@[i].len@ >= 2
&& rows@[i].head.index@ >= 1
&& rows@[i].head.index@ + rows@[i].len@ <= 18446744073709551616
))]
#[cfg_attr(creusot, ::creusot_std::macros::ensures(match result {
Cursor::Advanced(new_pos) => new_pos@ <= bytes@.len(),
Cursor::Refused(_) => true,
}))]
fn decode_columns(bytes: &[u8], pos: usize, rows: &[RunRow], out: &mut Vec<PureLocus>) -> Cursor {
let mut pos = pos;
let mut ri: usize = 0;
#[cfg_attr(creusot, variant(rows@.len() - ri@))]
#[cfg_attr(creusot, invariant(pos@ <= bytes@.len()))]
#[cfg_attr(creusot, invariant(ri@ <= rows@.len()))]
while ri < rows.len() {
let row = &rows[ri];
let station = row.head.station;
let base = row.head.index;
out.push(PureLocus {
station,
index: base,
anchor_tag: row.head.anchor_tag,
anchor_station: row.head.anchor_station,
anchor_index: row.head.anchor_index,
physical: row.head.physical,
logical: row.head.logical,
kairotic: row.head.kairotic,
rank_station: row.head.rank_station,
});
let mut previous_physical = row.head.physical;
let mut previous_logical = row.head.logical;
let len = row.len;
let mut k: u64 = 1;
#[cfg_attr(creusot, variant(len@ - k@))]
#[cfg_attr(creusot, invariant(pos@ <= bytes@.len()))]
#[cfg_attr(creusot, invariant(1 <= k@))]
while k < len {
if bytes.len() - pos < SNAPSHOT_WIRE_STEP_LEN {
return Cursor::Refused(PureSnapshotError::UnexpectedLength {
expected: expected_at(pos, SNAPSHOT_WIRE_STEP_LEN),
found: bytes.len(),
});
}
let step = be_u48(bytes, pos);
pos += SNAPSHOT_WIRE_STEP_LEN;
let index = base + k;
let (physical, logical) = if step == 0 {
rank_successor(previous_physical, previous_logical)
} else {
if step > u64::MAX - previous_physical {
return Cursor::Refused(PureSnapshotError::RankOverflow { station, index });
}
let advanced = previous_physical + step;
let (successor_physical, successor_logical) =
rank_successor(previous_physical, previous_logical);
if advanced == successor_physical && successor_logical == 0 {
return Cursor::Refused(PureSnapshotError::NonCanonicalRankStep {
station,
index,
});
}
(advanced, 0)
};
out.push(PureLocus {
station,
index,
anchor_tag: ANCHOR_TAG_AFTER,
anchor_station: station,
anchor_index: index - 1,
physical,
logical,
kairotic: row.head.kairotic,
rank_station: row.head.rank_station,
});
previous_physical = physical;
previous_logical = logical;
k += 1;
}
ri += 1;
}
Cursor::Advanced(pos)
}
#[cfg_attr(creusot, ::creusot_std::macros::check(terminates))]
#[cfg_attr(creusot, ::creusot_std::macros::requires(pos@ <= bytes@.len()))]
#[cfg_attr(creusot, ::creusot_std::macros::ensures(match result {
Cursor::Advanced(new_pos) => new_pos@ <= bytes@.len(),
Cursor::Refused(_) => true,
}))]
fn decode_free(bytes: &[u8], pos: usize, free_count: u32, out: &mut Vec<PureLocus>) -> Cursor {
let mut pos = pos;
let mut f: u32 = 0;
#[cfg_attr(creusot, variant(free_count@ - f@))]
#[cfg_attr(creusot, invariant(pos@ <= bytes@.len()))]
while f < free_count {
if bytes.len() - pos < SNAPSHOT_WIRE_FREE_PREFIX_LEN {
return Cursor::Refused(PureSnapshotError::UnexpectedLength {
expected: expected_at(pos, SNAPSHOT_WIRE_FREE_MIN_LEN),
found: bytes.len(),
});
}
let station = be_u32(bytes, pos);
let index = be_u64(bytes, pos + 4);
let anchor_tag = bytes[pos + 12];
pos += SNAPSHOT_WIRE_FREE_PREFIX_LEN;
if index == 0 {
return Cursor::Refused(PureSnapshotError::ZeroDot { station });
}
let mut anchor_station: u32 = 0;
let mut anchor_index: u64 = 0;
if anchor_tag == ANCHOR_TAG_AFTER || anchor_tag == ANCHOR_TAG_BEFORE {
if bytes.len() - pos < SNAPSHOT_WIRE_ANCHOR_LEN {
return Cursor::Refused(PureSnapshotError::UnexpectedLength {
expected: expected_at(pos, SNAPSHOT_WIRE_ANCHOR_LEN + SNAPSHOT_RANK_FRAME_LEN),
found: bytes.len(),
});
}
anchor_station = be_u32(bytes, pos);
anchor_index = be_u64(bytes, pos + 4);
pos += SNAPSHOT_WIRE_ANCHOR_LEN;
} else if anchor_tag != ANCHOR_TAG_ORIGIN {
return Cursor::Refused(PureSnapshotError::BadAnchorTag { tag: anchor_tag });
}
if bytes.len() - pos < SNAPSHOT_RANK_FRAME_LEN {
return Cursor::Refused(PureSnapshotError::UnexpectedLength {
expected: expected_at(pos, SNAPSHOT_RANK_FRAME_LEN),
found: bytes.len(),
});
}
let rank_version = bytes[pos];
if rank_version != RANK_WIRE_V1 {
return Cursor::Refused(PureSnapshotError::BadRankVersion(rank_version));
}
let physical = be_u64(bytes, pos + 1);
let logical = be_u16(bytes, pos + 9);
let kairotic = be_u16(bytes, pos + 11);
let rank_station = be_u32(bytes, pos + 13);
pos += SNAPSHOT_RANK_FRAME_LEN;
out.push(PureLocus {
station,
index,
anchor_tag,
anchor_station,
anchor_index,
physical,
logical,
kairotic,
rank_station,
});
f += 1;
}
Cursor::Advanced(pos)
}
#[cfg_attr(creusot, ::creusot_std::macros::check(terminates))]
const fn check_merged_order(
rows: &[RunRow],
runs: &[PureLocus],
free: &[PureLocus],
) -> Option<PureSnapshotError> {
let mut run_cursor: usize = 0;
let mut free_cursor: usize = 0;
let mut remaining_in_row: u64 = 0;
let mut row_index: usize = 0;
let mut has_previous = false;
let mut previous = PureLocus {
station: 0,
index: 0,
anchor_tag: 0,
anchor_station: 0,
anchor_index: 0,
physical: 0,
logical: 0,
kairotic: 0,
rank_station: 0,
};
#[cfg_attr(creusot, variant((runs@.len() - run_cursor@) + (free@.len() - free_cursor@)))]
#[cfg_attr(creusot, invariant(run_cursor@ <= runs@.len()))]
#[cfg_attr(creusot, invariant(free_cursor@ <= free@.len()))]
while run_cursor < runs.len() || free_cursor < free.len() {
let take_run = if run_cursor >= runs.len() {
false
} else if free_cursor >= free.len() {
true
} else {
let r = &runs[run_cursor];
let f = &free[free_cursor];
r.station < f.station || (r.station == f.station && r.index <= f.index)
};
let begins_chain: bool;
let current = if take_run {
if remaining_in_row == 0 {
if row_index < rows.len() {
let declared = rows[row_index].len;
remaining_in_row = if declared < 2 { 0 } else { declared - 1 };
row_index += 1;
}
begins_chain = true;
} else {
remaining_in_row -= 1;
begins_chain = false;
}
let current = &runs[run_cursor];
run_cursor += 1;
current
} else {
begins_chain = true;
let current = &free[free_cursor];
free_cursor += 1;
current
};
if has_previous {
let ascending = previous.station < current.station
|| (previous.station == current.station && previous.index < current.index);
if !ascending {
return Some(PureSnapshotError::NonAscendingLoci {
previous_station: previous.station,
previous_index: previous.index,
found_station: current.station,
found_index: current.index,
});
}
let consecutive = previous.station == current.station
&& previous.index < u64::MAX
&& current.index == previous.index + 1;
if consecutive && begins_chain && chainable(&previous, current) {
return Some(PureSnapshotError::SplitChain {
station: current.station,
index: current.index,
});
}
}
previous = PureLocus {
station: current.station,
index: current.index,
anchor_tag: current.anchor_tag,
anchor_station: current.anchor_station,
anchor_index: current.anchor_index,
physical: current.physical,
logical: current.logical,
kairotic: current.kairotic,
rank_station: current.rank_station,
};
has_previous = true;
}
None
}
#[cfg_attr(creusot, ::creusot_std::macros::check(terminates))]
#[cfg_attr(creusot, ::creusot_std::macros::ensures(match result {
Ok(d) => d.consumed@ <= bytes@.len(),
Err(_) => true,
}))]
pub fn decode_prefix(bytes: &[u8]) -> Result<PureSnapshot, PureSnapshotError> {
if bytes.len() < SNAPSHOT_WIRE_HEADER_LEN {
return Err(PureSnapshotError::UnexpectedLength {
expected: SNAPSHOT_WIRE_HEADER_LEN,
found: bytes.len(),
});
}
let version = bytes[0];
if version != SNAPSHOT_WIRE_V1 {
return Err(PureSnapshotError::UnknownVersion(version));
}
let run_count = be_u32(bytes, 1);
let free_count = be_u32(bytes, 5);
let pos = SNAPSHOT_WIRE_HEADER_LEN;
let mut rows: Vec<RunRow> = Vec::new();
let pos = match decode_rows(bytes, pos, run_count, &mut rows) {
Cursor::Refused(error) => return Err(error),
Cursor::Advanced(new_pos) => new_pos,
};
let mut runs: Vec<PureLocus> = Vec::new();
let pos = match decode_columns(bytes, pos, &rows, &mut runs) {
Cursor::Refused(error) => return Err(error),
Cursor::Advanced(new_pos) => new_pos,
};
let mut free: Vec<PureLocus> = Vec::new();
let pos = match decode_free(bytes, pos, free_count, &mut free) {
Cursor::Refused(error) => return Err(error),
Cursor::Advanced(new_pos) => new_pos,
};
if let Some(error) = check_merged_order(&rows, &runs, &free) {
return Err(error);
}
Ok(PureSnapshot {
runs,
free,
consumed: pos,
})
}