| 95 | fn push_node_id(buf: &mut Vec<u8>, id: NodeId) { |
| 96 | let id = u16::try_from(id.0).expect("node id exceeds u16 (vk wire format)"); |
| 97 | buf.extend_from_slice(&id.to_le_bytes()); |
| 98 | } |
| 99 | |
| 100 | fn push_node(buf: &mut Vec<u8>, node: &Node<Val>) { |
| 101 | match node { |
| 102 | Node::Const(c) => { |
| 103 | // Small/big constant split: most constants (selector weights, small |
| 104 | // literals) fit u16. |
| 105 | let v = c.as_canonical_u64(); |
| 106 | if let Ok(small) = u16::try_from(v) { |
| 107 | buf.push(0); |
| 108 | buf.extend_from_slice(&small.to_le_bytes()); |
| 109 | } else { |
| 110 | buf.push(1); |
| 111 | buf.extend_from_slice(&v.to_le_bytes()); |
| 112 | } |
| 113 | }, |
| 114 | Node::Var(col) => { |
| 115 | // Var tag packs (source, offset): 10 + 2·source + offset. |
| 116 | let source = match col.source { |
| 117 | Source::Preprocessed => 0u8, |
| 118 | Source::Main => 1, |
| 119 | Source::Stage2 => 2, |
| 120 | }; |
| 121 | let offset = match col.offset { |
| 122 | RowOffset::Current => 0u8, |
| 123 | RowOffset::Next => 1, |
| 124 | }; |
| 125 | buf.push(10 + 2 * source + offset); |
| 126 | let index = u16::try_from(col.index) |
| 127 | .expect("column index exceeds u16 (vk wire format)"); |
| 128 | buf.extend_from_slice(&index.to_le_bytes()); |
| 129 | }, |
| 130 | Node::Public(i) => { |
| 131 | buf.push(2); |
| 132 | buf.push(u8::try_from(*i).expect("public index exceeds u8")); |
| 133 | }, |
| 134 | Node::IsFirstRow => buf.push(3), |
| 135 | Node::IsLastRow => buf.push(4), |
| 136 | Node::IsTransition => buf.push(5), |
| 137 | Node::Add(a, b) => { |
| 138 | buf.push(6); |