使用 Base64 字符串文字编译 uint8_t 数组的初始化

问题描述 投票:0回答:1

我有一个源文件,其中包含表示图像的大字节数组。

下面是一个示例(实际上它可以是任何随机数据):

const uint8_t image_test_image[] ={
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 
    0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 
    0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 
    0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 
    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 
    0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
    ...
);

我有几个这样的源文件,它们占用了我的磁盘上的大量空间。 我可以删除空白区域,但这只能节省一点空间。 我想尝试两种不同的方法在编译时初始化该数组以减少源文件大小。

一种方法是使用 base64 字符串:

const uint8_t image_test_image[] =
base64decode(
  "AECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMz"
  "Q1Njc4OTo7PD0+P0BBQkNERUZH"
  ...
);

我认为应该可以制作一个仅包含 C++ 标头的 constexpr base64 解码器,该解码器采用字符串文字。示例如下:https://stackoverflow.com/a/59604914/15307950。但这需要您显式添加大小作为参数。我不想要这样。是否可以从参数值推导出模板参数值?

另一种方法是使用 uint64_t 数组或 uint64_t 初始化列表作为输入。选择某种字节顺序并考虑到字节数组可能不是 8 字节的倍数。

我现在使用C++14,但C++17也是可以的。在这个项目的未来,甚至 C++20 也可能成为可能。使用 std::array 就可以了。

编辑2:

uint64 数组的工作原理:

#include <stdio.h>
#include <stdint.h>
#include <array>

//#define LITTLE_ENDIAN

template <typename... T>
constexpr std::array<uint8_t, sizeof...(T)*8> u64_array_to_u8_array(T&&... t) 
{
    std::array<uint8_t, sizeof...(T)*8> out{};
    std::array<int64_t, sizeof...(T)> in = {t...};

    for (size_t i = 0; i < sizeof...(T)*8; ++ i) {
        #ifdef LITTLE_ENDIAN
        out[i] = uint64_t(in[i/8]) >> ((i%8)*8);//little endian
        #else
        out[i] = uint64_t(in[i/8]) >> ((7-i%8)*8);//big endian
        #endif
    }
    return out;
}

#ifdef LITTLE_ENDIAN
//little little
const auto image_test_image = u64_array_to_u8_array(
    0x0706050403020100,0x0F0E0D0C0B0A0908,
    0x1716151413121110,0x1F1E1D1C1B1A1918);
#else
//big endian
const auto image_test_image = u64_array_to_u8_array(
    0x0001020304050607,0x08090A0B0C0D0E0F,
    0x1011121314151617,0x18191A1B1C1D1E1F
);
#endif

int main() {
    for (size_t i = 0; i < sizeof(image_test_image); ++i)
    {
        printf("0x%02X, ", image_test_image[i]);
        if ((i%16==15) && image_test_image[i] != sizeof(image_test_image))
        {
            puts("");
        }
    }
    
    return 0;
}

现在我只需要知道如何使用字符串文字实现 constexpr base64 解码。

arrays c++17 constexpr
1个回答
0
投票

编译时将 Base64 字符串文字转换为字节数组

c++17(使用宏)

#include <array>
#include <string> // for std::char_traits
    
//based on https://stackoverflow.com/a/59604914/15307950

constexpr size_t decodeBase64Length(const char *s)
{
    size_t len = std::char_traits<char>::length(s);
    if (s[len - 2] == '=')
        return (len / 4) * 3 - 2;
    else if(s[len -1] == '=')
        return (len / 4) * 3 - 1;
    else
        return (len / 4) * 3 ;
}

constexpr std::array<int, 256> prepareBase64DecodeTable() {
    std::array<int, 256> T{ 0 }; // breaks constexpr: T.fill(-1) or missing initialization
    for (int i = 0; i < 256; i++)
        T[i] = -1;
    for (int i = 0; i < 64; i++)
        T["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i;
    return T;
}

// based on https://stackoverflow.com/a/34571089/3158571
template<int N>
constexpr std::array<std::byte, N> decodeBase64(const char *b64Str)
{
    constexpr auto T = prepareBase64DecodeTable();
    std::array<std::byte, N> out = { std::byte(0) };
    int valb = -8;
    for (size_t i = 0, val = 0, posOut = 0; i < std::char_traits<char>::length(b64Str) && T[b64Str[i]] != -1; i++) {
        val = (val << 6) + T[b64Str[i]];
        valb += 6;
        if (valb >= 0) {
            out[posOut++] = std::byte((val >> valb) & 0xFF);
            valb -= 8;
        }
    } 
    return out;
}

//added macro to allow string literal in to be used inline without a define
#define DECODE_B64(b64) decodeBase64<decodeBase64Length(b64)>(b64) 

用途:

constexpr auto b64a = DECODE_B64("SGVsbG8=");

在这里测试:https://godbolt.org/z/Esx77W1cG

c++20(使用自定义字符串文字)

#include <array>
#include <string> // for std::char_traits
#include <algorithm> // for std::copy_n

//from https://yongweiwu.wordpress.com/2022/06/19/compile-time-strings/
template <size_t N>
struct compile_time_string {
    constexpr compile_time_string(const char (&str)[N])
    {
        std::copy_n(str, N, value);
    }
    char value[N]{};
}; 
template <compile_time_string cts>
constexpr auto operator""_cts()
{
    return cts;
}

//based on https://stackoverflow.com/a/59604914/15307950
        
constexpr size_t decodeBase64Length(const char *s)
{
    size_t len = std::char_traits<char>::length(s);
    if (s[len - 2] == '=')
        return (len / 4) * 3 - 2;
    else if(s[len -1] == '=')
        return (len / 4) * 3 - 1;
    else
        return (len / 4) * 3 ;
}

constexpr size_t decodeBase64Length(std::string_view s)
{
    size_t len = s.length();
    if (s[len - 2] == '=')
        return (len / 4) * 3 - 2;
    else if(s[len -1] == '=')
        return (len / 4) * 3 - 1;
    else
        return (len / 4) * 3 ;
}


constexpr std::array<int, 256> prepareBase64DecodeTable() {
    std::array<int, 256> T{ 0 }; // breaks constexpr: T.fill(-1) or missing initialization
    for (int i = 0; i < 256; i++)
        T[i] = -1;
    for (int i = 0; i < 64; i++)
        T["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i;
    return T;
}

// based on https://stackoverflow.com/a/34571089/3158571
//template <size_t N>
template<auto s>
constexpr std::array<std::byte, decodeBase64Length(s.value)> decodeBase64()
{
    constexpr auto T = prepareBase64DecodeTable();
    std::array<std::byte, decodeBase64Length(s.value)> out = { std::byte(0) };
    int valb = -8;
    for (size_t i = 0, val = 0, posOut = 0; i < std::char_traits<char>::length(s.value) && T[s.value[i]] != -1; i++) {
        val = (val << 6) + T[s.value[i]];
        valb += 6;
        if (valb >= 0) {
            out[posOut++] = std::byte((val >> valb) & 0xFF);
            valb -= 8;
        }
    } 
    return out;
}

用途:

constexpr auto b64a = decodeBase64<"SGVsbG8="_cts>();

在这里测试:https://godbolt.org/z/TEzh3Mffh

© www.soinside.com 2019 - 2024. All rights reserved.