Как преобразовать Vec <Rgb <u8 >> в Vec <u8> - PullRequest
0 голосов
/ 08 ноября 2018

Используя ящик поршня image, я могу написать изображение, передав ему Vec<u8>, но мои фактические данные Vec<Rgb<u8>> (потому что с ними гораздо проще иметь дело, и я хочу динамически их наращивать) ).

Как я могу конвертировать Vec<Rgb<u8>> в Vec<u8>? Rgb<u8> действительно [u8; 3]. Это должно быть unsafe преобразование?

Ответы [ 2 ]

0 голосов
/ 08 ноября 2018

Ответ зависит от того, хорошо ли вы копируете данные. Если копирование не является для вас проблемой, вы можете сделать что-то вроде этого:

let img: Vec<Rgb<u8>> = ...;
let buf: Vec<u8> = img.iter().flat_map(|rgb| rgb.data.iter()).cloned().collect();

Если вы хотите выполнить преобразование без копирования, сначала нам нужно убедиться, что ваши исходные и конечные типы действительно имеют одинаковую структуру памяти. Rust дает очень мало гарантий относительно структуры памяти структур. В настоящее время он даже не гарантирует, что структура с одним элементом имеет ту же структуру памяти, что и сам элемент.

В данном конкретном случае макет памяти Rust не имеет значения, поскольку Rgb определяется как

#[repr(C)]
pub struct Rgb<T: Primitive> {
    pub data: [T; 3],
}

Атрибут #[repr(C)] указывает, что структура памяти структуры должна совпадать с эквивалентной структурой Си. Структура памяти C не полностью указана в стандарте C, но в соответствии с рекомендациями по небезопасному коду , существуют некоторые правила, действующие для "большинства" платформ:

  • Порядок полей сохраняется.
  • Первое поле начинается со смещения 0.
  • Предполагая, что структура не упакована, смещение каждого поля выравнивается по обязательному выравниванию ABI для типа этого поля, возможно создавая неиспользуемые биты заполнения.
  • Общий размер структуры округляется до общего выравнивания.

Как указано в комментариях, стандарт C теоретически допускает дополнительное заполнение в конце структуры. Однако сама библиотека изображений Piston предполагает, что срез данных канала имеет ту же структуру памяти, что и Rgb struct , поэтому, если вы находитесь на платформе, где это предположение не выполняется, все ставки все равно отключены (и я не смог найти никаких доказательств того, что такая платформа существует).

Rust гарантирует, что массивы, срезы и векторы плотно упакованы, и что структуры и массивы имеют выравнивание, равное максимальному выравниванию их элементов. Вместе с предположением, что расположение Rgb соответствует правилам, которые я цитирую выше, это гарантирует, что Rgb<u8> действительно выложен как три последовательных байта в памяти, и что Vec<Rgb<u8>> действительно является последовательным, плотно упакованным буфер значений RGB, поэтому наше преобразование безопасно. Нам все еще нужно использовать небезопасный код, чтобы написать его:

let p = img.as_mut_ptr();
let len = img.len() * mem::size_of::<Rgb<u8>>();
let cap = img.capacity() * mem::size_of::<Rgb<u8>>();
mem::forget(img);
let buf: Vec<u8> = unsafe { Vec::from_raw_parts(p as *mut u8, len, cap) };

Если вы хотите защитить от случая, когда в конце Rgb имеется дополнительный отступ, вы можете проверить, действительно ли size_of::<Rgb<u8>>() равен 3. Если это так, вы можете использовать небезопасную версию без копирования, в противном случае Вы должны использовать первую версию выше.

0 голосов
/ 08 ноября 2018

Вы выбираете формат хранения Vec<Rgb<u8>>, потому что с ним проще работать, и вы хотите, чтобы он динамично развивался. Но, как вы заметили, нет гарантии совместимости его хранилища с Vec<u8> и безопасного преобразования.

Почему бы не взять проблему по-другому и построить удобный фасад для Vec<u8>?

type Rgb = [u8; 3];

#[derive(Debug)]
struct Img(Vec<u8>);

impl Img {
    fn new() -> Img {
        Img(Vec::new())
    }

    fn push(&mut self, rgb: &Rgb) {
        self.0.push(rgb[0]);
        self.0.push(rgb[1]);
        self.0.push(rgb[2]);
    }

    // other convenient methods
}

fn main() {
    let mut img = Img::new();
    let rgb : Rgb = [1, 2, 3];
    img.push(&rgb);
    img.push(&rgb);
    println!("{:?}", img);
}
...