google / google/zerocopy

Shrink the size of a `Ref` in memory for some `B: ByteSlice`

Open
#368 1 comment 0 reactions 0 assignees View on GitHub
compatibility-nonbreaking experience-hard help wanted
Dominant language
Rust
Stars
2.6k
Forks
179
Avg merge
1d 19h
Merged PRs (30d)
29

Description

Currently `Ref` is defined as:

https://github.com/google/zerocopy/blob/52ffa4d1cbd81d0570276d20ddac783ba2a5ecf2/src/lib.rs#L1417

This has the consequence that, even if `T: Sized`, when in principle `Ref` could be a single-word thin pointer, `Ref` is still as large as `B`. Usually this is two words (e.g. for `&[u8]`), but sometimes it's more (e.g. for [`core::cell::Ref`](https://doc.rust-lang.org/std/cell/struct.Ref.html), which has a reference count).

For some types, this is unavoidable. E.g., no matter what we do, we need to store the entire `core::cell::Ref` - if we didn't, we'd be leaking memory. But for some types - such as `&[u8]` - we can reconstruct the original `B: ByteSlice` just from a pointer to `T`.

Thus, I propose the following addition to `ByteSlice`:

```rust
trait ByteSlice {
// Name to be bikeshedded later. We may not want to tie the name to the `Ref`
// type since we plan on maybe making `ByteSlice` unsealed at some point.
//
// For `&[u8]`/`&mut [u8]`, this is just `NonNullFoo`. For `core::cell::Ref`/
// `core::cell::RefMut`, this is just `Self`.
type Foo: AsRef<[u8]> + AsMut<[u8]>;

// Either discards `foo`, constructing `Foo` from `self` (e.g. for `core::cell::Ref`),
// or discards `self`, constructing `Foo` from `foo` (e.g. for `&[u8]`).
//
// SAFETY: Caller promises that `ptr` references a sequence of bytes which are
// all initialized. That ensures that we can implement `AsRef<[u8]>`/`AsMut<[u8]>`
// without a `T: AsBytes` bound.
unsafe fn into_foo(self, foo: NonNullFoo) -> Self::Foo;
}

struct NonNullFoo {
// Invariant: Always references a fully-initialized sequences of bytes. This is fine
// because this is always constructed from a `B: ByteSlice` during `Ref` construction.
inner: NonNull,
}

// NOTE: Caller needs to ensure the `NonNullFoo` doesn't live for too long since
// these methods are not `usnafe`. Might need to be a safety requirement on
// `ByteSlice::into_foo`.
impl AsRef<[u8]> for NonNullFoo { ... }
impl AsMut<[u8]> for NonNullFoo { ... }
```

Then, we update `Ref`:

```rust
struct Ref(::Foo, PhantomData);
```

cc @kupiakos , I know you were interested in having this, and I remember you mentioned taking a stab at some ideas for how to implement it

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.