Thanks to visit codestin.com
Credit goes to github.com

Skip to content

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

@joshlf

Description

@joshlf

Currently Ref is defined as:

pub struct Ref<B, T: ?Sized>(B, PhantomData<T>);

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, 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:

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<T>`. For `core::cell::Ref`/
    // `core::cell::RefMut`, this is just `Self`.
    type Foo<T: ?Sized>: 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<T: ?Sized>(self, foo: NonNullFoo<T>) -> Self::Foo;
}

struct NonNullFoo<T> {
    // 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<T>,
}

// 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<T> AsRef<[u8]> for NonNullFoo<T> { ... }
impl<T> AsMut<[u8]> for NonNullFoo<T> { ... }

Then, we update Ref:

struct Ref<B, T: ?Sized>(<B as ByteSlice>::Foo<T>, PhantomData<B>);

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    compatibility-nonbreakingChanges that are (likely to be) non-breakingexperience-hardThis issue is hard, and requires a lot of experiencehelp wantedExtra attention is needed

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions