Coverage Report

Created: 2026-07-16 06:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/simdutf/include/simdutf/base64_implementation.h
Line
Count
Source
1
#ifndef SIMDUTF_BASE64_IMPLEMENTATION_H
2
#define SIMDUTF_BASE64_IMPLEMENTATION_H
3
4
// this is not part of the public api
5
6
#include <algorithm> // for std::min
7
8
namespace simdutf {
9
10
template <typename chartype>
11
simdutf_warn_unused simdutf_constexpr23 result slow_base64_to_binary_safe_impl(
12
    const chartype *input, size_t length, char *output, size_t &outlen,
13
    base64_options options,
14
0
    last_chunk_handling_options last_chunk_options) noexcept {
15
0
  const bool ignore_garbage = (options & base64_default_accept_garbage) != 0;
16
0
  auto ri = simdutf::scalar::base64::find_end(input, length, options);
17
0
  size_t equallocation = ri.equallocation;
18
0
  size_t equalsigns = ri.equalsigns;
19
0
  length = ri.srclen;
20
0
  size_t full_input_length = ri.full_input_length;
21
0
  (void)full_input_length;
22
0
  if (length == 0) {
23
0
    outlen = 0;
24
0
    if (!ignore_garbage && equalsigns > 0) {
25
0
      return {INVALID_BASE64_CHARACTER, equallocation};
26
0
    }
27
0
    return {SUCCESS, 0};
28
0
  }
29
30
  // The parameters of base64_tail_decode_safe are:
31
  // - dst: the output buffer
32
  // - outlen: the size of the output buffer
33
  // - srcr: the input buffer
34
  // - length: the size of the input buffer
35
  // - padded_characters: the number of padding characters
36
  // - options: the options for the base64 decoder
37
  // - last_chunk_options: the options for the last chunk
38
  // The function will return the number of bytes written to the output buffer
39
  // and the number of bytes read from the input buffer.
40
  // The function will also return an error code if the input buffer is not
41
  // valid base64.
42
0
  full_result r = scalar::base64::base64_tail_decode_safe(
43
0
      output, outlen, input, length, equalsigns, options, last_chunk_options);
44
0
  r = scalar::base64::patch_tail_result(r, 0, 0, equallocation,
45
0
                                        full_input_length, last_chunk_options);
46
0
  outlen = r.output_count;
47
0
  if (!is_partial(last_chunk_options) && r.error == error_code::SUCCESS &&
48
0
      equalsigns > 0) {
49
    // additional checks
50
0
    if ((outlen % 3 == 0) || ((outlen % 3) + 1 + equalsigns != 4)) {
51
0
      r.error = error_code::INVALID_BASE64_CHARACTER;
52
0
    }
53
0
  }
54
0
  return {r.error, r.input_count}; // we cannot return r itself because it gets
55
                                   // converted to error/output_count
56
0
}
Unexecuted instantiation: simdutf::result simdutf::slow_base64_to_binary_safe_impl<char>(char const*, unsigned long, char*, unsigned long&, simdutf::base64_options, simdutf::last_chunk_handling_options)
Unexecuted instantiation: simdutf::result simdutf::slow_base64_to_binary_safe_impl<char16_t>(char16_t const*, unsigned long, char*, unsigned long&, simdutf::base64_options, simdutf::last_chunk_handling_options)
57
58
template <typename chartype>
59
simdutf_warn_unused simdutf_constexpr23 result base64_to_binary_safe_impl(
60
    const chartype *input, size_t length, char *output, size_t &outlen,
61
    base64_options options,
62
    last_chunk_handling_options last_chunk_handling_options,
63
0
    bool decode_up_to_bad_char) noexcept {
64
0
  static_assert(std::is_same<chartype, char>::value ||
65
0
                    std::is_same<chartype, char16_t>::value,
66
0
                "Only char and char16_t are supported.");
67
0
  size_t remaining_input_length = length;
68
0
  size_t remaining_output_length = outlen;
69
0
  size_t input_position = 0;
70
0
  size_t output_position = 0;
71
72
  // We also do a first pass using the fast path to decode as much as possible
73
0
  size_t safe_input = (std::min)(
74
0
      remaining_input_length,
75
0
      base64_length_from_binary(remaining_output_length / 3 * 3, options));
76
0
  bool done_with_partial = (safe_input == remaining_input_length);
77
0
  simdutf::full_result r;
78
79
#if SIMDUTF_CPLUSPLUS23
80
  if consteval {
81
    r = scalar::base64::base64_to_binary_details_impl(
82
        input + input_position, safe_input, output + output_position, options,
83
        done_with_partial
84
            ? last_chunk_handling_options
85
            : simdutf::last_chunk_handling_options::only_full_chunks);
86
  } else
87
#endif
88
0
  {
89
0
    r = get_active_implementation()->base64_to_binary_details(
90
0
        input + input_position, safe_input, output + output_position, options,
91
0
        done_with_partial
92
0
            ? last_chunk_handling_options
93
0
            : simdutf::last_chunk_handling_options::only_full_chunks);
94
0
  }
95
0
  simdutf_log_assert(r.input_count <= safe_input,
96
0
                     "You should not read more than safe_input");
97
0
  simdutf_log_assert(r.output_count <= remaining_output_length,
98
0
                     "You should not write more than remaining_output_length");
99
  // Technically redundant, but we want to be explicit about it.
100
0
  input_position += r.input_count;
101
0
  output_position += r.output_count;
102
0
  remaining_input_length -= r.input_count;
103
0
  remaining_output_length -= r.output_count;
104
0
  if (r.error != simdutf::error_code::SUCCESS) {
105
    // There is an error. We return.
106
0
    if (decode_up_to_bad_char &&
107
0
        r.error == error_code::INVALID_BASE64_CHARACTER) {
108
0
      return slow_base64_to_binary_safe_impl(
109
0
          input, length, output, outlen, options, last_chunk_handling_options);
110
0
    }
111
0
    outlen = output_position;
112
0
    return {r.error, input_position};
113
0
  }
114
115
0
  if (done_with_partial) {
116
    // We are done. We have decoded everything.
117
0
    outlen = output_position;
118
0
    return {simdutf::error_code::SUCCESS, input_position};
119
0
  }
120
  // We have decoded some data, but we still have some data to decode.
121
  // We need to decode the rest of the input buffer.
122
0
  r = simdutf::scalar::base64::base64_to_binary_details_safe_impl(
123
0
      input + input_position, remaining_input_length, output + output_position,
124
0
      remaining_output_length, options, last_chunk_handling_options);
125
0
  input_position += r.input_count;
126
0
  output_position += r.output_count;
127
0
  remaining_input_length -= r.input_count;
128
0
  remaining_output_length -= r.output_count;
129
130
0
  if (r.error != simdutf::error_code::SUCCESS) {
131
    // There is an error. We return.
132
0
    if (decode_up_to_bad_char &&
133
0
        r.error == error_code::INVALID_BASE64_CHARACTER) {
134
0
      return slow_base64_to_binary_safe_impl(
135
0
          input, length, output, outlen, options, last_chunk_handling_options);
136
0
    }
137
0
    outlen = output_position;
138
0
    return {r.error, input_position};
139
0
  }
140
0
  if (input_position < length) {
141
    // We cannot process the entire input in one go, so we need to
142
    // process it in two steps: first the fast path, then the slow path.
143
    // In some cases, the processing might 'eat up' trailing ignorable
144
    // characters in the fast path, but that can be a problem.
145
    // suppose we have just white space followed by a single base64 character.
146
    // If we first process the white space with the fast path, it will
147
    // eat all of it. But, by the JavaScript standard, we should consume
148
    // no character. See
149
    // https://tc39.es/proposal-arraybuffer-base64/spec/#sec-frombase64
150
0
    while (input_position > 0 &&
151
0
           base64_ignorable(input[input_position - 1], options)) {
152
0
      input_position--;
153
0
    }
154
0
  }
155
0
  outlen = output_position;
156
0
  return {simdutf::error_code::SUCCESS, input_position};
157
0
}
Unexecuted instantiation: simdutf::result simdutf::base64_to_binary_safe_impl<char>(char const*, unsigned long, char*, unsigned long&, simdutf::base64_options, simdutf::last_chunk_handling_options, bool)
Unexecuted instantiation: simdutf::result simdutf::base64_to_binary_safe_impl<char16_t>(char16_t const*, unsigned long, char*, unsigned long&, simdutf::base64_options, simdutf::last_chunk_handling_options, bool)
158
159
} // namespace simdutf
160
#endif // SIMDUTF_BASE64_IMPLEMENTATION_H