tenferro_runtime/program/
bindings.rs1use std::fmt;
2use std::sync::Arc;
3
4use tenferro_tensor::Tensor;
5
6use super::value::ProgramBuilderNonce;
7use super::{BindingKey, ProgramValue};
8
9pub(crate) struct PendingBinding {
10 pub(crate) key: BindingKey,
11 pub(crate) input: ProgramValue,
12 pub(crate) tensor: Arc<Tensor>,
13}
14
15struct ProgramBinding {
16 key: BindingKey,
17 tensor: Arc<Tensor>,
18}
19
20#[derive(Clone)]
22pub struct ProgramBindings {
23 owner: ProgramBuilderNonce,
24 entries: Arc<[ProgramBinding]>,
25}
26
27impl ProgramBindings {
28 pub(crate) fn freeze(owner: ProgramBuilderNonce, pending: Vec<PendingBinding>) -> Self {
29 let mut entries: Vec<_> = pending
30 .into_iter()
31 .map(|binding| ProgramBinding {
32 key: binding.key,
33 tensor: binding.tensor,
34 })
35 .collect();
36 entries.sort_unstable_by_key(|entry| entry.key.slot);
37 Self {
38 owner,
39 entries: entries.into(),
40 }
41 }
42
43 pub(crate) fn belongs_to(&self, owner: ProgramBuilderNonce) -> bool {
44 self.owner == owner
45 }
46
47 pub(crate) fn tensor_for_input(&self, input: ProgramValue) -> Option<Arc<Tensor>> {
48 self.tensor_ref_for_input(input).map(Arc::clone)
49 }
50
51 pub(crate) fn tensor_ref_for_input(&self, input: ProgramValue) -> Option<&Arc<Tensor>> {
52 if input.owner != self.owner {
53 return None;
54 }
55 self.entries
56 .binary_search_by_key(&input.slot, |entry| entry.key.slot)
57 .ok()
58 .and_then(|index| self.entries.get(index))
59 .map(|entry| &entry.tensor)
60 }
61
62 pub(crate) fn bound_inputs(&self) -> impl ExactSizeIterator<Item = ProgramValue> + '_ {
63 self.entries
64 .iter()
65 .map(|entry| ProgramValue::new(entry.key.slot, self.owner))
66 }
67
68 #[allow(dead_code, reason = "wired into GraphCompiler in Phase 3 A3")]
69 pub(crate) fn preserves_all(&self, source: &Self) -> bool {
70 if self.entries.len() < source.entries.len() {
71 return false;
72 }
73 let mut matched = vec![false; self.entries.len()];
74 source.entries.iter().all(|source_entry| {
75 self.entries
76 .iter()
77 .enumerate()
78 .find(|(index, entry)| {
79 !matched[*index] && Arc::ptr_eq(&entry.tensor, &source_entry.tensor)
80 })
81 .map(|(index, _)| {
82 matched[index] = true;
83 })
84 .is_some()
85 })
86 }
87
88 #[allow(dead_code, reason = "wired into GraphCompiler in Phase 3 A3")]
89 pub(crate) fn cache_exact_eq(&self, other: &Self) -> bool {
90 self.entries.len() == other.entries.len()
91 && self
92 .entries
93 .iter()
94 .zip(other.entries.iter())
95 .all(|(left, right)| {
96 left.key.slot == right.key.slot && Arc::ptr_eq(&left.tensor, &right.tensor)
97 })
98 }
99
100 pub fn len(&self) -> usize {
102 self.entries.len()
103 }
104
105 pub fn is_empty(&self) -> bool {
107 self.entries.is_empty()
108 }
109
110 pub fn get(&self, key: BindingKey) -> Option<&Tensor> {
112 if key.owner != self.owner {
113 return None;
114 }
115 self.entries
116 .binary_search_by_key(&key.slot, |entry| entry.key.slot)
117 .ok()
118 .and_then(|index| self.entries.get(index))
119 .filter(|entry| entry.key == key)
120 .map(|entry| entry.tensor.as_ref())
121 }
122
123 pub fn iter(&self) -> impl ExactSizeIterator<Item = (BindingKey, &Tensor)> + '_ {
125 self.entries
126 .iter()
127 .map(|entry| (entry.key, entry.tensor.as_ref()))
128 }
129}
130
131impl fmt::Debug for ProgramBindings {
132 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
133 formatter
134 .debug_struct("ProgramBindings")
135 .field("len", &self.entries.len())
136 .finish()
137 }
138}