Skip to main content

max / shop

kittygfx: decode chunked base64 into accumulator to cut alloc traffic feed() previously allocated a fresh Vec for each chunk's base64 decode, then extend_from_slice'd it into the reassembly accumulator. On a 4096-chunk 22MB transmission that came out to ~33.6 MB / 4121 alloc calls per parse; measurements pointed at per-chunk allocation churn as the bottleneck for shop's L1_parse throughput. Route raw base64 bytes into accept_chunked and decode straight into the accumulator's tail via base64::Engine::decode_slice. Single-shot path keeps the fresh-Vec decode since the caller owns the result. Payload-less actions (p/c/d) skip the wasted decode-and-drop but still validate any `;<payload>` was well-formed, preserving the malformed-input rejection. On mbp chunked-large-2048x2048-c4096: 33.6MB/4121 -> 16.85MB/25 alloc calls (50% bytes, 99% calls). Throughput 1432 -> 1847 MiB/s (+29%). Smaller fixtures unchanged or slight positive; no regressions.
Co-Authored-By
Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-07-24 22:19 UTC
Signed with PGP, not checked
Commit: b8c3d72439687e1f61ffb6fb605f931ca4595150
Parent: 99074a4
1 file changed, +45 insertions, -12 deletions
@@ -151,26 +151,35 @@
151 151 return None;
152 152 }
153 153
154 - let payload = match payload_b64 {
155 - Some(b) if !b.is_empty() => B64.decode(b).ok()?,
156 - _ => Vec::new(),
157 - };
158 -
159 154 match control.action {
160 155 // Payload-carrying actions share the chunk-reassembly path. A
161 156 // continuation chunk parses with the default action ('T'), so
162 157 // route the completed command using the *stored* first-chunk
163 158 // action, not the incoming chunk's action.
164 159 'T' | 't' | 'f' => {
165 - let (control, payload) = self.accept_chunked(control, payload)?;
160 + let b64 = payload_b64.unwrap_or(&[]);
161 + let (control, payload) = self.accept_chunked(control, b64)?;
166 162 Some(match control.action {
167 163 'f' => Command::FrameAppend { control, payload },
168 164 _ => Command::Transmit { control, payload },
169 165 })
170 166 }
171 - 'p' => Some(Command::Place { control }),
172 - 'c' => Some(Command::FrameCompose { control }),
173 - 'd' => Some(Command::Delete { control }),
167 + // Payload-less actions. If a `;<payload>` was sent anyway, validate
168 + // that it's decodable so a malformed one still errors — preserves
169 + // the pre-refactor contract without keeping the decoded bytes.
170 + 'p' | 'c' | 'd' => {
171 + if let Some(b) = payload_b64 {
172 + if !b.is_empty() && B64.decode(b).is_err() {
173 + return None;
174 + }
175 + }
176 + match control.action {
177 + 'p' => Some(Command::Place { control }),
178 + 'c' => Some(Command::FrameCompose { control }),
179 + 'd' => Some(Command::Delete { control }),
180 + _ => unreachable!(),
181 + }
182 + }
174 183 _ => {
175 184 tracing::trace!("kitty-graphics: unhandled action {}", control.action);
176 185 None
@@ -180,10 +189,15 @@
180 189
181 190 /// Reassemble chunked payload for actions that carry image data.
182 191 /// Returns `Some((control, payload))` when a transmission completes.
192 + ///
193 + /// Decodes base64 straight into the accumulator to avoid per-chunk
194 + /// transient allocation. On decode failure the accumulator is truncated
195 + /// back to its pre-call length so a bad chunk doesn't contaminate an
196 + /// in-flight transmission.
183 197 fn accept_chunked(
184 198 &mut self,
185 199 control: Control,
186 - payload: Vec<u8>,
200 + payload_b64: &[u8],
187 201 ) -> Option<(Control, Vec<u8>)> {
188 202 let key = if let Some(id) = control.id {
189 203 PartialKey::ById(id)
@@ -194,7 +208,13 @@
194 208 };
195 209
196 210 if !control.more_chunks && !self.partial.contains_key(&key) {
197 - // Single-shot — no reassembly needed.
211 + // Single-shot — no reassembly needed. Decode into a fresh Vec
212 + // sized to the payload; the caller owns the result.
213 + let payload = if payload_b64.is_empty() {
214 + Vec::new()
215 + } else {
216 + B64.decode(payload_b64).ok()?
217 + };
198 218 return Some((control, payload));
199 219 }
200 220
@@ -202,7 +222,20 @@
202 222 control: control.clone(),
203 223 payload: Vec::new(),
204 224 });
205 - entry.payload.extend_from_slice(&payload);
225 +
226 + if !payload_b64.is_empty() {
227 + let start = entry.payload.len();
228 + // Upper bound: 3 decoded bytes per 4 base64 chars, rounded up.
229 + let extra = payload_b64.len().div_ceil(4) * 3;
230 + entry.payload.resize(start + extra, 0);
231 + match B64.decode_slice(payload_b64, &mut entry.payload[start..]) {
232 + Ok(written) => entry.payload.truncate(start + written),
233 + Err(_) => {
234 + entry.payload.truncate(start);
235 + return None;
236 + }
237 + }
238 + }
206 239 // Merge in fields the first chunk didn't carry.
207 240 merge_control(&mut entry.control, &control);
208 241