I try to parse each field and return their mutable slice of the value.
The key-value pair is a pair of C-style string and an big-endian u32.
I know that borrow checker does not allow having multiple mutable borrow at the same time even they does not overlap with each other. So I try to use std::mem::transmute to stop it from compaining. Like the following:
use std::{collections::HashMap, io::Write};
fn parse(data: &mut [u8]) -> Result<HashMap<&[u8], &mut [u8; 4]>, &'static str> {
let mut map = HashMap::new();
let mut cursor = 0;
while cursor < data.len() {
let mut key_len = 0;
while data[cursor + key_len] != 0 && cursor + key_len < data.len() {
key_len += 1;
}
if cursor + key_len + 5 > data.len() {
return Err("Not enough data for value");
}
unsafe {
let key = std::mem::transmute(&data[cursor..cursor + key_len]);
let value = std::mem::transmute(
TryInto::<&mut [u8; 4]>::try_into(
&mut data[cursor + key_len + 1..cursor + key_len + 5],
)
.unwrap(),
);
map.insert(key, value);
}
cursor += key_len + 5;
}
Ok(map)
}
fn main() {
let mut data = vec![];
data.write(b"key1").unwrap();
data.write(&[0]).unwrap();
data.write(&24_u32.to_be_bytes()).unwrap();
data.write(b"key2").unwrap();
data.write(&[0]).unwrap();
data.write(&42_u32.to_be_bytes()).unwrap();
let parsed = parse(&mut data).unwrap();
println!("Value1: {}", u32::from_be_bytes(*parsed["key1".as_bytes()]));
println!("Value2: {}", u32::from_be_bytes(*parsed["key2".as_bytes()]));
}
But cargo +nightly miri run compains. How to fix that?
Compiling play v0.1.0 (/home/billy/Projects/play)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.05s
Running `/home/billy/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/cargo-miri runner target/miri/x86_64-unknown-linux-gnu/debug/play`
error: Undefined Behavior: trying to retag from <3015> for SharedReadOnly permission at alloc1100[0x0], but that tag does not exist in the borrow stack for this location
--> src/main.rs:22:24
|
22 | map.insert(key, value);
| ^^^
| |
| trying to retag from <3015> for SharedReadOnly permission at alloc1100[0x0], but that tag does not exist in the borrow stack for this location
| this error occurs as part of retag at alloc1100[0x0..0x4]
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
help: <3015> was created by a SharedReadOnly retag at offsets [0x0..0x4]
--> src/main.rs:15:23
|
15 | let key = std::mem::transmute(&data[cursor..cursor + key_len]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: <3015> was later invalidated at offsets [0x0..0x12] by a Unique retag
--> src/main.rs:18:26
|
18 | &mut data[cursor + key_len + 1..cursor + key_len + 5],
| ^^^^
= note: BACKTRACE (of the first span):
= note: inside `parse` at src/main.rs:22:24: 22:27
note: inside `main`
--> src/main.rs:37:18
|
37 | let parsed = parse(&mut data).unwrap();
| ^^^^^^^^^^^^^^^^
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error