summaryrefslogtreecommitdiff
path: root/src/base64.cc
blob: da72de1ebcc8306cff032d80ca1db2ee2a76c605 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#include "common.hh"

#include "base64.hh"

namespace stuff {

namespace {

int8_t value(char c) {
    if (c >= 'A' && c <= 'Z') {
        return c - 'A';
    }
    if (c >= 'a' && c <= 'z') {
        return 26 + c - 'a';
    }
    if (c >= '0' && c <= '9') {
        return 52 + c - '0';
    }
    if (c == '+' || c == '-') {
        return 62;
    }
    if (c == '/' || c == '_') {
        return 63;
    }
    return -1;
}

bool decode_one(const int8_t buf[4], std::string* output) {
    if (buf[1] == -1 || buf[0] == -1) {
        // There is no reason for the first or the second char to be
        // padding
        return false;
    }
    output->push_back((buf[0] << 2) | (buf[1] >> 4));
    if (buf[2] == -1) {
        return buf[3] == -1;  // No padding between non-padding
    }
    output->push_back(((buf[1] & 0xf) << 4) | (buf[2] >> 2));
    if (buf[3] == -1) return true;
    output->push_back(((buf[2] & 0x3) << 6) | buf[3]);
    return true;
}

}  // namespace

bool Base64::decode(const std::string& input, std::string* output) {
    int8_t buf[4];
    size_t fill = 0;
    bool done = false;
    output->clear();
    for (auto it = input.begin(); it != input.end(); ++it) {
        buf[fill] = value(*it);
        if (buf[fill] != -1 || *it == '=') {
            if (done) return false;  // crap after padding
            if (++fill == 4) {
                if (!decode_one(buf, output)) return false;
                done = buf[3] == -1;
                fill = 0;
            }
        }
    }
    if (fill != 0) {
        while (fill < 4) buf[fill] = -1;
        if (!decode_one(buf, output)) return false;
    }
    return true;
}

}  // namespace stuff