Skip to content

Commit 72fc1bb

Browse files
authored
Add Base64 Encode Decode in C (TheRenegadeCoder#4862)
1 parent 585c633 commit 72fc1bb

File tree

1 file changed

+205
-0
lines changed

1 file changed

+205
-0
lines changed

archive/c/c/base64-encode-decode.c

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
#include <ctype.h>
2+
#include <stdint.h>
3+
#include <stdio.h>
4+
#include <stdlib.h>
5+
#include <string.h>
6+
7+
static const unsigned char base64_alphabet[65] =
8+
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
9+
10+
static unsigned char* base64_encode(const unsigned char* input, size_t length, size_t* output_length);
11+
static unsigned char* base64_decode(const unsigned char* input, size_t length, size_t* output_length);
12+
static int is_valid_base64(const char* string);
13+
static void show_usage(void);
14+
15+
int main(int argc, char* argv[]) {
16+
if (argc != 3) {
17+
show_usage();
18+
}
19+
20+
const char* mode = argv[1];
21+
const char* input_text = argv[2];
22+
23+
if (strcmp(mode, "encode") == 0) {
24+
if (strlen(input_text) == 0) show_usage();
25+
26+
size_t input_length = strlen(input_text);
27+
unsigned char* data = base64_encode((const unsigned char*)input_text, input_length, NULL);
28+
if (!data) {
29+
show_usage();
30+
}
31+
32+
printf("%s\n", data);
33+
free(data);
34+
}
35+
else if (strcmp(mode, "decode") == 0) {
36+
if (strlen(input_text) == 0 || !is_valid_base64(input_text)) {
37+
show_usage();
38+
}
39+
40+
size_t input_length = strlen(input_text);
41+
size_t decoded_length;
42+
unsigned char* data = base64_decode((const unsigned char*)input_text, input_length, &decoded_length);
43+
if (!data || decoded_length == 0) {
44+
show_usage();
45+
}
46+
47+
fwrite(data, 1, decoded_length, stdout);
48+
fflush(stdout);
49+
free(data);
50+
}
51+
else {
52+
show_usage();
53+
}
54+
55+
return 0;
56+
}
57+
58+
static void show_usage(void) {
59+
fprintf(stderr, "Usage: please provide a mode and a string to encode/decode\n");
60+
exit(EXIT_FAILURE);
61+
}
62+
63+
static int is_valid_base64(const char* str) {
64+
if (!str) return 0;
65+
66+
size_t length = strlen(str);
67+
68+
// Base64 strings must have length multiple of 4
69+
if (length == 0 || length % 4 != 0) {
70+
return 0;
71+
}
72+
73+
// Each character must be alphanumeric, '+' or '/', or '=' for padding
74+
for (size_t i = 0; i < length; i++) {
75+
unsigned char c = (unsigned char)str[i];
76+
if (!isalnum(c) && c != '+' && c != '/' && c != '=') {
77+
return 0;
78+
}
79+
}
80+
81+
// Padding '=' only allowed at the end (max two)
82+
size_t padding_count = 0;
83+
if (length >= 1 && str[length - 1] == '=') {
84+
padding_count++;
85+
}
86+
if (length >= 2 && str[length - 2] == '=') {
87+
padding_count++;
88+
}
89+
// '=' should not appear before the padding section
90+
for (size_t i = 0; i < length - padding_count; i++) {
91+
if (str[i] == '=') {
92+
return 0;
93+
}
94+
}
95+
96+
return 1;
97+
}
98+
99+
static unsigned char* base64_encode(const unsigned char* input, size_t length, size_t* output_length) {
100+
if (!input) return NULL;
101+
102+
// 4 chars for every 3 bytes, rounded up
103+
size_t encoded_length = 4 * ((length + 2) / 3);
104+
unsigned char* data = malloc(encoded_length + 1); // +1 for NUL terminator
105+
if (!data) return NULL;
106+
107+
size_t input_index = 0, encoded_index = 0;
108+
while (input_index < length) {
109+
uint32_t buffer = 0;
110+
int remaining = (int)(length - input_index);
111+
112+
// Pack up to 3 bytes into 24 bits buffer
113+
buffer |= ((uint32_t)input[input_index++]) << 16;
114+
if (remaining > 1) {
115+
buffer |= ((uint32_t)input[input_index++]) << 8;
116+
}
117+
if (remaining > 2) {
118+
buffer |= (uint32_t)input[input_index++];
119+
}
120+
121+
// Extract 6-bit groups and map to Base64 chars
122+
data[encoded_index++] = base64_alphabet[(buffer >> 18) & 0x3F];
123+
data[encoded_index++] = base64_alphabet[(buffer >> 12) & 0x3F];
124+
data[encoded_index++] = (remaining > 1) ? base64_alphabet[(buffer >> 6) & 0x3F] : '=';
125+
data[encoded_index++] = (remaining > 2) ? base64_alphabet[buffer & 0x3F] : '=';
126+
}
127+
128+
data[encoded_index] = '\0'; // Null-terminate output string
129+
130+
if (output_length) {
131+
*output_length = encoded_index;
132+
}
133+
return data;
134+
}
135+
136+
static unsigned char* base64_decode(const unsigned char* input, size_t length, size_t* output_length) {
137+
if (!input) return NULL;
138+
139+
unsigned char decoding_table[256];
140+
memset(decoding_table, 0x80, 256); // 0x80 marks invalid chars
141+
142+
for (int i = 0; i < 64; i++) {
143+
decoding_table[base64_alphabet[i]] = (unsigned char)i;
144+
}
145+
decoding_table['='] = 0;
146+
147+
// Count valid base64 characters (ignore whitespace or invalid chars)
148+
size_t valid_char_count = 0;
149+
for (size_t i = 0; i < length; i++) {
150+
if (decoding_table[input[i]] != 0x80) {
151+
valid_char_count++;
152+
}
153+
}
154+
155+
if (valid_char_count == 0 || valid_char_count % 4 != 0) {
156+
return NULL;
157+
}
158+
159+
size_t decoded_length = valid_char_count / 4 * 3;
160+
unsigned char* data = malloc(decoded_length);
161+
if (!data) return NULL;
162+
163+
size_t input_index = 0, decoded_index = 0;
164+
unsigned char block[4];
165+
int padding_count = 0;
166+
size_t block_char_count = 0;
167+
168+
while (input_index < length) {
169+
unsigned char val = decoding_table[input[input_index++]];
170+
if (val == 0x80) {
171+
continue; // skip invalid chars (including whitespace)
172+
}
173+
174+
if (input[input_index - 1] == '=') {
175+
padding_count++;
176+
}
177+
block[block_char_count++] = val;
178+
179+
if (block_char_count == 4) {
180+
data[decoded_index++] = (block[0] << 2) | (block[1] >> 4);
181+
data[decoded_index++] = (block[1] << 4) | (block[2] >> 2);
182+
data[decoded_index++] = (block[2] << 6) | block[3];
183+
block_char_count = 0;
184+
185+
if (padding_count) {
186+
if (padding_count == 1) {
187+
decoded_index--;
188+
}
189+
else if (padding_count == 2) {
190+
decoded_index -= 2;
191+
}
192+
else {
193+
free(data);
194+
return NULL; // invalid padding length
195+
}
196+
break;
197+
}
198+
}
199+
}
200+
201+
if (output_length) {
202+
*output_length = decoded_index;
203+
}
204+
return data;
205+
}

0 commit comments

Comments
 (0)