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

Returning split up arrays by value #5

Closed
burdges opened this issue Oct 26, 2016 · 3 comments
Closed

Returning split up arrays by value #5

burdges opened this issue Oct 26, 2016 · 3 comments

Comments

@burdges
Copy link
Contributor

burdges commented Oct 26, 2016

There are situations where you want to split an array and return it by value on the stack. If you trust LLVM to remove a duplicate copy, then the clone_into_array function described here works.

There is a nice unsafe solution that works by building the type you want to return as a tuple and unsafely treating it as a single array :

pub struct LeafKey([u8; 16]);
pub struct MessageKey([u8; 32]);

fn myKDF(...) -> (MessageKey,OtherKey) {
    // let mut r: [u8; 32+16];
    let mut r: (MessageKey, OtherKey);
    debug_assert_eq!(mem::size_of_val(&r), 384/8);
    let mut sha = Sha3::sha3_384();
    sha.input(...);

    // sha.result(r);
    sha.result( 
      unsafe { mem::transmute::<&mut (MessageKey, OtherKey),&mut [u8;32+16]>(&r) } 
    );
    sha.reset();

    // (MessageKey(r[0..31]), LeafKey(r[32..47]))
    r
}

I'm curious how one should make this safer?

An obstacle is that often one must apply mem::transmute to a reference, but one wants to check that the referenced types have the same size, not just that two pointers have the same size.

We could use two wrappers for mem::transmute that check the type sizes in debug mode, like so :

#[inline]
unsafe fn transmute_ptr<A,B>(v: &A) -> &B {
    debug_assert_eq!(core::mem::size_of(A),core::mem::size_of(B));
    core::mem::transmute::<&A,&B>(v)
}

#[inline]
unsafe fn transmute_ptr_mut<A,B>(v: &mut A) -> &mut B {
    debug_assert_eq!(core::mem::size_of(A),core::mem::size_of(B));
    core::mem::transmute::<&mut A,&mut B>(v)
}

I think this becomes transmute_ptr<'l,A,B>(v: &l' A) -> &l' B and similarly for the _mut variant, so they reborrow v. As a result, you might need to wrap them inside another scope, well until rust-lang/rfcs#811 or similar.

In principle, one might give the compiler more ammunition to analyze the borrow by using macros, like :

macro_rules! mem_transmute_ptr {
    ($A:ty, $B:ty, $v:expr) => { {
        debug_assert_eq!(core::mem::size_of($A),core::mem::size_of($B));
        core::mem::transmute::<&$A,&$B>($v);
    } }
}

macro_rules! mem_transmute_ptr_mut {
    ($A:ty, $B:ty, $v:expr) => { {
        debug_assert_eq!(core::mem::size_of($A),core::mem::size_of($B));
        core::mem::transmute::<&mut $A,&mut $B>($v);
    } }
}

Any thoughts on the best way to do this sort of thing?

I asked about this on stackexchange, but actually this crate seems like the better place, as maybe there is something worth adding.

@droundy
Copy link
Owner

droundy commented Oct 31, 2016

On first thought (and I haven't been using rust in a while), I would think that you could do this with minimal overhead using array_ref! and a copy. Something like

let my_copy = *array_ref![data,5,2];

ought to create a copy of a portion of the array on the stack. Do you have a concern that this might be inefficient? Or is there something more expressive that you are looking for?

@burdges
Copy link
Contributor Author

burdges commented Oct 31, 2016

I donno if efficiency matters too much. If you're returning the array by value, then it's probably not such a big array.

Agreed, thanks!. It's much cleaner to write the following instead of a clone_from_slice based solutions like clone_into_array. And maybe * gives the compiler more freedom to optimize away the copy even.

let (a,b) = array_refs![&r,32,16];
(MessageKey(*a), LeafKey(*b))

I kinda liked that the mem::transmute based version seemingly avoids any indexing errors, but obviously it just moves those into the representation of the tuple, which sounds fragile, and your version gives the best of both worlds.

@burdges burdges closed this as completed Oct 31, 2016
@burdges
Copy link
Contributor Author

burdges commented Oct 31, 2016

I still feel mem_transmute_ptr/_mut look useful, but maybe this crate does not sound like the place for them to live now.

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

2 participants