Coverage Report

Created: 2025-09-04 07:09

/src/tarantool/third_party/base64.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Redistribution and use in source and binary forms, with or
3
 * without modification, are permitted provided that the following
4
 * conditions are met:
5
 *
6
 * 1. Redistributions of source code must retain the above
7
 *    copyright notice, this list of conditions and the
8
 *    following disclaimer.
9
 *
10
 * 2. Redistributions in binary form must reproduce the above
11
 *    copyright notice, this list of conditions and the following
12
 *    disclaimer in the documentation and/or other materials
13
 *    provided with the distribution.
14
 *
15
 * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
16
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
19
 * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
20
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
23
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
26
 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
 * SUCH DAMAGE.
28
 */
29
#include "base64.h"
30
#include <trivia/util.h>
31
32
/*
33
 * This is part of the libb64 project, and has been placed in the
34
 * public domain. For details, see
35
 * http://sourceforge.net/projects/libb64
36
 */
37
38
/* {{{ encode */
39
40
int
41
base64_encode_bufsize(int bin_len, int options)
42
0
{
43
0
  int base64_len = bin_len * 4 / 3;
44
0
  if ((options & BASE64_NOWRAP) == 0) {
45
    /* Account '\n' symbols. */
46
0
    base64_len += ((base64_len + BASE64_CHARS_PER_LINE - 1)/
47
0
             BASE64_CHARS_PER_LINE);
48
0
  } else if (bin_len % 3 != 0) {
49
0
    base64_len++;
50
0
  }
51
0
  if ((options & BASE64_NOPAD) == 0)
52
0
    base64_len += 4;
53
0
  return base64_len;
54
0
}
55
56
enum base64_encodestep { step_A, step_B, step_C };
57
58
static const char default_encoding[] =
59
  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
60
static const char urlsafe_encoding[] =
61
  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
62
63
struct base64_encodestate {
64
  enum base64_encodestep step;
65
  char result;
66
  int stepcount;
67
};
68
69
static inline void
70
base64_encodestate_init(struct base64_encodestate *state)
71
0
{
72
0
  state->step = step_A;
73
0
  state->result = 0;
74
0
  state->stepcount = 0;
75
0
}
76
77
static inline char
78
base64_encode_value(char value, const char *encoding)
79
0
{
80
0
  unsigned codepos = (unsigned) value;
81
0
  if (codepos > 64)
82
0
    return '=';
83
0
  return encoding[codepos];
84
0
}
85
86
static int
87
base64_encode_block(const char *in_bin, int in_len,
88
        char *out_base64, int out_len,
89
        struct base64_encodestate *state, const char *encoding,
90
        int options)
91
0
{
92
0
  const char *const in_end = in_bin + in_len;
93
0
  const char *in_pos = in_bin;
94
0
  char *out_pos = out_base64;
95
0
  char *out_end = out_base64  + out_len;
96
0
  char result;
97
0
  char fragment;
98
99
0
  result = state->result;
100
101
0
  switch (state->step)
102
0
  {
103
0
    while (1)
104
0
    {
105
0
  case step_A:
106
0
      if (in_pos == in_end || out_pos >= out_end) {
107
0
        state->step = step_A;
108
0
        goto out;
109
0
      }
110
0
      fragment = *in_pos++;
111
0
      result = (fragment & 0x0fc) >> 2;
112
0
      *out_pos++ = base64_encode_value(result, encoding);
113
0
      result = (fragment & 0x003) << 4;
114
0
      FALLTHROUGH;
115
0
  case step_B:
116
0
      if (in_pos == in_end || out_pos >= out_end) {
117
0
        state->step = step_B;
118
0
        goto out;
119
0
      }
120
0
      fragment = *in_pos++;
121
0
      result |= (fragment & 0x0f0) >> 4;
122
0
      *out_pos++ = base64_encode_value(result, encoding);
123
0
      result = (fragment & 0x00f) << 2;
124
0
      FALLTHROUGH;
125
0
  case step_C:
126
0
      if (in_pos == in_end || out_pos >= out_end) {
127
0
        state->step = step_C;
128
0
        goto out;
129
0
      }
130
0
      fragment = *in_pos++;
131
0
      result |= (fragment & 0x0c0) >> 6;
132
0
      *out_pos++ = base64_encode_value(result, encoding);
133
0
      if (out_pos >= out_end)
134
0
        goto out;
135
0
      result  = (fragment & 0x03f) >> 0;
136
0
      *out_pos++ = base64_encode_value(result, encoding);
137
138
      /*
139
       * Each full step (A->B->C) yields
140
       * 4 characters.
141
       */
142
0
      if ((options & BASE64_NOWRAP) == 0 &&
143
0
          ++state->stepcount * 4 == BASE64_CHARS_PER_LINE) {
144
0
        if (out_pos >= out_end)
145
0
          return out_pos - out_base64;
146
0
        *out_pos++ = '\n';
147
0
        state->stepcount = 0;
148
0
      }
149
0
    }
150
0
  }
151
0
out:
152
0
  state->result = result;
153
0
  return out_pos - out_base64;
154
0
}
155
156
static int
157
base64_encode_blockend(char *out_base64, int out_len,
158
           struct base64_encodestate *state, const char *encoding,
159
           int options)
