Skip to content

Commit a4b75f5

Browse files
committed
added base64 library
1 parent b5e7af7 commit a4b75f5

File tree

5 files changed

+189
-1
lines changed

5 files changed

+189
-1
lines changed

CMakeLists.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@ endif()
1717

1818
add_subdirectory(bstr)
1919
add_subdirectory(vec)
20-
add_subdirectory(flag)
2120

21+
add_subdirectory(flag)
2222
target_link_libraries(flag PRIVATE bstr vec)
2323

24+
add_subdirectory(base64)
25+
2426
# --- Executables ---
2527

2628
add_executable(bstr_test bstr/maintest.c)
@@ -32,6 +34,9 @@ target_link_libraries(vec_test vec)
3234
add_executable(flag_test flag/maintest.c)
3335
target_link_libraries(flag_test flag)
3436

37+
add_executable(base64_test base64/maintest.c)
38+
target_link_libraries(base64_test base64)
39+
3540
# --- Testing ---
3641

3742
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
@@ -40,4 +45,5 @@ if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
4045
add_test(NAME bstr COMMAND bstr_test)
4146
add_test(NAME vec COMMAND vec_test)
4247
add_test(NAME flag COMMAND flag_test)
48+
add_test(NAME base64 COMMAND base64_test)
4349
endif()

base64/CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
cmake_minimum_required(VERSION 3.10)
2+
3+
project(base64 DESCRIPTION "base64 implemention")
4+
5+
add_library(base64 STATIC base64.c)
6+
target_link_libraries(base64 PRIVATE m)
7+
target_compile_features(base64 PUBLIC c_std_11)

base64/base64.c

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
#include "base64.h"
2+
#include <math.h>
3+
#include <stddef.h>
4+
#include <stdint.h>
5+
#include <stdio.h>
6+
#include <stdlib.h>
7+
#include <string.h>
8+
9+
static const uint8_t base64_encode_map[] = {
10+
[0] = 'A', [1] = 'B', [2] = 'C', [3] = 'D', [4] = 'E', [5] = 'F',
11+
[6] = 'G', [7] = 'H', [8] = 'I', [9] = 'J', [10] = 'K', [11] = 'L',
12+
[12] = 'M', [13] = 'N', [14] = 'O', [15] = 'P', [16] = 'Q', [17] = 'R',
13+
[18] = 'S', [19] = 'T', [20] = 'U', [21] = 'V', [22] = 'W', [23] = 'X',
14+
[24] = 'Y', [25] = 'Z', [26] = 'a', [27] = 'b', [28] = 'c', [29] = 'd',
15+
[30] = 'e', [31] = 'f', [32] = 'g', [33] = 'h', [34] = 'i', [35] = 'j',
16+
[36] = 'k', [37] = 'l', [38] = 'm', [39] = 'n', [40] = 'o', [41] = 'p',
17+
[42] = 'q', [43] = 'r', [44] = 's', [45] = 't', [46] = 'u', [47] = 'v',
18+
[48] = 'w', [49] = 'x', [50] = 'y', [51] = 'z', [52] = '0', [53] = '1',
19+
[54] = '2', [55] = '3', [56] = '4', [57] = '5', [58] = '6', [59] = '7',
20+
[60] = '8', [61] = '9', [62] = '+', [63] = '/',
21+
// `=` -> padding
22+
};
23+
24+
static const uint8_t base64_decode_map[] = {
25+
['A'] = 0, ['B'] = 1, ['C'] = 2, ['D'] = 3, ['E'] = 4, ['F'] = 5,
26+
['G'] = 6, ['H'] = 7, ['I'] = 8, ['J'] = 9, ['K'] = 10, ['L'] = 11,
27+
['M'] = 12, ['N'] = 13, ['O'] = 14, ['P'] = 15, ['Q'] = 16, ['R'] = 17,
28+
['S'] = 18, ['T'] = 19, ['U'] = 20, ['V'] = 21, ['W'] = 22, ['X'] = 23,
29+
['Y'] = 24, ['Z'] = 25, ['a'] = 26, ['b'] = 27, ['c'] = 28, ['d'] = 29,
30+
['e'] = 30, ['f'] = 31, ['g'] = 32, ['h'] = 33, ['i'] = 34, ['j'] = 35,
31+
['k'] = 36, ['l'] = 37, ['m'] = 38, ['n'] = 39, ['o'] = 40, ['p'] = 41,
32+
['q'] = 42, ['r'] = 43, ['s'] = 44, ['t'] = 45, ['u'] = 46, ['v'] = 47,
33+
['w'] = 48, ['x'] = 49, ['y'] = 50, ['z'] = 51, ['0'] = 52, ['1'] = 53,
34+
['2'] = 54, ['3'] = 55, ['4'] = 56, ['5'] = 57, ['6'] = 58, ['7'] = 59,
35+
['8'] = 60, ['9'] = 61, ['+'] = 62, ['/'] = 63,
36+
// `=` -> padding
37+
};
38+
39+
// todo: fix encoding for strings with length < 3
40+
const char *const base64_encode(const uint8_t *const data, size_t data_size) {
41+
const size_t remaining = data_size % 3;
42+
const size_t base64_len = ceil(((double)data_size * 8) / 6) + 1;
43+
char *result = malloc(base64_len);
44+
if (!result) {
45+
return NULL;
46+
}
47+
memset(result, 0, base64_len);
48+
49+
size_t base64_idx = 0;
50+
for (size_t i = 0; i < data_size - remaining; i += 3) {
51+
uint8_t byte1 = data[i], byte2 = data[i + 1], byte3 = data[i + 2];
52+
53+
uint8_t octet1 = (byte1 & 0b11111100) >> 2,
54+
octet2 = ((byte1 & 0b11) << 4) | ((byte2 & 0b11110000) >> 4),
55+
octet3 = ((byte2 & 0b1111) << 2) | ((byte3 & 0b11000000) >> 6),
56+
octet4 = (byte3 & 0b111111);
57+
58+
result[base64_idx++] = base64_encode_map[octet1];
59+
result[base64_idx++] = base64_encode_map[octet2];
60+
result[base64_idx++] = base64_encode_map[octet3];
61+
result[base64_idx++] = base64_encode_map[octet4];
62+
}
63+
64+
if (remaining == 1) {
65+
uint8_t byte1 = data[data_size - 1];
66+
uint8_t octet1 = (byte1 & 0b11111100) >> 2;
67+
uint8_t octet2 = (byte1 & 0b11) << 4;
68+
69+
result[base64_idx++] = base64_encode_map[octet1];
70+
result[base64_idx++] = base64_encode_map[octet2];
71+
result[base64_idx++] = '=';
72+
result[base64_idx++] = '=';
73+
} else if (remaining == 2) {
74+
uint8_t byte1 = data[data_size - 2];
75+
uint8_t byte2 = data[data_size - 1];
76+
77+
uint8_t octet1 = (byte1 & 0b11111100) >> 2;
78+
uint8_t octet2 = ((byte1 & 0b11) << 4) | ((byte2 & 0b11110000) >> 4);
79+
uint8_t octet3 = ((byte2 & 0b1111) << 2);
80+
81+
result[base64_idx++] = base64_encode_map[octet1];
82+
result[base64_idx++] = base64_encode_map[octet2];
83+
result[base64_idx++] = base64_encode_map[octet3];
84+
result[base64_idx++] = '=';
85+
}
86+
87+
return result;
88+
}
89+
90+
const char *base64_decode(const char *base64_str, size_t base64_len) {
91+
size_t padding_len = 0;
92+
for (size_t i = base64_len - 1; base64_str[i] == '=' && i >= 0; i--) {
93+
++padding_len;
94+
}
95+
const size_t result_len = ((base64_len - padding_len) * 6) / 8 + 1;
96+
char *result = malloc(result_len);
97+
if (!result) {
98+
return NULL;
99+
}
100+
memset(result, 0, result_len);
101+
102+
size_t result_idx = 0;
103+
for (size_t i = 0; i < base64_len; i += 4) {
104+
uint8_t octet1 = base64_decode_map[base64_str[i]];
105+
uint8_t octet2 = base64_decode_map[base64_str[i + 1]];
106+
uint8_t octet3 = base64_decode_map[base64_str[i + 2]];
107+
uint8_t octet4 = base64_decode_map[base64_str[i + 3]];
108+
109+
uint8_t byte1 = (octet1 << 2) | ((octet2 & 0b110000) >> 4),
110+
byte2 = (octet2 << 4) | ((octet3 & 0b111100) >> 2),
111+
byte3 = (octet3 << 6) | octet4;
112+
113+
result[result_idx++] = byte1;
114+
result[result_idx++] = byte2;
115+
result[result_idx++] = byte3;
116+
}
117+
118+
return result;
119+
}

