Coverage Report

Created: 2026-09-14 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/simdutf/src/generic/base64.h
Line
Count
Source
1
/**
2
 * References and further reading:
3
 *
4
 * Wojciech Muła, Daniel Lemire, Base64 encoding and decoding at almost the
5
 * speed of a memory copy, Software: Practice and Experience 50 (2), 2020.
6
 * https://arxiv.org/abs/1910.05109
7
 *
8
 * Wojciech Muła, Daniel Lemire, Faster Base64 Encoding and Decoding using AVX2
9
 * Instructions, ACM Transactions on the Web 12 (3), 2018.
10
 * https://arxiv.org/abs/1704.00605
11
 *
12
 * Simon Josefsson. 2006. The Base16, Base32, and Base64 Data Encodings.
13
 * https://tools.ietf.org/html/rfc4648. (2006). Internet Engineering Task Force,
14
 * Request for Comments: 4648.
15
 *
16
 * Alfred Klomp. 2014a. Fast Base64 encoding/decoding with SSE vectorization.
17
 * http://www.alfredklomp.com/programming/sse-base64/. (2014).
18
 *
19
 * Alfred Klomp. 2014b. Fast Base64 stream encoder/decoder in C99, with SIMD
20
 * acceleration. https://github.com/aklomp/base64. (2014).
21
 *
22
 * Hanson Char. 2014. A Fast and Correct Base 64 Codec. (2014).
23
 * https://aws.amazon.com/blogs/developer/a-fast-and-correct-base-64-codec/
24
 *
25
 * Nick Kopp. 2013. Base64 Encoding on a GPU.
26
 * https://www.codeproject.com/Articles/276993/Base-Encoding-on-a-GPU. (2013).
27
 */