160
0
{
161
0
  char *out_pos = out_base64;
162
0
  char *out_end = out_base64 + out_len;
163
164
0
  switch (state->step) {
165
0
  case step_B:
166
0
    if (out_pos >= out_end)
167
0
      return out_pos - out_base64;
168
0
    *out_pos++ = base64_encode_value(state->result, encoding);
169
0
    if (out_pos + 1 >= out_end || (options & BASE64_NOPAD) != 0)
170
0
      return out_pos - out_base64;
171
0
    *out_pos++ = '=';
172
0
    *out_pos++ = '=';
173
0
    break;
174
0
  case step_C:
175
0
    if (out_pos >= out_end)
176
0
      return out_pos - out_base64;
177
0
    *out_pos++ = base64_encode_value(state->result, encoding);
178
0
    if (out_pos >= out_end || (options & BASE64_NOPAD) != 0)
179
0
      return out_pos - out_base64;
180
0
    *out_pos++ = '=';
181
0
    break;
182
0
  case step_A:
183
0
    break;
184
0
  }
185
0
  if (out_pos >= out_end)
186
0
    return out_pos - out_base64;
187
#if 0
188
  /* Sometimes the output is useful without a newline. */
189
  *out_pos++ = '\n';
190
  if (out_pos >= out_end)
191
    return out_pos - out_base64;
192
#endif
193
0
  *out_pos = '\0';
194
0
  return out_pos - out_base64;
195
0
}
196
197
int
198
base64_encode(const char *in_bin, int in_len,
199
        char *out_base64, int out_len, int options)
200
0
{
201
0
  const char *encoding;
202
0
  if ((options & BASE64_URLSAFE) == BASE64_URLSAFE)
203
0
    encoding = urlsafe_encoding;
204
0
  else
205
0
    encoding = default_encoding;
206
0
  struct base64_encodestate state;
207
0
  base64_encodestate_init(&state);
208
0
  int res = base64_encode_block(in_bin, in_len, out_base64,
209
0
              out_len, &state, encoding, options);
210
0
  return res + base64_encode_blockend(out_base64 + res, out_len - res,
211
0
              &state, encoding, options);
212
0
}
213
214
/* }}} */
215
216
/* {{{ decode */
217
218
int
219
base64_decode_bufsize(int base64_len)
220
0
{
221
0
  return 3 * base64_len / 4 + 1;
222
0
}
223
224
static int
225
base64_decode_value(int value)
226
4.84k
{
227
4.84k
  static const int decoding[] = {
228
4.84k
    62, -1, 62, -1, 63, 52, 53, 54, 55, 56, 57, 58,
229
4.84k
    59, 60, 61, -1, -1, -1, -2, -1, -1, -1,  0,  1,
230
4.84k
    2,   3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13,
231
4.84k
    14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
232
4.84k
    -1, -1, -1, -1, 63, -1, 26, 27, 28, 29, 30, 31,
233
4.84k
    32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
234
4.84k
    44, 45, 46, 47, 48, 49, 50, 51
235
4.84k
  };
236
4.84k
  static const int decoding_size =
237
4.84k
    sizeof(decoding) / sizeof(decoding[0]);
238
4.84k
  int codepos = value;
239
4.84k
  codepos -= 43;
240
4.84k
  if (codepos < 0 || codepos >= decoding_size)
241
2.19k
    return -1;
242
2.65k
  return decoding[codepos];
243
4.84k
}
244
245
int
246
base64_decode(const char *in_base64, int in_len,
247
        char *out_bin, int out_len)
248
77
{
249
77
  const char *in_pos = in_base64;
250
77
  const char *in_end = in_base64 + in_len;
251
77
  char *out_pos = out_bin;
252
77
  char *out_end = out_bin + out_len;
253
77
  int fragment;
254
255
700
  while (1)
256
700
  {
257
1.01k
    do {
258
1.01k
      if (in_pos == in_end || out_pos >= out_end)
259
10
        return out_pos - out_bin;
260
1.00k
      fragment = base64_decode_value(*in_pos++);
261
1.00k
    } while (fragment < 0);
262
690
    *out_pos = (fragment & 0x03f) << 2;
263
1.47k
    do {
264
1.47k
      if (in_pos == in_end || out_pos >= out_end)
265
24
        return out_pos - out_bin;
266
1.44k
      fragment = base64_decode_value(*in_pos++);
267
1.44k
    } while (fragment < 0);
268
666
    *out_pos++ |= (fragment & 0x030) >> 4;
269
666
    if (out_pos < out_end)
270
666
      *out_pos = (fragment & 0x00f) << 4;
271
1.35k
    do {
272
1.35k
      if (in_pos == in_end || out_pos >= out_end)
273
26
        return out_pos - out_bin;
274
1.33k
      fragment = base64_decode_value(*in_pos++);
275
1.33k
    } while (fragment < 0);
276
640
    *out_pos++ |= (fragment & 0x03c) >> 2;
277
640
    if (out_pos < out_end)
278
638
      *out_pos = (fragment & 0x003) << 6;
279
1.07k
    do {
280
1.07k
      if (in_pos == in_end || out_pos >= out_end)
281
17
        return out_pos - out_bin;
282
1.06k
      fragment = base64_decode_value(*in_pos++);
283
1.06k
    } while (fragment < 0);
284
623
    *out_pos++ |= (fragment & 0x03f);
285
623
  }
286
  /* control should not reach here */
287
0
  return out_pos - out_bin;
288
77
}
289
290
/* }}} */