pub fn hash_reader(
reader: impl Read,
max_bytes: u64,
) -> Result<ArtifactIdentity, ArtifactError>Expand description
Hash one stream with constant working memory and no reliance on metadata.
Reads at most max_bytes + 1 bytes, using the extra byte to detect overflow.
Retries interrupted reads only; no subprocess or network retry is performed.
A blocking reader’s timeouts remain the caller’s responsibility.
§Errors
Returns ArtifactError::LimitExceeded or the underlying read error.
An impossible reader byte count returns IO io::ErrorKind::InvalidData.
Examples found in repository?
examples/inspect_response.rs (line 39)
9fn main() -> Result<(), Box<dyn Error>> {
10 let mut args = std::env::args_os().skip(1);
11 let path = args.next().ok_or_else(|| {
12 invalid_argument(
13 "usage: inspect_response PATH json|hex|labeled MAX_INPUT_BYTES MAX_DECODED_BYTES",
14 )
15 })?;
16 let format = match args.next().as_deref().and_then(std::ffi::OsStr::to_str) {
17 Some("json") => ResponseFormat::Json,
18 Some("hex") => ResponseFormat::Hex,
19 Some("labeled") => ResponseFormat::LabeledHex,
20 _ => return Err(invalid_argument("format must be json, hex or labeled").into()),
21 };
22 let mut limit = || -> Result<usize, Box<dyn Error>> {
23 Ok(args
24 .next()
25 .ok_or_else(|| invalid_argument("missing response limit"))?
26 .to_str()
27 .ok_or_else(|| invalid_argument("limits must be UTF-8"))?
28 .parse()?)
29 };
30 let limits = ResponseLimits {
31 input_bytes: limit()?,
32 decoded_bytes: limit()?,
33 };
34 if args.next().is_some() {
35 return Err(invalid_argument("unexpected argument").into());
36 }
37 let input = read_file(Path::new(&path), limits.input_bytes)?;
38 let bytes = decode(&input, format, limits)?;
39 let identity = hash_reader(bytes.as_slice(), u64::try_from(limits.decoded_bytes)?)?;
40 println!(
41 "response_bytes={} sha256={}",
42 identity.bytes, identity.sha256
43 );
44 Ok(())
45}More examples
examples/inspect_artifact.rs (line 45)
9fn main() -> Result<(), Box<dyn Error>> {
10 let mut args = std::env::args_os().skip(1);
11 let path = args.next().ok_or_else(|| invalid_argument("usage: inspect_artifact PATH MAX_BYTES MAX_SECTIONS MAX_EXPORTS MAX_CUSTOM_SECTIONS [SHA256]"))?;
12 let mut number = || -> Result<usize, Box<dyn Error>> {
13 Ok(args
14 .next()
15 .ok_or_else(|| invalid_argument("missing inspection limit"))?
16 .to_str()
17 .ok_or_else(|| invalid_argument("inspection limits must be UTF-8"))?
18 .parse()?)
19 };
20 let limits = InspectionLimits {
21 module_bytes: number()?,
22 sections: number()?,
23 exports: u32::try_from(number()?)?,
24 custom_sections: number()?,
25 };
26 let expected = args
27 .next()
28 .map(|value| {
29 value
30 .into_string()
31 .map_err(|_| invalid_argument("digest must be UTF-8"))
32 })
33 .transpose()?;
34 if args.next().is_some() {
35 return Err(invalid_argument("unexpected argument").into());
36 }
37 let bytes = read_file(Path::new(&path), limits.module_bytes)?;
38 let identity = if let Some(expected) = expected {
39 verify_reader(
40 bytes.as_slice(),
41 limits.module_bytes as u64,
42 expected.parse()?,
43 )?
44 } else {
45 ic_host_tools::artifact::hash_reader(bytes.as_slice(), limits.module_bytes as u64)?
46 };
47 let facts = inspect(&bytes, limits)?;
48 println!(
49 "raw_bytes={} sha256={} code_section_bytes={} data_section_bytes={} defined_functions={} data_segments={}",
50 identity.bytes,
51 identity.sha256,
52 facts.code_section_bytes,
53 facts.data_section_bytes,
54 facts.defined_functions,
55 facts.data_segments
56 );
57 for (name, export) in facts.exports {
58 println!(
59 "export={name:?} kind={:?} index={}",
60 export.kind, export.index
61 );
62 }
63 for section in facts.custom_sections {
64 println!(
65 "custom_section={:?} bytes={}",
66 section.name,
67 section.data.len()
68 );
69 }
70 Ok(())
71}