Extrait de code Rust

Références

Terminologie concernant la possession

Si A possède B :

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 :

Voici un tableau de conversion :

TypeCode
SourceDestination
&strStringTODO
&[u8]TODO
&[u8; N]TODO
String&str&src
&[u8]TODO
&[u8; N]TODO
&[u8; N]CStrunsafe { CStr::from_ptr(src.as_ptr()) }
&strunsafe { CStr::from_ptr(src.as_ptr()) }.to_str().unwrap()
Stringunsafe { 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