Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Suggestion] Ownership-based API for zero-copy #15

Open
chrysn opened this issue Sep 1, 2020 · 0 comments
Open

[Suggestion] Ownership-based API for zero-copy #15

chrysn opened this issue Sep 1, 2020 · 0 comments

Comments

@chrysn
Copy link
Contributor

chrysn commented Sep 1, 2020

Considering expressing RIOT's network API (in particular GNRC; with their sockets it'd be different) using embedded-nal, their API could profit (read: avoid copies) from an ownership based model.

This suggestion is similar to #12, but while there things are about stream-based sockets, this is for datagram sockets. Like there, this might need an additional trait to implement (which could also be implemented by an alloc- or fixed-buffer backed wrapper for cases where a consumer needs it).

Rough API sketch:

trait OwnedReadable: UdpStack {
    // Anything more scatter-gather-y would make it easier to implement on
    // ring-buffer backed stacks, but harder to consume.
    type UdpMessage: AsRef<[u8]>;
    fn read_owned(&self, socket: &mut Self::UdpSocket) -> Result<Self::UdpMessage, nb::Error<Self::Error>>;
}

That would work well for the GNRC backend, which AFAICT guarantees contiguous allocation of inbound data. It'd also work well for the very small single-MTU stacks (which off my head I don't remember precisely; I think contiki worked like that). For backends like lwIP that don't guarantee contiguous allocation (because they put data across smaller slabs), it might be better to require something roughly like IntoIterator<Item=&[u8]>, which would then be expressed in its own trait anyway.

On the write side, I don't have that clear a suggestion, but I suppose combined usage would be a bit like

let received = block!(stack.receive(sock))?;
let extracted = parse(received);
drop(received);
let mut out = stack.write_owned_begin(sock, extracted.estimate_response_size())?; // might fail OOM
extracted.populate(&mut out); // might trim the response in the process
stack.write_owned(out);

Some applications might not be able to drop the received before starting the response; that's fully OK but uses more stack resources, so it'd be best practice to drop it ASAP to ensure that that works even on stacks that can have at most a single UDP message in flight.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant