Skip to content

Commit

Permalink
added base64 library
Browse files Browse the repository at this point in the history
  • Loading branch information
RaphGL committed Oct 27, 2024
1 parent b5e7af7 commit a4b75f5
Show file tree
Hide file tree
Showing 5 changed files with 189 additions and 1 deletion.
8 changes: 7 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ endif()

add_subdirectory(bstr)
add_subdirectory(vec)
add_subdirectory(flag)

add_subdirectory(flag)
target_link_libraries(flag PRIVATE bstr vec)

add_subdirectory(base64)

# --- Executables ---

add_executable(bstr_test bstr/maintest.c)
Expand All @@ -32,6 +34,9 @@ target_link_libraries(vec_test vec)
add_executable(flag_test flag/maintest.c)
target_link_libraries(flag_test flag)

add_executable(base64_test base64/maintest.c)
target_link_libraries(base64_test base64)

# --- Testing ---

if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
Expand All @@ -40,4 +45,5 @@ if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
add_test(NAME bstr COMMAND bstr_test)
add_test(NAME vec COMMAND vec_test)
add_test(NAME flag COMMAND flag_test)
add_test(NAME base64 COMMAND base64_test)
endif()
7 changes: 7 additions & 0 deletions base64/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
cmake_minimum_required(VERSION 3.10)

project(base64 DESCRIPTION "base64 implemention")

add_library(base64 STATIC base64.c)
target_link_libraries(base64 PRIVATE m)
target_compile_features(base64 PUBLIC c_std_11)
119 changes: 119 additions & 0 deletions base64/base64.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
#include "base64.h"
#include <math.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static const uint8_t base64_encode_map[] = {
[0] = 'A', [1] = 'B', [2] = 'C', [3] = 'D', [4] = 'E', [5] = 'F',
[6] = 'G', [7] = 'H', [8] = 'I', [9] = 'J', [10] = 'K', [11] = 'L',
[12] = 'M', [13] = 'N', [14] = 'O', [15] = 'P', [16] = 'Q', [17] = 'R',
[18] = 'S', [19] = 'T', [20] = 'U', [21] = 'V', [22] = 'W', [23] = 'X',
[24] = 'Y', [25] = 'Z', [26] = 'a', [27] = 'b', [28] = 'c', [29] = 'd',
[30] = 'e', [31] = 'f', [32] = 'g', [33] = 'h', [34] = 'i', [35] = 'j',
[36] = 'k', [37] = 'l', [38] = 'm', [39] = 'n', [40] = 'o', [41] = 'p',
[42] = 'q', [43] = 'r', [44] = 's', [45] = 't', [46] = 'u', [47] = 'v',
[48] = 'w', [49] = 'x', [50] = 'y', [51] = 'z', [52] = '0', [53] = '1',
[54] = '2', [55] = '3', [56] = '4', [57] = '5', [58] = '6', [59] = '7',
[60] = '8', [61] = '9', [62] = '+', [63] = '/',
// `=` -> padding
};

static const uint8_t base64_decode_map[] = {
['A'] = 0, ['B'] = 1, ['C'] = 2, ['D'] = 3, ['E'] = 4, ['F'] = 5,
['G'] = 6, ['H'] = 7, ['I'] = 8, ['J'] = 9, ['K'] = 10, ['L'] = 11,
['M'] = 12, ['N'] = 13, ['O'] = 14, ['P'] = 15, ['Q'] = 16, ['R'] = 17,
['S'] = 18, ['T'] = 19, ['U'] = 20, ['V'] = 21, ['W'] = 22, ['X'] = 23,
['Y'] = 24, ['Z'] = 25, ['a'] = 26, ['b'] = 27, ['c'] = 28, ['d'] = 29,
['e'] = 30, ['f'] = 31, ['g'] = 32, ['h'] = 33, ['i'] = 34, ['j'] = 35,
['k'] = 36, ['l'] = 37, ['m'] = 38, ['n'] = 39, ['o'] = 40, ['p'] = 41,
['q'] = 42, ['r'] = 43, ['s'] = 44, ['t'] = 45, ['u'] = 46, ['v'] = 47,
['w'] = 48, ['x'] = 49, ['y'] = 50, ['z'] = 51, ['0'] = 52, ['1'] = 53,
['2'] = 54, ['3'] = 55, ['4'] = 56, ['5'] = 57, ['6'] = 58, ['7'] = 59,
['8'] = 60, ['9'] = 61, ['+'] = 62, ['/'] = 63,
// `=` -> padding
};

