将 std::vector<uint8_t> 转换为打包 std::vector<uint64_t>

Noa*_*oah 5 c++ vector c++17

我正在寻找一种方法来有效且无需 UB 将 a 转换std::vector<uint8_t>std::vector<uint64_t>st 中的每个元素都保存std::vector<uint64_t>来自 8 个元素的信息std::vector<uint8_t>。其余元素应该用零填充,但这可以稍后完成。

到目前为止我想出的最好的方法是:

std::vector<uint64_t> foo(std::vector<uint8_t> const & v8) {
    std::vector<uint64_t> v64;
    v64.reserve((v8.size() + 7) / 8);
    size_t i = 0;
    uint64_t tmp;
    for(; i + 8 < v8.size(); i += 8) {
        memcpy(&tmp, v8.data() + i, 8);
        v64.push_back(tmp);
    }
    tmp = 0; // fill remainder with 0s.
    memcpy(&tmp, v8.data() + i, v8.size() - i);
    v64.push_back(tmp);
    return v64;
}

Run Code Online (Sandbox Code Playgroud)

但我希望有一些更干净/更好的方法。

Edit1:关于丢失字节顺序问题的解决方案。由@VainMain 指出。

可以在memcpy.

Luk*_*kas 2

如果您准备使用range-v3 库,则可以使用ranges::view::chunk(或可能很快可用的 C++23 标准库实现等效项)。

这将特别使您无需计算将打包值存储在以下位置的向量所需的大小:

#include <array>
#include <cstddef>
#include <span>
#include <vector>

#include <range/v3/all.hpp>

std::vector<std::uint64_t> pack(const std::span<const std::uint8_t> values)
{
    const auto chunked_view = ranges::view::chunk(values, 8);

    std::vector<std::uint64_t> packed(ranges::size(chunked_view));
    ranges::transform(chunked_view, packed.begin(), [](const auto& word) {

        std::array<std::uint8_t, 8> buf{0}; // init with all 0's
        ranges::copy(word, buf.begin());

        std::uint64_t packed_word;
        std::memcpy(&packed_word, buf.data(), 8);
        return packed_word;
    });
    
    return packed;
}
Run Code Online (Sandbox Code Playgroud)

示例(参见 godbolt.org

int main()
{
    std::array<uint8_t, 9> values;
    std::iota(values.begin(), values.end(), std::uint8_t{0});

    for (auto t : pack(values))
        std::cout << std::hex << t << std::endl;
    // prints
    // 706050403020100
    // 8
    return 0;
}
Run Code Online (Sandbox Code Playgroud)