Base64 encode and decode, line by line.

This commit is contained in:
Philippe Pittoli 2025-11-30 14:47:01 +01:00
commit a992bb8f3f
5 changed files with 110 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
bin/

7
README.md Normal file
View file

@ -0,0 +1,7 @@
# Base64 line-by-line
These applications encode and decode strins in Base64 by reading their inputs **line by line**.
Advantages:
- Ultra fast, not even a single memory allocation is performed
- Readable source code, it's just a function call and a loop over input

6
makefile Normal file
View file

@ -0,0 +1,6 @@
all: build
build:
[ -d ./bin ] || mkdir bin
cc -o bin/b64-lbl-encode src/b64-lbl-encode.c
cc -o bin/b64-lbl-decode src/b64-lbl-decode.c

53
src/b64-lbl-decode.c Normal file
View file

@ -0,0 +1,53 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFFER_LENGTH 50000
static const char *base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
int base64_decode(const char *input, unsigned char *output, size_t *output_length) {
int input_length = strlen(input);
if (input_length % 4 != 0) return -1; // Base64 input must be a multiple of 4.
// Calculate output length
*output_length = input_length / 4 * 3;
if (input[input_length - 1] == '=') (*output_length)--;
if (input[input_length - 2] == '=') (*output_length)--;
for (int i = 0, j = 0; i < input_length; ) {
unsigned int a = input[i] == '=' ? 0 : strchr(base64_chars, input[i]) - base64_chars; i++;
unsigned int b = input[i] == '=' ? 0 : strchr(base64_chars, input[i]) - base64_chars; i++;
unsigned int c = input[i] == '=' ? 0 : strchr(base64_chars, input[i]) - base64_chars; i++;
unsigned int d = input[i] == '=' ? 0 : strchr(base64_chars, input[i]) - base64_chars; i++;
output[j++] = (a << 2) | (b >> 4);
if (j < *output_length) output[j++] = (b << 4) | (c >> 2);
if (j < *output_length) output[j++] = (c << 6) | d;
}
return 0;
}
int main(void) {
char input[BUFFER_LENGTH];
unsigned char output[BUFFER_LENGTH]; // Maximum size for decoded output from a Base64 string
size_t output_length;
while (fgets(input, sizeof(input), stdin)) {
// Remove the newline character if present
input[strcspn(input, "\n")] = 0;
if (base64_decode(input, output, &output_length) == 0) {
fwrite(output, 1, output_length, stdout);
putchar('\n'); // Just to separate outputs if needed
} else {
fprintf(stderr, "Error decoding: %s\n", input);
}
}
return 0;
}

43
src/b64-lbl-encode.c Normal file
View file

@ -0,0 +1,43 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFFER_LENGTH 50000
static const char *base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
void base64_encode(const unsigned char *input, size_t input_length, char *output, size_t *output_length) {
int i, j;
*output_length = 4 * ((input_length + 2) / 3); // Calculate output size
for (i = 0, j = 0; i < input_length;) {
unsigned int a = i < input_length ? input[i++] : 0;
unsigned int b = i < input_length ? input[i++] : 0;
unsigned int c = i < input_length ? input[i++] : 0;
output[j++] = base64_chars[a >> 2];
output[j++] = base64_chars[((a & 3) << 4) | (b >> 4)];
output[j++] = (i > input_length + 1) ? '=' : base64_chars[((b & 15) << 2) | (c >> 6)];
output[j++] = (i > input_length) ? '=' : base64_chars[c & 63];
}
output[j] = '\0'; // Null-terminate the output string
}
int main() {
char input[BUFFER_LENGTH];
char output[BUFFER_LENGTH*4/3]; // Maximum size for encoded output (4/3 per byte)
size_t output_length;
while (fgets(input, sizeof(input), stdin)) {
// Remove the newline character if present
input[strcspn(input, "\n")] = 0;
base64_encode((unsigned char *)input, strlen(input), output, &output_length);
printf("%s\n", output);
}
return 0;
}