joeylib2/tools/common/toolBase64.c

115 lines
2.9 KiB
C

// Part of JoeyLib, Copyright (c) 2026 Scott Duensing, under the MIT License.
// See LICENSE.md for license information.
// SPDX-License-Identifier: MIT
//
// toolBase64.c - base64 decoding for the JoeyLib host tools
// (toolBase64.h).
#include <stdlib.h>
#include "toolBase64.h"
#define BASE64_PAD '='
#define BASE64_NONE 64u
#define QUAD 4u
#define LOW2_MASK 0x03u
#define LOW4_MASK 0x0Fu
#define SHIFT2 2u
#define SHIFT4 4u
#define SHIFT6 6u
// Where each run of the alphabet starts: A-Z, a-z, 0-9, then + and /.
#define AT_UPPER 0u
#define AT_LOWER 26u
#define AT_DIGIT 52u
#define AT_PLUS 62u
#define AT_SLASH 63u
static uint32_t sextet(char c);
// A base64 character's six bits, or BASE64_NONE for any other character.
static uint32_t sextet(char c) {
if (c >= 'A' && c <= 'Z') {
return (uint32_t)(c - 'A') + AT_UPPER;
}
if (c >= 'a' && c <= 'z') {
return (uint32_t)(c - 'a') + AT_LOWER;
}
if (c >= '0' && c <= '9') {
return (uint32_t)(c - '0') + AT_DIGIT;
}
if (c == '+') {
return AT_PLUS;
}
if (c == '/') {
return AT_SLASH;
}
return BASE64_NONE;
}
bool toolBase64Decode(const char *text, size_t len, uint8_t **out, size_t *outLen) {
uint8_t *data;
size_t n;
size_t i;
uint32_t quadPos;
uint32_t pads;
uint32_t left;
uint32_t v;
data = (uint8_t *)malloc(len / QUAD * 3u + 3u);
if (data == NULL) {
return false;
}
n = 0u;
quadPos = 0u;
pads = 0u;
left = 0u;
for (i = 0; i < len; i++) {
if (text[i] == BASE64_PAD) {
// Padding that fills a group of two or three characters out to
// four ends it, and the data; any other is skipped.
if (quadPos >= 2u) {
pads++;
if (quadPos + pads >= QUAD) {
quadPos = 0u;
break;
}
}
continue;
}
v = sextet(text[i]);
if (v == BASE64_NONE) {
continue;
}
pads = 0u;
switch (quadPos) {
case 0:
quadPos = 1u;
left = v;
break;
case 1:
quadPos = 2u;
data[n++] = (uint8_t)((left << SHIFT2) | (v >> SHIFT4));
left = v & LOW4_MASK;
break;
case 2:
quadPos = 3u;
data[n++] = (uint8_t)((left << SHIFT4) | (v >> SHIFT2));
left = v & LOW2_MASK;
break;
default:
quadPos = 0u;
data[n++] = (uint8_t)((left << SHIFT6) | v);
left = 0u;
break;
}
}
if (quadPos != 0u) {
free(data);
return false;
}
*out = data;
*outLen = n;
return true;
}