1use std::cell::RefCell;
4use std::collections::{BTreeMap, BTreeSet};
5use std::sync::Arc;
6
7use sva_ast::{Expr, Graph};
8use sva_formula::{Hash, NodeId};
9use sva_samples::{Buffer, Extent};
10
11use super::drive::Driver;
12use super::drive::Work;
13use super::end::{Ending, Fading, Heard, under};
14use super::terms::{Handle, NOTES, Terms, cut, placed};
15use super::value_graph::support::Supports;
16use super::value_graph::{Past, ValueGraph};
17use super::world::{Plan, Root, STREAMED, Walked, Wanted, World};
18use super::{Ends, RenderConfig, range_over};
19use crate::cache::{
20 Backend, CacheStats, Call, Counters, Kind, Memory, Phase, Recording, Stored, Tier,
21};
22use crate::error::{Diagnostic, EngineError, Located};
23use crate::recent::Recent;
24
25#[cfg(test)]
26mod rebuilt;
27
28pub const LATEST: usize = 256;
30
31#[derive(Clone, Debug, PartialEq)]
32pub struct StreamConfig {
33 pub block: usize,
34 pub channels: Option<usize>,
36 pub render: RenderConfig,
37}
38
39pub struct Stream {
42 config: StreamConfig,
43 world: World,
44 driver: Driver,
45 expr: Expr,
46 terms: Terms,
47 width: usize,
48 heard: BTreeMap<Handle, Heard>,
50 gain: Gained,
51 fading: Vec<Fading>,
53 faded: f64,
54 ending: Option<Option<i64>>,
56 treated_as_silent_from_sample: Option<i64>,
58 generation: u64,
59 live: bool,
60 dropped: Recent<String>,
61 late: usize,
62 built: Built,
63 demands: usize,
64 call: Call,
65}
66
67#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
68pub struct Counts {
69 pub dropped: usize,
70 pub late: usize,
72 pub terms: usize,
73 pub built: Built,
75 pub demands: usize,
77 pub tier: Counters,
78}
79
80#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
82pub struct Built {
83 pub parsed: usize,
85 pub instances: usize,
86 pub visited: usize,
87 pub typed: usize,
88 pub values: usize,
89 pub copied: usize,
91 pub lookups: usize,
92}
93
94impl Stream {
95 pub async fn open<B: Backend>(
96 graph: &Graph,
97 target: &Expr,
98 config: StreamConfig,
99 tier: &Tier<B>,
100 ) -> Result<Stream, EngineError> {
101 let call = tier.call(Kind::Stream, STREAMED, Phase::Typing);
102 Stream::open_called(graph, target, config, tier, call).await
103 }
104
105 pub async fn open_called<B: Backend>(
107 graph: &Graph,
108 target: &Expr,
109 config: StreamConfig,
110 tier: &Tier<B>,
111 call: Call,
112 ) -> Result<Stream, EngineError> {
113 call.phase(Phase::Typing);
114 let render = blocked(&config)?.render.clone();
115 if graph.defines(STREAMED) {
116 return Err(refusal(format!(
117 "this composition already has a node named `{STREAMED}`"
118 )));
119 }
120 let world = World::over(graph, render.rate, !graph.defines(NOTES));
121 let memory = tier.memory().clone();
122 let recording = Recording::over(&memory).latest(LATEST);
123 let value_graph = ValueGraph::new(&render.profile);
124 let memo = (memory, recording);
125 let driver = Driver::new(value_graph, Extent::new(0, 0), config.block, &render, memo);
126 let mut stream = Stream {
127 world,
128 driver,
129 expr: target.clone(),
130 terms: Terms::default(),
131 width: 0,
132 heard: BTreeMap::new(),
133 gain: Gained::default(),
134 fading: Vec::new(),
135 faded: 0.0,
136 ending: None,
137 treated_as_silent_from_sample: None,
138 generation: 0,
139 live: false,
140 dropped: Recent::keeping(LATEST),
141 late: 0,
142 built: Built::default(),
143 demands: 0,
144 config,
145 call,
146 };
147 let opening = Prospect {
148 target: target.clone(),
149 terms: Terms::default(),
150 term: None,
151 from: None,
152 answer: Changed::Edited,
153 landing: None,
154 parsed: 1,
155 };
156 let mut local = Local::default();
157 let round = tier.begin();
158 loop {
159 match stream.attempt(&opening, &mut local, (tier.memory(), round))? {
160 Attempt::Landed(_) => break,
161 Attempt::Asks(keys) => {
162 stream.call.phase(Phase::LookingUp);
163 local.look(keys, tier, round).await
164 }
165 Attempt::Reads(wants) => {
166 stream.call.phase(Phase::Fetching);
167 local.read(wants, tier).await
168 }
169 Attempt::Moved => unreachable!("nothing plays a stream before it opens"),
170 }
171 }
172 let mut needs = stream.needs();
173 while !needs.is_empty() {
174 let fetched = tier.fetch(&needs).await;
175 for (key, parts) in fetched.handed {
176 stream.driver.value_graph.took(key, &parts);
177 }
178 needs = fetched.left;
179 }
180 stream.call.rest();
181 stream.delivered();
182 Ok(stream)
183 }
184
185 fn delivered(&self) {
187 let driver = &self.driver;
188 let last = driver.end().unwrap_or(driver.last());
189 let total = (last != i64::MAX).then(|| (last - driver.start) as u64);
190 let every = u64::from(self.config.render.rate / 10);
191 let samples = (driver.at - driver.start) as u64;
192 self.call.delivered((samples, total, every), || {
193 (driver.work.computed_samples, driver.recording.tally())
194 });
195 }
196
197 pub fn graph(&self) -> &Graph {
198 &self.world.graph
199 }
200
201 fn prospect(&self, change: Change) -> Result<Prospect, Changed> {
202 let mut landing = None;
203 let prospect = |terms, term: Option<(Handle, Expr)>, from: Option<Graph>, answer| {
204 let roots = |(handle, term): &(Handle, Expr)| sva_ast::reads_of(&handle.node(), term);
205 Prospect {
206 target: self.expr.clone(),
207 terms,
208 from: from.map(|graph| (graph, term.as_ref().map(roots).unwrap_or_default())),
209 term,
210 answer,
211 landing: None,
212 parsed: 1,
213 }
214 };
215 Ok(match change {
216 Change::Target(graph, target) => {
217 let roots = sva_ast::reads_of(STREAMED, &target);
218 Prospect {
219 target,
220 from: Some((graph, roots)),
221 ..prospect(self.terms.clone(), None, None, Changed::Edited)
222 }
223 }
224 Change::Add(graph, term, at) => {
225 let term = match at {
226 Placed::Written => term,
227 Placed::Landing => placed(&term, *landing.insert(self.driver.at)),
228 };
229 let (terms, handle) = self.terms.added();
230 Prospect {
231 landing,
232 ..prospect(
233 terms,
234 Some((handle, term)),
235 Some(graph),
236 Changed::Added(handle),
237 )
238 }
239 }
240 Change::Replace(handle, graph, term, at) => {
241 let landed = self.terms.landed(handle).ok_or(Changed::Held(false))?;
242 let term = match at {
243 Placed::Written => term,
244 Placed::Landing => placed(&term, landed),
245 };
246 let terms = self.terms.clone();
247 prospect(
248 terms,
249 Some((handle, term)),
250 Some(graph),
251 Changed::Held(true),
252 )
253 }
254 Change::Remove(handle) => {
255 let at = self.driver.at;
256 let terms = self.terms.removed(handle).ok_or(Changed::Held(false))?;
257 let held = self.world.graph.expr(&handle.node());
258 let term = cut(held.expect("a held term's node"), at);
259 Prospect {
260 parsed: 0,
261 ..prospect(terms, Some((handle, term)), None, Changed::Held(true))
262 }
263 }
264 })
265 }
266
267 fn attempt(
270 &mut self,
271 prospect: &Prospect,
272 local: &mut Local,
273 (memory, round): (&Memory, u64),
274 ) -> Result<Attempt, EngineError> {
275 if prospect.landing.is_some_and(|at| at != self.driver.at) {
276 return Ok(Attempt::Moved);
277 }
278 let wanted = Wanted {
279 root: Root::Streamed(&prospect.target),
280 terms: &prospect.terms,
281 term: prospect.term.as_ref().map(|(h, e)| (*h, e)),
282 from: prospect.from.as_ref().map(|(g, roots)| (g, roots.clone())),
283 whole: false,
284 };
285 let seen = &mut self.driver.recording;
286 let mut found = |node: &str, key: Hash| memory.answered((key, round), (node, &mut *seen));
287 let walked = self.world.plan(&wanted, &self.config.render, &mut found)?;
288 let mut plan = match walked {
289 Walked::Asks(keys) => return Ok(Attempt::Asks(keys)),
290 Walked::Planned(plan) => plan,
291 };
292 let built = self.built(&mut plan);
293 let (root, range, treated_as_silent_from_sample) = match built {
294 Ok(held) => held,
295 Err(e) => {
296 let freed = self.world.abort();
297 self.driver.value_graph.abort(&freed);
298 return Err(e);
299 }
300 };
301 let from = self.driver.at.max(range.start);
302 let future = Extent::new(from, self.last(range.end).max(from));
303 let value_graph = &mut self.driver.value_graph;
304 let short = value_graph.short((root, future, Past::Stored));
305 let opened = short
306 .into_iter()
307 .try_for_each(|at| value_graph.read_on(&self.world.typing, at, &BTreeMap::new()));
308 if let Err(e) = opened {
309 let freed = self.world.abort();
310 self.driver.value_graph.abort(&freed);
311 return Err(e);
312 }
313 let asked_range = ahead(from, self.config.render.rate);
314 let wants = self.driver.value_graph.needs_made(root, asked_range);
315 let wants: Vec<(Hash, Extent)> = wants
316 .into_iter()
317 .filter(|(key, over)| !local.holds(*key, *over))
318 .collect();
319 if !wants.is_empty() {
320 let freed = self.world.abort();
321 self.driver.value_graph.abort(&freed);
322 return Ok(Attempt::Reads(wants));
323 }
324 self.treated_as_silent_from_sample = treated_as_silent_from_sample;
325 Ok(Attempt::Landed(self.land(
326 prospect,
327 (plan, root, range),
328 local,
329 )))
330 }
331
332 fn built(&mut self, plan: &mut Plan) -> Result<(usize, Extent, Option<i64>), EngineError> {
335 let typing = &mut self.world.typing;
336 let id = typing
337 .id(STREAMED)
338 .ok_or_else(|| EngineError::UnknownNode(STREAMED.to_string()))?;
339 let hits: BTreeMap<NodeId, Arc<Stored>> = plan
340 .stored
341 .iter()
342 .filter_map(|(path, stored)| Some((typing.id(path)?, Arc::clone(stored))))
343 .collect();
344 let value_graph = &mut self.driver.value_graph;
345 let root = value_graph.grow(typing, id, &hits)?;
346 let plays = value_graph.values[root].width;
347 let mut render = self.config.render.clone();
348 match self.width {
349 0 if self.config.channels == Some(0) => {
350 return Err(refusal("a stream of no channels".to_string()));
351 }
352 0 => widens(plays, self.config.channels.unwrap_or(plays))?,
353 width => {
354 widens(plays, width)?;
355 render.range.start = Some(self.driver.start);
356 }
357 }
358 let supports = Supports::over(typing, Some(&value_graph.supports));
359 let ending = Ending::new(typing, &render.profile, &supports);
360 let end = match (render.range.end, self.live) {
361 (None, false) => ending.of(id),
362 _ => ending.exact(id),
363 };
364 let range = range_over((&render, STREAMED), end.support, Ends::Pulled)?;
365 Ok((root, range, end.treated_as_silent_from_sample))
366 }
367
368 fn land(
370 &mut self,
371 prospect: &Prospect,
372 (mut plan, root, range): (Plan, usize, Extent),
373 local: &mut Local,
374 ) -> Changed {
375 let freed = self.world.commit(std::mem::take(&mut plan.found));
376 let now = self.driver.at;
377 let carried = self
378 .driver
379 .value_graph
380 .settled(root, &freed, (now, self.live));
381 self.driver
382 .value_graph
383 .offers(&self.world.typing, range, None);
384 for (key, parts) in &local.fetched {
385 self.driver.value_graph.took(*key, parts);
386 }
387 for at in &carried.silent {
388 self.dropped
389 .push(self.driver.value_graph.values[*at].name.clone());
390 }
391 match self.width {
392 0 => {
393 let plays = self.driver.value_graph.values[root].width;
394 self.width = self.config.channels.unwrap_or(plays);
395 (self.driver.start, self.driver.at) = (range.start, range.start);
396 }
397 _ => self.late += usize::from(self.driver.at > local.issued),
398 }
399 let last = self.last(range.end);
400 self.driver.bound(last);
401 self.expr = prospect.target.clone();
402 self.terms = prospect.terms.clone();
403 if let Changed::Added(handle) = prospect.answer {
404 self.terms.land(handle, self.driver.at);
405 }
406 self.hear();
407 self.built = Built {
408 parsed: prospect.parsed + plan.adopted,
409 instances: plan.named,
410 visited: plan.visited,
411 typed: self.world.typing.lowered().len(),
412 values: self.driver.value_graph.built,
413 copied: carried.taken,
414 lookups: local.lookups,
415 };
416 self.generation += 1;
417 self.retire_terms_below_silence_threshold();
418 prospect.answer
419 }
420
421 fn hear(&mut self) {
424 let typing = &self.world.typing;
425 let supports = Supports::over(typing, Some(&self.driver.value_graph.supports));
426 let ending = Ending::new(typing, &self.config.render.profile, &supports);
427 let ends = typing.id(STREAMED).zip(typing.id(NOTES));
428 let key = ends.and_then(|(root, notes)| ending.gain_key(root, notes));
429 let gain = match ends {
430 _ if key.is_some() && key == self.gain.of => self.gain.gain,
431 Some((root, notes)) => ending.gain(root, notes),
432 None => None,
433 };
434 let moved = gain != std::mem::replace(&mut self.gain, Gained { gain, of: key }).gain;
435 let lowered: BTreeSet<&str> = typing.lowered().iter().map(String::as_str).collect();
436 for handle in self.terms.handles() {
437 let node = handle.node();
438 if !moved && !lowered.contains(node.as_str()) && self.heard.contains_key(&handle) {
439 continue;
440 }
441 let id = typing.id(&node).expect("a sounding term is typed");
442 self.heard.insert(handle, ending.heard(id, gain));
443 }
444 self.ending = None;
445 }
446
447 fn needs(&self) -> Vec<(Hash, Extent)> {
448 let next = ahead(self.driver.at, self.config.render.rate);
449 self.driver.value_graph.needs(next)
450 }
451
452 pub fn read(&mut self, at: i64, n: usize) -> Result<Option<Buffer>, EngineError> {
455 let now = self.driver.at;
456 if at < now {
457 return Err(refused(
458 "engine.stream_behind",
459 format!("sample {at} is before sample {now}, where the stream stands"),
460 "read from the stream's position or later",
461 ));
462 }
463 if n == 0 {
464 return Err(refused(
465 "engine.empty_read",
466 format!("a read of no samples at sample {at}"),
467 "read one sample or more",
468 ));
469 }
470 match self.live {
471 true if at > now => {
472 for silenced in self.driver.skip(at)? {
473 self.dropped
474 .push(self.driver.value_graph.values[silenced].name.clone());
475 }
476 }
477 _ => {
478 let block = self.config.block;
479 while self.driver.at < at {
480 let step = block.min((at - self.driver.at) as usize);
481 self.reads_on(step)?;
482 if !self.driver.pulled(step)? {
483 return Ok(None);
484 }
485 self.retire_terms_below_silence_threshold();
486 }
487 }
488 }
489 self.reads_on(n)?;
490 let block = self.driver.read(n)?;
491 self.retire_terms_below_silence_threshold();
492 self.delivered();
493 Ok(block.map(|b| widened(b, self.width)))
494 }
495
496 fn reads_on(&mut self, n: usize) -> Result<(), EngineError> {
500 let at = self.driver.at;
501 let range = Extent::new(self.driver.start, self.driver.last());
502 let asked_range = Extent::new(at, at.saturating_add(n as i64).min(range.end).max(at));
503 let value_graph = &mut self.driver.value_graph;
504 if asked_range.is_empty() {
505 return Ok(());
506 }
507 for short in value_graph.short((value_graph.root, asked_range, Past::Held)) {
508 value_graph.read_on(&self.world.typing, short, &BTreeMap::new())?;
509 let landed = value_graph.landed(short);
510 let carried = value_graph.settled(value_graph.root, &[], (landed, self.live));
511 for silent in carried.silent {
512 self.dropped.push(value_graph.values[silent].name.clone());
513 }
514 value_graph.offers(&self.world.typing, range, None);
515 }
516 Ok(())
517 }
518
519 pub fn go_live(&mut self) {
522 self.live = true;
523 let last = self.last(self.driver.last());
524 self.driver.bound(last);
525 }
526
527 fn last(&self, range_end: i64) -> i64 {
528 match self.live {
529 true => self.config.render.range.end.unwrap_or(i64::MAX),
530 false => range_end,
531 }
532 }
533
534 pub fn dropped(&self) -> Vec<&str> {
536 self.dropped.iter().map(String::as_str).collect()
537 }
538
539 pub fn counts(&self) -> Counts {
540 Counts {
541 dropped: self.dropped.made(),
542 late: self.late,
543 terms: self.terms.count(),
544 built: self.built,
545 demands: self.demands,
546 tier: self.driver.recording.since(&self.driver.memory),
547 }
548 }
549
550 fn retire_terms_below_silence_threshold(&mut self) {
554 let now = self.driver.at;
555 let heard = &self.heard;
556 let ending = *self
557 .ending
558 .get_or_insert_with(|| heard.values().map(|h| h.support.end).min());
559 if ending.is_none_or(|end| end > now) {
560 return;
561 }
562 let (value_graph, tys) = (&self.driver.value_graph, &self.world.typing);
563 let last = self.driver.last();
564 let asked = match tys.id(NOTES).and_then(|notes| value_graph.of(notes)) {
565 Some(notes) if now < last => {
566 self.demands += 1;
567 let needs = value_graph.demand(Extent::new(now, last));
568 needs[notes].hold.iter().next().map(|asked| asked.start)
569 }
570 Some(_) => None,
571 None => Some(i64::MIN),
572 };
573 let silence_threshold = self.config.render.profile.silence_threshold_amplitude();
574 let gain = self.gain.gain.unwrap_or(f64::INFINITY);
575 if let Some(from) = asked {
576 let let_go = silence_threshold * 2f64.powi(-30);
578 let mut faded = self.faded;
579 self.fading.retain(|f| match f.from(from) {
580 b if b <= let_go => {
581 faded = (faded + b) * (1.0 + f64::EPSILON);
582 false
583 }
584 _ => true,
585 });
586 self.faded = faded;
587 }
588 let mut gone = BTreeSet::new();
589 for (handle, h) in &self.heard {
590 let end = h.support.end;
591 if end > now || asked.is_some_and(|from| end > from) {
592 continue;
593 }
594 let (Some(from), Some(fading)) = (asked, &h.fading) else {
595 gone.insert(*handle);
596 continue;
597 };
598 let own = fading.from(from);
599 if own > 0.0 {
600 let left: f64 = self.fading.iter().map(|f| f.from(from)).sum();
601 let ops = self.fading.len() as f64 + 3.0;
602 let left = (self.faded + left + own) * (1.0 + ops * f64::EPSILON);
603 if !under(gain, left, silence_threshold) {
604 continue;
605 }
606 self.fading.push(fading.clone());
607 }
608 gone.insert(*handle);
609 }
610 let heard = &self.heard;
611 let support = |handle: Handle| heard.get(&handle).map(|h| h.support);
612 let went = self
613 .terms
614 .retire(&|handle| gone.contains(&handle), &support);
615 if !went.is_empty() {
616 self.generation += 1;
617 }
618 for handle in went {
619 self.heard.remove(&handle);
620 self.ending = None;
621 }
622 }
623
624 pub fn evaluated(&self, node: &str) -> Vec<sva_samples::Extent> {
626 let value_graph = &self.driver.value_graph;
627 self.world
628 .typing
629 .id(node)
630 .and_then(|id| value_graph.of(id))
631 .map_or(Vec::new(), |at| value_graph.values[at].evaluated.clone())
632 }
633
634 pub fn cutting_below_silence_threshold(&self) -> sva_samples::CuttingBelowSilenceThreshold {
635 sva_samples::CuttingBelowSilenceThreshold {
636 silence_threshold_dbfs: self.config.render.profile.silence_threshold_dbfs,
637 treated_as_silent_from_sample: self
638 .treated_as_silent_from_sample
639 .map(|at| (STREAMED.to_string(), at))
640 .into_iter()
641 .collect(),
642 }
643 }
644
645 pub fn landed(&self, handle: Handle) -> Option<i64> {
646 self.terms.landed(handle)
647 }
648
649 pub fn position(&self) -> i64 {
650 self.driver.at
651 }
652
653 pub fn work(&self) -> Work {
654 self.driver.work
655 }
656
657 pub fn stats(&self) -> CacheStats {
658 self.driver.recording.stats(&self.driver.memory)
659 }
660
661 pub fn held_bytes(&self) -> usize {
663 self.driver.value_graph.bytes()
664 }
665
666 pub fn end(&self) -> Option<i64> {
667 self.driver.end()
668 }
669
670 pub fn width(&self) -> usize {
671 self.width
672 }
673
674 pub fn config(&self) -> &StreamConfig {
675 &self.config
676 }
677}
678
679#[derive(Clone, Copy, Default)]
680struct Gained {
681 gain: Option<f64>,
682 of: Option<Hash>,
683}
684
685pub enum Change {
687 Target(Graph, Expr),
688 Add(Graph, Expr, Placed),
689 Replace(Handle, Graph, Expr, Placed),
690 Remove(Handle),
692}
693
694#[derive(Clone, Copy, Debug, PartialEq, Eq)]
696pub enum Placed {
697 Written,
698 Landing,
699}
700
701#[derive(Clone, Copy, Debug, PartialEq, Eq)]
703pub enum Changed {
704 Edited,
705 Added(Handle),
706 Held(bool),
707}
708
709struct Prospect {
711 target: Expr,
712 terms: Terms,
713 term: Option<(Handle, Expr)>,
714 from: Option<(Graph, Vec<String>)>,
715 answer: Changed,
716 landing: Option<i64>,
717 parsed: usize,
719}
720
721enum Attempt {
723 Landed(Changed),
724 Asks(Vec<Hash>),
725 Reads(Vec<(Hash, Extent)>),
726 Moved,
728}
729
730#[derive(Default)]
732struct Local {
733 fetched: Vec<(Hash, Vec<Arc<Buffer>>)>,
734 asked: Vec<(Hash, Extent)>,
735 unread: BTreeSet<Hash>,
736 lookups: usize,
737 issued: i64,
738}
739
740impl Local {
741 async fn look<B: Backend>(&mut self, keys: Vec<Hash>, tier: &Tier<B>, round: u64) {
742 for key in keys {
743 self.lookups += 1;
744 tier.lookup(key, round).await;
745 }
746 }
747
748 async fn read<B: Backend>(&mut self, wants: Vec<(Hash, Extent)>, tier: &Tier<B>) {
750 let fetched = tier.fetch(&wants).await;
751 for (key, over) in wants {
752 if fetched.left.contains(&(key, over)) {
753 continue;
754 }
755 self.asked.push((key, over));
756 if !fetched.handed.iter().any(|(held, _)| *held == key) {
757 self.unread.insert(key);
758 }
759 }
760 self.fetched.extend(fetched.handed);
761 }
762
763 fn holds(&self, key: Hash, over: Extent) -> bool {
765 self.unread.contains(&key)
766 || self
767 .asked
768 .iter()
769 .any(|(k, e)| *k == key && e.intersect(over) == over)
770 }
771}
772
773pub async fn change<E: From<EngineError>, B: Backend>(
777 stream: &RefCell<Stream>,
778 build: impl FnMut(&Stream) -> Result<Change, E>,
779 tier: &Tier<B>,
780) -> Result<Changed, E> {
781 let target = stream.borrow().call.target().to_string();
782 let call = tier.call(Kind::Edit, &target, Phase::Typing);
783 let changed = changing(stream, build, (tier, &call)).await;
784 if changed.is_ok() {
785 call.end(Phase::Done);
786 }
787 changed
788}
789
790async fn changing<E: From<EngineError>, B: Backend>(
791 stream: &RefCell<Stream>,
792 mut build: impl FnMut(&Stream) -> Result<Change, E>,
793 (tier, call): (&Tier<B>, &Call),
794) -> Result<Changed, E> {
795 let mut local = Local {
796 issued: stream.borrow().driver.at,
797 ..Local::default()
798 };
799 let round = tier.begin();
800 stream.borrow_mut().driver.recording.begin();
801 loop {
802 let change = build(&stream.borrow())?;
803 let prospect = match stream.borrow().prospect(change) {
804 Ok(prospect) => prospect,
805 Err(answer) => return Ok(answer),
806 };
807 let generation = stream.borrow().generation;
808 loop {
809 if stream.borrow().generation != generation {
810 break;
811 }
812 let attempt =
813 stream
814 .borrow_mut()
815 .attempt(&prospect, &mut local, (tier.memory(), round))?;
816 match attempt {
817 Attempt::Landed(answer) => return Ok(answer),
818 Attempt::Moved => break,
819 Attempt::Asks(keys) => {
820 call.phase(Phase::LookingUp);
821 local.look(keys, tier, round).await
822 }
823 Attempt::Reads(wants) => {
824 call.phase(Phase::Fetching);
825 local.read(wants, tier).await
826 }
827 }
828 }
829 }
830}
831
832pub async fn fetch<B: Backend>(stream: &RefCell<Stream>, tier: &Tier<B>) {
835 let needs = stream.borrow().needs();
836 let fetched = tier.fetch(&needs).await;
837 let mut stream = stream.borrow_mut();
838 for (key, parts) in fetched.handed {
839 stream.driver.value_graph.took(key, &parts);
840 }
841}
842
843fn ahead(at: i64, rate: u32) -> Extent {
844 Extent::new(at, at.saturating_add(i64::from(rate)))
845}
846
847fn widens(plays: usize, width: usize) -> Result<(), EngineError> {
848 match plays == width || plays == 1 {
849 true => Ok(()),
850 false => Err(refused(
851 "engine.stream_width",
852 format!("this plays {plays} channel(s), and the stream plays {width}"),
853 "play as many channels as the stream, or one, or open a new stream for it",
854 )),
855 }
856}
857
858fn blocked(config: &StreamConfig) -> Result<&StreamConfig, EngineError> {
859 match config.block {
860 0 => Err(refusal("a block of no samples".to_string())),
861 _ => Ok(config),
862 }
863}
864
865pub(super) fn refusal(what: String) -> EngineError {
866 refused(
867 "engine.no_stream",
868 format!("this target opens no stream: {what}"),
869 "stream an expression over the nodes the composition defines",
870 )
871}
872
873fn refused(code: &str, message: String, help: &str) -> EngineError {
874 EngineError::refused(Diagnostic {
875 code: code.to_string(),
876 message,
877 location: Located::at(STREAMED, None),
878 help: help.to_string(),
879 })
880}
881
882fn widened(mut block: Buffer, width: usize) -> Buffer {
884 if block.planes.len() == 1 {
885 let copies = vec![block.planes[0].clone(); width.saturating_sub(1)];
886 block.planes.extend(copies);
887 }
888 block
889}