28
namespace simdutf {
29
namespace SIMDUTF_IMPLEMENTATION {
30
namespace {
31
namespace base64 {
32
33
/*
34
    The following template function implements API for Base64 decoding.
35
36
    An implementation is responsible for providing the `block64` type and
37
    associated methods that perform actual conversion. Please refer
38
    to any vectorized implementation to learn the API of these procedures.
39
*/
40
template <bool base64_url, bool ignore_garbage, bool default_or_url,
41
          typename chartype>
42
full_result
43
compress_decode_base64(char *dst, const chartype *src, size_t srclen,
44
                       base64_options options,
45
95.0k
                       last_chunk_handling_options last_chunk_options) {
46
95.0k
  const uint8_t *to_base64 =
47
95.0k
      default_or_url ? tables::base64::to_base64_default_or_url_value
48
95.0k
                     : (base64_url ? tables::base64::to_base64_url_value
49
95.0k
                                   : tables::base64::to_base64_value);
50
95.0k
  auto ri = simdutf::scalar::base64::find_end(src, srclen, options);
51
95.0k
  size_t equallocation = ri.equallocation;
52
95.0k
  size_t equalsigns = ri.equalsigns;
53
95.0k
  srclen = ri.srclen;
54
95.0k
  size_t full_input_length = ri.full_input_length;
55
95.0k
  if (srclen == 0) {
56
70.2k
    if (!ignore_garbage && equalsigns > 0) {
57
147
      return {INVALID_BASE64_CHARACTER, equallocation, 0, true};
58
147
    }
59
70.0k
    return {SUCCESS, full_input_length, 0};
60
70.2k
  }
61
24.8k
  char *end_of_safe_64byte_zone =
62
24.8k
      dst == nullptr
63
24.8k
          ? nullptr
64
24.8k
          : ((srclen + 3) / 4 * 3 >= 63 ? dst + (srclen + 3) / 4 * 3 - 63
65
23.8k
                                        : dst);
66
67
24.8k
  const chartype *const srcinit = src;
68
24.8k
  const char *const dstinit = dst;
69
24.8k
  const chartype *const srcend = src + srclen;
70
71
24.8k
  constexpr size_t block_size = 6;
72
24.8k
  static_assert(block_size >= 2, "block_size must be at least two");
73
24.8k
  char buffer[block_size * 64];
74
24.8k
  char *bufferptr = buffer;
75
24.8k
  if (srclen >= 64) {
76
16.7k
    const chartype *const srcend64 = src + srclen - 64;
77
3.51M
    while (src <= srcend64) {
78
3.49M
      block64 b(src);
79
3.49M
      src += 64;
80
3.49M
      uint64_t error = 0;
81
3.49M
      const uint64_t badcharmask =
82
3.49M
          b.to_base64_mask<base64_url, ignore_garbage, default_or_url>(&error);
83
3.49M
      if (!ignore_garbage && error) {
84
4.81k
        src -= 64;
85
4.81k
        const size_t error_offset = trailing_zeroes(error);
86
4.81k
        return {error_code::INVALID_BASE64_CHARACTER,
87
4.81k
                size_t(src - srcinit + error_offset), size_t(dst - dstinit)};
88
4.81k
      }
89
3.49M
      if (badcharmask != 0) {
90
458k
        bufferptr += b.compress_block(badcharmask, bufferptr);
91
3.03M
      } else if (bufferptr != buffer) {
92
371k
        b.copy_block(bufferptr);
93
371k
        bufferptr += 64;
94
2.66M
      } else {
95
2.66M
        if (dst >= end_of_safe_64byte_zone) {
96
2.06k
          b.base64_decode_block_safe(dst);
97
2.66M
        } else {
98
2.66M
          b.base64_decode_block(dst);
99
2.66M
        }
100
2.66M
        dst += 48;
101
2.66M
      }
102
3.49M
      if (bufferptr >= (block_size - 1) * 64 + buffer) {
103
718k
        for (size_t i = 0; i < (block_size - 2); i++) {
104
574k
          base64_decode_block(dst, buffer + i * 64);
105
574k
          dst += 48;
106
574k
        }
107
143k
        if (dst >= end_of_safe_64byte_zone) {
108
0
          base64_decode_block_safe(dst, buffer + (block_size - 2) * 64);
109
143k
        } else {
110
143k
          base64_decode_block(dst, buffer + (block_size - 2) * 64);
111
143k
        }
112
143k
        dst += 48;
113
143k
        std::memcpy(buffer, buffer + (block_size - 1) * 64,
114
143k
                    64); // 64 might be too much
115
143k
        bufferptr -= (block_size - 1) * 64;
116
143k
      }
117
3.49M
    }
118
16.7k
  }
119
120
20.0k
  char *buffer_start = buffer;
121
  // Optimization note: if this is almost full, then it is worth our
122
  // time, otherwise, we should just decode directly.
123
20.0k
  int last_block = (int)((bufferptr - buffer_start) % 64);
124
20.0k
  if (last_block != 0 && srcend - src + last_block >= 64) {
125
126
13.7k
    while ((bufferptr - buffer_start) % 64 != 0 && src < srcend) {
127
12.7k
      uint8_t val = to_base64[uint8_t(*src)];
128
12.7k
      *bufferptr = char(val);
129
12.7k
      if (!ignore_garbage &&
130
12.7k
          (!scalar::base64::is_eight_byte(*src) || val > 64)) {
131
293
        return {error_code::INVALID_BASE64_CHARACTER, size_t(src - srcinit),
132
293
                size_t(dst - dstinit)};
133
293
      }
134
12.4k
      bufferptr += (val <= 63);
135
12.4k
      src++;
136
12.4k
    }
137
1.33k
  }
138
139
25.4k
  for (; buffer_start + 64 <= bufferptr; buffer_start += 64) {
140
5.73k
    if (dst >= end_of_safe_64byte_zone) {
141
465
      base64_decode_block_safe(dst, buffer_start);
142
5.26k
    } else {
143
5.26k
      base64_decode_block(dst, buffer_start);
144
5.26k
    }
145
5.73k
    dst += 48;
146
5.73k
  }
147
19.7k
  if ((bufferptr - buffer_start) % 64 != 0) {
148
58.9k
    while (buffer_start + 4 < bufferptr) {
149
51.9k
      uint32_t triple = ((uint32_t(uint8_t(buffer_start[0])) << 3 * 6) +
150
51.9k
                         (uint32_t(uint8_t(buffer_start[1])) << 2 * 6) +
151
51.9k
                         (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) +
152
51.9k
                         (uint32_t(uint8_t(buffer_start[3])) << 0 * 6))
153
51.9k
                        << 8;
154
51.9k
#if !SIMDUTF_IS_BIG_ENDIAN
155
51.9k
      triple = scalar::u32_swap_bytes(triple);
156
51.9k
#endif
157
51.9k
      std::memcpy(dst, &triple, 3);
158
159
51.9k
      dst += 3;
160
51.9k
      buffer_start += 4;
161
51.9k
    }
162
6.93k
    if (buffer_start + 4 <= bufferptr) {
163
1.76k
      uint32_t triple = ((uint32_t(uint8_t(buffer_start[0])) << 3 * 6) +
164
1.76k
                         (uint32_t(uint8_t(buffer_start[1])) << 2 * 6) +
165
1.76k
                         (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) +
166
1.76k
                         (uint32_t(uint8_t(buffer_start[3])) << 0 * 6))
167
1.76k
                        << 8;
168
1.76k
#if !SIMDUTF_IS_BIG_ENDIAN
169
1.76k
      triple = scalar::u32_swap_bytes(triple);
170
1.76k
#endif
171
1.76k
      std::memcpy(dst, &triple, 3);
172
173
1.76k
      dst += 3;
174
1.76k
      buffer_start += 4;
175
1.76k
    }
176
    // we may have 1, 2 or 3 bytes left and we need to decode them so let us
177
    // backtrack
178
6.93k
    int leftover = int(bufferptr - buffer_start);
179
17.3k
    while (leftover > 0) {
180
10.3k
      if (!ignore_garbage) {
181
394k
        while (to_base64[uint8_t(*(src - 1))] == 64) {
182
383k
          src--;
183
383k
        }
184
10.3k
      } else {
185
0
        while (to_base64[uint8_t(*(src - 1))] >= 64) {
186
0
          src--;
187
0
        }
188
0
      }
189
10.3k
      src--;
190
10.3k
      leftover--;
191
10.3k
    }
192
6.93k
  }
193
19.7k
  if (src < srcend + equalsigns) {
194
17.4k
    full_result r = scalar::base64::base64_tail_decode(
195
17.4k
        dst, src, srcend - src, equalsigns, options, last_chunk_options);
196
17.4k
    r = scalar::base64::patch_tail_result(
197
17.4k
        r, size_t(src - srcinit), size_t(dst - dstinit), equallocation,
198
17.4k
        full_input_length, last_chunk_options);
199
    // When is_partial(last_chunk_options) is true, we must either end with
200
    // the end of the stream (beyond whitespace) or right after a non-ignorable
201
    // character or at the very beginning of the stream.
202
    // See https://tc39.es/proposal-arraybuffer-base64/spec/#sec-frombase64
203
17.4k
    if (is_partial(last_chunk_options) && r.error == error_code::SUCCESS &&
204
5.69k
        r.input_count < full_input_length) {
205
      // First check if we can extend the input to the end of the stream
206
137k
      while (r.input_count < full_input_length &&
207
136k
             base64_ignorable(*(srcinit + r.input_count), options)) {
208
133k
        r.input_count++;
209
133k
      }
210
      // If we are still not at the end of the stream, then we must backtrack
211
      // to the last non-ignorable character.
212
3.99k
      if (r.input_count < full_input_length) {
213
268k
        while (r.input_count > 0 &&
214
267k
               base64_ignorable(*(srcinit + r.input_count - 1), options)) {
215
265k
          r.input_count--;
216
265k
        }
217
3.85k
      }
218
3.99k
    }
219
17.4k
    return r;
220
17.4k
  }
221
2.28k
  if (!ignore_garbage && equalsigns > 0) {
222
0
    if ((size_t(dst - dstinit) % 3 == 0) ||
223
0
        ((size_t(dst - dstinit) % 3) + 1 + equalsigns != 4)) {
224
0
      return {INVALID_BASE64_CHARACTER, equallocation, size_t(dst - dstinit),
225
0
              true};
226
0
    }
227
0
  }
228
2.28k
  return {SUCCESS, srclen, size_t(dst - dstinit)};
229
2.28k
}
Unexecuted instantiation: simdutf.cpp:simdutf::full_result simdutf::haswell::(anonymous namespace)::base64::compress_decode_base64<false, true, true, char>(char*, char const*, unsigned long, simdutf::base64_options, simdutf::last_chunk_handling_options)
Unexecuted instantiation: simdutf.cpp:simdutf::full_result simdutf::haswell::(anonymous namespace)::base64::compress_decode_base64<false, false, true, char>(char*, char const*, unsigned long, simdutf::base64_options, simdutf::last_chunk_handling_options)
Unexecuted instantiation: simdutf.cpp:simdutf::full_result simdutf::haswell::(anonymous namespace)::base64::compress_decode_base64<true, true, false, char>(char*, char const*, unsigned long, simdutf::base64_options, simdutf::last_chunk_handling_options)
simdutf.cpp:simdutf::full_result simdutf::haswell::(anonymous namespace)::base64::compress_decode_base64<true, false, false, char>(char*, char const*, unsigned long, simdutf::base64_options, simdutf::last_chunk_handling_options)
Line
Count
Source
45
2.43k
                       last_chunk_handling_options last_chunk_options) {
46
2.43k
  const uint8_t *to_base64 =
47
2.43k
      default_or_url ? tables::base64::to_base64_default_or_url_value
48
2.43k
                     : (base64_url ? tables::base64::to_base64_url_value
49
2.43k
                                   : tables::base64::to_base64_value);
50
2.43k
  auto ri = simdutf::scalar::base64::find_end(src, srclen, options);
51
2.43k
  size_t equallocation = ri.equallocation;
52
2.43k
  size_t equalsigns = ri.equalsigns;
53
2.43k
  srclen = ri.srclen;
54
2.43k
  size_t full_input_length = ri.full_input_length;
55
2.43k
  if (srclen == 0) {
56
119
    if (!ignore_garbage && equalsigns > 0) {
57
18
      return {INVALID_BASE64_CHARACTER, equallocation, 0, true};
58
18
    }
59
101
    return {SUCCESS, full_input_length, 0};
60
119
  }
61
2.31k
  char *end_of_safe_64byte_zone =
62
2.31k
      dst == nullptr
63
2.31k
          ? nullptr
64
2.31k
          : ((srclen + 3) / 4 * 3 >= 63 ? dst + (srclen + 3) / 4 * 3 - 63
65
2.26k
                                        : dst);
66
67
2.31k
  const chartype *const srcinit = src;
68
2.31k
  const char *const dstinit = dst;
69
2.31k
  const chartype *const srcend = src + srclen;
70
71
2.31k
  constexpr size_t block_size = 6;
72
2.31k
  static_assert(block_size >= 2, "block_size must be at least two");
73
2.31k
  char buffer[block_size * 64];
74
2.31k
  char *bufferptr = buffer;
75
2.31k
  if (srclen >= 64) {
76
1.79k
    const chartype *const srcend64 = src + srclen - 64;
77
724k
    while (src <= srcend64) {
78
722k
      block64 b(src);
79
722k
      src += 64;
80
722k
      uint64_t error = 0;
81
722k
      const uint64_t badcharmask =
82
722k
          b.to_base64_mask<base64_url, ignore_garbage, default_or_url>(&error);
83
722k
      if (!ignore_garbage && error) {
84
456
        src -= 64;
85
456
        const size_t error_offset = trailing_zeroes(error);
86
456
        return {error_code::INVALID_BASE64_CHARACTER,
87
456
                size_t(src - srcinit + error_offset), size_t(dst - dstinit)};
88
456
      }
89
722k
      if (badcharmask != 0) {
90
58.1k
        bufferptr += b.compress_block(badcharmask, bufferptr);
91
664k
      } else if (bufferptr != buffer) {
92
35.6k
        b.copy_block(bufferptr);
93
35.6k
        bufferptr += 64;
94
628k
      } else {
95
628k
        if (dst >= end_of_safe_64byte_zone) {
96
283
          b.base64_decode_block_safe(dst);
97
628k
        } else {
98
628k
          b.base64_decode_block(dst);
99
628k
        }
100
628k
        dst += 48;
101
628k
      }
102
722k
      if (bufferptr >= (block_size - 1) * 64 + buffer) {
103
82.2k
        for (size_t i = 0; i < (block_size - 2); i++) {
104
65.8k
          base64_decode_block(dst, buffer + i * 64);
105
65.8k
          dst += 48;
106
65.8k
        }
107
16.4k
        if (dst >= end_of_safe_64byte_zone) {
108
0
          base64_decode_block_safe(dst, buffer + (block_size - 2) * 64);
109
16.4k
        } else {
110
16.4k
          base64_decode_block(dst, buffer + (block_size - 2) * 64);
111
16.4k
        }
112
16.4k
        dst += 48;
113
16.4k
        std::memcpy(buffer, buffer + (block_size - 1) * 64,
114
16.4k
                    64); // 64 might be too much
115
16.4k
        bufferptr -= (block_size - 1) * 64;
116
16.4k
      }
117
722k
    }
118
1.79k
  }
119
120
1.86k
  char *buffer_start = buffer;
121
  // Optimization note: if this is almost full, then it is worth our
122
  // time, otherwise, we should just decode directly.
123
1.86k
  int last_block = (int)((bufferptr - buffer_start) % 64);
124
1.86k
  if (last_block != 0 && srcend - src + last_block >= 64) {
125
126
1.69k
    while ((bufferptr - buffer_start) % 64 != 0 && src < srcend) {
127
1.56k
      uint8_t val = to_base64[uint8_t(*src)];
128
1.56k
      *bufferptr = char(val);
129
1.56k
      if (!ignore_garbage &&
130
1.56k
          (!scalar::base64::is_eight_byte(*src) || val > 64)) {
131
7
        return {error_code::INVALID_BASE64_CHARACTER, size_t(src - srcinit),
132
7
                size_t(dst - dstinit)};
133
7
      }
134
1.56k
      bufferptr += (val <= 63);
135
1.56k
      src++;
136
1.56k
    }
137
131
  }
138
139
2.54k
  for (; buffer_start + 64 <= bufferptr; buffer_start += 64) {
140
691
    if (dst >= end_of_safe_64byte_zone) {
141
57
      base64_decode_block_safe(dst, buffer_start);
142
634
    } else {
143
634
      base64_decode_block(dst, buffer_start);
144
634
    }
145
691
    dst += 48;
146
691
  }
147
1.85k
  if ((bufferptr - buffer_start) % 64 != 0) {
148
6.59k
    while (buffer_start + 4 < bufferptr) {
149
5.79k
      uint32_t triple = ((uint32_t(uint8_t(buffer_start[0])) << 3 * 6) +
150
5.79k
                         (uint32_t(uint8_t(buffer_start[1])) << 2 * 6) +
151
5.79k
                         (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) +
152
5.79k
                         (uint32_t(uint8_t(buffer_start[3])) << 0 * 6))
153
5.79k
                        << 8;
154
5.79k
#if !SIMDUTF_IS_BIG_ENDIAN
155
5.79k
      triple = scalar::u32_swap_bytes(triple);
156
5.79k
#endif
157
5.79k
      std::memcpy(dst, &triple, 3);
158
159
5.79k
      dst += 3;
160
5.79k
      buffer_start += 4;
161
5.79k
    }
162
797
    if (buffer_start + 4 <= bufferptr) {
163
207
      uint32_t triple = ((uint32_t(uint8_t(buffer_start[0])) << 3 * 6) +
164
207
                         (uint32_t(uint8_t(buffer_start[1])) << 2 * 6) +
165
207
                         (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) +
166
207
                         (uint32_t(uint8_t(buffer_start[3])) << 0 * 6))
167
207
                        << 8;
168
207
#if !SIMDUTF_IS_BIG_ENDIAN
169
207
      triple = scalar::u32_swap_bytes(triple);
170
207
#endif
171
207
      std::memcpy(dst, &triple, 3);
172
173
207
      dst += 3;
174
207
      buffer_start += 4;
175
207
    }
176
    // we may have 1, 2 or 3 bytes left and we need to decode them so let us
177
    // backtrack
178
797
    int leftover = int(bufferptr - buffer_start);
179
1.98k
    while (leftover > 0) {
180
1.18k
      if (!ignore_garbage) {
181
55.5k
        while (to_base64[uint8_t(*(src - 1))] == 64) {
182
54.3k
          src--;
183
54.3k
        }
184
1.18k
      } else {
185
0
        while (to_base64[uint8_t(*(src - 1))] >= 64) {
186
0
          src--;
187
0
        }
188
0
      }
189
1.18k
      src--;
190
1.18k
      leftover--;
191
1.18k
    }
192
797
  }
193
1.85k
  if (src < srcend + equalsigns) {
194
1.60k
    full_result r = scalar::base64::base64_tail_decode(
195
1.60k
        dst, src, srcend - src, equalsigns, options, last_chunk_options);
196
1.60k
    r = scalar::base64::patch_tail_result(
197
1.60k
        r, size_t(src - srcinit), size_t(dst - dstinit), equallocation,
198
1.60k
        full_input_length, last_chunk_options);
199
    // When is_partial(last_chunk_options) is true, we must either end with
200
    // the end of the stream (beyond whitespace) or right after a non-ignorable
201
    // character or at the very beginning of the stream.
202
    // See https://tc39.es/proposal-arraybuffer-base64/spec/#sec-frombase64
203
1.60k
    if (is_partial(last_chunk_options) && r.error == error_code::SUCCESS &&
204
486
        r.input_count < full_input_length) {
205
      // First check if we can extend the input to the end of the stream
206
1.35k
      while (r.input_count < full_input_length &&
207
1.34k
             base64_ignorable(*(srcinit + r.input_count), options)) {
208
937
        r.input_count++;
209
937
      }
210
      // If we are still not at the end of the stream, then we must backtrack
211
      // to the last non-ignorable character.
212
422
      if (r.input_count < full_input_length) {
213
68.9k
        while (r.input_count > 0 &&
214
68.7k
               base64_ignorable(*(srcinit + r.input_count - 1), options)) {
215
68.4k
          r.input_count--;
216
68.4k
        }
217
407
      }
218
422
    }
219
1.60k
    return r;
220
1.60k
  }
221
248
  if (!ignore_garbage && equalsigns > 0) {
222
0
    if ((size_t(dst - dstinit) % 3 == 0) ||
223
0
        ((size_t(dst - dstinit) % 3) + 1 + equalsigns != 4)) {
224
0
      return {INVALID_BASE64_CHARACTER, equallocation, size_t(dst - dstinit),
225
0
              true};
226
0
    }
227
0
  }
228
248
  return {SUCCESS, srclen, size_t(dst - dstinit)};
229
248
}
Unexecuted instantiation: simdutf.cpp:simdutf::full_result simdutf::haswell::(anonymous namespace)::base64::compress_decode_base64<false, true, false, char>(char*, char const*, unsigned long, simdutf::base64_options, simdutf::last_chunk_handling_options)
simdutf.cpp:simdutf::full_result simdutf::haswell::(anonymous namespace)::base64::compress_decode_base64<false, false, false, char>(char*, char const*, unsigned long, simdutf::base64_options, simdutf::last_chunk_handling_options)
Line
Count
Source
45
64.4k
                       last_chunk_handling_options last_chunk_options) {
46
64.4k
  const uint8_t *to_base64 =
47
64.4k
      default_or_url ? tables::base64::to_base64_default_or_url_value
48
64.4k
                     : (base64_url ? tables::base64::to_base64_url_value
49
64.4k
                                   : tables::base64::to_base64_value);
50
64.4k
  auto ri = simdutf::scalar::base64::find_end(src, srclen, options);
51
64.4k
  size_t equallocation = ri.equallocation;
52
64.4k
  size_t equalsigns = ri.equalsigns;
53
64.4k
  srclen = ri.srclen;
54
64.4k
  size_t full_input_length = ri.full_input_length;
55
64.4k
  if (srclen == 0) {
56
55.6k
    if (!ignore_garbage && equalsigns > 0) {
57
36
      return {INVALID_BASE64_CHARACTER, equallocation, 0, true};
58
36
    }
59
55.6k
    return {SUCCESS, full_input_length, 0};
60
55.6k
  }
61
8.84k
  char *end_of_safe_64byte_zone =
62
8.84k
      dst == nullptr
63
8.84k
          ? nullptr
64
8.84k
          : ((srclen + 3) / 4 * 3 >= 63 ? dst + (srclen + 3) / 4 * 3 - 63
65
8.69k
                                        : dst);
66
67
8.84k
  const chartype *const srcinit = src;
68
8.84k
  const char *const dstinit = dst;
69
8.84k
  const chartype *const srcend = src + srclen;
70
71
8.84k
  constexpr size_t block_size = 6;
72
8.84k
  static_assert(block_size >= 2, "block_size must be at least two");
73
8.84k
  char buffer[block_size * 64];
74
8.84k
  char *bufferptr = buffer;
75
8.84k
  if (srclen >= 64) {
76
4.96k
    const chartype *const srcend64 = src + srclen - 64;
77
901k
    while (src <= srcend64) {
78
898k
      block64 b(src);
79
898k
      src += 64;
80
898k
      uint64_t error = 0;
81
898k
      const uint64_t badcharmask =
82
898k
          b.to_base64_mask<base64_url, ignore_garbage, default_or_url>(&error);
83
898k
      if (!ignore_garbage && error) {
84
1.27k
        src -= 64;
85
1.27k
        const size_t error_offset = trailing_zeroes(error);
86
1.27k
        return {error_code::INVALID_BASE64_CHARACTER,
87
1.27k
                size_t(src - srcinit + error_offset), size_t(dst - dstinit)};
88
1.27k
      }
89
896k
      if (badcharmask != 0) {
90
63.6k
        bufferptr += b.compress_block(badcharmask, bufferptr);
91
833k
      } else if (bufferptr != buffer) {
92
126k
        b.copy_block(bufferptr);
93
126k
        bufferptr += 64;
94
706k
      } else {
95
706k
        if (dst >= end_of_safe_64byte_zone) {
96
1.04k
          b.base64_decode_block_safe(dst);
97
705k
        } else {
98
705k
          b.base64_decode_block(dst);
99
705k
        }
100
706k
        dst += 48;
101
706k
      }
102
896k
      if (bufferptr >= (block_size - 1) * 64 + buffer) {
103
177k
        for (size_t i = 0; i < (block_size - 2); i++) {
104
141k
          base64_decode_block(dst, buffer + i * 64);
105
141k
          dst += 48;
106
141k
        }
107
35.4k
        if (dst >= end_of_safe_64byte_zone) {
108
0
          base64_decode_block_safe(dst, buffer + (block_size - 2) * 64);
109
35.4k
        } else {
110
35.4k
          base64_decode_block(dst, buffer + (block_size - 2) * 64);
111
35.4k
        }
112
35.4k
        dst += 48;
113
35.4k
        std::memcpy(buffer, buffer + (block_size - 1) * 64,
114
35.4k
                    64); // 64 might be too much
115
35.4k
        bufferptr -= (block_size - 1) * 64;
116
35.4k
      }
117
896k
    }
118
4.96k
  }
119
120
7.56k
  char *buffer_start = buffer;
121
  // Optimization note: if this is almost full, then it is worth our
122
  // time, otherwise, we should just decode directly.
123
7.56k
  int last_block = (int)((bufferptr - buffer_start) % 64);
124
7.56k
  if (last_block != 0 && srcend - src + last_block >= 64) {
125
126
3.31k
    while ((bufferptr - buffer_start) % 64 != 0 && src < srcend) {
127
3.07k
      uint8_t val = to_base64[uint8_t(*src)];
128
3.07k
      *bufferptr = char(val);
129
3.07k
      if (!ignore_garbage &&
130
3.07k
          (!scalar::base64::is_eight_byte(*src) || val > 64)) {
131
20
        return {error_code::INVALID_BASE64_CHARACTER, size_t(src - srcinit),
132
20
                size_t(dst - dstinit)};
133
20
      }
134
3.05k
      bufferptr += (val <= 63);
135
3.05k
      src++;
136
3.05k
    }
137
260
  }
138
139
8.79k
  for (; buffer_start + 64 <= bufferptr; buffer_start += 64) {
140
1.25k
    if (dst >= end_of_safe_64byte_zone) {
141
106
      base64_decode_block_safe(dst, buffer_start);
142
1.14k
    } else {
143
1.14k
      base64_decode_block(dst, buffer_start);
144
1.14k
    }
145
1.25k
    dst += 48;
146
1.25k
  }
147
7.54k
  if ((bufferptr - buffer_start) % 64 != 0) {
148
12.2k
    while (buffer_start + 4 < bufferptr) {
149
10.8k
      uint32_t triple = ((uint32_t(uint8_t(buffer_start[0])) << 3 * 6) +
150
10.8k
                         (uint32_t(uint8_t(buffer_start[1])) << 2 * 6) +
151
10.8k
                         (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) +
152
10.8k
                         (uint32_t(uint8_t(buffer_start[3])) << 0 * 6))
153
10.8k
                        << 8;
154
10.8k
#if !SIMDUTF_IS_BIG_ENDIAN
155
10.8k
      triple = scalar::u32_swap_bytes(triple);
156
10.8k
#endif
157
10.8k
      std::memcpy(dst, &triple, 3);
158
159
10.8k
      dst += 3;
160
10.8k
      buffer_start += 4;
161
10.8k
    }
162
1.39k
    if (buffer_start + 4 <= bufferptr) {
163
358
      uint32_t triple = ((uint32_t(uint8_t(buffer_start[0])) << 3 * 6) +
164
358
                         (uint32_t(uint8_t(buffer_start[1])) << 2 * 6) +
165
358
                         (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) +
166
358
                         (uint32_t(uint8_t(buffer_start[3])) << 0 * 6))
167
358
                        << 8;
168
358
#if !SIMDUTF_IS_BIG_ENDIAN
169
358
      triple = scalar::u32_swap_bytes(triple);
170
358
#endif
171
358
      std::memcpy(dst, &triple, 3);
172
173
358
      dst += 3;
174
358
      buffer_start += 4;
175
358
    }
176
    // we may have 1, 2 or 3 bytes left and we need to decode them so let us
177
    // backtrack
178
1.39k
    int leftover = int(bufferptr - buffer_start);
179
3.49k
    while (leftover > 0) {
180
2.10k
      if (!ignore_garbage) {
181
123k
        while (to_base64[uint8_t(*(src - 1))] == 64) {
182
121k
          src--;
183
121k
        }
184
2.10k
      } else {
185
0
        while (to_base64[uint8_t(*(src - 1))] >= 64) {
186
0
          src--;
187
0
        }
188
0
      }
189
2.10k
      src--;
190
2.10k
      leftover--;
191
2.10k
    }
192
1.39k
  }
193
7.54k
  if (src < srcend + equalsigns) {
194
6.90k
    full_result r = scalar::base64::base64_tail_decode(
195
6.90k
        dst, src, srcend - src, equalsigns, options, last_chunk_options);
196
6.90k
    r = scalar::base64::patch_tail_result(
197
6.90k
        r, size_t(src - srcinit), size_t(dst - dstinit), equallocation,
198
6.90k
        full_input_length, last_chunk_options);
199
    // When is_partial(last_chunk_options) is true, we must either end with
200
    // the end of the stream (beyond whitespace) or right after a non-ignorable
201
    // character or at the very beginning of the stream.
202
    // See https://tc39.es/proposal-arraybuffer-base64/spec/#sec-frombase64
203
6.90k
    if (is_partial(last_chunk_options) && r.error == error_code::SUCCESS &&
204
2.13k
        r.input_count < full_input_length) {
205
      // First check if we can extend the input to the end of the stream
206
4.61k
      while (r.input_count < full_input_length &&
207
4.55k
             base64_ignorable(*(srcinit + r.input_count), options)) {
208
3.78k
        r.input_count++;
209
3.78k
      }
210
      // If we are still not at the end of the stream, then we must backtrack
211
      // to the last non-ignorable character.
212
830
      if (r.input_count < full_input_length) {
213
52.7k
        while (r.input_count > 0 &&
214
52.5k
               base64_ignorable(*(srcinit + r.input_count - 1), options)) {
215
51.9k
          r.input_count--;
216
51.9k
        }
217
773
      }
218
830
    }
219
6.90k
    return r;
220
6.90k
  }
221
638
  if (!ignore_garbage && equalsigns > 0) {
222
0
    if ((size_t(dst - dstinit) % 3 == 0) ||
223
0
        ((size_t(dst - dstinit) % 3) + 1 + equalsigns != 4)) {
224
0
      return {INVALID_BASE64_CHARACTER, equallocation, size_t(dst - dstinit),
225
0
              true};
226
0
    }
227
0
  }
228
638
  return {SUCCESS, srclen, size_t(dst - dstinit)};
229
638
}
Unexecuted instantiation: simdutf.cpp:simdutf::full_result simdutf::haswell::(anonymous namespace)::base64::compress_decode_base64<false, true, true, char16_t>(char*, char16_t const*, unsigned long, simdutf::base64_options, simdutf::last_chunk_handling_options)
Unexecuted instantiation: simdutf.cpp:simdutf::full_result simdutf::haswell::(anonymous namespace)::base64::compress_decode_base64<false, false, true, char16_t>(char*, char16_t const*, unsigned long, simdutf::base64_options, simdutf::last_chunk_handling_options)
Unexecuted instantiation: simdutf.cpp:simdutf::full_result simdutf::haswell::(anonymous namespace)::base64::compress_decode_base64<true, true, false, char16_t>(char*, char16_t const*, unsigned long, simdutf::base64_options, simdutf::last_chunk_handling_options)
simdutf.cpp:simdutf::full_result simdutf::haswell::(anonymous namespace)::base64::compress_decode_base64<true, false, false, char16_t>(char*, char16_t const*, unsigned long, simdutf::base64_options, simdutf::last_chunk_handling_options)
Line
Count
Source
45
2.04k
                       last_chunk_handling_options last_chunk_options) {
46
2.04k
  const uint8_t *to_base64 =
47
2.04k
      default_or_url ? tables::base64::to_base64_default_or_url_value
48
2.04k
                     : (base64_url ? tables::base64::to_base64_url_value
49
2.04k
                                   : tables::base64::to_base64_value);
50
2.04k
  auto ri = simdutf::scalar::base64::find_end(src, srclen, options);
51
2.04k
  size_t equallocation = ri.equallocation;
52
2.04k
  size_t equalsigns = ri.equalsigns;
53
2.04k
  srclen = ri.srclen;
54
2.04k
  size_t full_input_length = ri.full_input_length;
55
2.04k
  if (srclen == 0) {
56
114
    if (!ignore_garbage && equalsigns > 0) {
57
11
      return {INVALID_BASE64_CHARACTER, equallocation, 0, true};
58
11
    }
59
103
    return {SUCCESS, full_input_length, 0};
60
114
  }
61
1.93k
  char *end_of_safe_64byte_zone =
62
1.93k
      dst == nullptr
63
1.93k
          ? nullptr
64
1.93k
          : ((srclen + 3) / 4 * 3 >= 63 ? dst + (srclen + 3) / 4 * 3 - 63
65
1.79k
                                        : dst);
66
67
1.93k
  const chartype *const srcinit = src;
68
1.93k
  const char *const dstinit = dst;
69
1.93k
  const chartype *const srcend = src + srclen;
70
71
1.93k
  constexpr size_t block_size = 6;
72
1.93k
  static_assert(block_size >= 2, "block_size must be at least two");
73
1.93k
  char buffer[block_size * 64];
74
1.93k
  char *bufferptr = buffer;
75
1.93k
  if (srclen >= 64) {
76
1.43k
    const chartype *const srcend64 = src + srclen - 64;
77
51.1k
    while (src <= srcend64) {
78
50.1k
      block64 b(src);
79
50.1k
      src += 64;
80
50.1k
      uint64_t error = 0;
81
50.1k
      const uint64_t badcharmask =
82
50.1k
          b.to_base64_mask<base64_url, ignore_garbage, default_or_url>(&error);
83
50.1k
      if (!ignore_garbage && error) {
84
476
        src -= 64;
85
476
        const size_t error_offset = trailing_zeroes(error);
86
476
        return {error_code::INVALID_BASE64_CHARACTER,
87
476
                size_t(src - srcinit + error_offset), size_t(dst - dstinit)};
88
476
      }
89
49.6k
      if (badcharmask != 0) {
90
41.9k
        bufferptr += b.compress_block(badcharmask, bufferptr);
91
41.9k
      } else if (bufferptr != buffer) {
92
6.70k
        b.copy_block(bufferptr);
93
6.70k
        bufferptr += 64;
94
6.70k
      } else {
95
1.05k
        if (dst >= end_of_safe_64byte_zone) {
96
13
          b.base64_decode_block_safe(dst);
97
1.04k
        } else {
98
1.04k
          b.base64_decode_block(dst);
99
1.04k
        }
100
1.05k
        dst += 48;
101
1.05k
      }
102
49.6k
      if (bufferptr >= (block_size - 1) * 64 + buffer) {
103
27.2k
        for (size_t i = 0; i < (block_size - 2); i++) {
104
21.7k
          base64_decode_block(dst, buffer + i * 64);
105
21.7k
          dst += 48;
106
21.7k
        }
107
5.44k
        if (dst >= end_of_safe_64byte_zone) {
108
0
          base64_decode_block_safe(dst, buffer + (block_size - 2) * 64);
109
5.44k
        } else {
110
5.44k
          base64_decode_block(dst, buffer + (block_size - 2) * 64);
111
5.44k
        }
112
5.44k
        dst += 48;
113
5.44k
        std::memcpy(buffer, buffer + (block_size - 1) * 64,
114
5.44k
                    64); // 64 might be too much
115
5.44k
        bufferptr -= (block_size - 1) * 64;
116
5.44k
      }
117
49.6k
    }
118
1.43k
  }
119
120
1.45k
  char *buffer_start = buffer;
121
  // Optimization note: if this is almost full, then it is worth our
122
  // time, otherwise, we should just decode directly.
123
1.45k
  int last_block = (int)((bufferptr - buffer_start) % 64);
124
1.45k
  if (last_block != 0 && srcend - src + last_block >= 64) {
125
126
1.62k
    while ((bufferptr - buffer_start) % 64 != 0 && src < srcend) {
127
1.53k
      uint8_t val = to_base64[uint8_t(*src)];
128
1.53k
      *bufferptr = char(val);
129
1.53k
      if (!ignore_garbage &&
130
1.53k
          (!scalar::base64::is_eight_byte(*src) || val > 64)) {
131
67
        return {error_code::INVALID_BASE64_CHARACTER, size_t(src - srcinit),
132
67
                size_t(dst - dstinit)};
133
67
      }
134
1.46k
      bufferptr += (val <= 63);
135
1.46k
      src++;
136
1.46k
    }
137
162
  }
138
139
1.91k
  for (; buffer_start + 64 <= bufferptr; buffer_start += 64) {
140
525
    if (dst >= end_of_safe_64byte_zone) {
141
38
      base64_decode_block_safe(dst, buffer_start);
142
487
    } else {
143
487
      base64_decode_block(dst, buffer_start);
144
487
    }
145
525
    dst += 48;
146
525
  }
147
1.39k
  if ((bufferptr - buffer_start) % 64 != 0) {
148
6.17k
    while (buffer_start + 4 < bufferptr) {
149
5.41k
      uint32_t triple = ((uint32_t(uint8_t(buffer_start[0])) << 3 * 6) +
150
5.41k
                         (uint32_t(uint8_t(buffer_start[1])) << 2 * 6) +
151
5.41k
                         (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) +
152
5.41k
                         (uint32_t(uint8_t(buffer_start[3])) << 0 * 6))
153
5.41k
                        << 8;
154
5.41k
#if !SIMDUTF_IS_BIG_ENDIAN
155
5.41k
      triple = scalar::u32_swap_bytes(triple);
156
5.41k
#endif
157
5.41k
      std::memcpy(dst, &triple, 3);
158
159
5.41k
      dst += 3;
160
5.41k
      buffer_start += 4;
161
5.41k
    }
162
762
    if (buffer_start + 4 <= bufferptr) {
163
188
      uint32_t triple = ((uint32_t(uint8_t(buffer_start[0])) << 3 * 6) +
164
188
                         (uint32_t(uint8_t(buffer_start[1])) << 2 * 6) +
165
188
                         (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) +
166
188
                         (uint32_t(uint8_t(buffer_start[3])) << 0 * 6))
167
188
                        << 8;
168
188
#if !SIMDUTF_IS_BIG_ENDIAN
169
188
      triple = scalar::u32_swap_bytes(triple);
170
188
#endif
171
188
      std::memcpy(dst, &triple, 3);
172
173
188
      dst += 3;
174
188
      buffer_start += 4;
175
188
    }
176
    // we may have 1, 2 or 3 bytes left and we need to decode them so let us
177
    // backtrack
178
762
    int leftover = int(bufferptr - buffer_start);
179
1.90k
    while (leftover > 0) {
180
1.14k
      if (!ignore_garbage) {
181
55.6k
        while (to_base64[uint8_t(*(src - 1))] == 64) {
182
54.4k
          src--;
183
54.4k
        }
184
1.14k
      } else {
185
0
        while (to_base64[uint8_t(*(src - 1))] >= 64) {
186
0
          src--;
187
0
        }
188
0
      }
189
1.14k
      src--;
190
1.14k
      leftover--;
191
1.14k
    }
192
762
  }
193
1.39k
  if (src < srcend + equalsigns) {
194
1.19k
    full_result r = scalar::base64::base64_tail_decode(
195
1.19k
        dst, src, srcend - src, equalsigns, options, last_chunk_options);
196
1.19k
    r = scalar::base64::patch_tail_result(
197
1.19k
        r, size_t(src - srcinit), size_t(dst - dstinit), equallocation,
198
1.19k
        full_input_length, last_chunk_options);
199
    // When is_partial(last_chunk_options) is true, we must either end with
200
    // the end of the stream (beyond whitespace) or right after a non-ignorable
201
    // character or at the very beginning of the stream.
202
    // See https://tc39.es/proposal-arraybuffer-base64/spec/#sec-frombase64
203
1.19k
    if (is_partial(last_chunk_options) && r.error == error_code::SUCCESS &&
204
572
        r.input_count < full_input_length) {
205
      // First check if we can extend the input to the end of the stream
206
31.1k
      while (r.input_count < full_input_length &&
207
31.1k
             base64_ignorable(*(srcinit + r.input_count), options)) {
208
30.6k
        r.input_count++;
209
30.6k
      }
210
      // If we are still not at the end of the stream, then we must backtrack
211
      // to the last non-ignorable character.
212
523
      if (r.input_count < full_input_length) {
213
41.9k
        while (r.input_count > 0 &&
214
41.7k
               base64_ignorable(*(srcinit + r.input_count - 1), options)) {
215
41.4k
          r.input_count--;
216
41.4k
        }
217
508
      }
218
523
    }
219
1.19k
    return r;
220
1.19k
  }
221
198
  if (!ignore_garbage && equalsigns > 0) {
222
0
    if ((size_t(dst - dstinit) % 3 == 0) ||
223
0
        ((size_t(dst - dstinit) % 3) + 1 + equalsigns != 4)) {
224
0
      return {INVALID_BASE64_CHARACTER, equallocation, size_t(dst - dstinit),
225
0
              true};
226
0
    }
227
0
  }
228
198
  return {SUCCESS, srclen, size_t(dst - dstinit)};
229
198
}
Unexecuted instantiation: simdutf.cpp:simdutf::full_result simdutf::haswell::(anonymous namespace)::base64::compress_decode_base64<false, true, false, char16_t>(char*, char16_t const*, unsigned long, simdutf::base64_options, simdutf::last_chunk_handling_options)
simdutf.cpp:simdutf::full_result simdutf::haswell::(anonymous namespace)::base64::compress_decode_base64<false, false, false, char16_t>(char*, char16_t const*, unsigned long, simdutf::base64_options, simdutf::last_chunk_handling_options)
Line
Count
Source
45
2.18k
                       last_chunk_handling_options last_chunk_options) {
46
2.18k
  const uint8_t *to_base64 =
47
2.18k
      default_or_url ? tables::base64::to_base64_default_or_url_value
48
2.18k
                     : (base64_url ? tables::base64::to_base64_url_value
49
2.18k
                                   : tables::base64::to_base64_value);
50
2.18k
  auto ri = simdutf::scalar::base64::find_end(src, srclen, options);
51
2.18k
  size_t equallocation = ri.equallocation;
52
2.18k
  size_t equalsigns = ri.equalsigns;
53
2.18k
  srclen = ri.srclen;
54
2.18k
  size_t full_input_length = ri.full_input_length;
55
2.18k
  if (srclen == 0) {
56
181
    if (!ignore_garbage && equalsigns > 0) {
57
19
      return {INVALID_BASE64_CHARACTER, equallocation, 0, true};
58
19
    }
59
162
    return {SUCCESS, full_input_length, 0};
60
181
  }
61
1.99k
  char *end_of_safe_64byte_zone =
62
1.99k
      dst == nullptr
63
1.99k
          ? nullptr
64
1.99k
          : ((srclen + 3) / 4 * 3 >= 63 ? dst + (srclen + 3) / 4 * 3 - 63
65
1.85k
                                        : dst);
66
67
1.99k
  const chartype *const srcinit = src;
68
1.99k
  const char *const dstinit = dst;
69
1.99k
  const chartype *const srcend = src + srclen;
70
71
1.99k
  constexpr size_t block_size = 6;
72
1.99k
  static_assert(block_size >= 2, "block_size must be at least two");
73
1.99k
  char buffer[block_size * 64];
74
1.99k
  char *bufferptr = buffer;
75
1.99k
  if (srclen >= 64) {
76
1.46k
    const chartype *const srcend64 = src + srclen - 64;
77
97.1k
    while (src <= srcend64) {
78
96.2k
      block64 b(src);
79
96.2k
      src += 64;
80
96.2k
      uint64_t error = 0;
81
96.2k
      const uint64_t badcharmask =
82
96.2k
          b.to_base64_mask<base64_url, ignore_garbage, default_or_url>(&error);
83
96.2k
      if (!ignore_garbage && error) {
84
511
        src -= 64;
85
511
        const size_t error_offset = trailing_zeroes(error);
86
511
        return {error_code::INVALID_BASE64_CHARACTER,
87
511
                size_t(src - srcinit + error_offset), size_t(dst - dstinit)};
88
511
      }
89
95.7k
      if (badcharmask != 0) {
90
78.1k
        bufferptr += b.compress_block(badcharmask, bufferptr);
91
78.1k
      } else if (bufferptr != buffer) {
92
16.7k
        b.copy_block(bufferptr);
93
16.7k
        bufferptr += 64;
94
16.7k
      } else {
95
808
        if (dst >= end_of_safe_64byte_zone) {
96
7
          b.base64_decode_block_safe(dst);
97
801
        } else {
98
801
          b.base64_decode_block(dst);
99
801
        }
100
808
        dst += 48;
101
808
      }
102
95.7k
      if (bufferptr >= (block_size - 1) * 64 + buffer) {
103
73.4k
        for (size_t i = 0; i < (block_size - 2); i++) {
104
58.7k
          base64_decode_block(dst, buffer + i * 64);
105
58.7k
          dst += 48;
106
58.7k
        }
107
14.6k
        if (dst >= end_of_safe_64byte_zone) {
108
0
          base64_decode_block_safe(dst, buffer + (block_size - 2) * 64);
109
14.6k
        } else {
110
14.6k
          base64_decode_block(dst, buffer + (block_size - 2) * 64);
111
14.6k
        }
112
14.6k
        dst += 48;
113
14.6k
        std::memcpy(buffer, buffer + (block_size - 1) * 64,
114
14.6k
                    64); // 64 might be too much
115
14.6k
        bufferptr -= (block_size - 1) * 64;
116
14.6k
      }
117
95.7k
    }
118
1.46k
  }
119
120
1.48k
  char *buffer_start = buffer;
121
  // Optimization note: if this is almost full, then it is worth our
122
  // time, otherwise, we should just decode directly.
123
1.48k
  int last_block = (int)((bufferptr - buffer_start) % 64);
124
1.48k
  if (last_block != 0 && srcend - src + last_block >= 64) {
125
126
1.40k
    while ((bufferptr - buffer_start) % 64 != 0 && src < srcend) {
127
1.29k
      uint8_t val = to_base64[uint8_t(*src)];
128
1.29k
      *bufferptr = char(val);
129
1.29k
      if (!ignore_garbage &&
130
1.29k
          (!scalar::base64::is_eight_byte(*src) || val > 64)) {
131
59
        return {error_code::INVALID_BASE64_CHARACTER, size_t(src - srcinit),
132
59
                size_t(dst - dstinit)};
133
59
      }
134
1.23k
      bufferptr += (val <= 63);
135
1.23k
      src++;
136
1.23k
    }
137
171
  }
138
139
2.05k
  for (; buffer_start + 64 <= bufferptr; buffer_start += 64) {
140
626
    if (dst >= end_of_safe_64byte_zone) {
141
48
      base64_decode_block_safe(dst, buffer_start);
142
578
    } else {
143
578
      base64_decode_block(dst, buffer_start);
144
578
    }
145
626
    dst += 48;
146
626
  }
147
1.42k
  if ((bufferptr - buffer_start) % 64 != 0) {
148
6.45k
    while (buffer_start + 4 < bufferptr) {
149
5.68k
      uint32_t triple = ((uint32_t(uint8_t(buffer_start[0])) << 3 * 6) +
150
5.68k
                         (uint32_t(uint8_t(buffer_start[1])) << 2 * 6) +
151
5.68k
                         (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) +
152
5.68k
                         (uint32_t(uint8_t(buffer_start[3])) << 0 * 6))
153
5.68k
                        << 8;
154
5.68k
#if !SIMDUTF_IS_BIG_ENDIAN
155
5.68k
      triple = scalar::u32_swap_bytes(triple);
156
5.68k
#endif
157
5.68k
      std::memcpy(dst, &triple, 3);
158
159
5.68k
      dst += 3;
160
5.68k
      buffer_start += 4;
161
5.68k
    }
162
762
    if (buffer_start + 4 <= bufferptr) {
163
182
      uint32_t triple = ((uint32_t(uint8_t(buffer_start[0])) << 3 * 6) +
164
182
                         (uint32_t(uint8_t(buffer_start[1])) << 2 * 6) +
165
182
                         (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) +
166
182
                         (uint32_t(uint8_t(buffer_start[3])) << 0 * 6))
167
182
                        << 8;
168
182
#if !SIMDUTF_IS_BIG_ENDIAN
169
182
      triple = scalar::u32_swap_bytes(triple);
170
182
#endif
171
182
      std::memcpy(dst, &triple, 3);
172
173
182
      dst += 3;
174
182
      buffer_start += 4;
175
182
    }
176
    // we may have 1, 2 or 3 bytes left and we need to decode them so let us
177
    // backtrack
178
762
    int leftover = int(bufferptr - buffer_start);
179
1.91k
    while (leftover > 0) {
180
1.14k
      if (!ignore_garbage) {
181
101k
        while (to_base64[uint8_t(*(src - 1))] == 64) {
182
100k
          src--;
183
100k
        }
184
1.14k
      } else {
185
0
        while (to_base64[uint8_t(*(src - 1))] >= 64) {
186
0
          src--;
187
0
        }
188
0
      }
189
1.14k
      src--;
190
1.14k
      leftover--;
191
1.14k
    }
192
762
  }
193
1.42k
  if (src < srcend + equalsigns) {
194
1.23k
    full_result r = scalar::base64::base64_tail_decode(
195
1.23k
        dst, src, srcend - src, equalsigns, options, last_chunk_options);
196
1.23k
    r = scalar::base64::patch_tail_result(
197
1.23k
        r, size_t(src - srcinit), size_t(dst - dstinit), equallocation,
198
1.23k
        full_input_length, last_chunk_options);
199
    // When is_partial(last_chunk_options) is true, we must either end with
200
    // the end of the stream (beyond whitespace) or right after a non-ignorable
201
    // character or at the very beginning of the stream.
202
    // See https://tc39.es/proposal-arraybuffer-base64/spec/#sec-frombase64
203
1.23k
    if (is_partial(last_chunk_options) && r.error == error_code::SUCCESS &&
204
564
        r.input_count < full_input_length) {
205
      // First check if we can extend the input to the end of the stream
206
57.0k
      while (r.input_count < full_input_length &&
207
57.0k
             base64_ignorable(*(srcinit + r.input_count), options)) {
208
56.5k
        r.input_count++;
209
56.5k
      }
210
      // If we are still not at the end of the stream, then we must backtrack
211
      // to the last non-ignorable character.
212
501
      if (r.input_count < full_input_length) {
213
20.7k
        while (r.input_count > 0 &&
214
20.5k
               base64_ignorable(*(srcinit + r.input_count - 1), options)) {
215
20.2k
          r.input_count--;
216
20.2k
        }
217
485
      }
218
501
    }
219
1.23k
    return r;
220
1.23k
  }
221
198
  if (!ignore_garbage && equalsigns > 0) {
222
0
    if ((size_t(dst - dstinit) % 3 == 0) ||
223
0
        ((size_t(dst - dstinit) % 3) + 1 + equalsigns != 4)) {
224
0
      return {INVALID_BASE64_CHARACTER, equallocation, size_t(dst - dstinit),
225
0
              true};
226
0
    }
227
0
  }
228
198
  return {SUCCESS, srclen, size_t(dst - dstinit)};
229
198
}
Unexecuted instantiation: simdutf.cpp:simdutf::full_result simdutf::westmere::(anonymous namespace)::base64::compress_decode_base64<false, true, true, char>(char*, char const*, unsigned long, simdutf::base64_options, simdutf::last_chunk_handling_options)
Unexecuted instantiation: simdutf.cpp:simdutf::full_result simdutf::westmere::(anonymous namespace)::base64::compress_decode_base64<false, false, true, char>(char*, char const*, unsigned long, simdutf::base64_options, simdutf::last_chunk_handling_options)
Unexecuted instantiation: simdutf.cpp:simdutf::full_result simdutf::westmere::(anonymous namespace)::base64::compress_decode_base64<true, true, false, char>(char*, char const*, unsigned long, simdutf::base64_options, simdutf::last_chunk_handling_options)
simdutf.cpp:simdutf::full_result simdutf::westmere::(anonymous namespace)::base64::compress_decode_base64<true, false, false, char>(char*, char const*, unsigned long, simdutf::base64_options, simdutf::last_chunk_handling_options)
Line
Count
Source
45
2.23k
                       last_chunk_handling_options last_chunk_options) {
46
2.23k
  const uint8_t *to_base64 =
47
2.23k
      default_or_url ? tables::base64::to_base64_default_or_url_value
48
2.23k
                     : (base64_url ? tables::base64::to_base64_url_value
49
2.23k
                                   : tables::base64::to_base64_value);
50
2.23k
  auto ri = simdutf::scalar::base64::find_end(src, srclen, options);
51
2.23k
  size_t equallocation = ri.equallocation;
52
2.23k
  size_t equalsigns = ri.equalsigns;
53
2.23k
  srclen = ri.srclen;
54
2.23k
  size_t full_input_length = ri.full_input_length;
55
2.23k
  if (srclen == 0) {
56
42
    if (!ignore_garbage && equalsigns > 0) {
57
15
      return {INVALID_BASE64_CHARACTER, equallocation, 0, true};
58
15
    }
59
27
    return {SUCCESS, full_input_length, 0};
60
42
  }
61
2.19k
  char *end_of_safe_64byte_zone =
62
2.19k
      dst == nullptr
63
2.19k
          ? nullptr
64
2.19k
          : ((srclen + 3) / 4 * 3 >= 63 ? dst + (srclen + 3) / 4 * 3 - 63
65
2.13k
                                        : dst);
66
67
2.19k
  const chartype *const srcinit = src;
68
2.19k
  const char *const dstinit = dst;
69
2.19k
  const chartype *const srcend = src + srclen;
70
71
2.19k
  constexpr size_t block_size = 6;
72
2.19k
  static_assert(block_size >= 2, "block_size must be at least two");
73
2.19k
  char buffer[block_size * 64];
74
2.19k
  char *bufferptr = buffer;
75
2.19k
  if (srclen >= 64) {
76
1.72k
    const chartype *const srcend64 = src + srclen - 64;
77
719k
    while (src <= srcend64) {
78
718k
      block64 b(src);
79
718k
      src += 64;
80
718k
      uint64_t error = 0;
81
718k
      const uint64_t badcharmask =
82
718k
          b.to_base64_mask<base64_url, ignore_garbage, default_or_url>(&error);
83
718k
      if (!ignore_garbage && error) {
84
435
        src -= 64;
85
435
        const size_t error_offset = trailing_zeroes(error);
86
435
        return {error_code::INVALID_BASE64_CHARACTER,
87
435
                size_t(src - srcinit + error_offset), size_t(dst - dstinit)};
88
435
      }
89
718k
      if (badcharmask != 0) {
90
54.0k
        bufferptr += b.compress_block(badcharmask, bufferptr);
91
663k
      } else if (bufferptr != buffer) {
92
35.5k
        b.copy_block(bufferptr);
93
35.5k
        bufferptr += 64;
94
628k
      } else {
95
628k
        if (dst >= end_of_safe_64byte_zone) {
96
283
          b.base64_decode_block_safe(dst);
97
628k
        } else {
98
628k
          b.base64_decode_block(dst);
99
628k
        }
100
628k
        dst += 48;
101
628k
      }
102
718k
      if (bufferptr >= (block_size - 1) * 64 + buffer) {
103
81.4k
        for (size_t i = 0; i < (block_size - 2); i++) {
104
65.1k
          base64_decode_block(dst, buffer + i * 64);
105
65.1k
          dst += 48;
106
65.1k
        }
107
16.2k
        if (dst >= end_of_safe_64byte_zone) {
108
0
          base64_decode_block_safe(dst, buffer + (block_size - 2) * 64);
109
16.2k
        } else {
110
16.2k
          base64_decode_block(dst, buffer + (block_size - 2) * 64);
111
16.2k
        }
112
16.2k
        dst += 48;
113
16.2k
        std::memcpy(buffer, buffer + (block_size - 1) * 64,
114
16.2k
                    64); // 64 might be too much
115
16.2k
        bufferptr -= (block_size - 1) * 64;
116
16.2k
      }
117
718k
    }
118
1.72k
  }
119
120
1.75k
  char *buffer_start = buffer;
121
  // Optimization note: if this is almost full, then it is worth our
122
  // time, otherwise, we should just decode directly.
123
1.75k
  int last_block = (int)((bufferptr - buffer_start) % 64);
124
1.75k
  if (last_block != 0 && srcend - src + last_block >= 64) {
125
126
1.31k
    while ((bufferptr - buffer_start) % 64 != 0 && src < srcend) {
127
1.20k
      uint8_t val = to_base64[uint8_t(*src)];
128
1.20k
      *bufferptr = char(val);
129
1.20k
      if (!ignore_garbage &&
130
1.20k
          (!scalar::base64::is_eight_byte(*src) || val > 64)) {
131
7
        return {error_code::INVALID_BASE64_CHARACTER, size_t(src - srcinit),
132
7
                size_t(dst - dstinit)};
133
7
      }
134
1.20k
      bufferptr += (val <= 63);
135
1.20k
      src++;
136
1.20k
    }
137
118
  }
138
139
2.41k
  for (; buffer_start + 64 <= bufferptr; buffer_start += 64) {
140
662
    if (dst >= end_of_safe_64byte_zone) {
141
57
      base64_decode_block_safe(dst, buffer_start);
142
605
    } else {
143
605
      base64_decode_block(dst, buffer_start);
144
605
    }
145
662
    dst += 48;
146
662
  }
147
1.74k
  if ((bufferptr - buffer_start) % 64 != 0) {
148
6.28k
    while (buffer_start + 4 < bufferptr) {
149
5.53k
      uint32_t triple = ((uint32_t(uint8_t(buffer_start[0])) << 3 * 6) +
150
5.53k
                         (uint32_t(uint8_t(buffer_start[1])) << 2 * 6) +
151
5.53k
                         (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) +
152
5.53k
                         (uint32_t(uint8_t(buffer_start[3])) << 0 * 6))
153
5.53k
                        << 8;
154
5.53k
#if !SIMDUTF_IS_BIG_ENDIAN
155
5.53k
      triple = scalar::u32_swap_bytes(triple);
156
5.53k
#endif
157
5.53k
      std::memcpy(dst, &triple, 3);
158
159
5.53k
      dst += 3;
160
5.53k
      buffer_start += 4;
161
5.53k
    }
162
748
    if (buffer_start + 4 <= bufferptr) {
163
197
      uint32_t triple = ((uint32_t(uint8_t(buffer_start[0])) << 3 * 6) +
164
197
                         (uint32_t(uint8_t(buffer_start[1])) << 2 * 6) +
165
197
                         (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) +
166
197
                         (uint32_t(uint8_t(buffer_start[3])) << 0 * 6))
167
197
                        << 8;
168
197
#if !SIMDUTF_IS_BIG_ENDIAN
169
197
      triple = scalar::u32_swap_bytes(triple);
170
197
#endif
171
197
      std::memcpy(dst, &triple, 3);
172
173
197
      dst += 3;
174
197
      buffer_start += 4;
175
197
    }
176
    // we may have 1, 2 or 3 bytes left and we need to decode them so let us
177
    // backtrack
178
748
    int leftover = int(bufferptr - buffer_start);
179
1.85k
    while (leftover > 0) {
180
1.10k
      if (!ignore_garbage) {
181
15.2k
        while (to_base64[uint8_t(*(src - 1))] == 64) {
182
14.1k
          src--;
183
14.1k
        }
184
1.10k
      } else {
185
0
        while (to_base64[uint8_t(*(src - 1))] >= 64) {
186
0
          src--;
187
0
        }
188
0
      }
189
1.10k
      src--;
190
1.10k
      leftover--;
191
1.10k
    }
192
748
  }
193
1.74k
  if (src < srcend + equalsigns) {
194
1.50k
    full_result r = scalar::base64::base64_tail_decode(
195
1.50k
        dst, src, srcend - src, equalsigns, options, last_chunk_options);
196
1.50k
    r = scalar::base64::patch_tail_result(
197
1.50k
        r, size_t(src - srcinit), size_t(dst - dstinit), equallocation,
198
1.50k
        full_input_length, last_chunk_options);
199
    // When is_partial(last_chunk_options) is true, we must either end with
200
    // the end of the stream (beyond whitespace) or right after a non-ignorable
201
    // character or at the very beginning of the stream.
202
    // See https://tc39.es/proposal-arraybuffer-base64/spec/#sec-frombase64
203
1.50k
    if (is_partial(last_chunk_options) && r.error == error_code::SUCCESS &&
204
410
        r.input_count < full_input_length) {
205
      // First check if we can extend the input to the end of the stream
206
1.06k
      while (r.input_count < full_input_length &&
207
1.05k
             base64_ignorable(*(srcinit + r.input_count), options)) {
208
718
        r.input_count++;
209
718
      }
210
      // If we are still not at the end of the stream, then we must backtrack
211
      // to the last non-ignorable character.
212
351
      if (r.input_count < full_input_length) {
213
22.2k
        while (r.input_count > 0 &&
214
22.1k
               base64_ignorable(*(srcinit + r.input_count - 1), options)) {
215
21.9k
          r.input_count--;
216
21.9k
        }
217
340
      }
218
351
    }
219
1.50k
    return r;
220
1.50k
  }
221
242
  if (!ignore_garbage && equalsigns > 0) {
222
0
    if ((size_t(dst - dstinit) % 3 == 0) ||
223
0
        ((size_t(dst - dstinit) % 3) + 1 + equalsigns != 4)) {
224
0
      return {INVALID_BASE64_CHARACTER, equallocation, size_t(dst - dstinit),
225
0
              true};
226
0
    }
227
0
  }
228
242
  return {SUCCESS, srclen, size_t(dst - dstinit)};
229
242
}
Unexecuted instantiation: simdutf.cpp:simdutf::full_result simdutf::westmere::(anonymous namespace)::base64::compress_decode_base64<false, true, false, char>(char*, char const*, unsigned long, simdutf::base64_options, simdutf::last_chunk_handling_options)
simdutf.cpp:simdutf::full_result simdutf::westmere::(anonymous namespace)::base64::compress_decode_base64<false, false, false, char>(char*, char const*, unsigned long, simdutf::base64_options, simdutf::last_chunk_handling_options)
Line
Count
Source
45
17.8k
                       last_chunk_handling_options last_chunk_options) {
46
17.8k
  const uint8_t *to_base64 =
47
17.8k
      default_or_url ? tables::base64::to_base64_default_or_url_value
48
17.8k
                     : (base64_url ? tables::base64::to_base64_url_value
49
17.8k
                                   : tables::base64::to_base64_value);
50
17.8k
  auto ri = simdutf::scalar::base64::find_end(src, srclen, options);
51
17.8k
  size_t equallocation = ri.equallocation;
52
17.8k
  size_t equalsigns = ri.equalsigns;
53
17.8k
  srclen = ri.srclen;
54
17.8k
  size_t full_input_length = ri.full_input_length;
55
17.8k
  if (srclen == 0) {
56
14.0k
    if (!ignore_garbage && equalsigns > 0) {
57
21
      return {INVALID_BASE64_CHARACTER, equallocation, 0, true};
58
21
    }
59
14.0k
    return {SUCCESS, full_input_length, 0};
60
14.0k
  }
61
3.81k
  char *end_of_safe_64byte_zone =
62
3.81k
      dst == nullptr
63
3.81k
          ? nullptr
64
3.81k
          : ((srclen + 3) / 4 * 3 >= 63 ? dst + (srclen + 3) / 4 * 3 - 63
65
3.69k
                                        : dst);
66
67
3.81k
  const chartype *const srcinit = src;
68
3.81k
  const char *const dstinit = dst;
69
3.81k
  const chartype *const srcend = src + srclen;
70
71
3.81k
  constexpr size_t block_size = 6;
72
3.81k
  static_assert(block_size >= 2, "block_size must be at least two");
73
3.81k
  char buffer[block_size * 64];
74
3.81k
  char *bufferptr = buffer;
75
3.81k
  if (srclen >= 64) {
76
2.57k
    const chartype *const srcend64 = src + srclen - 64;
77
881k
    while (src <= srcend64) {
78
879k
      block64 b(src);
79
879k
      src += 64;
80
879k
      uint64_t error = 0;
81
879k
      const uint64_t badcharmask =
82
879k
          b.to_base64_mask<base64_url, ignore_garbage, default_or_url>(&error);
83
879k
      if (!ignore_garbage && error) {
84
694
        src -= 64;
85
694
        const size_t error_offset = trailing_zeroes(error);
86
694
        return {error_code::INVALID_BASE64_CHARACTER,
87
694
                size_t(src - srcinit + error_offset), size_t(dst - dstinit)};
88
694
      }
89
878k
      if (badcharmask != 0) {
90
55.0k
        bufferptr += b.compress_block(badcharmask, bufferptr);
91
823k
      } else if (bufferptr != buffer) {
92
126k
        b.copy_block(bufferptr);
93
126k
        bufferptr += 64;
94
697k
      } else {
95
697k
        if (dst >= end_of_safe_64byte_zone) {
96
413
          b.base64_decode_block_safe(dst);
97
696k
        } else {
98
696k
          b.base64_decode_block(dst);
99
696k
        }
100
697k
        dst += 48;
101
697k
      }
102
878k
      if (bufferptr >= (block_size - 1) * 64 + buffer) {
103
176k
        for (size_t i = 0; i < (block_size - 2); i++) {
104
141k
          base64_decode_block(dst, buffer + i * 64);
105
141k
          dst += 48;
106
141k
        }
107
35.2k
        if (dst >= end_of_safe_64byte_zone) {
108
0
          base64_decode_block_safe(dst, buffer + (block_size - 2) * 64);
109
35.2k
        } else {
110
35.2k
          base64_decode_block(dst, buffer + (block_size - 2) * 64);
111
35.2k
        }
112
35.2k
        dst += 48;
113
35.2k
        std::memcpy(buffer, buffer + (block_size - 1) * 64,
114
35.2k
                    64); // 64 might be too much
115
35.2k
        bufferptr -= (block_size - 1) * 64;
116
35.2k
      }
117
878k
    }
118
2.57k
  }
119
120
3.12k
  char *buffer_start = buffer;
121
  // Optimization note: if this is almost full, then it is worth our
122
  // time, otherwise, we should just decode directly.
123
3.12k
  int last_block = (int)((bufferptr - buffer_start) % 64);
124
3.12k
  if (last_block != 0 && srcend - src + last_block >= 64) {
125
126
1.74k
    while ((bufferptr - buffer_start) % 64 != 0 && src < srcend) {
127
1.58k
      uint8_t val = to_base64[uint8_t(*src)];
128
1.58k
      *bufferptr = char(val);
129
1.58k
      if (!ignore_garbage &&
130
1.58k
          (!scalar::base64::is_eight_byte(*src) || val > 64)) {
131
9
        return {error_code::INVALID_BASE64_CHARACTER, size_t(src - srcinit),
132
9
                size_t(dst - dstinit)};
133
9
      }
134
1.57k
      bufferptr += (val <= 63);
135
1.57k
      src++;
136
1.57k
    }
137
169
  }
138
139
3.96k
  for (; buffer_start + 64 <= bufferptr; buffer_start += 64) {
140
853
    if (dst >= end_of_safe_64byte_zone) {
141
73
      base64_decode_block_safe(dst, buffer_start);
142
780
    } else {
143
780
      base64_decode_block(dst, buffer_start);
144
780
    }
145
853
    dst += 48;
146
853
  }
147
3.11k
  if ((bufferptr - buffer_start) % 64 != 0) {
148
8.63k
    while (buffer_start + 4 < bufferptr) {
149
7.64k
      uint32_t triple = ((uint32_t(uint8_t(buffer_start[0])) << 3 * 6) +
150
7.64k
                         (uint32_t(uint8_t(buffer_start[1])) << 2 * 6) +
151
7.64k
                         (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) +
152
7.64k
                         (uint32_t(uint8_t(buffer_start[3])) << 0 * 6))
153
7.64k
                        << 8;
154
7.64k
#if !SIMDUTF_IS_BIG_ENDIAN
155
7.64k
      triple = scalar::u32_swap_bytes(triple);
156
7.64k
#endif
157
7.64k
      std::memcpy(dst, &triple, 3);
158
159
7.64k
      dst += 3;
160
7.64k
      buffer_start += 4;
161
7.64k
    }
162
991
    if (buffer_start + 4 <= bufferptr) {
163
264
      uint32_t triple = ((uint32_t(uint8_t(buffer_start[0])) << 3 * 6) +
164
264
                         (uint32_t(uint8_t(buffer_start[1])) << 2 * 6) +
165
264
                         (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) +
166
264
                         (uint32_t(uint8_t(buffer_start[3])) << 0 * 6))
167
264
                        << 8;
168
264
#if !SIMDUTF_IS_BIG_ENDIAN
169
264
      triple = scalar::u32_swap_bytes(triple);
170
264
#endif
171
264
      std::memcpy(dst, &triple, 3);
172
173
264
      dst += 3;
174
264
      buffer_start += 4;
175
264
    }
176
    // we may have 1, 2 or 3 bytes left and we need to decode them so let us
177
    // backtrack
178
991
    int leftover = int(bufferptr - buffer_start);
179
2.44k
    while (leftover > 0) {
180
1.45k
      if (!ignore_garbage) {
181
11.4k
        while (to_base64[uint8_t(*(src - 1))] == 64) {
182
10.0k
          src--;
183
10.0k
        }
184
1.45k
      } else {
185
0
        while (to_base64[uint8_t(*(src - 1))] >= 64) {
186
0
          src--;
187
0
        }
188
0
      }
189
1.45k
      src--;
190
1.45k
      leftover--;
191
1.45k
    }
192
991
  }
193
3.11k
  if (src < srcend + equalsigns) {
194
2.74k
    full_result r = scalar::base64::base64_tail_decode(
195
2.74k
        dst, src, srcend - src, equalsigns, options, last_chunk_options);
196
2.74k
    r = scalar::base64::patch_tail_result(
197
2.74k
        r, size_t(src - srcinit), size_t(dst - dstinit), equallocation,
198
2.74k
        full_input_length, last_chunk_options);
199
    // When is_partial(last_chunk_options) is true, we must either end with
200
    // the end of the stream (beyond whitespace) or right after a non-ignorable
201
    // character or at the very beginning of the stream.
202
    // See https://tc39.es/proposal-arraybuffer-base64/spec/#sec-frombase64
203
2.74k
    if (is_partial(last_chunk_options) && r.error == error_code::SUCCESS &&
204
506
        r.input_count < full_input_length) {
205
      // First check if we can extend the input to the end of the stream
206
1.35k
      while (r.input_count < full_input_length &&
207
1.34k
             base64_ignorable(*(srcinit + r.input_count), options)) {
208
912
        r.input_count++;
209
912
      }
210
      // If we are still not at the end of the stream, then we must backtrack
211
      // to the last non-ignorable character.
212
443
      if (r.input_count < full_input_length) {
213
6.58k
        while (r.input_count > 0 &&
214
6.47k
               base64_ignorable(*(srcinit + r.input_count - 1), options)) {
215
6.14k
          r.input_count--;
216
6.14k
        }
217
433
      }
218
443
    }
219
2.74k
    return r;
220
2.74k
  }
221
371
  if (!ignore_garbage && equalsigns > 0) {
222
0
    if ((size_t(dst - dstinit) % 3 == 0) ||
223
0
        ((size_t(dst - dstinit) % 3) + 1 + equalsigns != 4)) {
224
0
      return {INVALID_BASE64_CHARACTER, equallocation, size_t(dst - dstinit),
225
0
              true};
226
0
    }
227
0
  }
228
371
  return {SUCCESS, srclen, size_t(dst - dstinit)};
229
371
}
Unexecuted instantiation: simdutf.cpp:simdutf::full_result simdutf::westmere::(anonymous namespace)::base64::compress_decode_base64<false, true, true, char16_t>(char*, char16_t const*, unsigned long, simdutf::base64_options, simdutf::last_chunk_handling_options)
Unexecuted instantiation: simdutf.cpp:simdutf::full_result simdutf::westmere::(anonymous namespace)::base64::compress_decode_base64<false, false, true, char16_t>(char*, char16_t const*, unsigned long, simdutf::base64_options, simdutf::last_chunk_handling_options)
Unexecuted instantiation: simdutf.cpp:simdutf::full_result simdutf::westmere::(anonymous namespace)::base64::compress_decode_base64<true, true, false, char16_t>(char*, char16_t const*, unsigned long, simdutf::base64_options, simdutf::last_chunk_handling_options)
simdutf.cpp:simdutf::full_result simdutf::westmere::(anonymous namespace)::base64::compress_decode_base64<true, false, false, char16_t>(char*, char16_t const*, unsigned long, simdutf::base64_options, simdutf::last_chunk_handling_options)
Line
Count
Source
45
1.84k
                       last_chunk_handling_options last_chunk_options) {
46
1.84k
  const uint8_t *to_base64 =
47
1.84k
      default_or_url ? tables::base64::to_base64_default_or_url_value
48
1.84k
                     : (base64_url ? tables::base64::to_base64_url_value
49
1.84k
                                   : tables::base64::to_base64_value);
50
1.84k
  auto ri = simdutf::scalar::base64::find_end(src, srclen, options);
51
1.84k
  size_t equallocation = ri.equallocation;
52
1.84k
  size_t equalsigns = ri.equalsigns;
53
1.84k
  srclen = ri.srclen;
54
1.84k
  size_t full_input_length = ri.full_input_length;
55
1.84k
  if (srclen == 0) {
56
24
    if (!ignore_garbage && equalsigns > 0) {
57
10
      return {INVALID_BASE64_CHARACTER, equallocation, 0, true};
58
10
    }
59
14
    return {SUCCESS, full_input_length, 0};
60
24
  }
61
1.82k
  char *end_of_safe_64byte_zone =
62
1.82k
      dst == nullptr
63
1.82k
          ? nullptr
64
1.82k
          : ((srclen + 3) / 4 * 3 >= 63 ? dst + (srclen + 3) / 4 * 3 - 63
65
1.68k
                                        : dst);
66
67
1.82k
  const chartype *const srcinit = src;
68
1.82k
  const char *const dstinit = dst;
69
1.82k
  const chartype *const srcend = src + srclen;
70
71
1.82k
  constexpr size_t block_size = 6;
72
1.82k
  static_assert(block_size >= 2, "block_size must be at least two");
73
1.82k
  char buffer[block_size * 64];
74
1.82k
  char *bufferptr = buffer;
75
1.82k
  if (srclen >= 64) {
76
1.39k
    const chartype *const srcend64 = src + srclen - 64;
77
41.0k
    while (src <= srcend64) {
78
40.0k
      block64 b(src);
79
40.0k
      src += 64;
80
40.0k
      uint64_t error = 0;
81
40.0k
      const uint64_t badcharmask =
82
40.0k
          b.to_base64_mask<base64_url, ignore_garbage, default_or_url>(&error);
83
40.0k
      if (!ignore_garbage && error) {
84
464
        src -= 64;
85
464
        const size_t error_offset = trailing_zeroes(error);
86
464
        return {error_code::INVALID_BASE64_CHARACTER,
87
464
                size_t(src - srcinit + error_offset), size_t(dst - dstinit)};
88
464
      }
89
39.6k
      if (badcharmask != 0) {
90
31.8k
        bufferptr += b.compress_block(badcharmask, bufferptr);
91
31.8k
      } else if (bufferptr != buffer) {
92
6.68k
        b.copy_block(bufferptr);
93
6.68k
        bufferptr += 64;
94
6.68k
      } else {
95
1.05k
        if (dst >= end_of_safe_64byte_zone) {
96
13
          b.base64_decode_block_safe(dst);
97
1.04k
        } else {
98
1.04k
          b.base64_decode_block(dst);
99
1.04k
        }
100
1.05k
        dst += 48;
101
1.05k
      }
102
39.6k
      if (bufferptr >= (block_size - 1) * 64 + buffer) {
103
26.9k
        for (size_t i = 0; i < (block_size - 2); i++) {
104
21.5k
          base64_decode_block(dst, buffer + i * 64);
105
21.5k
          dst += 48;
106
21.5k
        }
107
5.38k
        if (dst >= end_of_safe_64byte_zone) {
108
0
          base64_decode_block_safe(dst, buffer + (block_size - 2) * 64);
109
5.38k
        } else {
110
5.38k
          base64_decode_block(dst, buffer + (block_size - 2) * 64);
111
5.38k
        }
112
5.38k
        dst += 48;
113
5.38k
        std::memcpy(buffer, buffer + (block_size - 1) * 64,
114
5.38k
                    64); // 64 might be too much
115
5.38k
        bufferptr -= (block_size - 1) * 64;
116
5.38k
      }
117
39.6k
    }
118
1.39k
  }
119
120
1.35k
  char *buffer_start = buffer;
121
  // Optimization note: if this is almost full, then it is worth our
122
  // time, otherwise, we should just decode directly.
123
1.35k
  int last_block = (int)((bufferptr - buffer_start) % 64);
124
1.35k
  if (last_block != 0 && srcend - src + last_block >= 64) {
125
126
1.41k
    while ((bufferptr - buffer_start) % 64 != 0 && src < srcend) {
127
1.32k
      uint8_t val = to_base64[uint8_t(*src)];
128
1.32k
      *bufferptr = char(val);
129
1.32k
      if (!ignore_garbage &&
130
1.32k
          (!scalar::base64::is_eight_byte(*src) || val > 64)) {
131
66
        return {error_code::INVALID_BASE64_CHARACTER, size_t(src - srcinit),
132
66
                size_t(dst - dstinit)};
133
66
      }
134
1.25k
      bufferptr += (val <= 63);
135
1.25k
      src++;
136
1.25k
    }
137
156
  }
138
139
1.79k
  for (; buffer_start + 64 <= bufferptr; buffer_start += 64) {
140
504
    if (dst >= end_of_safe_64byte_zone) {
141
38
      base64_decode_block_safe(dst, buffer_start);
142
466
    } else {
143
466
      base64_decode_block(dst, buffer_start);
144
466
    }
145
504
    dst += 48;
146
504
  }
147
1.29k
  if ((bufferptr - buffer_start) % 64 != 0) {
148
6.06k
    while (buffer_start + 4 < bufferptr) {
149
5.32k
      uint32_t triple = ((uint32_t(uint8_t(buffer_start[0])) << 3 * 6) +
150
5.32k
                         (uint32_t(uint8_t(buffer_start[1])) << 2 * 6) +
151
5.32k
                         (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) +
152
5.32k
                         (uint32_t(uint8_t(buffer_start[3])) << 0 * 6))
153
5.32k
                        << 8;
154
5.32k
#if !SIMDUTF_IS_BIG_ENDIAN
155
5.32k
      triple = scalar::u32_swap_bytes(triple);
156
5.32k
#endif
157
5.32k
      std::memcpy(dst, &triple, 3);
158
159
5.32k
      dst += 3;
160
5.32k
      buffer_start += 4;
161
5.32k
    }
162
739
    if (buffer_start + 4 <= bufferptr) {
163
183
      uint32_t triple = ((uint32_t(uint8_t(buffer_start[0])) << 3 * 6) +
164
183
                         (uint32_t(uint8_t(buffer_start[1])) << 2 * 6) +
165
183
                         (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) +
166
183
                         (uint32_t(uint8_t(buffer_start[3])) << 0 * 6))
167
183
                        << 8;
168
183
#if !SIMDUTF_IS_BIG_ENDIAN
169
183
      triple = scalar::u32_swap_bytes(triple);
170
183
#endif
171
183
      std::memcpy(dst, &triple, 3);
172
173
183
      dst += 3;
174
183
      buffer_start += 4;
175
183
    }
176
    // we may have 1, 2 or 3 bytes left and we need to decode them so let us
177
    // backtrack
178
739
    int leftover = int(bufferptr - buffer_start);
179
1.85k
    while (leftover > 0) {
180
1.11k
      if (!ignore_garbage) {
181
11.5k
        while (to_base64[uint8_t(*(src - 1))] == 64) {
182
10.4k
          src--;
183
10.4k
        }
184
1.11k
      } else {
185
0
        while (to_base64[uint8_t(*(src - 1))] >= 64) {
186
0
          src--;
187
0
        }
188
0
      }
189
1.11k
      src--;
190
1.11k
      leftover--;
191
1.11k
    }
192
739
  }
193
1.29k
  if (src < srcend + equalsigns) {
194
1.09k
    full_result r = scalar::base64::base64_tail_decode(
195
1.09k
        dst, src, srcend - src, equalsigns, options, last_chunk_options);
196
1.09k
    r = scalar::base64::patch_tail_result(
197
1.09k
        r, size_t(src - srcinit), size_t(dst - dstinit), equallocation,
198
1.09k
        full_input_length, last_chunk_options);
199
    // When is_partial(last_chunk_options) is true, we must either end with
200
    // the end of the stream (beyond whitespace) or right after a non-ignorable
201
    // character or at the very beginning of the stream.
202
    // See https://tc39.es/proposal-arraybuffer-base64/spec/#sec-frombase64
203
1.09k
    if (is_partial(last_chunk_options) && r.error == error_code::SUCCESS &&
204
512
        r.input_count < full_input_length) {
205
      // First check if we can extend the input to the end of the stream
206
30.0k
      while (r.input_count < full_input_length &&
207
30.0k
             base64_ignorable(*(srcinit + r.input_count), options)) {
208
29.5k
        r.input_count++;
209
29.5k
      }
210
      // If we are still not at the end of the stream, then we must backtrack
211
      // to the last non-ignorable character.
212
472
      if (r.input_count < full_input_length) {
213
39.5k
        while (r.input_count > 0 &&
214
39.4k
               base64_ignorable(*(srcinit + r.input_count - 1), options)) {
215
39.1k
          r.input_count--;
216
39.1k
        }
217
462
      }
218
472
    }
219
1.09k
    return r;
220
1.09k
  }
221
195
  if (!ignore_garbage && equalsigns > 0) {
222
0
    if ((size_t(dst - dstinit) % 3 == 0) ||
223
0
        ((size_t(dst - dstinit) % 3) + 1 + equalsigns != 4)) {
224
0
      return {INVALID_BASE64_CHARACTER, equallocation, size_t(dst - dstinit),
225
0
              true};
226
0
    }
227
0
  }
228
195
  return {SUCCESS, srclen, size_t(dst - dstinit)};
229
195
}
Unexecuted instantiation: simdutf.cpp:simdutf::full_result simdutf::westmere::(anonymous namespace)::base64::compress_decode_base64<false, true, false, char16_t>(char*, char16_t const*, unsigned long, simdutf::base64_options, simdutf::last_chunk_handling_options)
simdutf.cpp:simdutf::full_result simdutf::westmere::(anonymous namespace)::base64::compress_decode_base64<false, false, false, char16_t>(char*, char16_t const*, unsigned long, simdutf::base64_options, simdutf::last_chunk_handling_options)
Line
Count
Source
45
1.93k
                       last_chunk_handling_options last_chunk_options) {
46
1.93k
  const uint8_t *to_base64 =
47
1.93k
      default_or_url ? tables::base64::to_base64_default_or_url_value
48
1.93k
                     : (base64_url ? tables::base64::to_base64_url_value
49
1.93k
                                   : tables::base64::to_base64_value);
50
1.93k
  auto ri = simdutf::scalar::base64::find_end(src, srclen, options);
51
1.93k
  size_t equallocation = ri.equallocation;
52
1.93k
  size_t equalsigns = ri.equalsigns;
53
1.93k
  srclen = ri.srclen;
54
1.93k
  size_t full_input_length = ri.full_input_length;
55
1.93k
  if (srclen == 0) {
56
37
    if (!ignore_garbage && equalsigns > 0) {
57
17
      return {INVALID_BASE64_CHARACTER, equallocation, 0, true};
58
17
    }
59
20
    return {SUCCESS, full_input_length, 0};
60
37
  }
61
1.90k
  char *end_of_safe_64byte_zone =
62
1.90k
      dst == nullptr
63
1.90k
          ? nullptr
64
1.90k
          : ((srclen + 3) / 4 * 3 >= 63 ? dst + (srclen + 3) / 4 * 3 - 63
65
1.76k
                                        : dst);
66
67
1.90k
  const chartype *const srcinit = src;
68
1.90k
  const char *const dstinit = dst;
69
1.90k
  const chartype *const srcend = src + srclen;
70
71
1.90k
  constexpr size_t block_size = 6;
72
1.90k
  static_assert(block_size >= 2, "block_size must be at least two");
73
1.90k
  char buffer[block_size * 64];
74
1.90k
  char *bufferptr = buffer;
75
1.90k
  if (srclen >= 64) {
76
1.43k
    const chartype *const srcend64 = src + srclen - 64;
77
94.0k
    while (src <= srcend64) {
78
93.1k
      block64 b(src);
79
93.1k
      src += 64;
80
93.1k
      uint64_t error = 0;
81
93.1k
      const uint64_t badcharmask =
82
93.1k
          b.to_base64_mask<base64_url, ignore_garbage, default_or_url>(&error);
83
93.1k
      if (!ignore_garbage && error) {
84
504
        src -= 64;
85
504
        const size_t error_offset = trailing_zeroes(error);
86
504
        return {error_code::INVALID_BASE64_CHARACTER,
87
504
                size_t(src - srcinit + error_offset), size_t(dst - dstinit)};
88
504
      }
89
92.6k
      if (badcharmask != 0) {
90
75.1k
        bufferptr += b.compress_block(badcharmask, bufferptr);
91
75.1k
      } else if (bufferptr != buffer) {
92
16.6k
        b.copy_block(bufferptr);
93
16.6k
        bufferptr += 64;
94
16.6k
      } else {
95
808
        if (dst >= end_of_safe_64byte_zone) {
96
7
          b.base64_decode_block_safe(dst);
97
801
        } else {
98
801
          b.base64_decode_block(dst);
99
801
        }
100
808
        dst += 48;
101
808
      }
102
92.6k
      if (bufferptr >= (block_size - 1) * 64 + buffer) {
103
73.3k
        for (size_t i = 0; i < (block_size - 2); i++) {
104
58.6k
          base64_decode_block(dst, buffer + i * 64);
105
58.6k
          dst += 48;
106
58.6k
        }
107
14.6k
        if (dst >= end_of_safe_64byte_zone) {
108
0
          base64_decode_block_safe(dst, buffer + (block_size - 2) * 64);
109
14.6k
        } else {
110
14.6k
          base64_decode_block(dst, buffer + (block_size - 2) * 64);
111
14.6k
        }
112
14.6k
        dst += 48;
113
14.6k
        std::memcpy(buffer, buffer + (block_size - 1) * 64,
114
14.6k
                    64); // 64 might be too much
115
14.6k
        bufferptr -= (block_size - 1) * 64;
116
14.6k
      }
117
92.6k
    }
118
1.43k
  }
119
120
1.39k
  char *buffer_start = buffer;
121
  // Optimization note: if this is almost full, then it is worth our
122
  // time, otherwise, we should just decode directly.
123
1.39k
  int last_block = (int)((bufferptr - buffer_start) % 64);
124
1.39k
  if (last_block != 0 && srcend - src + last_block >= 64) {
125
126
1.27k
    while ((bufferptr - buffer_start) % 64 != 0 && src < srcend) {
127
1.16k
      uint8_t val = to_base64[uint8_t(*src)];
128
1.16k
      *bufferptr = char(val);
129
1.16k
      if (!ignore_garbage &&
130
1.16k
          (!scalar::base64::is_eight_byte(*src) || val > 64)) {
131
58
        return {error_code::INVALID_BASE64_CHARACTER, size_t(src - srcinit),
132
58
                size_t(dst - dstinit)};
133
58
      }
134
1.10k
      bufferptr += (val <= 63);
135
1.10k
      src++;
136
1.10k
    }
137
167
  }
138
139
1.96k
  for (; buffer_start + 64 <= bufferptr; buffer_start += 64) {
140
623
    if (dst >= end_of_safe_64byte_zone) {
141
48
      base64_decode_block_safe(dst, buffer_start);
142
575
    } else {
143
575
      base64_decode_block(dst, buffer_start);
144
575
    }
145
623
    dst += 48;
146
623
  }
147
1.34k
  if ((bufferptr - buffer_start) % 64 != 0) {
148
6.42k
    while (buffer_start + 4 < bufferptr) {
149
5.67k
      uint32_t triple = ((uint32_t(uint8_t(buffer_start[0])) << 3 * 6) +
150
5.67k
                         (uint32_t(uint8_t(buffer_start[1])) << 2 * 6) +
151
5.67k
                         (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) +
152
5.67k
                         (uint32_t(uint8_t(buffer_start[3])) << 0 * 6))
153
5.67k
                        << 8;
154
5.67k
#if !SIMDUTF_IS_BIG_ENDIAN
155
5.67k
      triple = scalar::u32_swap_bytes(triple);
156
5.67k
#endif
157
5.67k
      std::memcpy(dst, &triple, 3);
158
159
5.67k
      dst += 3;
160
5.67k
      buffer_start += 4;
161
5.67k
    }
162
745
    if (buffer_start + 4 <= bufferptr) {
163
182
      uint32_t triple = ((uint32_t(uint8_t(buffer_start[0])) << 3 * 6) +
164
182
                         (uint32_t(uint8_t(buffer_start[1])) << 2 * 6) +
165
182
                         (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) +
166
182
                         (uint32_t(uint8_t(buffer_start[3])) << 0 * 6))
167
182
                        << 8;
168
182
#if !SIMDUTF_IS_BIG_ENDIAN
169
182
      triple = scalar::u32_swap_bytes(triple);
170
182
#endif
171
182
      std::memcpy(dst, &triple, 3);
172
173
182
      dst += 3;
174
182
      buffer_start += 4;
175
182
    }
176
    // we may have 1, 2 or 3 bytes left and we need to decode them so let us
177
    // backtrack
178
745
    int leftover = int(bufferptr - buffer_start);
179
1.86k
    while (leftover > 0) {
180
1.12k
      if (!ignore_garbage) {
181
19.9k
        while (to_base64[uint8_t(*(src - 1))] == 64) {
182
18.8k
          src--;
183
18.8k
        }
184
1.12k
      } else {
185
0
        while (to_base64[uint8_t(*(src - 1))] >= 64) {
186
0
          src--;
187
0
        }
188
0
      }
189
1.12k
      src--;
190
1.12k
      leftover--;
191
1.12k
    }
192
745
  }
193
1.34k
  if (src < srcend + equalsigns) {
194
1.14k
    full_result r = scalar::base64::base64_tail_decode(
195
1.14k
        dst, src, srcend - src, equalsigns, options, last_chunk_options);
196
1.14k
    r = scalar::base64::patch_tail_result(
197
1.14k
        r, size_t(src - srcinit), size_t(dst - dstinit), equallocation,
198
1.14k
        full_input_length, last_chunk_options);
199
    // When is_partial(last_chunk_options) is true, we must either end with
200
    // the end of the stream (beyond whitespace) or right after a non-ignorable
201
    // character or at the very beginning of the stream.
202
    // See https://tc39.es/proposal-arraybuffer-base64/spec/#sec-frombase64
203
1.14k
    if (is_partial(last_chunk_options) && r.error == error_code::SUCCESS &&
204
509
        r.input_count < full_input_length) {
205
      // First check if we can extend the input to the end of the stream
206
10.3k
      while (r.input_count < full_input_length &&
207
10.2k
             base64_ignorable(*(srcinit + r.input_count), options)) {
208
9.85k
        r.input_count++;
209
9.85k
      }
210
      // If we are still not at the end of the stream, then we must backtrack
211
      // to the last non-ignorable character.
212
454
      if (r.input_count < full_input_length) {
213
16.2k
        while (r.input_count > 0 &&
214
16.1k
               base64_ignorable(*(srcinit + r.input_count - 1), options)) {
215
15.8k
          r.input_count--;
216
15.8k
        }
217
442
      }
218
454
    }
219
1.14k
    return r;
220
1.14k
  }
221
198
  if (!ignore_garbage && equalsigns > 0) {
222
0
    if ((size_t(dst - dstinit) % 3 == 0) ||
223
0
        ((size_t(dst - dstinit) % 3) + 1 + equalsigns != 4)) {
224
0
      return {INVALID_BASE64_CHARACTER, equallocation, size_t(dst - dstinit),
225
0
              true};
226
0
    }
227
0
  }
228
198
  return {SUCCESS, srclen, size_t(dst - dstinit)};
229
198
}
230
231
} // namespace base64
232
} // unnamed namespace
233
} // namespace SIMDUTF_IMPLEMENTATION
234
} // namespace simdutf