How it works...

To demonstrate how to read and write a binary file, we will create a little custom binary protocol. It will start with what is called a magic number, that is, a certain hardcoded value. Our magic number will be the binary representation of the MyProtocol string. We can put a b before the string to tell Rust that we want the text to be represented as a binary slice (&[u8]) instead of a string slice(&str) [26].

Many protocols and files start with magic numbers to indicate what they are. For example, the internal headers of .zip files start with the magic hex numbers 0x50 and 0x4B. These represent the initials PH in ASCII, which is short for the name of its creator Phil Katz. Another example would be PDF; it starts with 0x25, 0x50, 0x44, and 0x46, which stands for PDF%, followed by a version number.

Afterward, we follow it by the binary representation of either LE or BE to tell the reader the endianness of the rest of the data [31]. Finally, we have the payload, which is just an arbitrary amount of u32 numbers, encoded in the aforementioned endianness [35 and 36]. By putting 0x in front of our number, we tell Rust to treat it as a hexadecimal number and convert it into decimal for us. As such, Rust treats 0xDEAD as the same value as 57005.

Let's put it all together and write a binary file containing MyProtocolLE5700548879. Other files we could have created in accordance with our protocol would be MyProtocolBE92341739241842518425 or MyProtocolLE0000, and so on.

If you read the previous recipes, write_dummy_protocol should be easy to understand. We use a combination of good old write_all from the standard library to write our binary texts and write_u32 from byteorder to write the values that require an endianness.

The reading of the protocol is split into the  read_protocol and read_protocol_payload functions. The first verifies the validity of the protocol by reading the magic numbers and then calls the latter, which reads the remaining numbers as the payload.

We validate the magic numbers as follows:

  1. As we know the exact size of the magic numbers used, prepare buffers of those exact sizes.
  2. Fill them with just as many bytes.
  3. Compare the bytes with the expected magic number.
  4. If they don't match, return an error.

After parsing both magic numbers, we can parse the actual data contained in the payload. Remember, we defined it as an arbitrary amount of 32 bit (= 4 bytes) long, unsigned numbers. To parse them, we are going to repeatedly read up to four bytes into a buffer called raw_payload. We are then going to examine the amount of bytes that were actually read. This number can have three forms in our case, as demonstrated nicely by our match.

The first value we are interested in is zero, which means that there are no more bytes to read, that is, we have reached the end. In this case, we can return our payload:

// Zero means we reached the end
0 => return Ok(payload),

The second value is SIZE_OF_U32, which we have previously defined as four. Receiving this value means that our reader has successfully read four bytes into a four-byte-long buffer. This means that we have successfully read a value! Let's parse it into a u32 and push it into our payload vector:

// SIZE_OF_U32 means we read a complete number
SIZE_OF_U32 => {
let as_u32 = raw_payload.as_ref().read_u32::<E>()?;
payload.push(as_u32)
}

We have to call as_ref() on our buffer because a fixed-size array doesn't implement Read. Since a slice does implement said trait and a reference to an array is implicitly convertible into a slice, we work on a reference to raw_payload instead.

The third and last value we can expect is everything other than zero or four. In this case, the reader was not able to read four bytes, which means that our buffer ended in something other than a u32 and is malformed. We can react to this by returning an error:

// Anything else means the last element was not
// a valid u32
_ => {
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"Payload ended unexpectedly",
))
}
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.160.131