// todo: fix encoding for strings with length < 3
const char *const base64_encode(const uint8_t *const data, size_t data_size) {
const size_t remaining = data_size % 3;
const size_t base64_len = ceil(((double)data_size * 8) / 6) + 1;
char *result = malloc(base64_len);
if (!result) {
return NULL;
}
memset(result, 0, base64_len);

size_t base64_idx = 0;
for (size_t i = 0; i < data_size - remaining; i += 3) {
uint8_t byte1 = data[i], byte2 = data[i + 1], byte3 = data[i + 2];

uint8_t octet1 = (byte1 & 0b11111100) >> 2,
octet2 = ((byte1 & 0b11) << 4) | ((byte2 & 0b11110000) >> 4),
octet3 = ((byte2 & 0b1111) << 2) | ((byte3 & 0b11000000) >> 6),
octet4 = (byte3 & 0b111111);

result[base64_idx++] = base64_encode_map[octet1];
result[base64_idx++] = base64_encode_map[octet2];
result[base64_idx++] = base64_encode_map[octet3];
result[base64_idx++] = base64_encode_map[octet4];
}

if (remaining == 1) {
uint8_t byte1 = data[data_size - 1];
uint8_t octet1 = (byte1 & 0b11111100) >> 2;
uint8_t octet2 = (byte1 & 0b11) << 4;

result[base64_idx++] = base64_encode_map[octet1];
result[base64_idx++] = base64_encode_map[octet2];
result[base64_idx++] = '=';
result[base64_idx++] = '=';
} else if (remaining == 2) {
uint8_t byte1 = data[data_size - 2];
uint8_t byte2 = data[data_size - 1];

uint8_t octet1 = (byte1 & 0b11111100) >> 2;
uint8_t octet2 = ((byte1 & 0b11) << 4) | ((byte2 & 0b11110000) >> 4);
uint8_t octet3 = ((byte2 & 0b1111) << 2);

result[base64_idx++] = base64_encode_map[octet1];
result[base64_idx++] = base64_encode_map[octet2];
result[base64_idx++] = base64_encode_map[octet3];
result[base64_idx++] = '=';
}

return result;
}

const char *base64_decode(const char *base64_str, size_t base64_len) {
size_t padding_len = 0;
for (size_t i = base64_len - 1; base64_str[i] == '=' && i >= 0; i--) {
++padding_len;
}
const size_t result_len = ((base64_len - padding_len) * 6) / 8 + 1;
char *result = malloc(result_len);
if (!result) {
return NULL;
}
memset(result, 0, result_len);

size_t result_idx = 0;
for (size_t i = 0; i < base64_len; i += 4) {
uint8_t octet1 = base64_decode_map[base64_str[i]];
uint8_t octet2 = base64_decode_map[base64_str[i + 1]];
uint8_t octet3 = base64_decode_map[base64_str[i + 2]];
uint8_t octet4 = base64_decode_map[base64_str[i + 3]];

uint8_t byte1 = (octet1 << 2) | ((octet2 & 0b110000) >> 4),
byte2 = (octet2 << 4) | ((octet3 & 0b111100) >> 2),
byte3 = (octet3 << 6) | octet4;

result[result_idx++] = byte1;
result[result_idx++] = byte2;
result[result_idx++] = byte3;
}

return result;
}
10 changes: 10 additions & 0 deletions base64/base64.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#ifndef BASE64_H
#define BASE64_H

#include <stddef.h>
#include <stdint.h>

const char *const base64_encode(const uint8_t *const data, size_t data_size);
const char *base64_decode(const char *base64_str, size_t base64_len);

#endif
46 changes: 46 additions & 0 deletions base64/maintest.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#include "base64.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void) {
const char *decodee = "light w";
char *encoded = (char *)base64_encode((uint8_t *)decodee, strlen(decodee));
char *decoded = (char *)base64_decode(encoded, strlen(encoded));
assert(strcmp(decodee, decoded) == 0);

free((void *)encoded);
free((void *)decoded);

// -------

encoded = (char *)base64_encode((uint8_t *)"Man", 3);
assert(strcmp(encoded, "TWFu") == 0);
decoded = (char *)base64_decode(encoded, strlen(encoded));
assert(strcmp(decoded, "Man") == 0);
free((void *)encoded);
free((void *)decoded);

// ------

encoded = (char *)base64_encode((uint8_t *)"light work.", 11);
assert(strcmp(encoded, "bGlnaHQgd29yay4=") == 0);
decoded = (char *)base64_decode(encoded, strlen(encoded));
assert(strcmp(decoded, "light work.") == 0);
free((void *)encoded);
free((void *)decoded);

// ------

// todo: still crashing atm as function fails for args with len < 3
// encoded = (char *)base64_encode((uint8_t *)"ma", 2);
// assert(strcmp(encoded, "TWE=") == 0);
// decoded = (char *)base64_decode(encoded, strlen(encoded));
// assert(strcmp(decoded, "TWE=") == 0);
// free((void *)encoded);
// free((void *)decoded);

puts("tests passed.");
return 0;
}

0 comments on commit a4b75f5

Please sign in to comment.