1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
// Originally by Fränz Friederes, MIT License
// https://github.com/cryptii/cryptii/blob/main/src/Encoder/Ascii85.js
// Modified by Anton Zhiyanov, MIT License
// https://github.com/nalgeon/sqlean/
// Base85 (Ascii85) encoding/decoding
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
uint8_t* base85_encode(const uint8_t* src, size_t len, size_t* out_len) {
uint8_t* encoded = malloc(len * 5 / 4 + 5);
if (encoded == NULL) {
*out_len = 0;
return NULL;
}
// Encode each tuple of 4 bytes
uint32_t digits[5], tuple;
size_t pos = 0;
for (size_t i = 0; i < len; i += 4) {
// Read 32-bit unsigned integer from bytes following the
// big-endian convention (most significant byte first)
tuple = (((src[i]) << 24) + ((src[i + 1] << 16) & 0xFF0000) + ((src[i + 2] << 8) & 0xFF00) +
((src[i + 3]) & 0xFF));
if (tuple > 0) {
// Calculate 5 digits by repeatedly dividing
// by 85 and taking the remainder
for (size_t j = 0; j < 5; j++) {
digits[4 - j] = tuple % 85;
tuple = tuple / 85;
}
// Omit final characters added due to bytes of padding
size_t num_padding = 0;
if (len < i + 4) {
num_padding = (i + 4) - len;
}
for (size_t j = 0; j < 5 - num_padding; j++) {
encoded[pos++] = digits[j] + 33;
}
} else {
// An all-zero tuple is encoded as a single character
encoded[pos++] = 'z';
}
}
*out_len = len * 5 / 4 + (len % 4 ? 1 : 0);
encoded[*out_len] = '\0';
return encoded;
}
uint8_t* base85_decode(const uint8_t* src, size_t len, size_t* out_len) {
uint8_t* decoded = malloc(len * 4 / 5);
if (decoded == NULL) {
*out_len = 0;
return NULL;
}
uint8_t digits[5], tupleBytes[4];
uint32_t tuple;
size_t pos = 0;
for (size_t i = 0; i < len;) {
if (src[i] == 'z') {
// A single character encodes an all-zero tuple
decoded[pos++] = 0;
decoded[pos++] = 0;
decoded[pos++] = 0;
decoded[pos++] = 0;
i++;
} else {
// Retrieve radix-85 digits of tuple
for (int k = 0; k < 5; k++) {
if (i + k < len) {
uint8_t digit = src[i + k] - 33;
if (digit < 0 || digit > 84) {
*out_len = 0;
free(decoded);
return NULL;
}
digits[k] = digit;
} else {
digits[k] = 84; // Pad with 'u'
}
}
// Create 32-bit binary number from digits and handle padding
// tuple = a * 85^4 + b * 85^3 + c * 85^2 + d * 85 + e
tuple = digits[0] * 52200625 + digits[1] * 614125 + digits[2] * 7225 + digits[3] * 85 +
digits[4];
// Get bytes from tuple
tupleBytes[0] = (tuple >> 24) & 0xff;
tupleBytes[1] = (tuple >> 16) & 0xff;
tupleBytes[2] = (tuple >> 8) & 0xff;
tupleBytes[3] = tuple & 0xff;
// Remove bytes of padding
int padding = 0;
if (i + 4 >= len) {
padding = i + 4 - len;
}
// Append bytes to result
for (int k = 0; k < 4 - padding; k++) {
decoded[pos++] = tupleBytes[k];
}
i += 5;
}
}
*out_len = len * 4 / 5;
return decoded;
}