Convertir un VEC <U32> a VEC <U8> en el lugar y con mínimo sobrecargo -- asting campo con rust campo con unsafe camp Relacionados El problema

Converting a Vec<u32> to Vec<u8> in-place and with minimal overhead


9
vote

problema

Español

Estoy tratando de convertir un Vec de u32 S a Vec S, preferiblemente en el lugar y sin demasiado arriba.

Mi solución actual se basa en el código inseguro para volver a construir el Vec . ¿Existe una mejor manera de hacer esto, y cuáles son los riesgos asociados con mi solución?

  use std::mem; use std::vec::Vec;  fn main() {     let mut vec32 = vec![1u32, 2];     let vec8;     unsafe {         let length = vec32.len() * 4; // size of u8 = 4 * size of u32         let capacity = vec32.capacity() * 4; // ^         let mutptr = vec32.as_mut_ptr() as *mut u8;         mem::forget(vec32); // don't run the destructor for vec32          // construct new vec         vec8 = Vec::from_raw_parts(mutptr, length, capacity);     }      println!("{:?}", vec8) }   

enlace de patio de óxido

Original en ingles

I'm trying to convert a Vec of u32s to a Vec of u8s, preferably in-place and without too much overhead.

My current solution relies on unsafe code to re-construct the Vec. Is there a better way to do this, and what are the risks associated with my solution?

use std::mem; use std::vec::Vec;  fn main() {     let mut vec32 = vec![1u32, 2];     let vec8;     unsafe {         let length = vec32.len() * 4; // size of u8 = 4 * size of u32         let capacity = vec32.capacity() * 4; // ^         let mutptr = vec32.as_mut_ptr() as *mut u8;         mem::forget(vec32); // don't run the destructor for vec32          // construct new vec         vec8 = Vec::from_raw_parts(mutptr, length, capacity);     }      println!("{:?}", vec8) } 

Rust Playground link

        
   
   

Lista de respuestas

12
 
vote
vote
La mejor respuesta
 
  1. Cada vez que escriba un bloque unsafe , I Formamente Anime a las personas a incluir un comentario sobre el bloque que explica por qué cree que el código es realmente seguro < / em>. Ese tipo de información es útil para las personas que leen el código en el futuro.

  2. En lugar de agregar comentarios sobre el "número mágico" 4, simplemente use mem::size_of::<u32> . Incluso iría tan lejos como para usar size_of para u8 y realiza la división para la máxima claridad.

  3. Puede devolver el bloque de VEC recién creado desde el bloque Vec4.

  4. Como se mencionó en los comentarios, "Dumping" un bloque de datos como este hace que el formato de datos sea dependiente de la plataforma ; Obtendrá diferentes respuestas en Little Endian y Big Endian Systems. Esto puede llevar a dolores de cabeza masivos de depuración en el futuro. Los formatos de archivo codifican la plataforma endianness en el archivo (lo que hace que el trabajo del lector sea más difícil) o solo escriba un endinanness específico al archivo (haciendo que el trabajo del escritor sea más difícil).

  5. Probablemente muevo todo el bloque Vec5 a una función y le daría un nombre, solo para fines de organización.

  6. No necesita importar Vec , está en el preludio.

  use std::mem;  fn main() {     let mut vec32 = vec![1u32, 2];      // I copy-pasted this code from StackOverflow without reading the answer      // surrounding it that told me to write a comment explaining why this code      // is actually safe for my own use case.     let vec8 = unsafe {         let ratio = mem::size_of::<u32>() / mem::size_of::<u8>();          let length = vec32.len() * ratio;         let capacity = vec32.capacity() * ratio;         let ptr = vec32.as_mut_ptr() as *mut u8;          // Don't run the destructor for vec32         mem::forget(vec32);          // Construct new Vec         Vec::from_raw_parts(ptr, length, capacity)     };      println!("{:?}", vec8) }   

parque infantil

Mi mayor preocupación desconocida acerca de este código radica en la alineación de la memoria asociada con el Vec .

Oxty's subyacente al aloCator asignates y desasignates memoria con un específico Layout . mem::size_of::<u32>0 contiene dicha información como el tamaño y alineación del puntero.

Supongo que este código necesita el mem::size_of::<u32>1111 para que coincida entre las llamadas emparejadas a mem::size_of::<u32>2 y mem::size_of::<u32>3 . Si ese es el caso, cayendo El mem::size_of::<u32>4 construido a partir de un mem::size_of::<u32>5 puede indicar al asignador la alineación incorrecta Dado que esa información es basado en el tipo de elemento .

Sin un mejor conocimiento, la "mejor" cosa que debe hacer sería dejar el Vec16 como es y simplemente obtener un 99887776617 a él. La rebanada no tiene interacción con el asignador, evitando este problema.

Incluso sin interactuar con el asignador, ¡debe tener cuidado con la alineación!

Ver también:

  • Cómo rebanar un gran VEC & LT; I32 & GT; AS & AMP; [U8]?
  • https://stackoverflow.com/a/48309116/155423
 
  1. Whenever writing an unsafe block, I strongly encourage people to include a comment on the block explaining why you think the code is actually safe. That type of information is useful for the people who read the code in the future.

  2. Instead of adding comments about the "magic number" 4, just use mem::size_of::<u32>. I'd even go so far as to use size_of for u8 and perform the division for maximum clarity.

  3. You can return the newly-created Vec from the unsafe block.

  4. As mentioned in the comments, "dumping" a block of data like this makes the data format platform dependent; you will get different answers on little endian and big endian systems. This can lead to massive debugging headaches in the future. File formats either encode the platform endianness into the file (making the reader's job harder) or only write a specific endinanness to the file (making the writer's job harder).

  5. I'd probably move the whole unsafe block to a function and give it a name, just for organization purposes.

  6. You don't need to import Vec, it's in the prelude.

use std::mem;  fn main() {     let mut vec32 = vec![1u32, 2];      // I copy-pasted this code from StackOverflow without reading the answer      // surrounding it that told me to write a comment explaining why this code      // is actually safe for my own use case.     let vec8 = unsafe {         let ratio = mem::size_of::<u32>() / mem::size_of::<u8>();          let length = vec32.len() * ratio;         let capacity = vec32.capacity() * ratio;         let ptr = vec32.as_mut_ptr() as *mut u8;          // Don't run the destructor for vec32         mem::forget(vec32);          // Construct new Vec         Vec::from_raw_parts(ptr, length, capacity)     };      println!("{:?}", vec8) } 

Playground

My biggest unknown worry about this code lies in the alignment of the memory associated with the Vec.

Rust's underlying allocator allocates and deallocates memory with a specific Layout. Layout contains such information as the size and alignment of the pointer.

I'd assume that this code needs the Layout to match between paired calls to alloc and dealloc. If that's the case, dropping the Vec<u8> constructed from a Vec<u32> might tell the allocator the wrong alignment since that information is based on the element type.

Without better knowledge, the "best" thing to do would be to leave the Vec<u32> as-is and simply get a &[u8] to it. The slice has no interaction with the allocator, avoiding this problem.

Even without interacting with the allocator, you need to be careful about alignment!

See also:

  • How to slice a large Vec<i32> as &[u8]?
  • https://stackoverflow.com/a/48309116/155423
 
 
         
         
3
 
vote

Si el conversión en el lugar no es tan obligatorio, algo como lo administra órdenes de bytes Control y evita el bloque inseguro:

  mem::size_of::<u32>8  
 

If in-place convert is not so mandatory, something like this manages bytes order control and avoids the unsafe block:

extern crate byteorder;  use byteorder::{WriteBytesExt, BigEndian};  fn main() {     let vec32: Vec<u32> = vec![0xaabbccdd, 2];     let mut vec8: Vec<u8> = vec![];      for elem in vec32 {         vec8.write_u32::<BigEndian>(elem).unwrap();     }      println!("{:?}", vec8); } 
 
 
 
 

Relacionados problema

4  Se negó a establecer una "conexión" de encabezado inseguro "  ( Refused to set unsafe header connection ) 
Estoy trabajando en una aplicación de plataforma cruzada que se dirige a las plataformas Android e IOS. Estoy usando jquery 1.9.1, JQUERY MOBILE 1.3.1 y PHONE...

12  Declaración fija en C #  ( Fixed statement in c sharp ) 
Tenemos un código similar a lo siguiente en uno de nuestros proyectos. ¿Alguien puede explicar (en inglés simple) por qué se necesita aquí la declaración fija...

0  Obtener tipo de objeto y luego lanzarlo?  ( Get type of object and then cast it ) 
Estoy tratando de hacer un poco de "Memcpy" Hack en C #. Sigo atascado en esta parte, porque no convertirá System.Type a byte * public unsafe void memc...

3  SQL Server 2005: ¿Es seguro usar @@ Identity?  ( Sql server 2005is it safe to use identity ) 
Tengo un procedimiento en el que estoy insertando el registro en la tabla de empleados. ¡NAD obteniendo EPPID usando @@ Identity? Cuando este procedimiento se...

4  Inseguro en vb.net [duplicado]  ( Unsafe in vb net ) 
Esta pregunta ya tiene respuestas aquí : ¿Cómo puedo usar un código inseguro en vb.net? ...

5  ¿Método genérico de bitconverter?  ( Generic bitconverter like method ) 
Recientemente he encontrado una situación en la que necesito crear un método genérico para leer un tipo de datos de una matriz de bytes. He creado la siguie...

3  Problemas de ergonomía con matrices de bytes de tamaño fijo en óxido  ( Ergonomics issues with fixed size byte arrays in rust ) 
Oxidal Lamentablemente no puede producir una matriz de tamaño fijo [u8; 16] con un operador de corte de tamaño fijo s[0..16] . Tirará errores como "la matr...

2  ¿Cómo configurar un elemento de campo de búfer fijo usando la reflexión?  ( How to set a fixed buffer field element using reflection ) 
Aquí hay un $fmt = datefmt_create( 'fa_IR@calendar=persian', IntlDateFormatter::FULL, IntlDateFormatter::FULL, 'Asia/Tehran', IntlDateFor...

1  El compilador dice que no puedo tomar la dirección del campo readonly de un tipo de valor  ( Compiler says i cannot take the address of a value types readonly field ) 
Tengo una estructura: struct S { public readonly int Value1; public readonly int Value2; public S(int value1, int value2) { this.Value1...

9  ¿Son posibles matrices eficientes de tamaño fijo sin usar un código inseguro en .NET?  ( Are efficient fixed size arrays possible without using unsafe code in net ) 
¿Hay una forma Good de implementar una matriz de tamaño fijo en .NET que no requiere un código inseguro? Mi objetivo es crear un tipo de valor que represe...




© 2022 respuesta.top Reservados todos los derechos. Centro de preguntas y respuestas reservados todos los derechos