Skip to main content

uiharu_core/
signal.rs

1// SPDX-License-Identifier: Apache-2.0
2
3//! Logic samples as decoders read them: blocks of bit-packed samples, and the cursor that
4//! walks a stream of blocks looking for edges and reading levels.
5//!
6//! The cursor remembers the levels just before its position across blocks, so an edge on the
7//! first sample of a block is found like any other: where the samples are cut into blocks does
8//! not change what a decoder sees. Only the start of the stream and the first sample after a
9//! gap have no earlier sample to compare with.
10
11use std::fmt;
12use std::future::poll_fn;
13use std::sync::Arc;
14use std::task::Poll;
15
16use crate::node::{Inbox, Wait};
17use crate::schema::{EventSchema, SchemaId};
18use crate::stream::Item;
19use crate::time::{Tick, TickRange};
20
21/// The most channels a source may have.
22pub const MAX_CHANNELS: u8 = 64;
23
24/// The schema of a source's output: blocks of logic samples.
25pub const LOGIC_SAMPLES: SchemaId = SchemaId::new("logic.samples", 1);
26
27impl EventSchema for Arc<LogicBlock> {
28    const SCHEMA: SchemaId = LOGIC_SAMPLES;
29}
30
31/// A set of channels: bit `i` stands for channel `i`.
32#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
33pub struct ChannelMask(pub u64);
34
35impl ChannelMask {
36    /// No channels.
37    pub const EMPTY: ChannelMask = ChannelMask(0);
38
39    /// Channel `channel` alone.
40    pub const fn one(channel: u8) -> Self {
41        ChannelMask(1u64 << (channel & 63))
42    }
43
44    /// The set with `channel` added.
45    pub const fn with(self, channel: u8) -> Self {
46        ChannelMask(self.0 | (1u64 << (channel & 63)))
47    }
48
49    /// Whether `channel` is in the set.
50    pub const fn has(self, channel: u8) -> bool {
51        self.0 & (1u64 << (channel & 63)) != 0
52    }
53
54    /// Whether the set is empty.
55    pub const fn is_empty(self) -> bool {
56        self.0 == 0
57    }
58
59    /// The set of the given channels; a line that is not connected (`None`) is left out.
60    pub fn of(channels: impl IntoIterator<Item = Option<u8>>) -> Self {
61        channels
62            .into_iter()
63            .flatten()
64            .fold(ChannelMask::EMPTY, ChannelMask::with)
65    }
66
67    /// The channels in the set, lowest first.
68    pub fn iter(self) -> impl Iterator<Item = u8> {
69        let mut bits = self.0;
70        std::iter::from_fn(move || {
71            if bits == 0 {
72                return None;
73            }
74            let channel = bits.trailing_zeros() as u8;
75            bits &= bits - 1;
76            Some(channel)
77        })
78    }
79}
80
81/// The levels of every channel at one tick: bit `i` set means channel `i` is high.
82#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
83pub struct Levels(pub u64);
84
85impl Levels {
86    /// Whether `channel` is high.
87    pub const fn high(self, channel: u8) -> bool {
88        self.0 & (1u64 << (channel & 63)) != 0
89    }
90
91    /// The channels, among `mask`, whose level differs from `other`.
92    pub const fn changed_from(self, other: Levels, mask: ChannelMask) -> ChannelMask {
93        ChannelMask((self.0 ^ other.0) & mask.0)
94    }
95}
96
97/// A block of samples: `len` ticks from `start` on `channels` channels, bit-packed per
98/// channel, least significant bit first.
99#[derive(Clone, PartialEq, Eq)]
100pub struct LogicBlock {
101    start: Tick,
102    len: u64,
103    channels: u8,
104    /// Words per channel.
105    stride: usize,
106    /// Channel `c` holds words `c * stride .. (c + 1) * stride`.
107    words: Vec<u64>,
108}
109
110/// Why samples do not make a block.
111#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)]
112pub enum BlockError {
113    /// A block holds at least one sample.
114    #[error("a block holds at least one sample")]
115    Empty,
116    /// More channels than a block can hold.
117    #[error("{0} channels: a block holds at most {MAX_CHANNELS}")]
118    TooManyChannels(u8),
119    /// The words given do not match the channel count and length.
120    #[error("{found} words given for {channels} channels of {len} samples ({expected} needed)")]
121    WrongSize {
122        /// The channels of the block.
123        channels: u8,
124        /// Its samples.
125        len: u64,
126        /// The words that would hold them.
127        expected: usize,
128        /// The words given.
129        found: usize,
130    },
131}
132
133impl LogicBlock {
134    /// A block from its words, `channels * ⌈len / 64⌉` of them, channel by channel; bits past
135    /// `len` in a channel's last word are ignored.
136    pub fn from_words(
137        start: Tick,
138        len: u64,
139        channels: u8,
140        mut words: Vec<u64>,
141    ) -> Result<Self, BlockError> {
142        if len == 0 {
143            return Err(BlockError::Empty);
144        }
145        if channels > MAX_CHANNELS {
146            return Err(BlockError::TooManyChannels(channels));
147        }
148        let stride = usize::try_from(len.div_ceil(64)).map_err(|_| BlockError::WrongSize {
149            channels,
150            len,
151            expected: usize::MAX,
152            found: words.len(),
153        })?;
154        let expected = stride * usize::from(channels);
155        if words.len() != expected {
156            return Err(BlockError::WrongSize {
157                channels,
158                len,
159                expected,
160                found: words.len(),
161            });
162        }
163        // The padding bits must be clear: the edge scan relies on it.
164        let valid = len % 64;
165        if valid != 0 {
166            let keep = (1u64 << valid) - 1;
167            for channel in 0..usize::from(channels) {
168                words[channel * stride + stride - 1] &= keep;
169            }
170        }
171        Ok(Self {
172            start,
173            len,
174            channels,
175            stride,
176            words,
177        })
178    }
179
180    /// A block from samples interleaved sample by sample, `⌈channels / 8⌉` bytes each, bit `i`
181    /// of byte `j` being channel `8 j + i`: the layout of raw binary exports.
182    pub fn from_interleaved(start: Tick, channels: u8, bytes: &[u8]) -> Result<Self, BlockError> {
183        if channels > MAX_CHANNELS {
184            return Err(BlockError::TooManyChannels(channels));
185        }
186        let unit = usize::from(channels).div_ceil(8).max(1);
187        let len = bytes.len() / unit;
188        if len == 0 {
189            return Err(BlockError::Empty);
190        }
191        let stride = len.div_ceil(64);
192        let channels_len = usize::from(channels);
193        let mut words = vec![0u64; stride * channels_len];
194        // 64 samples of one byte group are a 64 × 8 bit matrix, sample by channel: eight rows
195        // of eight samples each. Transposing each row's 8 × 8 bits, then the 8 × 8 bytes of
196        // the rows, gives the eight channels' words of the tile.
197        for (index, tile) in bytes[..len * unit].chunks(64 * unit).enumerate() {
198            for group in 0..channels_len.div_ceil(8) {
199                let mut rows = [0u64; 8];
200                for (row, samples) in tile.chunks(8 * unit).enumerate() {
201                    rows[row] = if unit == 1 && samples.len() == 8 {
202                        u64::from_le_bytes([
203                            samples[0], samples[1], samples[2], samples[3], samples[4], samples[5],
204                            samples[6], samples[7],
205                        ])
206                    } else {
207                        samples
208                            .chunks_exact(unit)
209                            .enumerate()
210                            .fold(0u64, |row, (sample, bytes)| {
211                                row | u64::from(bytes[group]) << (8 * sample)
212                            })
213                    };
214                }
215                let lanes = transpose_64x8(rows);
216                let first = 8 * group;
217                for channel in first..(first + 8).min(channels_len) {
218                    words[channel * stride + index] = lanes[channel - first];
219                }
220            }
221        }
222        Ok(Self {
223            start,
224            len: len as u64,
225            channels,
226            stride,
227            words,
228        })
229    }
230
231    /// The first tick of the block.
232    pub fn start(&self) -> Tick {
233        self.start
234    }
235
236    /// The samples in the block.
237    pub fn len(&self) -> u64 {
238        self.len
239    }
240
241    /// Whether the block holds no sample; never, for a block that was built.
242    pub fn is_empty(&self) -> bool {
243        self.len == 0
244    }
245
246    /// The first tick after the block.
247    pub fn end(&self) -> Tick {
248        self.start.offset(self.len)
249    }
250
251    /// The ticks of the block.
252    pub fn range(&self) -> TickRange {
253        TickRange::new(self.start, self.end())
254    }
255
256    /// The channels of the block.
257    pub fn channels(&self) -> u8 {
258        self.channels
259    }
260
261    /// Whether `tick` is in the block.
262    pub fn contains(&self, tick: Tick) -> bool {
263        self.range().contains(tick)
264    }
265
266    /// The words of `channel`, least significant bit first.
267    pub fn words(&self, channel: u8) -> &[u64] {
268        let channel = usize::from(channel);
269        &self.words[channel * self.stride..(channel + 1) * self.stride]
270    }
271
272    fn word(&self, channel: u8, index: usize) -> u64 {
273        self.words[usize::from(channel) * self.stride + index]
274    }
275
276    /// The level of `channel` at `tick`, if the block holds it.
277    pub fn level(&self, channel: u8, tick: Tick) -> Option<bool> {
278        if channel >= self.channels || !self.contains(tick) {
279            return None;
280        }
281        let offset = self.start.until(tick);
282        let word = self.word(channel, (offset / 64) as usize);
283        Some(word >> (offset % 64) & 1 != 0)
284    }
285
286    /// The levels of every channel at `tick`, if the block holds it.
287    pub fn levels(&self, tick: Tick) -> Option<Levels> {
288        if !self.contains(tick) {
289            return None;
290        }
291        let offset = self.start.until(tick);
292        let (index, bit) = ((offset / 64) as usize, offset % 64);
293        // A plain indexed loop; iterator adaptors over the strided words measured slower.
294        let mut levels = 0u64;
295        for channel in 0..self.channels {
296            levels |= (self.word(channel, index) >> bit & 1) << channel;
297        }
298        Some(Levels(levels))
299    }
300
301    /// The same samples starting at `start` instead.
302    pub fn rebased(&self, start: Tick) -> LogicBlock {
303        LogicBlock {
304            start,
305            ..self.clone()
306        }
307    }
308
309    /// The part of the block covering `range`, or `None` when `range` reaches outside it or
310    /// is empty.
311    pub fn slice(&self, range: TickRange) -> Option<LogicBlock> {
312        if range.is_empty() || range.start < self.start || range.end > self.end() {
313            return None;
314        }
315        let mut builder = LogicBlockBuilder::new(range.start, self.channels);
316        let mut tick = range.start;
317        while tick < range.end {
318            builder.push(self.levels(tick)?);
319            tick = tick.next();
320        }
321        builder.finish().ok()
322    }
323
324    /// The first tick at or after `from` whose levels, on the channels of `mask`, differ from
325    /// the tick before it, taking `prev` as the levels of `from - 1`; `from` must be in the
326    /// block. Returns the tick and the channels that changed.
327    pub fn next_change(
328        &self,
329        mask: ChannelMask,
330        from: Tick,
331        prev: Levels,
332    ) -> Option<(Tick, ChannelMask)> {
333        if !self.contains(from) {
334            return None;
335        }
336        // The watched channels the block has; a channel it does not have never changes.
337        let present = if self.channels >= MAX_CHANNELS {
338            u64::MAX
339        } else {
340            (1u64 << self.channels) - 1
341        };
342        let watched = mask.0 & present;
343        if watched == 0 {
344            return None;
345        }
346        let first = self.start.until(from) as usize;
347        let (first_word, shift) = (first / 64, first % 64);
348        let (stride, words) = (self.stride, &self.words);
349        // Where the samples of `channel` in word `index` differ from the sample before each:
350        // the last sample of the previous word stands before the first of this one, and `prev`
351        // before `from`. Bits below `shift` in the first word are masked out by the caller.
352        let edges = |channel: usize, index: usize| -> u64 {
353            let current = words[channel * stride + index];
354            let before = if index == first_word {
355                ((current << 1) & !(1u64 << shift)) | (u64::from(prev.high(channel as u8)) << shift)
356            } else {
357                (current << 1) | (words[channel * stride + index - 1] >> 63)
358            };
359            current ^ before
360        };
361        let last_word = stride - 1;
362        let valid_in_last = self.len - 64 * last_word as u64;
363        for index in first_word..stride {
364            let mut diff = 0u64;
365            let mut bits = watched;
366            while bits != 0 {
367                let channel = bits.trailing_zeros() as usize;
368                bits &= bits - 1;
369                diff |= edges(channel, index);
370            }
371            if index == first_word {
372                diff &= u64::MAX << shift;
373            }
374            if index == last_word && valid_in_last < 64 {
375                diff &= (1u64 << valid_in_last) - 1;
376            }
377            if diff == 0 {
378                continue;
379            }
380            let bit = diff.trailing_zeros();
381            let tick = self.start.offset(64 * index as u64 + u64::from(bit));
382            // The channels that changed at `tick`, from the same edge words: no second look
383            // at the samples.
384            let mut changed = 0u64;
385            let mut bits = watched;
386            while bits != 0 {
387                let channel = bits.trailing_zeros() as usize;
388                bits &= bits - 1;
389                changed |= (edges(channel, index) >> bit & 1) << channel;
390            }
391            return Some((tick, ChannelMask(changed)));
392        }
393        None
394    }
395}
396
397/// Transposes an 8 × 8 bit matrix held as eight bytes: byte `r` is row `r`, bit `c` of it
398/// column `c`. Swaps the 1 × 1, 2 × 2 and 4 × 4 blocks across the diagonal in turn.
399fn transpose_8x8(mut matrix: u64) -> u64 {
400    let swap = (matrix ^ (matrix >> 7)) & 0x00aa_00aa_00aa_00aa;
401    matrix ^= swap ^ (swap << 7);
402    let swap = (matrix ^ (matrix >> 14)) & 0x0000_cccc_0000_cccc;
403    matrix ^= swap ^ (swap << 14);
404    let swap = (matrix ^ (matrix >> 28)) & 0x0000_0000_f0f0_f0f0;
405    matrix ^ swap ^ (swap << 28)
406}
407
408/// Transposes a 64 × 8 bit matrix: `rows[r]` holds samples `8 r .. 8 r + 8` as bytes, bit
409/// `c` of each being channel `c`; the result holds channel `c`'s 64 samples in element `c`.
410/// Each row's bits are transposed, then the bytes of the rows, by halves, quarters and single
411/// bytes.
412fn transpose_64x8(rows: [u64; 8]) -> [u64; 8] {
413    let mut t = rows.map(transpose_8x8);
414    for row in 0..4 {
415        let (a, b) = (t[row], t[row + 4]);
416        t[row] = (a & 0x0000_0000_ffff_ffff) | (b << 32);
417        t[row + 4] = (a >> 32) | (b & 0xffff_ffff_0000_0000);
418    }
419    for row in [0, 1, 4, 5] {
420        let (a, b) = (t[row], t[row + 2]);
421        t[row] = (a & 0x0000_ffff_0000_ffff) | ((b & 0x0000_ffff_0000_ffff) << 16);
422        t[row + 2] = ((a & 0xffff_0000_ffff_0000) >> 16) | (b & 0xffff_0000_ffff_0000);
423    }
424    for row in [0, 2, 4, 6] {
425        let (a, b) = (t[row], t[row + 1]);
426        t[row] = (a & 0x00ff_00ff_00ff_00ff) | ((b & 0x00ff_00ff_00ff_00ff) << 8);
427        t[row + 1] = ((a & 0xff00_ff00_ff00_ff00) >> 8) | (b & 0xff00_ff00_ff00_ff00);
428    }
429    t
430}
431
432impl fmt::Debug for LogicBlock {
433    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
434        f.debug_struct("LogicBlock")
435            .field("start", &self.start)
436            .field("len", &self.len)
437            .field("channels", &self.channels)
438            .finish_non_exhaustive()
439    }
440}
441
442/// Builds a [`LogicBlock`] sample by sample.
443#[derive(Clone, Debug)]
444pub struct LogicBlockBuilder {
445    start: Tick,
446    channels: u8,
447    len: u64,
448    /// Words per channel, grown as samples come.
449    lanes: Vec<Vec<u64>>,
450}
451
452impl LogicBlockBuilder {
453    /// An empty block of `channels` channels starting at `start`.
454    pub fn new(start: Tick, channels: u8) -> Self {
455        let channels = channels.min(MAX_CHANNELS);
456        Self {
457            start,
458            channels,
459            len: 0,
460            lanes: vec![Vec::new(); usize::from(channels)],
461        }
462    }
463
464    /// The samples pushed so far.
465    pub fn len(&self) -> u64 {
466        self.len
467    }
468
469    /// Whether no sample was pushed.
470    pub fn is_empty(&self) -> bool {
471        self.len == 0
472    }
473
474    /// The tick the next sample will have.
475    pub fn next_tick(&self) -> Tick {
476        self.start.offset(self.len)
477    }
478
479    /// Appends one sample.
480    pub fn push(&mut self, levels: Levels) {
481        let bit = self.len % 64;
482        if bit == 0 {
483            for lane in &mut self.lanes {
484                lane.push(0);
485            }
486        }
487        for (channel, lane) in self.lanes.iter_mut().enumerate() {
488            if levels.0 >> channel & 1 != 0
489                && let Some(word) = lane.last_mut()
490            {
491                *word |= 1u64 << bit;
492            }
493        }
494        self.len += 1;
495    }
496
497    /// Appends `count` samples with the same levels.
498    pub fn push_repeated(&mut self, levels: Levels, count: u64) {
499        for _ in 0..count {
500            self.push(levels);
501        }
502    }
503
504    /// The block; an error when no sample was pushed.
505    pub fn finish(self) -> Result<LogicBlock, BlockError> {
506        let stride = self.len.div_ceil(64) as usize;
507        let mut words = Vec::with_capacity(stride * usize::from(self.channels));
508        for lane in self.lanes {
509            words.extend(lane);
510        }
511        LogicBlock::from_words(self.start, self.len, self.channels, words)
512    }
513}
514
515/// What a source feeds into a pipeline.
516#[derive(Clone, Debug)]
517pub enum SignalItem {
518    /// The next samples; they follow the previous block or the previous gap directly.
519    Data(Arc<LogicBlock>),
520    /// The source's clock has reached this tick; no sample before it is still to come.
521    Progress(Tick),
522    /// These samples were lost; the next block starts at the end of the gap.
523    Gap(TickRange),
524    /// The source has stopped for good at this tick.
525    End(Tick),
526}
527
528/// What a wait for an edge finds.
529#[derive(Clone, Copy, Debug, PartialEq, Eq)]
530pub enum Change {
531    /// A tick where some watched channels changed.
532    At {
533        /// The tick of the change.
534        tick: Tick,
535        /// The watched channels whose level differs from the tick before.
536        changed: ChannelMask,
537        /// The levels of every channel at `tick`.
538        levels: Levels,
539    },
540    /// No watched channel changed before the deadline of [`SignalCursor::next_change_before`];
541    /// the cursor now stands at this tick.
542    Quiet(Tick),
543    /// Samples are missing; the cursor now stands at the end of the gap.
544    Gap(TickRange),
545    /// No change up to this tick, which the source has reached: either the source said so, or
546    /// the cursor used up the block ending there and the next one has not arrived.
547    Progress(Tick),
548    /// The samples have ended.
549    End(Tick),
550}
551
552/// What reading the levels at the cursor finds.
553#[derive(Clone, Copy, Debug, PartialEq, Eq)]
554pub enum Sample {
555    /// The levels at the cursor's tick.
556    At {
557        /// The tick read.
558        tick: Tick,
559        /// The levels of every channel.
560        levels: Levels,
561    },
562    /// Samples are missing; the cursor now stands at the end of the gap.
563    Gap(TickRange),
564    /// The source has reached this tick without delivering the cursor's sample yet.
565    Progress(Tick),
566    /// The samples have ended.
567    End(Tick),
568}
569
570/// A decoder's view of a source: a position in its samples, with the levels just before it.
571///
572/// The cursor moves forward only. [`SignalCursor::next_change`] scans for the next edge on the
573/// watched channels, [`SignalCursor::next_change_before`] does the same up to a deadline,
574/// [`SignalCursor::seek`] jumps ahead to a tick and [`SignalCursor::sample`] reads the levels
575/// where the cursor stands. Blocks the cursor has moved past are released; when it uses a block
576/// up and the next one has not arrived, it reports the block's end as progress once, so that
577/// decoders built on time can see time pass.
578pub struct SignalCursor {
579    inbox: Inbox<Arc<LogicBlock>>,
580    /// The block the cursor stands in, until it is used up.
581    block: Option<Arc<LogicBlock>>,
582    /// The next tick to look at.
583    pos: Tick,
584    /// The levels at `prev_tick`, the last tick seen before `pos`.
585    prev: Levels,
586    /// `None` at the start and right after a gap: no tick before `pos` was seen.
587    prev_tick: Option<Tick>,
588    /// The end of a block used up with the next one not yet there, until it is announced.
589    exhausted: Option<Tick>,
590}
591
592impl fmt::Debug for SignalCursor {
593    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
594        f.debug_struct("SignalCursor")
595            .field("pos", &self.pos)
596            .field("prev", &self.prev)
597            .field("prev_tick", &self.prev_tick)
598            .finish_non_exhaustive()
599    }
600}
601
602/// What the cursor found in the block it stands in.
603enum Found {
604    Change(Tick, ChannelMask, Levels),
605    Quiet(Tick),
606    /// The block is used up; its last levels were noted.
607    Exhausted,
608}
609
610impl crate::node::Readable for SignalCursor {
611    fn poll_ready(&self) -> Poll<()> {
612        if self.block.is_some() {
613            return Poll::Ready(());
614        }
615        self.inbox.poll_ready()
616    }
617}
618
619impl SignalCursor {
620    pub(crate) fn new(inbox: Inbox<Arc<LogicBlock>>) -> Self {
621        Self {
622            inbox,
623            block: None,
624            pos: Tick::ZERO,
625            prev: Levels(0),
626            prev_tick: None,
627            exhausted: None,
628        }
629    }
630
631    /// The next tick the cursor will look at.
632    pub fn position(&self) -> Tick {
633        self.pos
634    }
635
636    /// Moves the cursor forward to `tick`, to sample there or to wait for edges from there on;
637    /// a tick not after the position leaves the cursor where it is.
638    pub fn seek(&mut self, tick: Tick) {
639        if tick > self.pos {
640            self.pos = tick;
641        }
642        // Blocks that end at or before the position will never be read: letting them go
643        // frees the source's queue, which matters to a node that reads the samples beside
644        // another stream and moves through them as that stream tells it to.
645        if let Some(block) = self.block.take() {
646            if block.end() <= self.pos {
647                self.release(&block);
648            } else {
649                self.block = Some(block);
650            }
651        }
652        // Progress the position is past is no news either. A gap stays, whatever the position:
653        // a decoder that seeks across missing samples must learn of them, and pass them on.
654        while self.block.is_none() {
655            let passed = match self.inbox.poll_peek() {
656                Poll::Ready(item) => match &*item {
657                    Item::Event(event) if event.payload.end() <= self.pos => {
658                        Some(Some(Arc::clone(&event.payload)))
659                    }
660                    Item::Progress(tick) if *tick <= self.pos => Some(None),
661                    _ => None,
662                },
663                Poll::Pending => None,
664            };
665            let Some(block) = passed else {
666                break;
667            };
668            self.inbox.advance();
669            if let Some(block) = block {
670                self.release(&block);
671            }
672        }
673    }
674
675    /// Notes the last levels of `block`, stands at its end and lets it go.
676    fn release(&mut self, block: &LogicBlock) {
677        let last = Tick(block.end().0 - 1);
678        self.prev = block.levels(last).unwrap_or_default();
679        self.prev_tick = Some(last);
680        self.pos = self.pos.max(block.end());
681        self.exhausted = Some(block.end());
682        self.block = None;
683    }
684
685    /// Takes the next item from the source if one is waiting; `Pending` sets the node to wait
686    /// for input. A block is kept as the current block; the others are returned.
687    fn poll_source(&mut self) -> Poll<Option<Item<Arc<LogicBlock>>>> {
688        let taken = match self.inbox.poll_peek() {
689            Poll::Pending => return Poll::Pending,
690            Poll::Ready(item) => match &*item {
691                Item::Event(event) => {
692                    self.block = Some(Arc::clone(&event.payload));
693                    // The first block decides where the samples start; later ones follow the
694                    // position, which may stand ahead of them after a seek.
695                    self.pos = self.pos.max(event.payload.start());
696                    None
697                }
698                Item::Gap(range) => Some(Item::Gap(*range)),
699                Item::Progress(tick) => Some(Item::Progress(*tick)),
700                Item::End(tick) => Some(Item::End(*tick)),
701            },
702        };
703        // The end stays in the queue: every later wait finds it again.
704        if !matches!(taken, Some(Item::End(_))) {
705            self.inbox.advance();
706        }
707        if let Some(Item::Gap(range)) = taken {
708            self.pos = self.pos.max(range.end);
709            self.prev_tick = None;
710        }
711        self.exhausted = None;
712        Poll::Ready(taken)
713    }
714
715    /// Scans the current block from the position for a change on `mask` before `until`.
716    fn scan(&mut self, block: &LogicBlock, mask: ChannelMask, until: Option<Tick>) -> Found {
717        if block.end() <= self.pos {
718            self.release(block);
719            return Found::Exhausted;
720        }
721        let limit = until.map_or(block.end(), |until| until.min(block.end()));
722        if limit <= self.pos {
723            return Found::Quiet(self.pos);
724        }
725        let before = Tick(self.pos.0.saturating_sub(1));
726        let (from, prev) = if self.prev_tick == Some(before) && self.pos > Tick::ZERO {
727            (self.pos, self.prev)
728        } else if self.pos > block.start() {
729            (self.pos, block.levels(before).unwrap_or_default())
730        } else {
731            // Nothing before the first sample of the stream or after a gap: it is no edge.
732            (self.pos.next(), block.levels(self.pos).unwrap_or_default())
733        };
734        if from < limit
735            && let Some((tick, changed)) = block.next_change(mask, from, prev)
736            && tick < limit
737        {
738            let levels = block.levels(tick).unwrap_or_default();
739            self.prev = levels;
740            self.prev_tick = Some(tick);
741            self.pos = tick.next();
742            return Found::Change(tick, changed, levels);
743        }
744        if limit < block.end() {
745            let last = Tick(limit.0 - 1);
746            self.prev = block.levels(last).unwrap_or_default();
747            self.prev_tick = Some(last);
748            self.pos = limit;
749            return Found::Quiet(limit);
750        }
751        self.release(block);
752        Found::Exhausted
753    }
754
755    fn poll_change(&mut self, mask: ChannelMask, until: Option<Tick>) -> Poll<Change> {
756        let cx = self.inbox.cx().clone();
757        if !cx.has_budget() {
758            cx.wait(Wait::Budget);
759            return Poll::Pending;
760        }
761        loop {
762            // The block is taken out while it is scanned, which spares an Arc clone on the
763            // hottest path, and put back unless the scan used it up.
764            if let Some(block) = self.block.take() {
765                let found = self.scan(&block, mask, until);
766                if !matches!(found, Found::Exhausted) {
767                    self.block = Some(block);
768                }
769                match found {
770                    Found::Change(tick, changed, levels) => {
771                        cx.spend();
772                        return Poll::Ready(Change::At {
773                            tick,
774                            changed,
775                            levels,
776                        });
777                    }
778                    Found::Quiet(tick) => {
779                        cx.spend();
780                        return Poll::Ready(Change::Quiet(tick));
781                    }
782                    // A deadline at the block's end is reached without the next block.
783                    Found::Exhausted => continue,
784                }
785            } else if let Some(until) = until
786                && until <= self.pos
787            {
788                cx.spend();
789                return Poll::Ready(Change::Quiet(self.pos));
790            }
791            match self.poll_source() {
792                Poll::Pending => {
793                    if let Some(end) = self.exhausted.take() {
794                        cx.spend();
795                        return Poll::Ready(Change::Progress(end));
796                    }
797                    return Poll::Pending;
798                }
799                Poll::Ready(None) => continue,
800                Poll::Ready(Some(Item::Gap(range))) => {
801                    cx.spend();
802                    return Poll::Ready(Change::Gap(range));
803                }
804                Poll::Ready(Some(Item::Progress(tick))) => {
805                    cx.spend();
806                    return Poll::Ready(Change::Progress(tick));
807                }
808                Poll::Ready(Some(Item::End(tick))) => {
809                    cx.spend();
810                    return Poll::Ready(Change::End(tick));
811                }
812                Poll::Ready(Some(Item::Event(_))) => unreachable!("blocks are kept, not returned"),
813            }
814        }
815    }
816
817    fn poll_sample(&mut self) -> Poll<Sample> {
818        let cx = self.inbox.cx().clone();
819        if !cx.has_budget() {
820            cx.wait(Wait::Budget);
821            return Poll::Pending;
822        }
823        loop {
824            if let Some(block) = self.block.take() {
825                if let Some(levels) = block.levels(self.pos) {
826                    self.block = Some(block);
827                    cx.spend();
828                    return Poll::Ready(Sample::At {
829                        tick: self.pos,
830                        levels,
831                    });
832                }
833                self.release(&block);
834            }
835            match self.poll_source() {
836                Poll::Pending => {
837                    if let Some(end) = self.exhausted.take() {
838                        cx.spend();
839                        return Poll::Ready(Sample::Progress(end));
840                    }
841                    return Poll::Pending;
842                }
843                Poll::Ready(None) => continue,
844                Poll::Ready(Some(Item::Gap(range))) => {
845                    cx.spend();
846                    return Poll::Ready(Sample::Gap(range));
847                }
848                Poll::Ready(Some(Item::Progress(tick))) => {
849                    cx.spend();
850                    return Poll::Ready(Sample::Progress(tick));
851                }
852                Poll::Ready(Some(Item::End(tick))) => {
853                    cx.spend();
854                    return Poll::Ready(Sample::End(tick));
855                }
856                Poll::Ready(Some(Item::Event(_))) => unreachable!("blocks are kept, not returned"),
857            }
858        }
859    }
860
861    /// Waits for the next tick at or after the cursor where a channel of `mask` changes level,
862    /// and moves past it. Channels that change at the same tick are reported together.
863    pub fn next_change(&mut self, mask: ChannelMask) -> impl Future<Output = Change> + '_ {
864        poll_fn(move |_| self.poll_change(mask, None))
865    }
866
867    /// Like [`SignalCursor::next_change`], but gives up at `until`: when no watched channel
868    /// changes before that tick, the cursor moves there and [`Change::Quiet`] is returned.
869    /// With an empty mask this waits for the samples up to `until` to arrive.
870    pub fn next_change_before(
871        &mut self,
872        mask: ChannelMask,
873        until: Tick,
874    ) -> impl Future<Output = Change> + '_ {
875        poll_fn(move |_| self.poll_change(mask, Some(until)))
876    }
877
878    /// Waits for the sample at the cursor and reads every channel's level there, without
879    /// moving the cursor.
880    pub fn sample(&mut self) -> impl Future<Output = Sample> + '_ {
881        poll_fn(move |_| self.poll_sample())
882    }
883}
884
885#[cfg(test)]
886mod tests {
887    use super::*;
888
889    fn block(start: u64, samples: &[u64], channels: u8) -> LogicBlock {
890        let mut builder = LogicBlockBuilder::new(Tick(start), channels);
891        for levels in samples {
892            builder.push(Levels(*levels));
893        }
894        builder.finish().expect("a block")
895    }
896
897    #[test]
898    fn builder_packs_bits_per_channel() {
899        let block = block(10, &[0b01, 0b11, 0b10, 0b00], 2);
900        assert_eq!(block.len(), 4);
901        assert_eq!(block.end(), Tick(14));
902        assert_eq!(block.words(0), &[0b0011]);
903        assert_eq!(block.words(1), &[0b0110]);
904        assert_eq!(block.level(0, Tick(10)), Some(true));
905        assert_eq!(block.level(1, Tick(10)), Some(false));
906        assert_eq!(block.level(0, Tick(14)), None);
907        assert_eq!(block.levels(Tick(11)), Some(Levels(0b11)));
908    }
909
910    #[test]
911    fn interleaved_bytes_follow_the_export_layout() {
912        let block =
913            LogicBlock::from_interleaved(Tick(0), 12, &[0x01, 0x08, 0xff, 0x0f]).expect("a block");
914        assert_eq!(block.len(), 2);
915        assert_eq!(block.levels(Tick(0)), Some(Levels(0x801)));
916        assert_eq!(block.levels(Tick(1)), Some(Levels(0xfff)));
917        let four = LogicBlock::from_interleaved(Tick(5), 4, &[0x3, 0x0, 0xc]).expect("a block");
918        assert_eq!(four.len(), 3);
919        assert_eq!(four.levels(Tick(7)), Some(Levels(0xc)));
920    }
921
922    /// The sample-by-sample construction `from_interleaved` replaced.
923    fn interleaved_by_sample(
924        start: Tick,
925        channels: u8,
926        bytes: &[u8],
927    ) -> Result<LogicBlock, BlockError> {
928        let unit = usize::from(channels).div_ceil(8).max(1);
929        let len = bytes.len() / unit;
930        let mut builder = LogicBlockBuilder::new(start, channels);
931        for sample in bytes.chunks_exact(unit).take(len) {
932            let mut levels = 0u64;
933            for (group, byte) in sample.iter().enumerate() {
934                levels |= u64::from(*byte) << (8 * group);
935            }
936            builder.push(Levels(levels));
937        }
938        builder.finish()
939    }
940
941    proptest::proptest! {
942        #[test]
943        fn interleaved_matches_the_sample_by_sample_build(
944            channels in 0u8..=64,
945            bytes in proptest::collection::vec(proptest::prelude::any::<u8>(), 0..1200),
946        ) {
947            let fast = LogicBlock::from_interleaved(Tick(3), channels, &bytes);
948            let slow = interleaved_by_sample(Tick(3), channels, &bytes);
949            proptest::prop_assert_eq!(fast, slow);
950        }
951    }
952
953    #[test]
954    fn transposes_eight_by_eight() {
955        // Row 0 all ones becomes column 0 all ones; the identity stays itself.
956        assert_eq!(transpose_8x8(0xff), 0x0101_0101_0101_0101);
957        assert_eq!(transpose_8x8(0x0101_0101_0101_0101), 0xff);
958        let identity = (0..8).fold(0u64, |m, i| m | 1u64 << (9 * i));
959        assert_eq!(transpose_8x8(identity), identity);
960        // Element (row 2, column 5) moves to (row 5, column 2).
961        assert_eq!(transpose_8x8(1 << (8 * 2 + 5)), 1 << (8 * 5 + 2));
962    }
963
964    #[test]
965    fn rejects_bad_blocks() {
966        assert_eq!(
967            LogicBlockBuilder::new(Tick(0), 1)
968                .finish()
969                .expect_err("fails"),
970            BlockError::Empty
971        );
972        assert!(matches!(
973            LogicBlock::from_words(Tick(0), 65, 1, vec![0]),
974            Err(BlockError::WrongSize {
975                expected: 2,
976                found: 1,
977                ..
978            })
979        ));
980        assert_eq!(
981            LogicBlock::from_words(Tick(0), 1, 65, vec![]).expect_err("fails"),
982            BlockError::TooManyChannels(65)
983        );
984    }
985
986    #[test]
987    fn finds_changes_within_and_across_words() {
988        let mut samples = vec![0u64; 100];
989        samples[3] = 1;
990        samples[4] = 1;
991        samples[64] = 2;
992        samples[99] = 3;
993        let block = block(0, &samples, 2);
994        let both = ChannelMask::one(0).with(1);
995        assert_eq!(
996            block.next_change(both, Tick(0), Levels(0)),
997            Some((Tick(3), ChannelMask::one(0)))
998        );
999        assert_eq!(
1000            block.next_change(both, Tick(4), Levels(1)),
1001            Some((Tick(5), ChannelMask::one(0)))
1002        );
1003        assert_eq!(
1004            block.next_change(both, Tick(6), Levels(0)),
1005            Some((Tick(64), ChannelMask::one(1)))
1006        );
1007        assert_eq!(
1008            block.next_change(ChannelMask::one(0), Tick(6), Levels(0)),
1009            Some((Tick(99), ChannelMask::one(0)))
1010        );
1011        assert_eq!(block.next_change(both, Tick(99), Levels(0b11)), None);
1012        assert_eq!(block.next_change(both, Tick(100), Levels(0)), None);
1013        // `prev` stands for the tick before `from`: a difference there is a change at `from`.
1014        assert_eq!(
1015            block.next_change(both, Tick(0), Levels(0b11)),
1016            Some((Tick(0), both))
1017        );
1018    }
1019
1020    #[test]
1021    fn padding_bits_are_no_change() {
1022        let block = block(0, &[1, 1, 1], 1);
1023        assert_eq!(
1024            block.next_change(ChannelMask::one(0), Tick(1), Levels(1)),
1025            None
1026        );
1027    }
1028
1029    #[test]
1030    fn slices_copy_the_samples() {
1031        let samples: Vec<u64> = (0..200).map(|i| i % 3).collect();
1032        let block = block(1000, &samples, 2);
1033        let slice = block
1034            .slice(TickRange::new(Tick(1063), Tick(1130)))
1035            .expect("a slice");
1036        assert_eq!(slice.start(), Tick(1063));
1037        assert_eq!(slice.len(), 67);
1038        for tick in 1063..1130 {
1039            assert_eq!(slice.levels(Tick(tick)), block.levels(Tick(tick)));
1040        }
1041        assert!(block.slice(TickRange::new(Tick(999), Tick(1001))).is_none());
1042        assert!(block.slice(TickRange::at(Tick(1001))).is_none());
1043    }
1044
1045    #[test]
1046    fn rebasing_moves_the_samples() {
1047        let block = block(10, &[1, 0, 1], 1);
1048        let moved = block.rebased(Tick(500));
1049        assert_eq!(moved.range(), TickRange::new(Tick(500), Tick(503)));
1050        assert_eq!(moved.level(0, Tick(502)), Some(true));
1051        assert_eq!(moved.words(0), block.words(0));
1052    }
1053
1054    use std::cell::RefCell;
1055    use std::future::Future;
1056    use std::pin::Pin;
1057    use std::rc::Rc;
1058    use std::task::{Context, Poll, Waker};
1059
1060    use crate::node::NodeCx;
1061    use crate::stream::{Broadcast, Event};
1062
1063    fn poll<F: Future>(future: Pin<&mut F>) -> Poll<F::Output> {
1064        future.poll(&mut Context::from_waker(Waker::noop()))
1065    }
1066
1067    /// A cursor on a queue the test pushes into by hand.
1068    fn cursor() -> (SignalCursor, Rc<RefCell<Broadcast<Arc<LogicBlock>>>>) {
1069        let queue = Rc::new(RefCell::new(Broadcast::new(16)));
1070        let cx = NodeCx::new();
1071        cx.refill(1_000);
1072        (SignalCursor::new(Inbox::new(Rc::clone(&queue), cx)), queue)
1073    }
1074
1075    fn push(queue: &Rc<RefCell<Broadcast<Arc<LogicBlock>>>>, block: LogicBlock) {
1076        let last = Tick(block.end().0 - 1);
1077        let item = Item::Event(Event {
1078            seq: 0,
1079            span: block.range(),
1080            settled: last,
1081            payload: Arc::new(block),
1082        });
1083        queue
1084            .borrow_mut()
1085            .try_push(item)
1086            .map_err(|_| ())
1087            .expect("fits");
1088    }
1089
1090    #[test]
1091    fn seeks_ahead_and_keeps_the_level_before_the_position() {
1092        let (mut cursor, queue) = cursor();
1093        // Channel 0: low for 100 ticks, then high from 100 on; cut into blocks of 40.
1094        let mut samples = vec![0u64; 100];
1095        samples.extend(std::iter::repeat_n(1u64, 60));
1096        let whole = block(0, &samples, 1);
1097        for start in (0..160).step_by(40) {
1098            push(
1099                &queue,
1100                whole
1101                    .slice(TickRange::new(Tick(start), Tick(start + 40)))
1102                    .expect("a slice"),
1103            );
1104        }
1105        cursor.seek(Tick(100));
1106        // The edge at exactly the sought tick is found: the level before it is known.
1107        let change = poll(Box::pin(cursor.next_change(ChannelMask::one(0))).as_mut());
1108        assert_eq!(
1109            change,
1110            Poll::Ready(Change::At {
1111                tick: Tick(100),
1112                changed: ChannelMask::one(0),
1113                levels: Levels(1)
1114            })
1115        );
1116        cursor.seek(Tick(50));
1117        assert_eq!(cursor.position(), Tick(101), "a seek never moves back");
1118        cursor.seek(Tick(130));
1119        let sample = poll(Box::pin(cursor.sample()).as_mut());
1120        assert_eq!(
1121            sample,
1122            Poll::Ready(Sample::At {
1123                tick: Tick(130),
1124                levels: Levels(1)
1125            })
1126        );
1127    }
1128
1129    #[test]
1130    fn a_deadline_ends_the_wait_quietly() {
1131        let (mut cursor, queue) = cursor();
1132        // Channel 0 rises at 30 and stays high.
1133        let mut samples = vec![0u64; 50];
1134        for sample in &mut samples[30..] {
1135            *sample = 1;
1136        }
1137        push(&queue, block(0, &samples, 1));
1138        let until =
1139            poll(Box::pin(cursor.next_change_before(ChannelMask::one(0), Tick(20))).as_mut());
1140        assert_eq!(until, Poll::Ready(Change::Quiet(Tick(20))));
1141        assert_eq!(cursor.position(), Tick(20));
1142        let found =
1143            poll(Box::pin(cursor.next_change_before(ChannelMask::one(0), Tick(40))).as_mut());
1144        assert!(matches!(
1145            found,
1146            Poll::Ready(Change::At { tick: Tick(30), .. })
1147        ));
1148        // A deadline already passed is quiet at once.
1149        let past =
1150            poll(Box::pin(cursor.next_change_before(ChannelMask::one(0), Tick(10))).as_mut());
1151        assert_eq!(past, Poll::Ready(Change::Quiet(Tick(31))));
1152        // A deadline beyond the block waits for the next block.
1153        let pending =
1154            poll(Box::pin(cursor.next_change_before(ChannelMask::one(0), Tick(80))).as_mut());
1155        assert_eq!(
1156            pending,
1157            Poll::Ready(Change::Progress(Tick(50))),
1158            "the used-up block is announced"
1159        );
1160        let pending =
1161            poll(Box::pin(cursor.next_change_before(ChannelMask::one(0), Tick(80))).as_mut());
1162        assert_eq!(pending, Poll::Pending);
1163        push(&queue, block(50, &[1u64; 50], 1));
1164        let quiet =
1165            poll(Box::pin(cursor.next_change_before(ChannelMask::one(0), Tick(80))).as_mut());
1166        assert_eq!(quiet, Poll::Ready(Change::Quiet(Tick(80))));
1167    }
1168
1169    #[test]
1170    fn a_deadline_at_the_end_of_a_block_needs_no_next_block() {
1171        let (mut cursor, queue) = cursor();
1172        push(&queue, block(0, &[0u64; 50], 1));
1173        let quiet =
1174            poll(Box::pin(cursor.next_change_before(ChannelMask::one(0), Tick(50))).as_mut());
1175        assert_eq!(quiet, Poll::Ready(Change::Quiet(Tick(50))));
1176        assert_eq!(cursor.position(), Tick(50));
1177        // The used-up block is still announced before the cursor waits.
1178        assert_eq!(
1179            poll(Box::pin(cursor.next_change(ChannelMask::one(0))).as_mut()),
1180            Poll::Ready(Change::Progress(Tick(50)))
1181        );
1182        assert_eq!(
1183            poll(Box::pin(cursor.next_change(ChannelMask::one(0))).as_mut()),
1184            Poll::Pending
1185        );
1186    }
1187
1188    #[test]
1189    fn a_used_up_block_is_announced_once() {
1190        let (mut cursor, queue) = cursor();
1191        push(&queue, block(0, &[0u64; 10], 1));
1192        assert_eq!(
1193            poll(Box::pin(cursor.next_change(ChannelMask::one(0))).as_mut()),
1194            Poll::Ready(Change::Progress(Tick(10)))
1195        );
1196        assert_eq!(
1197            poll(Box::pin(cursor.next_change(ChannelMask::one(0))).as_mut()),
1198            Poll::Pending
1199        );
1200        assert_eq!(poll(Box::pin(cursor.sample()).as_mut()), Poll::Pending);
1201        // The edge on the first sample of the next block is still found.
1202        push(&queue, block(10, &[1u64; 10], 1));
1203        assert!(matches!(
1204            poll(Box::pin(cursor.next_change(ChannelMask::one(0))).as_mut()),
1205            Poll::Ready(Change::At { tick: Tick(10), .. })
1206        ));
1207    }
1208
1209    #[test]
1210    fn the_first_sample_after_a_gap_is_no_edge() {
1211        let (mut cursor, queue) = cursor();
1212        push(&queue, block(0, &[0u64; 10], 1));
1213        queue
1214            .borrow_mut()
1215            .try_push(Item::Gap(TickRange::new(Tick(10), Tick(20))))
1216            .map_err(|_| ())
1217            .expect("fits");
1218        push(&queue, block(20, &[1u64; 10], 1));
1219        assert_eq!(
1220            poll(Box::pin(cursor.next_change(ChannelMask::one(0))).as_mut()),
1221            Poll::Ready(Change::Gap(TickRange::new(Tick(10), Tick(20))))
1222        );
1223        assert_eq!(
1224            poll(Box::pin(cursor.sample()).as_mut()),
1225            Poll::Ready(Sample::At {
1226                tick: Tick(20),
1227                levels: Levels(1)
1228            })
1229        );
1230        assert_eq!(
1231            poll(Box::pin(cursor.next_change(ChannelMask::one(0))).as_mut()),
1232            Poll::Ready(Change::Progress(Tick(30)))
1233        );
1234    }
1235
1236    #[test]
1237    fn masks_iterate_channels() {
1238        let mask = ChannelMask::one(3).with(0).with(63);
1239        assert_eq!(mask.iter().collect::<Vec<_>>(), vec![0, 3, 63]);
1240        assert!(ChannelMask::EMPTY.iter().next().is_none());
1241    }
1242}