Skip to content

Add ptr::{read,write}_unaligned #1725

New issue

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

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

Already on GitHub? # to your account

Merged
merged 1 commit into from
Nov 23, 2016
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions text/0000-unaligned-access.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
- Feature Name: unaligned_access
- Start Date: 2016-08-22
- RFC PR: (leave this empty)
- Rust Issue: (leave this empty)

# Summary
[summary]: #summary

Add two functions, `ptr::read_unaligned` and `ptr::write_unaligned`, which allows reading/writing to an unaligned pointer. All other functions that access memory (`ptr::{read,write}`, `ptr::copy{_nonoverlapping}`, etc) require that a pointer be suitably aligned for its type.

# Motivation
[motivation]: #motivation

One major use case is to make working with packed structs easier:

```rust
#[repr(packed)]
struct Packed(u8, u16, u8);

let mut a = Packed(0, 1, 0);
unsafe {
let b = ptr::read_unaligned(&a.1);
ptr::write_unaligned(&mut a.1, b + 1);
}
```

Other use cases generally involve parsing some file formats or network protocols that use unaligned values.

# Detailed design
[design]: #detailed-design

The implementation of these functions are simple wrappers around `ptr::copy_nonoverlapping`. The pointers are cast to `u8` to ensure that LLVM does not make any assumptions about the alignment.

```rust
pub unsafe fn read_unaligned<T>(p: *const T) -> T {
let mut r = mem::uninitialized();
ptr::copy_nonoverlapping(p as *const u8,
&mut r as *mut _ as *mut u8,
mem::size_of::<T>());
r
}

pub unsafe fn write_unaligned<T>(p: *mut T, v: T) {
ptr::copy_nonoverlapping(&v as *const _ as *const u8,
p as *mut u8,
mem::size_of::<T>());
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

forget(v) is missing in the reference implementation here; this should move v into the new location, just like write does.

```

# Drawbacks
[drawbacks]: #drawbacks

There functions aren't *stricly* necessary since they are just convenience wrappers around `ptr::copy_nonoverlapping`.

# Alternatives
[alternatives]: #alternatives

We could simply not add these, however figuring out how to do unaligned access properly is extremely unintuitive: you need to cast the pointer to `*mut u8` and then call `ptr::copy_nonoverlapping`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about passing the alignment explicitly, with the functions proposed here being only for an alignment of 1?
That way, instead of always doing stack copies, they can generate optimal LLVM IR for that given alignment.
They could also be used with larger alignments to potentially hint that, e.g. SIMD can be used, although that works better for variants of the ptr::copy{,_nonoverlapping} functions.

Now is that better than having the alignment as part of the pointer?
I don't know, #[align(1) *mut u32 crossed my mind and it's good enough for some cases, but doesn't work with generics (would have to be something that works with polymorphic constants, i.e. part of the typesystem).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these use cases can be covered by using a newtype with an explicit alignment attribute:

#[align(1)]
struct UnalignedU32(u32);

This makes more sense since the alignment is attached to the type that is pointed to rather than the pointer itself.

Copy link
Member

@eddyb eddyb Aug 22, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking about taking a reference to a packed field, to be clear. Otherwise we could do:

#[align(1)]
struct Unaligned<T>(T);
struct AlignAs<T, U>([U; 0], Unaligned<T>);


# Unresolved questions
[unresolved]: #unresolved-questions

None