diff options
Diffstat (limited to 'third_party/rust/arrayref/README.md')
-rw-r--r-- | third_party/rust/arrayref/README.md | 109 |
1 files changed, 109 insertions, 0 deletions
diff --git a/third_party/rust/arrayref/README.md b/third_party/rust/arrayref/README.md new file mode 100644 index 0000000000..10603a089f --- /dev/null +++ b/third_party/rust/arrayref/README.md @@ -0,0 +1,109 @@ +# arrayref + +[![Build Status](https://travis-ci.org/droundy/arrayref.svg?branch=master)](https://travis-ci.org/droundy/arrayref) +[![Coverage Status](https://coveralls.io/repos/droundy/arrayref/badge.svg?branch=master&service=github)](https://coveralls.io/github/droundy/arrayref?branch=master) + +[Documentation](https://docs.rs/arrayref) + +This is a very small rust module, which contains just four macros, for +the taking of array references to slices of... sliceable things. +These macros (which are awkwardly named) should be perfectly safe, and +have seen just a tad of code review. + +## Why would I want this? + +The goal of arrayref is to enable the effective use of APIs that +involve array references rather than slices, for situations where +parameters must have a given size. As an example, consider the +`byteorder` crate. This is a very nice crate with a simple API +containing functions that look like: + +```rust +fn read_u16(buf: &[u8]) -> u16; +fn write_u16(buf: &mut [u8], n: u16); +``` + +Looking at this, you might wonder why they accept a slice reference as +input. After all, they always want just two bytes. These functions +must panic if given a slice that is too small, which means that unless +they are inlined, then a runtime bounds-check is forced, even if it +may be statically known that the input is the right size. + +Wouldn't it be nicer if we had functions more like + +```rust +fn read_u16_array(buf: &[u8; 2]) -> u16; +fn write_u16_array(buf: &mut [u8; 2], n: u16); +``` + +The type signature would tell users precisely what size of input is +required, and the compiler could check at compile time that the input +is of the appropriate size: this sounds like the zero-cost +abstractions rust is famous for! However, there is a catch, which +arises when you try to *use* these nicer functions, which is that +usually you are looking at two bytes in a stream. So, e.g. consider +that we are working with a hypothetical (and simplified) ipv6 address. + +Doing this with our array version (which looks so beautiful in terms +of accurately describing what we want!) looks terrible: + +```rust +let addr: &[u8; 16] = ...; +let mut segments = [0u16; 8]; +// array-based API +for i in 0 .. 8 { + let mut two_bytes = [addr[2*i], addr[2*i+1]]; + segments[i] = read_u16_array(&two_bytes); +} +// slice-based API +for i in 0 .. 8 { + segments[i] = read_u16(&addr[2*i..]); +} +``` + +The array-based approach looks way worse. We need to create a fresh +copy of the bytes, just so it will be in an array of the proper size! +Thus the whole "zero-cost abstraction" argument for using array +references fails. The trouble is that there is no (safe) way (until +[RFC 495][1] lands) to obtain an array reference to a portion of a +larger array or slice. Doing so is the equivalent of taking a slice +with a size known at compile time, and ought to be built into the +language. + +[1]: https://github.com/rust-lang/rfcs/blob/master/text/0495-array-pattern-changes.md + +The arrayref crate allows you to do this kind of slicing. So the +above (very contrived) example can be implemented with array +references as: + +```rust +let addr: &[u8; 16] = ...; +let mut segments = [0u16; 8]; +// array-based API with arrayref +for i in 0 .. 8 { + segments[i] = read_u16_array(array_ref![addr,2*i,2]); +} +``` + +Here the `array_ref![addr,2*i,2]` macro allows us to take an array +reference to a slice consisting of two bytes starting at `2*i`. Apart +from the syntax (less nice than slicing), it is essentially the same +as the slice approach. However, this code makes explicit the +need for precisely *two* bytes both in the caller, and in the function +signature. + +This module provides three other macros providing related +functionality, which you can read about in the +[documentation](https://droundy.github.io/arrayref). + +For an example of how these macros can be used in an actual program, +see [my rust translation of tweetnacl][2], which uses `arrayref` +to almost exclusively accept array references in functions, with the +only exception being those which truly expect data of arbitrary +length. In my opinion, the result is code that is far more legible +than the original C code, since the size of each argument is +explicit. Moreover (although I have not tested this), the use of +array references rather than slices *should* result in far fewer +bounds checks, since almost all sizes are known at compile time. + +[2]: https://github.com/droundy/onionsalt/blob/master/src/crypto.rs |