C++实现Base32算法

326 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

一、前言

Base32是一种使用32个ASCII字符将任意字符串重新编码成5bit一组的可显字符串的编码算法,Base32与Base64的实现原理类似,在国产系统使用场景中,经常出现Base64的字符显示重复,比如大写字母O和数字0,用肉眼无法分辨,这就造成了很多麻烦,所以Base32使用32个字符,可以有效避免使用容易混淆的字符,大大提高识别的正确率

二、代码

#ifndef YBASE32_H
#define YBASE32_H

#include <string>

class YBase32{
public:
    static int toBase32Length(int count);
    static int fromBase32Length(int count);

    static void toBase32(char* dest, const char* src, int length);
    static void fromBase32(char* dest, const char* src, int length);

    static std::string toBase32(const std::string& string);
    static std::string fromBase32(const std::string& string);
};

#endif // YBASE32_H

#include "YBase32.h"
#include "string.h"

static char map[33] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
static uint8_t reverse_map[96] = {
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    255, 255, 24, 25, 26, 27, 28, 29, 30, 31, 255, 255, 255, 255, 255, 255,
    255, 0, 1, 2, 3, 4, 5, 6, 7, 255, 8, 9, 10, 11, 12, 255,
    13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 255, 255, 255, 255, 255,
};

int YBase32::toBase32Length(int count)
{
    int byteCount = count * 8;
    int length = byteCount / 5;
    if(byteCount % 5 != 0){
        ++length;
    }
    return length;
}

int YBase32::fromBase32Length(int count)
{
    return count * 5 / 8;
}

//https://gitee.com/YuanDad
namespace YBase32NameSpace {
template <typename T>
inline T QyAbs(const T &t) { return t >= 0 ? t : -t; }
}
//https://gitee.com/YuanDad

using namespace YBase32NameSpace;
//https://gitee.com/YuanDad

static char mask[5] = {1, 3, 7, 15, 31};
void YBase32::toBase32(char *dest, const char *src, int length)
{
    int left = 0, maxLength = length * 8, cursor = 0;
    while(true){
        int right = left + 5;
        if(right < maxLength){
            int x = left / 8, used = left % 8, remain = 8 - used;
            int tmp = 0;
            if(remain < 5){
                tmp = (src[x] & mask[remain - 1]) << (5 - remain);
                tmp += uchar(src[x + 1]) >> (3 + remain);
            }else{
                tmp = (src[x] >> (remain - 5) & mask[4]);
            }
            dest[cursor] = map[QyAbs(tmp)];
        }else if(right > maxLength){
            int tmp = (src[length - 1] & mask[maxLength - left - 1]) << (right - maxLength);
            dest[cursor] = map[QyAbs(tmp)];
            return;
        }else{
            int tmp = src[length - 1] & mask[4];
            dest[cursor] = map[QyAbs(tmp)];
            return;
        }
        ++cursor;
        left = right;
    }
}

void YBase32::fromBase32(char *dest, const char *src, int length)
{
    int left = 0, maxLength = fromBase32Length(length) * 8, cursor = 0;
    while(true){
        int right = left + 8;
        int x = left / 5, used = left % 5, count = 5 - used, sum = count;
        dest[cursor] = static_cast<char>(reverse_map[static_cast<uint>(src[x])] & mask[count - 1]);
        if(count < 3){
            ++x;
            sum += 5;
            dest[cursor] = static_cast<char>(dest[cursor] << 5) + reverse_map[static_cast<uint>(src[x])];
        }
        if(sum < 8){
            int remain = 8 - sum;
            dest[cursor] = static_cast<char>(dest[cursor] << remain) + (reverse_map[static_cast<uint>(src[x + 1])] >> (5 - remain));
        }

        if(right == maxLength){
            return;
        }

        ++cursor;
        left = right;
    }
}

std::string YBase32::toBase32(const std::string &string)
{
    std::string result(static_cast<size_t>(toBase32Length(static_cast<int>(string.size()))), '\0');
    toBase32(const_cast<char*>(result.data()), string.data(), static_cast<int>(string.size()));
    return result;
}

std::string YBase32::fromBase32(const std::string &string)
{
    std::string result(static_cast<size_t>(fromBase32Length(static_cast<int>(string.size()))), '\0');
    fromBase32(const_cast<char*>(result.data()), string.data(), static_cast<int>(string.size()));
    return result;
}