From a992bb8f3f56f3e85f3bf7678d58a3eb94b6307c Mon Sep 17 00:00:00 2001 From: Philippe Pittoli Date: Sun, 30 Nov 2025 14:47:01 +0100 Subject: [PATCH] Base64 encode and decode, line by line. --- .gitignore | 1 + README.md | 7 ++++++ makefile | 6 +++++ src/b64-lbl-decode.c | 53 ++++++++++++++++++++++++++++++++++++++++++++ src/b64-lbl-encode.c | 43 +++++++++++++++++++++++++++++++++++ 5 files changed, 110 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 makefile create mode 100644 src/b64-lbl-decode.c create mode 100644 src/b64-lbl-encode.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6dd29b7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +bin/ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..8e48e70 --- /dev/null +++ b/README.md @@ -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 diff --git a/makefile b/makefile new file mode 100644 index 0000000..02a7d47 --- /dev/null +++ b/makefile @@ -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 diff --git a/src/b64-lbl-decode.c b/src/b64-lbl-decode.c new file mode 100644 index 0000000..31d6eff --- /dev/null +++ b/src/b64-lbl-decode.c @@ -0,0 +1,53 @@ +#include +#include +#include + +#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; +} diff --git a/src/b64-lbl-encode.c b/src/b64-lbl-encode.c new file mode 100644 index 0000000..9b2337f --- /dev/null +++ b/src/b64-lbl-encode.c @@ -0,0 +1,43 @@ +#include +#include +#include + +#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; +}