use utils::{
collections::Vec, string::ToString, ByteReader, ByteWriter, Deserializable,
DeserializationError, Serializable,
};
const NUM_AUX_SEGMENTS: usize = 1;
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct TraceInfo {
layout: TraceLayout,
length: usize,
meta: Vec<u8>,
}
impl TraceInfo {
pub const MIN_TRACE_LENGTH: usize = 8;
pub const MAX_TRACE_WIDTH: usize = 255;
pub const MAX_META_LENGTH: usize = 65535;
pub const MAX_RAND_SEGMENT_ELEMENTS: usize = 255;
pub fn new(width: usize, length: usize) -> Self {
Self::with_meta(width, length, vec![])
}
pub fn with_meta(width: usize, length: usize, meta: Vec<u8>) -> Self {
assert!(width > 0, "trace width must be greater than 0");
let layout = TraceLayout::new(width, [0], [0]);
Self::new_multi_segment(layout, length, meta)
}
pub fn new_multi_segment(layout: TraceLayout, length: usize, meta: Vec<u8>) -> Self {
assert!(
length >= Self::MIN_TRACE_LENGTH,
"trace length must be at least {}, but was {}",
Self::MIN_TRACE_LENGTH,
length
);
assert!(
length.is_power_of_two(),
"trace length must be a power of two, but was {}",
length
);
assert!(
meta.len() <= Self::MAX_META_LENGTH,
"number of metadata bytes cannot be greater than {}, but was {}",
Self::MAX_META_LENGTH,
meta.len()
);
TraceInfo {
layout,
length,
meta,
}
}
pub fn layout(&self) -> &TraceLayout {
&self.layout
}
pub fn width(&self) -> usize {
self.layout.main_trace_width() + self.layout().aux_trace_width()
}
pub fn length(&self) -> usize {
self.length
}
pub fn meta(&self) -> &[u8] {
&self.meta
}
pub fn is_multi_segment(&self) -> bool {
self.layout.num_aux_segments > 0
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct TraceLayout {
main_segment_width: usize,
aux_segment_widths: [usize; NUM_AUX_SEGMENTS],
aux_segment_rands: [usize; NUM_AUX_SEGMENTS],
num_aux_segments: usize,
}
impl TraceLayout {
pub fn new(
main_width: usize,
aux_widths: [usize; NUM_AUX_SEGMENTS],
aux_rands: [usize; NUM_AUX_SEGMENTS],
) -> Self {
assert!(
main_width > 0,
"main trace segment must consist of at least one column"
);
let full_width = main_width + aux_widths.iter().sum::<usize>();
assert!(
full_width <= TraceInfo::MAX_TRACE_WIDTH,
"total number of columns in the trace cannot be greater than {}, but was {}",
TraceInfo::MAX_TRACE_WIDTH,
full_width
);
let mut was_zero_width = false;
let mut num_aux_segments = 0;
for (&width, &num_rand_elements) in aux_widths.iter().zip(aux_rands.iter()) {
if width != 0 {
assert!(
!was_zero_width,
"a non-empty trace segment cannot follow an empty segment"
);
assert!(
num_rand_elements > 0,
"number of random elements for a non-empty trace segment must be greater than zero"
);
num_aux_segments += 1;
} else {
assert!(
num_rand_elements == 0,
"number of random elements for an empty trace segment must be zero"
);
was_zero_width = true;
}
assert!(
num_rand_elements <= TraceInfo::MAX_RAND_SEGMENT_ELEMENTS,
"number of random elements required by a segment cannot exceed {}, but was {}",
TraceInfo::MAX_RAND_SEGMENT_ELEMENTS,
num_rand_elements
);
}
Self {
main_segment_width: main_width,
aux_segment_widths: aux_widths,
aux_segment_rands: aux_rands,
num_aux_segments,
}
}
pub fn main_trace_width(&self) -> usize {
self.main_segment_width
}
pub fn aux_trace_width(&self) -> usize {
self.aux_segment_widths.iter().sum()
}
pub fn num_segments(&self) -> usize {
self.num_aux_segments + 1
}
pub fn num_aux_segments(&self) -> usize {
self.num_aux_segments
}
pub fn get_aux_segment_width(&self, segment_idx: usize) -> usize {
self.aux_segment_widths[segment_idx]
}
pub fn get_aux_segment_rand_elements(&self, segment_idx: usize) -> usize {
self.aux_segment_rands[segment_idx]
}
}
impl Serializable for TraceLayout {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
target.write_u8(self.main_segment_width as u8);
for &w in self.aux_segment_widths.iter() {
debug_assert!(
w <= u8::MAX as usize,
"aux segment width does not fit into u8 value"
);
target.write_u8(w as u8);
}
for &rc in self.aux_segment_rands.iter() {
debug_assert!(
rc <= u8::MAX as usize,
"aux segment random element count does not fit into u8 value"
);
target.write_u8(rc as u8);
}
}
}
impl Deserializable for TraceLayout {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let main_width = source.read_u8()? as usize;
if main_width == 0 {
return Err(DeserializationError::InvalidValue(
"main trace segment width must be greater than zero".to_string(),
));
}
let mut was_zero_width = false;
let mut aux_widths = [0; NUM_AUX_SEGMENTS];
for width in aux_widths.iter_mut() {
*width = source.read_u8()? as usize;
if *width != 0 {
if was_zero_width {
return Err(DeserializationError::InvalidValue(
"a non-empty trace segment cannot follow an empty segment".to_string(),
));
}
} else {
was_zero_width = true;
}
}
let full_trace_width = main_width + aux_widths.iter().sum::<usize>();
if full_trace_width >= TraceInfo::MAX_TRACE_WIDTH {
return Err(DeserializationError::InvalidValue(format!(
"full trace width cannot be greater than {}, but was {}",
TraceInfo::MAX_TRACE_WIDTH,
full_trace_width
)));
}
let mut aux_rands = [0; NUM_AUX_SEGMENTS];
for (num_rand_elements, &width) in aux_rands.iter_mut().zip(aux_widths.iter()) {
*num_rand_elements = source.read_u8()? as usize;
if width == 0 && *num_rand_elements != 0 {
return Err(DeserializationError::InvalidValue(
"an empty trace segment cannot require random elements".to_string(),
));
} else if width != 0 && *num_rand_elements == 0 {
return Err(DeserializationError::InvalidValue(
"a non-empty trace segment must require at least one random element"
.to_string(),
));
} else if *num_rand_elements > TraceInfo::MAX_RAND_SEGMENT_ELEMENTS {
return Err(DeserializationError::InvalidValue(format!(
"number of random elements required by a segment cannot exceed {}, but was {}",
TraceInfo::MAX_RAND_SEGMENT_ELEMENTS,
*num_rand_elements
)));
}
}
Ok(TraceLayout::new(main_width, aux_widths, aux_rands))
}
}