holos_tda/distributed/
manifest.rs1use super::compose::{job_id, require_canonical_vertices};
2use super::model::{ArtifactId, DistributedInterfaceError, DistributedInterfaceManifest};
3use super::wire::{
4 MANIFEST_MAGIC, Reader, VERSION, decode_ids, decode_usizes, encode_ids, encode_usizes,
5 put_usize,
6};
7
8impl DistributedInterfaceManifest {
9 pub fn job(&self) -> ArtifactId {
11 self.job
12 }
13
14 pub fn max_dim(&self) -> usize {
16 self.max_dim
17 }
18
19 pub fn modulus(&self) -> u32 {
21 self.modulus
22 }
23
24 pub fn separator_vertices(&self) -> &[usize] {
26 &self.separator_vertices
27 }
28
29 pub fn output_protected_vertices(&self) -> &[usize] {
31 &self.output_protected_vertices
32 }
33
34 pub fn shards(&self) -> &[ArtifactId] {
36 &self.shards
37 }
38
39 pub fn folds(&self) -> &[ArtifactId] {
41 &self.folds
42 }
43
44 pub fn result(&self) -> ArtifactId {
46 self.result
47 }
48
49 pub fn encode(&self) -> Result<Vec<u8>, DistributedInterfaceError> {
51 let mut output = Vec::new();
52 output.extend_from_slice(MANIFEST_MAGIC);
53 output.extend_from_slice(&VERSION.to_be_bytes());
54 output.extend_from_slice(self.job.as_bytes());
55 put_usize(&mut output, self.max_dim)?;
56 output.extend_from_slice(&self.modulus.to_be_bytes());
57 encode_usizes(&mut output, &self.separator_vertices)?;
58 encode_usizes(&mut output, &self.output_protected_vertices)?;
59 encode_ids(&mut output, &self.shards)?;
60 encode_ids(&mut output, &self.folds)?;
61 output.extend_from_slice(self.result.as_bytes());
62 Ok(output)
63 }
64
65 pub fn decode(bytes: &[u8], maximum_bytes: usize) -> Result<Self, DistributedInterfaceError> {
67 check_manifest_size(bytes.len(), maximum_bytes)?;
68 let mut reader = Reader::new(bytes);
69 check_manifest_identity(&mut reader)?;
70 let manifest = decode_manifest_body(&mut reader)?;
71 check_manifest_shape(&manifest, reader.remaining())?;
72 check_manifest_binding(&manifest)?;
73 Ok(manifest)
74 }
75}
76
77fn check_manifest_size(actual: usize, limit: usize) -> Result<(), DistributedInterfaceError> {
78 if actual > limit {
79 return Err(DistributedInterfaceError::new(
80 "manifest exceeds the byte limit",
81 ));
82 }
83 Ok(())
84}
85
86fn check_manifest_identity(reader: &mut Reader<'_>) -> Result<(), DistributedInterfaceError> {
87 if reader.take(8)? != MANIFEST_MAGIC || reader.u16()? != VERSION {
88 return Err(DistributedInterfaceError::new(
89 "unsupported distributed manifest",
90 ));
91 }
92 Ok(())
93}
94
95fn decode_manifest_body(
96 reader: &mut Reader<'_>,
97) -> Result<DistributedInterfaceManifest, DistributedInterfaceError> {
98 Ok(DistributedInterfaceManifest {
99 job: ArtifactId(reader.array32()?),
100 max_dim: reader.usize()?,
101 modulus: reader.u32()?,
102 separator_vertices: decode_usizes(reader)?,
103 output_protected_vertices: decode_usizes(reader)?,
104 shards: decode_ids(reader)?,
105 folds: decode_ids(reader)?,
106 result: ArtifactId(reader.array32()?),
107 })
108}
109
110fn check_manifest_shape(
111 manifest: &DistributedInterfaceManifest,
112 remaining: usize,
113) -> Result<(), DistributedInterfaceError> {
114 if remaining != 0 || manifest.shards.is_empty() || manifest.folds.len() != manifest.shards.len()
115 {
116 return Err(DistributedInterfaceError::new(
117 "distributed manifest shape is invalid",
118 ));
119 }
120 require_canonical_vertices(&manifest.separator_vertices)?;
121 require_canonical_vertices(&manifest.output_protected_vertices)?;
122 Ok(())
123}
124
125fn check_manifest_binding(
126 manifest: &DistributedInterfaceManifest,
127) -> Result<(), DistributedInterfaceError> {
128 let expected = job_id(
129 manifest.max_dim,
130 manifest.modulus,
131 &manifest.separator_vertices,
132 &manifest.output_protected_vertices,
133 &manifest.shards,
134 );
135 if expected != manifest.job {
136 return Err(DistributedInterfaceError::new(
137 "distributed manifest binding is invalid",
138 ));
139 }
140 Ok(())
141}