将Vec <u32>转换为Vec <u8>,并且开销最小

时间:2018-04-06 10:13:02

标签: casting rust unsafe

我尝试将Vec u32转换为Vec u8Vec,最好是就地而且没有太多开销。< / p>

我当前的解决方案依赖于不安全的代码来重新构建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) } 。有没有更好的方法来做到这一点,以及与我的解决方案相关的风险是什么?

//app.js
    function newDoc(event) {
        alert(this.className)
        var root = document.getElementById('root'),
            originTree = root.innerHTML;
        root.innerHTML = ''   


//create all needed element
    var previewBox1 = document.createElement('div'),
        previewBox2 = document.createElement('div'),
        previewCommon = document.createElement('div') ,
        tankImg = document.createElement('div'),
        previewLabel = document.createElement('div'),
        backToList = document.createElement('div'),
        table = document.createElement('div'),
        previewBox1Flag = document.createElement('div'),
        previewBox1Model = document.createElement('div'),
        previewBox1Level = 

document.createElement('div');

    // find tank object by Model name
    for (let i = 0; i < tanks.length; ++i){
        if (tanks[i].model === this.className){
            var clickedObj = tanks[i];
            break;
        }
    }


// fill by data object
    tankImg.style.backgroundImage = "url(" + clickedObj.preview + ")";
//in previous line is my trouble!
    previewBox1Flag.style.backgroundImage = "url(" + 
clickedObj["country_image"] + ")";
    previewBox1Level.innerHTML = "(level " + clickedObj.level + ")";
    previewBox1Model.innerHTML = clickedObj.model.toUpperCase();
    backToList.innerHTML = 'Back to list view';
    previewLabel.innerHTML = 'Preview';
    table.innerHTML = 'Characteristic';

//    to construck all the element 
    previewCommon.appendChild(previewBox1Flag);
    previewCommon.appendChild(previewBox1Model);
    previewCommon.appendChild(previewBox1Level);

    previewBox1.appendChild(previewCommon);
    previewBox1.appendChild(previewLabel);
    previewBox1.appendChild(tankImg);
    previewBox1.appendChild(backToList);
    previewBox2.appendChild(table);
    root.appendChild(previewBox1);
    root.appendChild(previewBox2);  
}

function makePreviewObject(list){
    var rootDiv = document.getElementById('root'),
        newUl = document.createElement('ul');
    for (let i = 0;i < list.length; ++i){

        var nextLi = document.createElement('li'),
            newImgDiv = document.createElement('div'),
            newCommonDiv = document.createElement('div'),
            divForFlag = document.createElement('div'),
            divForLevel = document.createElement('div'),
            divForModel = document.createElement('div');

        nextLi.className = list[i].model;
        newImgDiv.className = 'newImgDiv';
        newCommonDiv.className = 'newCommonDiv';
        divForFlag.className = 'divForFlag';
        newImgDiv.style.backgroundImage = 'url(' + list[i].preview + ')';
        divForFlag.style.backgroundImage = 'url(' + list[i]['country_image'] 
+ ')';
        divForLevel.innerHTML = list[i].level;
        divForModel.innerHTML = lengthOffSet(list[i].model.toUpperCase());

        newCommonDiv.appendChild(divForFlag);
        newCommonDiv.appendChild(divForLevel);
        newCommonDiv.appendChild(divForModel);

        nextLi.addEventListener('click', newDoc);
        nextLi.appendChild(newImgDiv);
        nextLi.appendChild(newCommonDiv);

        newUl.appendChild(nextLi);

        rootDiv.appendChild(newUl); 
    }

}

function lengthOffSet(string){
    if (string.length < 14){
        return string
    } else{
        return string.substr(0, 11) + '...'
    }
}

makePreviewObject(tanks);


//tanks.js
var tanks = [
  {
    model: "Object 140",
    preview: "images/tanks/object_140.png",
    country: "ussr",
    country_image: "images/countries/ussr.png",
    level: 10,
    details: {
      damage: 420,
      breoning: 264,
      attack_speed: 9.09,
      time_of_targeting: 2.1,
      ammunition: 50
    }
  },
  {
    model: "t-62a",
    preview: "images/tanks/t62a.png",
    country: "ussr",
    country_image: "images/countries/ussr.png",
    level: 10,
    details: {
      damage: 630,
      breoning: 246,
      attack_speed: 9.09,
      time_of_targeting: 2,
      ammunition: 50
    }
  }
]



//index.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="styles.css">
</head>
<body>
  <div id="root"></div>

  <script src="tanks.js"></script>
  <script src="app.js"></script>

</body>
</html>

Rust Playground link

2 个答案:

答案 0 :(得分:6)

  1. 每当编写unsafe块时,我强烈鼓励人们在块上添加注释,解释为什么您认为代码实际上是安全的 。这类信息对将来阅读代码的人很有用。

  2. 只需使用mem::size_of::<u32>,而不是添加有关“幻数”4的评论。我甚至会为size_of使用u8并执行除法以获得最大的清晰度。

  3. 您可以从unsafe区块返回新创建的Vec。

  4. 正如评论中所提到的,“倾销”这样的数据块使得数据格式平台依赖;你将在小端和大端系统上得到不同的答案。这可能导致未来的大量调试问题。文件格式要么将平台字节序编码到文件中(使读者的工作更难),要么只对文件写一个特定的内容(使作者的工作更难)。

  5. 我可能会将整个unsafe块移动到一个函数中并为其命名,仅用于组织目的。

  6. 您无需导入Vec,它就在前奏中。

  7.     
    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

    我对此代码最大的未知担心在于与Vec关联的内存对齐。

    Rust的底层分配器allocatesdeallocates内存特定Layout Layout包含指针的 size alignment 等信息。

    我认为此代码需要Layout才能在对allocdealloc的成对调用之间进行匹配。如果是这种情况,请dropping the Vec<u8> constructed from a Vec<u32> might tell the allocator the wrong alignment,因为该信息为based on the element type

    如果没有更好的知识,“最好”的做法就是让Vec<u32>保持原样并简单地获得&[u8]。切片与分配器没有交互,避免了这个问题。

    另见:

答案 1 :(得分:0)

如果就地转换不是强制性的,那么像这样管理bytes order控制并避免不安全的阻止:

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);
}