Branch data Line data Source code
1 : : // Copyright 2011-2025 David Robillard <d@drobilla.net>
2 : : // SPDX-License-Identifier: ISC
3 : :
4 : : #undef NDEBUG
5 : :
6 : : #include <exess/exess.h>
7 : :
8 : : #include <assert.h>
9 : : #include <stdint.h>
10 : : #include <stdlib.h>
11 : : #include <string.h>
12 : :
13 : : static void
14 : 23 : check(const size_t expected_read_count,
15 : : const char* const string,
16 : : const ExessStatus expected_status,
17 : : const size_t expected_write_count,
18 : : const char* const expected_value)
19 : : {
20 : 23 : char buf[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
21 : :
22 : 23 : ExessVariableResult r = exess_read_base64(string, sizeof(buf), buf);
23 [ - + ]: 23 : assert(r.status == expected_status);
24 [ - + ]: 23 : assert(r.read_count == expected_read_count);
25 [ - + ]: 23 : assert(r.write_count == expected_write_count);
26 [ + + - + ]: 23 : assert(r.write_count > 0 || buf[0] == 1);
27 [ + + ]: 23 : if (expected_write_count > 0) {
28 [ - + ]: 13 : assert(!strncmp(buf, expected_value, expected_write_count));
29 [ - + ]: 13 : assert(r.write_count <= exess_decoded_base64_size(strlen(string)));
30 : : }
31 : 23 : }
32 : :
33 : : static void
34 : 1 : test_rfc4648_cases(void)
35 : : {
36 : 1 : char buf[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
37 : :
38 [ - + ]: 1 : assert(!exess_write_base64(6, "foobar", sizeof(buf), buf).status);
39 [ - + ]: 1 : assert(!strcmp(buf, "Zm9vYmFy"));
40 : :
41 [ - + ]: 1 : assert(!exess_write_base64(5, "fooba", sizeof(buf), buf).status);
42 [ - + ]: 1 : assert(!strcmp(buf, "Zm9vYmE="));
43 : :
44 [ - + ]: 1 : assert(!exess_write_base64(4, "foob", sizeof(buf), buf).status);
45 [ - + ]: 1 : assert(!strcmp(buf, "Zm9vYg=="));
46 : :
47 [ - + ]: 1 : assert(!exess_write_base64(3, "foo", sizeof(buf), buf).status);
48 [ - + ]: 1 : assert(!strcmp(buf, "Zm9v"));
49 : :
50 [ - + ]: 1 : assert(!exess_write_base64(2, "fo", sizeof(buf), buf).status);
51 [ - + ]: 1 : assert(!strcmp(buf, "Zm8="));
52 : :
53 [ - + ]: 1 : assert(!exess_write_base64(1, "f", sizeof(buf), buf).status);
54 [ - + ]: 1 : assert(!strcmp(buf, "Zg=="));
55 : 1 : }
56 : :
57 : : static void
58 : 1 : test_whitespace(void)
59 : : {
60 : 1 : check(8, "Zm9vYmFy", EXESS_SUCCESS, 6, "foobar");
61 : 1 : check(9, "\tZm9vYmFy", EXESS_SUCCESS, 6, "foobar");
62 : 1 : check(9, "Z\nm9vYmFy", EXESS_SUCCESS, 6, "foobar");
63 : 1 : check(9, "Zm\r9vYmFy", EXESS_SUCCESS, 6, "foobar");
64 : 1 : check(9, "Zm9 vYmFy", EXESS_SUCCESS, 6, "foobar");
65 : 1 : check(9, "Zm9v\tYmFy", EXESS_SUCCESS, 6, "foobar");
66 : 1 : check(9, "Zm9vY\nmFy", EXESS_SUCCESS, 6, "foobar");
67 : 1 : check(12, "\t\n\r Zm9vYmFy", EXESS_SUCCESS, 6, "foobar");
68 : 1 : check(12, "Zm9vYmFy\t\n\r ", EXESS_SUCCESS, 6, "foobar");
69 : 1 : }
70 : :
71 : : static void
72 : 1 : test_syntax_errors(void)
73 : : {
74 : 1 : check(1, "Z", EXESS_EXPECTED_BASE64, 0, NULL);
75 : 1 : check(2, "ZZ", EXESS_EXPECTED_BASE64, 0, NULL);
76 : 1 : check(3, "ZZZ", EXESS_EXPECTED_BASE64, 0, NULL);
77 : :
78 : 1 : check(4, "=ZZZ", EXESS_BAD_VALUE, 0, NULL);
79 : 1 : check(4, "Z=ZZ", EXESS_BAD_VALUE, 0, NULL);
80 : 1 : check(4, "ZZ=Z", EXESS_BAD_VALUE, 0, NULL);
81 : :
82 : 1 : check(0, "!m9vYmFy", EXESS_EXPECTED_BASE64, 0, NULL);
83 : 1 : check(1, "Z!9vYmFy", EXESS_EXPECTED_BASE64, 0, NULL);
84 : 1 : check(2, "Zm!vYmFy", EXESS_EXPECTED_BASE64, 0, NULL);
85 : 1 : check(3, "Zm9!YmFy", EXESS_EXPECTED_BASE64, 0, NULL);
86 : 1 : check(4, "Zm9v!mFy", EXESS_EXPECTED_BASE64, 3, "foo");
87 : 1 : check(5, "Zm9vY!Fy", EXESS_EXPECTED_BASE64, 3, "foo");
88 : 1 : check(6, "Zm9vYm!y", EXESS_EXPECTED_BASE64, 3, "foo");
89 : 1 : check(7, "Zm9vYmF!", EXESS_EXPECTED_BASE64, 3, "foo");
90 : 1 : }
91 : :
92 : : static void
93 : 1 : test_read_overflow(void)
94 : : {
95 : 1 : char buf[6] = {1, 2, 3, 4, 5, 6};
96 : :
97 [ + + ]: 4 : for (unsigned space = 0; space < 3; ++space) {
98 : 3 : const ExessVariableResult r = exess_read_base64("Zm9vYmFy", space, buf);
99 [ - + ]: 3 : assert(r.status == EXESS_NO_SPACE);
100 [ - + ]: 3 : assert(r.read_count == 8U);
101 [ - + ]: 3 : assert(r.write_count == 6U);
102 [ - + ]: 3 : assert(buf[0] == 1);
103 : : }
104 : :
105 [ + + ]: 4 : for (unsigned space = 3; space < 6; ++space) {
106 : 3 : const ExessVariableResult r = exess_read_base64("Zm9vYmFy", space, buf);
107 [ - + ]: 3 : assert(r.status == EXESS_NO_SPACE);
108 [ - + ]: 3 : assert(r.read_count == 8U);
109 [ - + ]: 3 : assert(r.write_count == 6U);
110 [ - + ]: 3 : assert(!strncmp(buf, "foo", 3));
111 : : }
112 : :
113 : 1 : const ExessVariableResult r = exess_read_base64("Zm9vYmFy", 6U, buf);
114 [ - + ]: 1 : assert(r.status == EXESS_SUCCESS);
115 [ - + ]: 1 : assert(r.read_count == 8U);
116 [ - + ]: 1 : assert(r.write_count == 6U);
117 [ - + ]: 1 : assert(!strncmp(buf, "foobar", 6));
118 : 1 : }
119 : :
120 : : static void
121 : 1 : test_round_trip(void)
122 : : {
123 [ + + ]: 256 : for (size_t size = 1; size < 256; ++size) {
124 : : // Allocate and generate data
125 : 255 : uint8_t* const data = (uint8_t*)malloc(size);
126 [ - + ]: 255 : assert(data);
127 [ + + ]: 32895 : for (size_t i = 0; i < size; ++i) {
128 : 32640 : data[i] = (uint8_t)((size + i) % 256);
129 : : }
130 : :
131 : : // Allocate buffer for encoding with minimum required size
132 : 255 : const size_t str_len = exess_write_base64(size, data, 0, NULL).count;
133 : 255 : char* const str = (char*)malloc(str_len + 1);
134 [ - + ]: 255 : assert(str);
135 : :
136 : : // Encode data to string buffer
137 [ - + ]: 255 : assert(!exess_write_base64(size, data, str_len + 1, str).status);
138 [ - + ]: 255 : assert(strlen(str) == str_len);
139 [ - + ]: 255 : assert(str_len % 4 == 0);
140 : :
141 : : // Allocate buffer for decoded data with the same size as the input
142 : 255 : uint8_t* const decoded = (uint8_t*)malloc(size);
143 [ - + ]: 255 : assert(decoded);
144 : :
145 : : // Decode and check that data matches the original input
146 : 255 : const ExessVariableResult r = exess_read_base64(str, size, decoded);
147 [ - + ]: 255 : assert(!r.status);
148 [ - + ]: 255 : assert(r.read_count == str_len);
149 [ - + ]: 255 : assert(r.write_count == size);
150 [ - + ]: 255 : assert(!memcmp(decoded, data, size));
151 : :
152 : 255 : free(decoded);
153 : 255 : free(str);
154 : 255 : free(data);
155 : : }
156 : 1 : }
157 : :
158 : : int
159 : 1 : main(void)
160 : : {
161 : 1 : test_rfc4648_cases();
162 : 1 : test_whitespace();
163 : 1 : test_syntax_errors();
164 : 1 : test_read_overflow();
165 : 1 : test_round_trip();
166 : :
167 : 1 : return 0;
168 : : }
|