Skip to content

Conversation

jrvanwhy
Copy link
Collaborator

Note: This is currently far from complete, just publishing so no-one else duplicates the effort I've already put into it.

This is attempt to resolve the last question from this comment: jrvanwhy#1 (comment)

The problem is that we want developers to be able to write APIs that can take both stack-allocated buffers and buffers from .rodata (and ideally .data as well) without having to duplicate all their code. The idea is that APIs would take a Pin<&mut allow::Ref<>>:

fn console_write(to_write: Pin<&mut allow::Ref<Rw, [u8]>>) -> Result<usize, ErrorCode>;

allow::Ref can be constructed from a &'static [u8], &'static mut [u8], allow::Buffer, etc. so APIs that accept an allow::Ref can be used in several different contexts.

Under this design, it will still be possible to use an allow::Buffer directly to perform Allow calls, as some API implementations (particularly those that only need small, fixed-size buffers) may choose to allocate their own buffers internally.

@DanutAldea
Copy link
Contributor

I created a PR with an attempt at embedding the Ref in the Buffer structure that can be found here. The proposed design solves the issue mentioned in the jrvanwhy#1 (comment). Unfortunately, the increase in memory consumption on the more complex example is quite bad both on ARM and RISCV.

Sadly, any other design that encapsulated the reference in the allow buffer structure that I managed to test would require having a capsule API similar to this:

fn console_write<const N: usize>(to_write: Pin<&mut allow::Ref<Rw, [u8; N]>>) -> Result<usize, ErrorCode>;

I left it as a draft PR, as I am not entirely sure that there is some value in merging it. If you have any other ideas I could help explore, I'd be happy to do so.

This is a rethinking of the design. With this design, a console write command would have the protoype:

```
fn console_write(to_write: AllowRef<&[u8]>) -> Result<usize, ErrorCode>;
```

An `AllowRef` can only exist if the buffer if something guarantees that the buffer it points to will be unshared before being dropped. There are multiple ways to construct an `AllowRef`:

1. From a `'static` reference, where the buffer will never be dropped.
2. Form a `Pin<&mut Buffer>`, where `Buffer::drop` unshares the buffer.

This new design is currently incomplete, and I'm not sure what the size impact will be.
@jrvanwhy
Copy link
Collaborator Author

jrvanwhy commented Oct 2, 2025

Hmm, I was trying to stick with using Pin's guarantees rather than introducing our own similar type (to keep things simple, and let libtock-rs' users use tooling built for Pin).

I started working on a refactoring that changes console_write's prototype to:

fn console_write(to_write: AllowRef<&[u8]>) -> Result<usize, ErrorCode>;

AllowRef is a type that is similar to Pin, but instead of the invariant being "the pointee will be dropped before it is deallocated", the invariant is "the pointee will be unshared before it is deallocated". This removes the embedded Ref type from Buffer, which I think will remove the unsizing coercion issues we've been having.

That said, I haven't had time to finish implementing it. I've pushed what I've done so far. I'm not sure what the code size cost will be.

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

Successfully merging this pull request may close these issues.

2 participants