Zero-copy TX: span_writer packer, static buffers, no vector returns

This commit is contained in:
Ian Gulliver
2026-04-10 22:18:44 +09:00
parent 94895fd2fe
commit e2a5d97dae
10 changed files with 173 additions and 133 deletions

View File

@@ -22,20 +22,21 @@ void dispatch_schedule_ms(uint32_t ms, std::function<void()> fn) {
}
[[noreturn]] void dispatch_run(std::span<const handler_entry> handlers) {
std::unordered_map<int8_t, std::vector<std::vector<uint8_t>> (*)(uint32_t, std::span<const uint8_t>)> handler_map;
std::unordered_map<int8_t, handler_fn> handler_map;
for (auto& entry : handlers) {
handler_map[entry.type_id] = entry.handle;
}
static usb_cdc usb;
static static_vector<uint8_t, 256> usb_rx_buf;
static uint8_t tx_buf[1514];
net_set_handler([&](std::span<const uint8_t> payload) -> std::vector<std::vector<uint8_t>> {
net_set_handler([&](std::span<const uint8_t> payload, span_writer &out) -> size_t {
auto msg = try_decode(payload.data(), payload.size());
if (!msg) return {};
if (!msg) return 0;
auto it = handler_map.find(msg->type_id);
if (it == handler_map.end()) return {};
return it->second(msg->message_id, msg->payload);
if (it == handler_map.end()) return 0;
return it->second(msg->message_id, msg->payload, out);
});
while (true) {
@@ -62,13 +63,16 @@ void dispatch_schedule_ms(uint32_t ms, std::function<void()> fn) {
auto it = handler_map.find(msg->type_id);
if (it != handler_map.end()) {
for (auto& response : it->second(msg->message_id, msg->payload)) {
if (response.size() > usb.tx.free()) {
auto err = encode_response(msg->message_id,
DeviceError{2, "response too large: " + std::to_string(response.size())});
usb.send(err);
span_writer out(tx_buf, sizeof(tx_buf));
size_t resp_len = it->second(msg->message_id, msg->payload, out);
if (resp_len > 0) {
if (resp_len > usb.tx.free()) {
span_writer err_out(tx_buf, sizeof(tx_buf));
size_t err_len = encode_response_into(err_out, msg->message_id,
DeviceError{2, "response too large: " + std::to_string(resp_len)});
usb.send(std::span<const uint8_t>{tx_buf, err_len});
} else {
usb.send(response);
usb.send(std::span<const uint8_t>{tx_buf, resp_len});
}
}
}

View File

@@ -5,12 +5,12 @@
#include "net.h"
#include "debug_log.h"
std::vector<std::vector<uint8_t>> handle_picoboot(uint32_t message_id, std::span<const uint8_t>) {
size_t handle_picoboot(uint32_t message_id, std::span<const uint8_t>, span_writer &out) {
dispatch_schedule_ms(100, []{ reset_usb_boot(0, 1); });
return {encode_response(message_id, ResponsePICOBOOT{})};
return encode_response_into(out, message_id, ResponsePICOBOOT{});
}
std::vector<std::vector<uint8_t>> handle_info(uint32_t message_id, std::span<const uint8_t>) {
size_t handle_info(uint32_t message_id, std::span<const uint8_t>, span_writer &out) {
ResponseInfo resp;
pico_unique_board_id_t uid;
pico_get_unique_board_id(&uid);
@@ -19,12 +19,12 @@ std::vector<std::vector<uint8_t>> handle_info(uint32_t message_id, std::span<con
resp.mac = ns.mac;
resp.ip = ns.ip;
resp.firmware_name = firmware_name;
return {encode_response(message_id, resp)};
return encode_response_into(out, message_id, resp);
}
std::vector<std::vector<uint8_t>> handle_log(uint32_t message_id, std::span<const uint8_t>) {
size_t handle_log(uint32_t message_id, std::span<const uint8_t>, span_writer &out) {
ResponseLog resp;
for (auto& e : dlog_drain())
resp.entries.push_back(LogEntry{e.timestamp_us, std::move(e.message)});
return {encode_response(message_id, resp)};
return encode_response_into(out, message_id, resp);
}

View File

@@ -139,6 +139,8 @@ static void handle_arp(const uint8_t* frame, size_t len) {
send_raw(&reply, sizeof(reply));
}
static uint8_t tx_buf[1514];
static void handle_udp(const uint8_t* frame, size_t len) {
if (len < sizeof(udp_header)) return;
auto& pkt = *reinterpret_cast<const udp_header*>(frame);
@@ -154,41 +156,36 @@ static void handle_udp(const uint8_t* frame, size_t len) {
auto* payload = frame + sizeof(udp_header);
size_t payload_len = udp_len - 8;
auto responses = msg_handler(std::span<const uint8_t>{payload, payload_len});
span_writer resp(tx_buf + sizeof(udp_header), sizeof(tx_buf) - sizeof(udp_header));
size_t resp_len = msg_handler(std::span<const uint8_t>{payload, payload_len}, resp);
if (resp_len == 0) return;
for (auto& resp : responses) {
uint8_t reply_buf[1514];
size_t udp_data_len = resp.size();
size_t ip_total = 20 + 8 + udp_data_len;
size_t reply_len = sizeof(eth_header) + ip_total;
if (reply_len > sizeof(reply_buf)) continue;
size_t ip_total = 20 + 8 + resp_len;
size_t reply_len = sizeof(eth_header) + ip_total;
auto& rip = *reinterpret_cast<ipv4_header*>(reply_buf);
rip.eth.dst = pkt.ip.eth.src;
rip.eth.src = state.mac;
rip.eth.ethertype = ETH_IPV4;
rip.ver_ihl = 0x45;
rip.dscp_ecn = 0;
rip.total_len = __builtin_bswap16(ip_total);
rip.identification = 0;
rip.flags_frag = 0;
rip.ttl = 64;
rip.protocol = 17;
rip.checksum = 0;
rip.src = state.ip;
rip.dst = pkt.ip.src;
rip.checksum = ip_checksum(rip.ip_start(), 20);
auto& rip = *reinterpret_cast<ipv4_header*>(tx_buf);
rip.eth.dst = pkt.ip.eth.src;
rip.eth.src = state.mac;
rip.eth.ethertype = ETH_IPV4;
rip.ver_ihl = 0x45;
rip.dscp_ecn = 0;
rip.total_len = __builtin_bswap16(ip_total);
rip.identification = 0;
rip.flags_frag = 0;
rip.ttl = 64;
rip.protocol = 17;
rip.checksum = 0;
rip.src = state.ip;
rip.dst = pkt.ip.src;
rip.checksum = ip_checksum(rip.ip_start(), 20);
auto& rudp = *reinterpret_cast<udp_header*>(reply_buf);
rudp.src_port = PICOMAP_PORT;
rudp.dst_port = pkt.src_port;
rudp.length = __builtin_bswap16(8 + udp_data_len);
rudp.checksum = 0;
auto& rudp = *reinterpret_cast<udp_header*>(tx_buf);
rudp.src_port = PICOMAP_PORT;
rudp.dst_port = pkt.src_port;
rudp.length = __builtin_bswap16(8 + resp_len);
rudp.checksum = 0;
memcpy(reply_buf + sizeof(udp_header), resp.data(), udp_data_len);
send_raw(reply_buf, reply_len);
}
send_raw(tx_buf, reply_len);
}
static void handle_icmp(const uint8_t* frame, size_t len) {