-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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>()); | ||
} | ||
``` | ||
|
||
# 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`. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 Now is that better than having the alignment as part of the pointer? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
There was a problem hiding this comment.
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 movev
into the new location, just likewrite
does.