Extrait de code Rust
Références
Terminologie concernant la possession
- Possession (owning).
- Emprunt (borrow).
- Durée de vie (life span).
Si A possède B :
Aest le seul à pouvoir créer et détruireB.Bdisparaît quandAdisparaît.Cpeut emprunterBmais ne peut pas le détruire.Bne peut être détruit siBest emprunté.Ane peut être détruit siBest emprunté.
Byte swap un Vec<u8>
Deux façon « claires » :
pub fn swap16(src: &mut Vec<u8>) {
for chunk in src.chunks_exact_mut(2) {
chunk.swap(0, 1);
}
}
pub fn swap16(src: &mut Vec<u8>) {
src.chunks_exact_mut(2).for_each(|chunk| {
let (s1, s2) = chunk.split_at_mut(1);
std::mem::swap(&mut s1[0], &mut s2[0]);
});
}
La méthode qui semble générer le code assembleur le plus efficace est :
pub fn sawp16(my_vec: &mut Vec<u8>) {
let (head, body, tail) = unsafe { my_vec.align_to_mut::<u16>() };
for v in body {
*v = v.swap_bytes();
}
}
Notez l’utilisation de l’instruction rol :
.LBB18_2:
rol word ptr [rcx + rdx], 8
add rdx, 2
cmp rax, rdx
jne .LBB18_2
Changer u16 par u32 permet d’utiliser l’instruction bswap :
.LBB6_1:
cmp rcx, rdx
je .LBB6_2
mov esi, dword ptr [rax + rdx]
bswap esi
mov dword ptr [rax + rdx], esi
add rdx, 4
jmp .LBB6_1
.LBB6_2:
add rsp, 48
pop rbx
ret
Conversion de string
En C, une string est un char*. En rust, une chaîne de caractère peut être de plusieurs types différents :
&str: Une slice, une vue immutable sur une string. La meilleur façon de passer un string dans du code.String: La string de base, en Rust.&[u8]: L’équivalent C d’unchar*.&[u8; N]: L’équivalent C d’unchar*, mais avec la taille.Vec<u8>&u8OsStrOsStringPathPathBufCStr: Référence Rust vers une string C. La string n’est pas possédé par Rust.CString: String exposable en C et possédé par Rust.&'static str: Idem que&str, mais statique.
Voici un tableau de conversion :
| Type | Code | |
|---|---|---|
| Source | Destination | |
&str | String | TODO |
&[u8] | TODO | |
&[u8; N] | TODO | |
String | &str | |
&[u8] | TODO | |
&[u8; N] | TODO | |
&[u8; N] | CStr | unsafe { CStr::from_ptr(src.as_ptr()) } |
&str | unsafe { CStr::from_ptr(src.as_ptr()) }.to_str().unwrap() | |
String | unsafe { CStr::from_ptr(src.as_ptr()) }.to_str().unwrap().to_string() | |
Transférer le contenu d’une slice à une autre d’un type différent.
On souhaite générer un memcpy.
Ce code copie le contenu de la slice u16 src vers la slice u8 (octets) dst :
pub fn u16_to_u8_cpy(src: &[u16], dst: &mut [u8]) {
src.iter()
.zip(dst.chunks_exact_mut(2))
.for_each(|(a, b)| b.copy_from_slice(&a.to_ne_bytes()));
}
C’est le 2 qui permet de composer la slice u8 en [u8; 2], le même type renvoyé par to_ne_bytes().
Le code assembleur (1.51, avec option -C opt-level=3) est le suivant :
example::u16_to_u8_cpy:
push rax
shr rcx
cmp rcx, rsi
cmovae rcx, rsi
test rcx, rcx
je .LBB0_2
mov rax, rdi
add rcx, rcx
mov rdi, rdx
mov rsi, rax
mov rdx, rcx
call qword ptr [rip + memcpy@GOTPCREL]
.LBB0_2:
pop rax
ret
Notez la présence du memcpy.
La version u32 fonctionne aussi (en composant la slice u32 en [u8; 4]) :
pub fn u32_to_u8_cpy(bytes: &[u32], my_bytes: &mut [u8]) {
bytes.iter()
.zip(my_bytes.chunks_exact_mut(4))
.for_each(|(a, b)| b.copy_from_slice(&a.to_ne_bytes()));
}
Dernière mise à jour : mer. 03 mars 2021