use std::fmt;
use std::future::poll_fn;
use std::sync::Arc;
use std::task::Poll;
use crate::node::{Inbox, Wait};
use crate::schema::{EventSchema, SchemaId};
use crate::stream::Item;
use crate::time::{Tick, TickRange};
pub const MAX_CHANNELS: u8 = 64;
pub const LOGIC_SAMPLES: SchemaId = SchemaId::new("logic.samples", 1);
impl EventSchema for Arc<LogicBlock> {
const SCHEMA: SchemaId = LOGIC_SAMPLES;
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub struct ChannelMask(pub u64);
impl ChannelMask {
pub const EMPTY: ChannelMask = ChannelMask(0);
pub const fn one(channel: u8) -> Self {
ChannelMask(1u64 << (channel & 63))
}
pub const fn with(self, channel: u8) -> Self {
ChannelMask(self.0 | (1u64 << (channel & 63)))
}
pub const fn has(self, channel: u8) -> bool {
self.0 & (1u64 << (channel & 63)) != 0
}
pub const fn is_empty(self) -> bool {
self.0 == 0
}
pub fn of(channels: impl IntoIterator<Item = Option<u8>>) -> Self {
channels
.into_iter()
.flatten()
.fold(ChannelMask::EMPTY, ChannelMask::with)
}
pub fn iter(self) -> impl Iterator<Item = u8> {
let mut bits = self.0;
std::iter::from_fn(move || {
if bits == 0 {
return None;
}
let channel = bits.trailing_zeros() as u8;
bits &= bits - 1;
Some(channel)
})
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub struct Levels(pub u64);
impl Levels {
pub const fn high(self, channel: u8) -> bool {
self.0 & (1u64 << (channel & 63)) != 0
}
pub const fn changed_from(self, other: Levels, mask: ChannelMask) -> ChannelMask {
ChannelMask((self.0 ^ other.0) & mask.0)
}
}
#[derive(Clone, PartialEq, Eq)]
pub struct LogicBlock {
start: Tick,
len: u64,
channels: u8,
stride: usize,
words: Vec<u64>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)]
pub enum BlockError {
#[error("a block holds at least one sample")]
Empty,
#[error("{0} channels: a block holds at most {MAX_CHANNELS}")]
TooManyChannels(u8),
#[error("{found} words given for {channels} channels of {len} samples ({expected} needed)")]
WrongSize {
channels: u8,
len: u64,
expected: usize,
found: usize,
},
}
impl LogicBlock {
pub fn from_words(
start: Tick,
len: u64,
channels: u8,
mut words: Vec<u64>,
) -> Result<Self, BlockError> {
if len == 0 {
return Err(BlockError::Empty);
}
if channels > MAX_CHANNELS {
return Err(BlockError::TooManyChannels(channels));
}
let stride = usize::try_from(len.div_ceil(64)).map_err(|_| BlockError::WrongSize {
channels,
len,
expected: usize::MAX,
found: words.len(),
})?;
let expected = stride * usize::from(channels);
if words.len() != expected {
return Err(BlockError::WrongSize {
channels,
len,
expected,
found: words.len(),
});
}
let valid = len % 64;
if valid != 0 {
let keep = (1u64 << valid) - 1;
for channel in 0..usize::from(channels) {
words[channel * stride + stride - 1] &= keep;
}
}
Ok(Self {
start,
len,
channels,
stride,
words,
})
}
pub fn from_interleaved(start: Tick, channels: u8, bytes: &[u8]) -> Result<Self, BlockError> {
if channels > MAX_CHANNELS {
return Err(BlockError::TooManyChannels(channels));
}
let unit = usize::from(channels).div_ceil(8).max(1);
let len = bytes.len() / unit;
if len == 0 {
return Err(BlockError::Empty);
}
let stride = len.div_ceil(64);
let channels_len = usize::from(channels);
let mut words = vec![0u64; stride * channels_len];
for (index, tile) in bytes[..len * unit].chunks(64 * unit).enumerate() {
for group in 0..channels_len.div_ceil(8) {
let mut rows = [0u64; 8];
for (row, samples) in tile.chunks(8 * unit).enumerate() {
rows[row] = if unit == 1 && samples.len() == 8 {
u64::from_le_bytes([
samples[0], samples[1], samples[2], samples[3], samples[4], samples[5],
samples[6], samples[7],
])
} else {
samples
.chunks_exact(unit)
.enumerate()
.fold(0u64, |row, (sample, bytes)| {
row | u64::from(bytes[group]) << (8 * sample)
})
};
}
let lanes = transpose_64x8(rows);
let first = 8 * group;
for channel in first..(first + 8).min(channels_len) {
words[channel * stride + index] = lanes[channel - first];
}
}
}
Ok(Self {
start,
len: len as u64,
channels,
stride,
words,
})
}
pub fn start(&self) -> Tick {
self.start
}
pub fn len(&self) -> u64 {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn end(&self) -> Tick {
self.start.offset(self.len)
}
pub fn range(&self) -> TickRange {
TickRange::new(self.start, self.end())
}
pub fn channels(&self) -> u8 {
self.channels
}
pub fn contains(&self, tick: Tick) -> bool {
self.range().contains(tick)
}
pub fn words(&self, channel: u8) -> &[u64] {
let channel = usize::from(channel);
&self.words[channel * self.stride..(channel + 1) * self.stride]
}
fn word(&self, channel: u8, index: usize) -> u64 {
self.words[usize::from(channel) * self.stride + index]
}
pub fn level(&self, channel: u8, tick: Tick) -> Option<bool> {
if channel >= self.channels || !self.contains(tick) {
return None;
}
let offset = self.start.until(tick);
let word = self.word(channel, (offset / 64) as usize);
Some(word >> (offset % 64) & 1 != 0)
}
pub fn levels(&self, tick: Tick) -> Option<Levels> {
if !self.contains(tick) {
return None;
}
let offset = self.start.until(tick);
let (index, bit) = ((offset / 64) as usize, offset % 64);
let mut levels = 0u64;
for channel in 0..self.channels {
levels |= (self.word(channel, index) >> bit & 1) << channel;
}
Some(Levels(levels))
}
pub fn rebased(&self, start: Tick) -> LogicBlock {
LogicBlock {
start,
..self.clone()
}
}
pub fn slice(&self, range: TickRange) -> Option<LogicBlock> {
if range.is_empty() || range.start < self.start || range.end > self.end() {
return None;
}
let mut builder = LogicBlockBuilder::new(range.start, self.channels);
let mut tick = range.start;
while tick < range.end {
builder.push(self.levels(tick)?);
tick = tick.next();
}
builder.finish().ok()
}
pub fn next_change(
&self,
mask: ChannelMask,
from: Tick,
prev: Levels,
) -> Option<(Tick, ChannelMask)> {
if !self.contains(from) {
return None;
}
let present = if self.channels >= MAX_CHANNELS {
u64::MAX
} else {
(1u64 << self.channels) - 1
};
let watched = mask.0 & present;
if watched == 0 {
return None;
}
let first = self.start.until(from) as usize;
let (first_word, shift) = (first / 64, first % 64);
let (stride, words) = (self.stride, &self.words);
let edges = |channel: usize, index: usize| -> u64 {
let current = words[channel * stride + index];
let before = if index == first_word {
((current << 1) & !(1u64 << shift)) | (u64::from(prev.high(channel as u8)) << shift)
} else {
(current << 1) | (words[channel * stride + index - 1] >> 63)
};
current ^ before
};
let last_word = stride - 1;
let valid_in_last = self.len - 64 * last_word as u64;
for index in first_word..stride {
let mut diff = 0u64;
let mut bits = watched;
while bits != 0 {
let channel = bits.trailing_zeros() as usize;
bits &= bits - 1;
diff |= edges(channel, index);
}
if index == first_word {
diff &= u64::MAX << shift;
}
if index == last_word && valid_in_last < 64 {
diff &= (1u64 << valid_in_last) - 1;
}
if diff == 0 {
continue;
}
let bit = diff.trailing_zeros();
let tick = self.start.offset(64 * index as u64 + u64::from(bit));
let mut changed = 0u64;
let mut bits = watched;
while bits != 0 {
let channel = bits.trailing_zeros() as usize;
bits &= bits - 1;
changed |= (edges(channel, index) >> bit & 1) << channel;
}
return Some((tick, ChannelMask(changed)));
}
None
}
}
fn transpose_8x8(mut matrix: u64) -> u64 {
let swap = (matrix ^ (matrix >> 7)) & 0x00aa_00aa_00aa_00aa;
matrix ^= swap ^ (swap << 7);
let swap = (matrix ^ (matrix >> 14)) & 0x0000_cccc_0000_cccc;
matrix ^= swap ^ (swap << 14);
let swap = (matrix ^ (matrix >> 28)) & 0x0000_0000_f0f0_f0f0;
matrix ^ swap ^ (swap << 28)
}
fn transpose_64x8(rows: [u64; 8]) -> [u64; 8] {
let mut t = rows.map(transpose_8x8);
for row in 0..4 {
let (a, b) = (t[row], t[row + 4]);
t[row] = (a & 0x0000_0000_ffff_ffff) | (b << 32);
t[row + 4] = (a >> 32) | (b & 0xffff_ffff_0000_0000);
}
for row in [0, 1, 4, 5] {
let (a, b) = (t[row], t[row + 2]);
t[row] = (a & 0x0000_ffff_0000_ffff) | ((b & 0x0000_ffff_0000_ffff) << 16);
t[row + 2] = ((a & 0xffff_0000_ffff_0000) >> 16) | (b & 0xffff_0000_ffff_0000);
}
for row in [0, 2, 4, 6] {
let (a, b) = (t[row], t[row + 1]);
t[row] = (a & 0x00ff_00ff_00ff_00ff) | ((b & 0x00ff_00ff_00ff_00ff) << 8);
t[row + 1] = ((a & 0xff00_ff00_ff00_ff00) >> 8) | (b & 0xff00_ff00_ff00_ff00);
}
t
}
impl fmt::Debug for LogicBlock {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("LogicBlock")
.field("start", &self.start)
.field("len", &self.len)
.field("channels", &self.channels)
.finish_non_exhaustive()
}
}
#[derive(Clone, Debug)]
pub struct LogicBlockBuilder {
start: Tick,
channels: u8,
len: u64,
lanes: Vec<Vec<u64>>,
}
impl LogicBlockBuilder {
pub fn new(start: Tick, channels: u8) -> Self {
let channels = channels.min(MAX_CHANNELS);
Self {
start,
channels,
len: 0,
lanes: vec![Vec::new(); usize::from(channels)],
}
}
pub fn len(&self) -> u64 {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn next_tick(&self) -> Tick {
self.start.offset(self.len)
}
pub fn push(&mut self, levels: Levels) {
let bit = self.len % 64;
if bit == 0 {
for lane in &mut self.lanes {
lane.push(0);
}
}
for (channel, lane) in self.lanes.iter_mut().enumerate() {
if levels.0 >> channel & 1 != 0
&& let Some(word) = lane.last_mut()
{
*word |= 1u64 << bit;
}
}
self.len += 1;
}
pub fn push_repeated(&mut self, levels: Levels, count: u64) {
for _ in 0..count {
self.push(levels);
}
}
pub fn finish(self) -> Result<LogicBlock, BlockError> {
let stride = self.len.div_ceil(64) as usize;
let mut words = Vec::with_capacity(stride * usize::from(self.channels));
for lane in self.lanes {
words.extend(lane);
}
LogicBlock::from_words(self.start, self.len, self.channels, words)
}
}
#[derive(Clone, Debug)]
pub enum SignalItem {
Data(Arc<LogicBlock>),
Progress(Tick),
Gap(TickRange),
End(Tick),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Change {
At {
tick: Tick,
changed: ChannelMask,
levels: Levels,
},
Quiet(Tick),
Gap(TickRange),
Progress(Tick),
End(Tick),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Sample {
At {
tick: Tick,
levels: Levels,
},
Gap(TickRange),
Progress(Tick),
End(Tick),
}
pub struct SignalCursor {
inbox: Inbox<Arc<LogicBlock>>,
block: Option<Arc<LogicBlock>>,
pos: Tick,
prev: Levels,
prev_tick: Option<Tick>,
exhausted: Option<Tick>,
}
impl fmt::Debug for SignalCursor {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SignalCursor")
.field("pos", &self.pos)
.field("prev", &self.prev)
.field("prev_tick", &self.prev_tick)
.finish_non_exhaustive()
}
}
enum Found {
Change(Tick, ChannelMask, Levels),
Quiet(Tick),
Exhausted,
}
impl crate::node::Readable for SignalCursor {
fn poll_ready(&self) -> Poll<()> {
if self.block.is_some() {
return Poll::Ready(());
}
self.inbox.poll_ready()
}
}
impl SignalCursor {
pub(crate) fn new(inbox: Inbox<Arc<LogicBlock>>) -> Self {
Self {
inbox,
block: None,
pos: Tick::ZERO,
prev: Levels(0),
prev_tick: None,
exhausted: None,
}
}
pub fn position(&self) -> Tick {
self.pos
}
pub fn seek(&mut self, tick: Tick) {
if tick > self.pos {
self.pos = tick;
}
if let Some(block) = self.block.take() {
if block.end() <= self.pos {
self.release(&block);
} else {
self.block = Some(block);
}
}
while self.block.is_none() {
let passed = match self.inbox.poll_peek() {
Poll::Ready(item) => match &*item {
Item::Event(event) if event.payload.end() <= self.pos => {
Some(Some(Arc::clone(&event.payload)))
}
Item::Progress(tick) if *tick <= self.pos => Some(None),
_ => None,
},
Poll::Pending => None,
};
let Some(block) = passed else {
break;
};
self.inbox.advance();
if let Some(block) = block {
self.release(&block);
}
}
}
fn release(&mut self, block: &LogicBlock) {
let last = Tick(block.end().0 - 1);
self.prev = block.levels(last).unwrap_or_default();
self.prev_tick = Some(last);
self.pos = self.pos.max(block.end());
self.exhausted = Some(block.end());
self.block = None;
}
fn poll_source(&mut self) -> Poll<Option<Item<Arc<LogicBlock>>>> {
let taken = match self.inbox.poll_peek() {
Poll::Pending => return Poll::Pending,
Poll::Ready(item) => match &*item {
Item::Event(event) => {
self.block = Some(Arc::clone(&event.payload));
self.pos = self.pos.max(event.payload.start());
None
}
Item::Gap(range) => Some(Item::Gap(*range)),
Item::Progress(tick) => Some(Item::Progress(*tick)),
Item::End(tick) => Some(Item::End(*tick)),
},
};
if !matches!(taken, Some(Item::End(_))) {
self.inbox.advance();
}
if let Some(Item::Gap(range)) = taken {
self.pos = self.pos.max(range.end);
self.prev_tick = None;
}
self.exhausted = None;
Poll::Ready(taken)
}
fn scan(&mut self, block: &LogicBlock, mask: ChannelMask, until: Option<Tick>) -> Found {
if block.end() <= self.pos {
self.release(block);
return Found::Exhausted;
}
let limit = until.map_or(block.end(), |until| until.min(block.end()));
if limit <= self.pos {
return Found::Quiet(self.pos);
}
let before = Tick(self.pos.0.saturating_sub(1));
let (from, prev) = if self.prev_tick == Some(before) && self.pos > Tick::ZERO {
(self.pos, self.prev)
} else if self.pos > block.start() {
(self.pos, block.levels(before).unwrap_or_default())
} else {
(self.pos.next(), block.levels(self.pos).unwrap_or_default())
};
if from < limit
&& let Some((tick, changed)) = block.next_change(mask, from, prev)
&& tick < limit
{
let levels = block.levels(tick).unwrap_or_default();
self.prev = levels;
self.prev_tick = Some(tick);
self.pos = tick.next();
return Found::Change(tick, changed, levels);
}
if limit < block.end() {
let last = Tick(limit.0 - 1);
self.prev = block.levels(last).unwrap_or_default();
self.prev_tick = Some(last);
self.pos = limit;
return Found::Quiet(limit);
}
self.release(block);
Found::Exhausted
}
fn poll_change(&mut self, mask: ChannelMask, until: Option<Tick>) -> Poll<Change> {
let cx = self.inbox.cx().clone();
if !cx.has_budget() {
cx.wait(Wait::Budget);
return Poll::Pending;
}
loop {
if let Some(block) = self.block.take() {
let found = self.scan(&block, mask, until);
if !matches!(found, Found::Exhausted) {
self.block = Some(block);
}
match found {
Found::Change(tick, changed, levels) => {
cx.spend();
return Poll::Ready(Change::At {
tick,
changed,
levels,
});
}
Found::Quiet(tick) => {
cx.spend();
return Poll::Ready(Change::Quiet(tick));
}
Found::Exhausted => continue,
}
} else if let Some(until) = until
&& until <= self.pos
{
cx.spend();
return Poll::Ready(Change::Quiet(self.pos));
}
match self.poll_source() {
Poll::Pending => {
if let Some(end) = self.exhausted.take() {
cx.spend();
return Poll::Ready(Change::Progress(end));
}
return Poll::Pending;
}
Poll::Ready(None) => continue,
Poll::Ready(Some(Item::Gap(range))) => {
cx.spend();
return Poll::Ready(Change::Gap(range));
}
Poll::Ready(Some(Item::Progress(tick))) => {
cx.spend();
return Poll::Ready(Change::Progress(tick));
}
Poll::Ready(Some(Item::End(tick))) => {
cx.spend();
return Poll::Ready(Change::End(tick));
}
Poll::Ready(Some(Item::Event(_))) => unreachable!("blocks are kept, not returned"),
}
}
}
fn poll_sample(&mut self) -> Poll<Sample> {
let cx = self.inbox.cx().clone();
if !cx.has_budget() {
cx.wait(Wait::Budget);
return Poll::Pending;
}
loop {
if let Some(block) = self.block.take() {
if let Some(levels) = block.levels(self.pos) {
self.block = Some(block);
cx.spend();
return Poll::Ready(Sample::At {
tick: self.pos,
levels,
});
}
self.release(&block);
}
match self.poll_source() {
Poll::Pending => {
if let Some(end) = self.exhausted.take() {
cx.spend();
return Poll::Ready(Sample::Progress(end));
}
return Poll::Pending;
}
Poll::Ready(None) => continue,
Poll::Ready(Some(Item::Gap(range))) => {
cx.spend();
return Poll::Ready(Sample::Gap(range));
}
Poll::Ready(Some(Item::Progress(tick))) => {
cx.spend();
return Poll::Ready(Sample::Progress(tick));
}
Poll::Ready(Some(Item::End(tick))) => {
cx.spend();
return Poll::Ready(Sample::End(tick));
}
Poll::Ready(Some(Item::Event(_))) => unreachable!("blocks are kept, not returned"),
}
}
}
pub fn next_change(&mut self, mask: ChannelMask) -> impl Future<Output = Change> + '_ {
poll_fn(move |_| self.poll_change(mask, None))
}
pub fn next_change_before(
&mut self,
mask: ChannelMask,
until: Tick,
) -> impl Future<Output = Change> + '_ {
poll_fn(move |_| self.poll_change(mask, Some(until)))
}
pub fn sample(&mut self) -> impl Future<Output = Sample> + '_ {
poll_fn(move |_| self.poll_sample())
}
}
#[cfg(test)]
mod tests {
use super::*;
fn block(start: u64, samples: &[u64], channels: u8) -> LogicBlock {
let mut builder = LogicBlockBuilder::new(Tick(start), channels);
for levels in samples {
builder.push(Levels(*levels));
}
builder.finish().expect("a block")
}
#[test]
fn builder_packs_bits_per_channel() {
let block = block(10, &[0b01, 0b11, 0b10, 0b00], 2);
assert_eq!(block.len(), 4);
assert_eq!(block.end(), Tick(14));
assert_eq!(block.words(0), &[0b0011]);
assert_eq!(block.words(1), &[0b0110]);
assert_eq!(block.level(0, Tick(10)), Some(true));
assert_eq!(block.level(1, Tick(10)), Some(false));
assert_eq!(block.level(0, Tick(14)), None);
assert_eq!(block.levels(Tick(11)), Some(Levels(0b11)));
}
#[test]
fn interleaved_bytes_follow_the_export_layout() {
let block =
LogicBlock::from_interleaved(Tick(0), 12, &[0x01, 0x08, 0xff, 0x0f]).expect("a block");
assert_eq!(block.len(), 2);
assert_eq!(block.levels(Tick(0)), Some(Levels(0x801)));
assert_eq!(block.levels(Tick(1)), Some(Levels(0xfff)));
let four = LogicBlock::from_interleaved(Tick(5), 4, &[0x3, 0x0, 0xc]).expect("a block");
assert_eq!(four.len(), 3);
assert_eq!(four.levels(Tick(7)), Some(Levels(0xc)));
}
fn interleaved_by_sample(
start: Tick,
channels: u8,
bytes: &[u8],
) -> Result<LogicBlock, BlockError> {
let unit = usize::from(channels).div_ceil(8).max(1);
let len = bytes.len() / unit;
let mut builder = LogicBlockBuilder::new(start, channels);
for sample in bytes.chunks_exact(unit).take(len) {
let mut levels = 0u64;
for (group, byte) in sample.iter().enumerate() {
levels |= u64::from(*byte) << (8 * group);
}
builder.push(Levels(levels));
}
builder.finish()
}
proptest::proptest! {
#[test]
fn interleaved_matches_the_sample_by_sample_build(
channels in 0u8..=64,
bytes in proptest::collection::vec(proptest::prelude::any::<u8>(), 0..1200),
) {
let fast = LogicBlock::from_interleaved(Tick(3), channels, &bytes);
let slow = interleaved_by_sample(Tick(3), channels, &bytes);
proptest::prop_assert_eq!(fast, slow);
}
}
#[test]
fn transposes_eight_by_eight() {
assert_eq!(transpose_8x8(0xff), 0x0101_0101_0101_0101);
assert_eq!(transpose_8x8(0x0101_0101_0101_0101), 0xff);
let identity = (0..8).fold(0u64, |m, i| m | 1u64 << (9 * i));
assert_eq!(transpose_8x8(identity), identity);
assert_eq!(transpose_8x8(1 << (8 * 2 + 5)), 1 << (8 * 5 + 2));
}
#[test]
fn rejects_bad_blocks() {
assert_eq!(
LogicBlockBuilder::new(Tick(0), 1)
.finish()
.expect_err("fails"),
BlockError::Empty
);
assert!(matches!(
LogicBlock::from_words(Tick(0), 65, 1, vec![0]),
Err(BlockError::WrongSize {
expected: 2,
found: 1,
..
})
));
assert_eq!(
LogicBlock::from_words(Tick(0), 1, 65, vec![]).expect_err("fails"),
BlockError::TooManyChannels(65)
);
}
#[test]
fn finds_changes_within_and_across_words() {
let mut samples = vec![0u64; 100];
samples[3] = 1;
samples[4] = 1;
samples[64] = 2;
samples[99] = 3;
let block = block(0, &samples, 2);
let both = ChannelMask::one(0).with(1);
assert_eq!(
block.next_change(both, Tick(0), Levels(0)),
Some((Tick(3), ChannelMask::one(0)))
);
assert_eq!(
block.next_change(both, Tick(4), Levels(1)),
Some((Tick(5), ChannelMask::one(0)))
);
assert_eq!(
block.next_change(both, Tick(6), Levels(0)),
Some((Tick(64), ChannelMask::one(1)))
);
assert_eq!(
block.next_change(ChannelMask::one(0), Tick(6), Levels(0)),
Some((Tick(99), ChannelMask::one(0)))
);
assert_eq!(block.next_change(both, Tick(99), Levels(0b11)), None);
assert_eq!(block.next_change(both, Tick(100), Levels(0)), None);
assert_eq!(
block.next_change(both, Tick(0), Levels(0b11)),
Some((Tick(0), both))
);
}
#[test]
fn padding_bits_are_no_change() {
let block = block(0, &[1, 1, 1], 1);
assert_eq!(
block.next_change(ChannelMask::one(0), Tick(1), Levels(1)),
None
);
}
#[test]
fn slices_copy_the_samples() {
let samples: Vec<u64> = (0..200).map(|i| i % 3).collect();
let block = block(1000, &samples, 2);
let slice = block
.slice(TickRange::new(Tick(1063), Tick(1130)))
.expect("a slice");
assert_eq!(slice.start(), Tick(1063));
assert_eq!(slice.len(), 67);
for tick in 1063..1130 {
assert_eq!(slice.levels(Tick(tick)), block.levels(Tick(tick)));
}
assert!(block.slice(TickRange::new(Tick(999), Tick(1001))).is_none());
assert!(block.slice(TickRange::at(Tick(1001))).is_none());
}
#[test]
fn rebasing_moves_the_samples() {
let block = block(10, &[1, 0, 1], 1);
let moved = block.rebased(Tick(500));
assert_eq!(moved.range(), TickRange::new(Tick(500), Tick(503)));
assert_eq!(moved.level(0, Tick(502)), Some(true));
assert_eq!(moved.words(0), block.words(0));
}
use std::cell::RefCell;
use std::future::Future;
use std::pin::Pin;
use std::rc::Rc;
use std::task::{Context, Poll, Waker};
use crate::node::NodeCx;
use crate::stream::{Broadcast, Event};
fn poll<F: Future>(future: Pin<&mut F>) -> Poll<F::Output> {
future.poll(&mut Context::from_waker(Waker::noop()))
}
fn cursor() -> (SignalCursor, Rc<RefCell<Broadcast<Arc<LogicBlock>>>>) {
let queue = Rc::new(RefCell::new(Broadcast::new(16)));
let cx = NodeCx::new();
cx.refill(1_000);
(SignalCursor::new(Inbox::new(Rc::clone(&queue), cx)), queue)
}
fn push(queue: &Rc<RefCell<Broadcast<Arc<LogicBlock>>>>, block: LogicBlock) {
let last = Tick(block.end().0 - 1);
let item = Item::Event(Event {
seq: 0,
span: block.range(),
settled: last,
payload: Arc::new(block),
});
queue
.borrow_mut()
.try_push(item)
.map_err(|_| ())
.expect("fits");
}
#[test]
fn seeks_ahead_and_keeps_the_level_before_the_position() {
let (mut cursor, queue) = cursor();
let mut samples = vec![0u64; 100];
samples.extend(std::iter::repeat_n(1u64, 60));
let whole = block(0, &samples, 1);
for start in (0..160).step_by(40) {
push(
&queue,
whole
.slice(TickRange::new(Tick(start), Tick(start + 40)))
.expect("a slice"),
);
}
cursor.seek(Tick(100));
let change = poll(Box::pin(cursor.next_change(ChannelMask::one(0))).as_mut());
assert_eq!(
change,
Poll::Ready(Change::At {
tick: Tick(100),
changed: ChannelMask::one(0),
levels: Levels(1)
})
);
cursor.seek(Tick(50));
assert_eq!(cursor.position(), Tick(101), "a seek never moves back");
cursor.seek(Tick(130));
let sample = poll(Box::pin(cursor.sample()).as_mut());
assert_eq!(
sample,
Poll::Ready(Sample::At {
tick: Tick(130),
levels: Levels(1)
})
);
}
#[test]
fn a_deadline_ends_the_wait_quietly() {
let (mut cursor, queue) = cursor();
let mut samples = vec![0u64; 50];
for sample in &mut samples[30..] {
*sample = 1;
}
push(&queue, block(0, &samples, 1));
let until =
poll(Box::pin(cursor.next_change_before(ChannelMask::one(0), Tick(20))).as_mut());
assert_eq!(until, Poll::Ready(Change::Quiet(Tick(20))));
assert_eq!(cursor.position(), Tick(20));
let found =
poll(Box::pin(cursor.next_change_before(ChannelMask::one(0), Tick(40))).as_mut());
assert!(matches!(
found,
Poll::Ready(Change::At { tick: Tick(30), .. })
));
let past =
poll(Box::pin(cursor.next_change_before(ChannelMask::one(0), Tick(10))).as_mut());
assert_eq!(past, Poll::Ready(Change::Quiet(Tick(31))));
let pending =
poll(Box::pin(cursor.next_change_before(ChannelMask::one(0), Tick(80))).as_mut());
assert_eq!(
pending,
Poll::Ready(Change::Progress(Tick(50))),
"the used-up block is announced"
);
let pending =
poll(Box::pin(cursor.next_change_before(ChannelMask::one(0), Tick(80))).as_mut());
assert_eq!(pending, Poll::Pending);
push(&queue, block(50, &[1u64; 50], 1));
let quiet =
poll(Box::pin(cursor.next_change_before(ChannelMask::one(0), Tick(80))).as_mut());
assert_eq!(quiet, Poll::Ready(Change::Quiet(Tick(80))));
}
#[test]
fn a_deadline_at_the_end_of_a_block_needs_no_next_block() {
let (mut cursor, queue) = cursor();
push(&queue, block(0, &[0u64; 50], 1));
let quiet =
poll(Box::pin(cursor.next_change_before(ChannelMask::one(0), Tick(50))).as_mut());
assert_eq!(quiet, Poll::Ready(Change::Quiet(Tick(50))));
assert_eq!(cursor.position(), Tick(50));
assert_eq!(
poll(Box::pin(cursor.next_change(ChannelMask::one(0))).as_mut()),
Poll::Ready(Change::Progress(Tick(50)))
);
assert_eq!(
poll(Box::pin(cursor.next_change(ChannelMask::one(0))).as_mut()),
Poll::Pending
);
}
#[test]
fn a_used_up_block_is_announced_once() {
let (mut cursor, queue) = cursor();
push(&queue, block(0, &[0u64; 10], 1));
assert_eq!(
poll(Box::pin(cursor.next_change(ChannelMask::one(0))).as_mut()),
Poll::Ready(Change::Progress(Tick(10)))
);
assert_eq!(
poll(Box::pin(cursor.next_change(ChannelMask::one(0))).as_mut()),
Poll::Pending
);
assert_eq!(poll(Box::pin(cursor.sample()).as_mut()), Poll::Pending);
push(&queue, block(10, &[1u64; 10], 1));
assert!(matches!(
poll(Box::pin(cursor.next_change(ChannelMask::one(0))).as_mut()),
Poll::Ready(Change::At { tick: Tick(10), .. })
));
}
#[test]
fn the_first_sample_after_a_gap_is_no_edge() {
let (mut cursor, queue) = cursor();
push(&queue, block(0, &[0u64; 10], 1));
queue
.borrow_mut()
.try_push(Item::Gap(TickRange::new(Tick(10), Tick(20))))
.map_err(|_| ())
.expect("fits");
push(&queue, block(20, &[1u64; 10], 1));
assert_eq!(
poll(Box::pin(cursor.next_change(ChannelMask::one(0))).as_mut()),
Poll::Ready(Change::Gap(TickRange::new(Tick(10), Tick(20))))
);
assert_eq!(
poll(Box::pin(cursor.sample()).as_mut()),
Poll::Ready(Sample::At {
tick: Tick(20),
levels: Levels(1)
})
);
assert_eq!(
poll(Box::pin(cursor.next_change(ChannelMask::one(0))).as_mut()),
Poll::Ready(Change::Progress(Tick(30)))
);
}
#[test]
fn masks_iterate_channels() {
let mask = ChannelMask::one(3).with(0).with(63);
assert_eq!(mask.iter().collect::<Vec<_>>(), vec![0, 3, 63]);
assert!(ChannelMask::EMPTY.iter().next().is_none());
}
}