base64/base64.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#ifndef BASE64_H
2+
#define BASE64_H
3+
4+
#include <stddef.h>
5+
#include <stdint.h>
6+
7+
const char *const base64_encode(const uint8_t *const data, size_t data_size);
8+
const char *base64_decode(const char *base64_str, size_t base64_len);
9+
10+
#endif

base64/maintest.c

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#include "base64.h"
2+
#include <assert.h>
3+
#include <stdio.h>
4+
#include <stdlib.h>
5+
#include <string.h>
6+
7+
int main(void) {
8+
const char *decodee = "light w";
9+
char *encoded = (char *)base64_encode((uint8_t *)decodee, strlen(decodee));
10+
char *decoded = (char *)base64_decode(encoded, strlen(encoded));
11+
assert(strcmp(decodee, decoded) == 0);
12+
13+
free((void *)encoded);
14+
free((void *)decoded);
15+
16+
// -------
17+
18+
encoded = (char *)base64_encode((uint8_t *)"Man", 3);
19+
assert(strcmp(encoded, "TWFu") == 0);
20+
decoded = (char *)base64_decode(encoded, strlen(encoded));
21+
assert(strcmp(decoded, "Man") == 0);
22+
free((void *)encoded);
23+
free((void *)decoded);
24+
25+
// ------
26+
27+
encoded = (char *)base64_encode((uint8_t *)"light work.", 11);
28+
assert(strcmp(encoded, "bGlnaHQgd29yay4=") == 0);
29+
decoded = (char *)base64_decode(encoded, strlen(encoded));
30+
assert(strcmp(decoded, "light work.") == 0);
31+
free((void *)encoded);
32+
free((void *)decoded);
33+
34+
// ------
35+
36+
// todo: still crashing atm as function fails for args with len < 3
37+
// encoded = (char *)base64_encode((uint8_t *)"ma", 2);
38+
// assert(strcmp(encoded, "TWE=") == 0);
39+
// decoded = (char *)base64_decode(encoded, strlen(encoded));
40+
// assert(strcmp(decoded, "TWE=") == 0);
41+
// free((void *)encoded);
42+
// free((void *)decoded);
43+
44+
puts("tests passed.");
45+
return 0;
46+
}

0 commit comments

Comments
 (0)