【问题标题】:Migrating to unique_ptr from a dynamic array and failing to convert to std::span从动态数组迁移到 unique_ptr 并且无法转换为 std::span
【发布时间】:2021-07-02 11:53:50
【问题描述】:

我正在尝试加密和解密文件,基本上是在练习 std::spanstd::unique_ptr。这些问题在代码中进行了注释。

  1. rc4 的第二个参数是 std::span,但我的参数是 vector<uint8_t> 用于加密,unique_ptr<uint8_t> 用于解密,我无法转换为。
  2. *output++ = elem ^ s[(s[i] + s[j]) % 256]; 不能用 unique_ptr 完成。
const auto log_data = read_log_file(log_path);
const auto log_size = static_cast<std::size_t>(std::filesystem::file_size(log_path));

// Generate a random key
std::uint8_t key[32];
generate_random_key(key);

// Encrypt
const auto result = std::make_unique<std::uint8_t>(log_size);

rc4_context context{};
rc4(&context, log_data, key, std::move(result)); // TODO: log_data: Cannot convert lvalue of type const std::vector<uint8_t> to parameter type const std::span<uint8_t>

// Decrypt
const auto result2 = std::make_unique<std::uint8_t>(log_size);
rc4(&context, std::move(result), key, std::move(result2)); // TODO: Again the second parameter cannot be casted from unique_ptr<unsigned char> to span<uint8_t>
std::vector<std::uint8_t> read_log_file(const std::wstring& path)
{
    std::ifstream ifs(path, std::ios::binary | std::ios::ate);

    if (!ifs)
        throw std::runtime_error("Could not load file.");

    const auto end = ifs.tellg();
    ifs.seekg(0, std::ios::beg);

    const auto size = static_cast<std::size_t>(end - ifs.tellg());

    if (size == 0)
        return {};

    std::vector<std::uint8_t> buffer(size);

    if (!ifs.read(reinterpret_cast<char*>(buffer.data()), buffer.size()))
        throw std::runtime_error("Could not read file.");

    return buffer;
}

int rc4(rc4_context* context, const std::span<std::uint8_t>& data, const std::span<std::uint8_t>& key, const std::unique_ptr<std::uint8_t>& output)
{
    // INITIALIZATION
    std::uint32_t i, j;

    // Check parameters
    if (!context || !key.empty())
        return ERROR_INVALID_PARAMETER;

    // Clear context
    context->i = 0;
    context->j = 0;

    // Initialize the S array with identity permutation
    for (i = 0; i < 256; i++)
    {
        context->s[i] = static_cast<std::uint8_t>(i);
    }

    // S is then processed for 256 iterations
    for (i = 0, j = 0; i < 256; i++)
    {
        // Randomize the permutations using the supplied key
        j = (j + context->s[i] + key[i % key.size()]) % 256;

        // Swap the values of S[i] and S[j]
        const auto temp = context->s[i];
        context->s[i] = context->s[j];
        context->s[j] = temp;
    }

    // MAIN LOGIC PART
    // Restore context
    i = context->i;
    j = context->j;
    auto* s = context->s;
    
    // Encryption loop
    for (auto elem : data)
    {
        // Adjust indices
        i = (i + 1) % 256;
        j = (j + s[i]) % 256;

        // Swap the values of S[i] and S[j]
        std::swap(s[i], s[j]);

        // Valid output?
        if (output)
        {
            // XOR the input data with the RC4 stream
            *output++ = elem ^ s[(s[i] + s[j]) % 256]; // TODO: error happens here, because of *output++
        }
    }

    // Save context
    context->i = i;
    context->j = j;

    return NO_ERROR;
}

【问题讨论】:

  • 每当你想到“动态数组”时,你下一个想到的几乎总是std::vector
  • const auto result = std::make_unique&lt;std::uint8_t&gt;(log_size); 中,您分配一个 uint8_t,并使用值log_size 进行初始化。那是你真正想要的吗?
  • 另外,the std::span constructor 有几个重载可用于从向量创建跨度(使用迭代器)。
  • 4294967295 看起来像使用 unsigned 眼睛的 signed 32 位值 -1。
  • @TedLyngmo 这可能是std::dynamic_extent 的值,它是没有编译时大小的跨度的特殊常量。

标签: c++ vector unique-ptr c++20


【解决方案1】:

我也建议将std::vector&lt;uint8_t&gt; 也用于result,但这里是修复代码的方法。我已将重点放在问题上。

#include <cstdint>
#include <iostream>
#include <memory>
#include <span>
#include <vector>

// rc4's span should be over const uint8_t's:
void rc4(const std::span<const std::uint8_t>& data) {
    std::cout << data.size() << '\n';
}

int main() {
    size_t log_size = 100;

    const std::vector<uint8_t> log_data{1, 2, 3};   // ... since this is const
    rc4(log_data);

    // you should allocate an array, not a single uint8_t here:
    const auto result = std::make_unique<std::uint8_t[]>(log_size);

    // and you can create the span like this in your argument list:
    rc4( {result.get(), log_size} );
}

输出:

3
100

使用vectors 进行输入和输出的替代方法:

#include <cstdint>
#include <iostream>
#include <memory>
#include <utility>
#include <vector>

int rc4(const std::vector<std::uint8_t>& data,
        std::vector<std::uint8_t>& output)
{
    output.clear();
    output.reserve(data.size());

    for(auto byte : data) {
        output.push_back(0xFF - byte);              // a simple algoritm
    }

    return 0; // NO_ERROR;
}

int main() {  
    const std::vector<uint8_t> log_data{'A', 'B', 'C'};
    std::vector<std::uint8_t> result;

    rc4(log_data, result);                          // encrypt

    for(auto byte : result) {
        std::cout << std::hex << (int)byte << '\n'; // probably be, bd, bc
    }
    std::cout << '\n';

    std::vector<std::uint8_t> result2;
    rc4(result, result2);                           // decrypt

    for(auto byte : result2) {
        std::cout << byte;                          // prints ABC
    }
    std::cout << '\n';
}

【讨论】:

  • 谢谢!我实际上制作了结果vector,因为它看起来更好。 const std::vector&lt;std::uint8_t&gt; result(log_size);。剩下一个问题。 if (output) { // XOR the input data with the RC4 stream *output++ = elem ^ s[(s[i] + s[j]) % 256]; } 这不适用于vector/unique_ptr。增量不适合他们
  • @nop const std::unique_ptr&lt;std::uint8_t&gt;&amp; output 也是指向单个 uint8_t 的指针。您也必须将其设为一个数组(或vector&lt;uint8_t&gt;),但您不能对其中任何一个执行output++。使用成员 operator[] 或者,如果您使用 vector&lt;uint8_t&gt;,则使用其 begin() 迭代器并向前推进。
  • 我使用了向量。这是函数https://pastebin.com/B4QnknER。我把它写成if (output.empty()) { // XOR the input data with the RC4 stream output[x] = data[x] ^ s[(s[i] + s[j]) % 256]; }。还是 .begin() 更好?
  • @nop 我可能会从一个空的输出向量开始,然后将 push_back 新值放入其中。我使用您原来的unique_ptr 制作了一个example,但我想这已经是旧闻了? :-)
  • @nop 我也添加了使用向量的替代方法。
猜你喜欢
  • 2021-10-10
  • 2014-03-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-26
  • 2021-01-04
  • 2018-02-02
  • 2012-12-11
相关资源
最近更新 更多