Deserialize an expression from bytes (iterative to avoid stack overflow).
(buf: &mut &[u8])
| 255 | /// every delta must be ≥ 1, so the section is strictly ascending and |
| 256 | /// duplicate-free by construction. `addr_at(i)` resolves an index to |
| 257 | /// the i-th §2 address; the caller has just scanned §2, so the order |
| 258 | /// is at hand in every reader. |
| 259 | fn read_hints_section( |
| 260 | buf: &mut &[u8], |
| 261 | consts_len: usize, |
| 262 | addr_at: impl Fn(usize) -> Address, |
| 263 | reader: &str, |
| 264 | ) -> Result<Vec<(Address, ReducibilityHints)>, String> { |
| 265 | let n = get_u64(buf)? as usize; |
| 266 | if n > consts_len { |
| 267 | return Err(format!( |
| 268 | "{reader}: hint count {n} exceeds const count \ |
| 269 | {consts_len}{PRE_COMPACT_KEYS}" |
| 270 | )); |
| 271 | } |
| 272 | // Each hint entry needs at least two bytes (Tag0 delta + Tag0 hint). |
| 273 | if n > buf.len() / 2 { |
| 274 | return Err(format!("{reader}: hint count {n} exceeds remaining buffer")); |
| 275 | } |
| 276 | let mut hints = Vec::with_capacity(n); |
| 277 | // Index of the next entry must be ≥ cursor (= previous index + 1). |
| 278 | let mut cursor: u64 = 0; |
| 279 | for k in 0..n { |
| 280 | let delta = get_u64(buf)?; |
| 281 | if delta == 0 { |
| 282 | return Err(format!( |
| 283 | "{reader}: hint entry {k} has zero index delta (§3 must be \ |
| 284 | strictly ascending){PRE_COMPACT_KEYS}" |
| 285 | )); |
| 286 | } |
| 287 | let idx = cursor.checked_add(delta - 1).ok_or_else(|| { |
| 288 | format!( |
| 289 | "{reader}: hint entry {k} index delta overflows{PRE_COMPACT_KEYS}" |
| 290 | ) |
| 291 | })?; |
| 292 | if idx >= consts_len as u64 { |
| 293 | return Err(format!( |
| 294 | "{reader}: hint entry {k} resolves to constant index {idx}, \ |
| 295 | out of range ({consts_len} consts){PRE_COMPACT_KEYS}" |
| 296 | )); |
| 297 | } |
| 298 | let hint = unfuse_hint(get_u64(buf)?) |
| 299 | .map_err(|e| format!("{reader}: hint entry {k}: {e}"))?; |
| 300 | hints.push((addr_at(idx as usize), hint)); |
| 301 | cursor = idx + 1; |
| 302 | } |
| 303 | Ok(hints) |
| 304 | } |
| 305 | |
| 306 | fn get_address(buf: &mut &[u8]) -> Result<Address, String> { |
| 307 | if buf.len() < 32 { |
| 308 | return Err(format!("get_address: need 32 bytes, have {}", buf.len())); |
| 309 | } |
| 310 | let (bytes, rest) = buf.split_at(32); |
| 311 | *buf = rest; |
| 312 | Address::from_slice(bytes).map_err(|_| "get_address: invalid".to_string()) |
| 313 | } |
| 314 |