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
:
A
est le seul à pouvoir créer et détruireB
.B
disparaît quandA
disparaît.C
peut emprunterB
mais ne peut pas le détruire.B
ne peut être détruit siB
est emprunté.A
ne peut être détruit siB
est 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>
&u8
OsStr
OsString
Path
PathBuf
CStr
: 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