From 94c97223ba8affaab08559c12105846b8fb14c7b Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Fri, 6 Oct 2023 20:48:37 +0200 Subject: [PATCH] Optimize stack usage of Dispatch::poll Dispatch::poll has to copy the request so that the app can write the response to the interchange. Previously, we called Responder::take_request to obtain the message out of the interchange. This implementation had a stack usage of 15280 bytes while the message size is only 7609 bytes. With this patch, we only request a reference from the responder and manually copy the message to a buffer. This reduces the stack size to 7664 bytes. I have no idea why the take_request implementation creates an additional copy of the message. But as this function is the root for all CTAPHID commands, this means we can save around 7 kB stack virtually everywhere. Similar optimizations may be possible in other functions too. --- CHANGELOG.md | 1 + src/dispatch.rs | 11 +++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 34ea1f9..24d3c39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +- Optimize stack usage of `Dispatch::poll` ## [0.1.1] - 2022-08-22 - adjust to `interchange` API change diff --git a/src/dispatch.rs b/src/dispatch.rs index 7a77fdb..951d3da 100644 --- a/src/dispatch.rs +++ b/src/dispatch.rs @@ -106,14 +106,17 @@ impl<'pipe, 'interrupt> Dispatch<'pipe, 'interrupt> { #[inline(never)] pub fn poll(&mut self, apps: &mut [&mut dyn App<'interrupt>]) -> bool { - let maybe_request = self.responder.take_request(); - if let Some((command, message)) = maybe_request { + // We could call take_request directly, but for some reason this doubles stack usage. + let mut message_buffer = Message::new(); + if let Ok((command, message)) = self.responder.request() { // info_now!("cmd: {}", u8::from(command)); // info_now!("cmd: {:?}", command); - if let Some(app) = Self::find_app(command, apps) { + message_buffer.extend_from_slice(message).unwrap(); + + if let Some(app) = Self::find_app(*command, apps) { // match app.call(command, self.responder.response_mut().unwrap()) { - self.call_app(*app, command, &message); + self.call_app(*app, *command, &message_buffer); } else { self.reply_with_error(Error::InvalidCommand); }