Coverage Report

Created: 2026-08-13 06:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/simdutf/fuzz/base64.cpp
Line
Count
Source
1
#include <cstddef>
2
#include <cstdint>
3
#include <array>
4
#include <cstdlib>
5
6
#include "helpers/common.h"
7
#include "simdutf.h"
8
9
constexpr std::array options = {
10
    simdutf::base64_default,
11
    simdutf::base64_url,
12
    simdutf::base64_default_no_padding,
13
    simdutf::base64_url_with_padding,
14
};
15
16
constexpr std::array last_chunk = {
17
    simdutf::last_chunk_handling_options::loose,
18
    simdutf::last_chunk_handling_options::strict,
19
    simdutf::last_chunk_handling_options::stop_before_partial};
20
21
struct decoderesult {
22
  std::size_t maxbinarylength{};
23
  simdutf::result convertresult{};
24
  auto operator<=>(const decoderesult&) const = default;
25
};
26
27
template <typename FromChar>
28
void decode(std::span<const FromChar> base64_, const auto selected_option,
29
2.25k
            const auto last_chunk_option) {
30
2.25k
  std::vector<FromChar> base64(begin(base64_), end(base64_));
31
2.25k
  const auto implementations = get_supported_implementations();
32
2.25k
  std::vector<decoderesult> results;
33
2.25k
  results.reserve(implementations.size());
34
6.76k
  for (auto impl : implementations) {
35
6.76k
    auto& r = results.emplace_back();
36
6.76k
    r.maxbinarylength =
37
6.76k
        impl->maximal_binary_length_from_base64(base64.data(), base64.size());
38
    // binary_length_from_base64 is not compared across implementations: for
39
    // invalid input the implementations may legitimately return different
40
    // estimates, so keep it as a local rather than a field of decoderesult.
41
6.76k
    const std::size_t binarylength =
42
6.76k
        impl->binary_length_from_base64(base64.data(), base64.size());
43
    // binary_length_from_base64 must never exceed the maximal upper bound.
44
6.76k
    if (binarylength > r.maxbinarylength) {
45
0
      std::cerr << "binary_length_from_base64 (" << binarylength
46
0
                << ") > maximal_binary_length_from_base64 ("
47
0
                << r.maxbinarylength << ") for impl " << impl->name() << "\n";
48
0
      std::abort();
49
0
    }
50
6.76k
    std::vector<char> output(r.maxbinarylength);
51
6.76k
    r.convertresult =
52
6.76k
        impl->base64_to_binary(base64.data(), base64.size(), output.data(),
53
6.76k
                               selected_option, last_chunk_option);
54
    // binary_length_from_base64 takes no last_chunk_handling option, so its
55
    // estimate only equals the decoded size under loose handling. Under strict
56
    // and stop_before_partial a partial final chunk is dropped, so a SUCCESS
57
    // can legitimately write fewer bytes than the estimate (e.g. "QQQ" with
58
    // stop_before_partial reports 2 but decodes 0). Only assert equality for
59
    // loose to avoid false positives.
60
6.76k
    if (last_chunk_option == simdutf::last_chunk_handling_options::loose &&
61
3.03k
        r.convertresult.error == simdutf::error_code::SUCCESS &&
62
1.21k
        binarylength != r.convertresult.count) {
63
0
      std::cerr << "binary_length_from_base64 (" << binarylength
64
0
                << ") != decoded count (" << r.convertresult.count
65
0
                << ") for impl " << impl->name() << "\n";
66
0
      std::abort();
67
0
    }
68
6.76k
  }
69
4.51k
  auto neq = [](const auto& a, const auto& b) { return a != b; };
auto decode<char, simdutf::base64_options, simdutf::last_chunk_handling_options>(std::__1::span<char const, 18446744073709551615ul>, simdutf::base64_options, simdutf::last_chunk_handling_options)::{lambda(auto:1 const&, auto:2 const&)#1}::operator()<decoderesult, {lambda(auto:1 const&, auto:2 const&)#1}::operator()>(decoderesult const&, {lambda(auto:1 const&, auto:2 const&)#1}::operator() const&) const
Line
Count
Source
69
2.03k
  auto neq = [](const auto& a, const auto& b) { return a != b; };
auto decode<char16_t, simdutf::base64_options, simdutf::last_chunk_handling_options>(std::__1::span<char16_t const, 18446744073709551615ul>, simdutf::base64_options, simdutf::last_chunk_handling_options)::{lambda(auto:1 const&, auto:2 const&)#1}::operator()<decoderesult, {lambda(auto:1 const&, auto:2 const&)#1}::operator()>(decoderesult const&, {lambda(auto:1 const&, auto:2 const&)#1}::operator() const&) const
Line
Count
Source
69
2.47k
  auto neq = [](const auto& a, const auto& b) { return a != b; };
70
2.25k
  if (std::ranges::adjacent_find(results, neq) != results.end()) {
71
0
    std::cerr << "output differs between implementations for decode\n";
72
0
    const auto implementations = get_supported_implementations();
73
0
    std::size_t i = 0;
74
0
    for (const auto& r : results) {
75
0
      std::cerr << "impl " << implementations[i]->name()
76
0
                << " got maxbinarylength=" << r.maxbinarylength
77
0
                << " convertresult=" << r.convertresult << "\n";
78
0
      ++i;
79
0
    }
80
0
    std::cerr << "option: " << selected_option << '\n';
81
0
    std::cerr << "data: "
82
0
              << (std::is_same_v<FromChar, char> ? "char" : "char16_t") << "{";
83
0
    for (int v : base64) {
84
0
      std::cerr << v << ", ";
85
0
    }
86
0
    std::cerr << "}\n";
87
0
    std::abort();
88
0
  }
89
2.25k
}
void decode<char, simdutf::base64_options, simdutf::last_chunk_handling_options>(std::__1::span<char const, 18446744073709551615ul>, simdutf::base64_options, simdutf::last_chunk_handling_options)
Line
Count
Source
29
1.01k
            const auto last_chunk_option) {
30
1.01k
  std::vector<FromChar> base64(begin(base64_), end(base64_));
31
1.01k
  const auto implementations = get_supported_implementations();
32
1.01k
  std::vector<decoderesult> results;
33
1.01k
  results.reserve(implementations.size());
34
3.05k
  for (auto impl : implementations) {
35
3.05k
    auto& r = results.emplace_back();
36
3.05k
    r.maxbinarylength =
37
3.05k
        impl->maximal_binary_length_from_base64(base64.data(), base64.size());
38
    // binary_length_from_base64 is not compared across implementations: for
39
    // invalid input the implementations may legitimately return different
40
    // estimates, so keep it as a local rather than a field of decoderesult.
41
3.05k
    const std::size_t binarylength =
42
3.05k
        impl->binary_length_from_base64(base64.data(), base64.size());
43
    // binary_length_from_base64 must never exceed the maximal upper bound.
44
3.05k
    if (binarylength > r.maxbinarylength) {
45
0
      std::cerr << "binary_length_from_base64 (" << binarylength
46
0
                << ") > maximal_binary_length_from_base64 ("
47
0
                << r.maxbinarylength << ") for impl " << impl->name() << "\n";
48
0
      std::abort();
49
0
    }
50
3.05k
    std::vector<char> output(r.maxbinarylength);
51
3.05k
    r.convertresult =
52
3.05k
        impl->base64_to_binary(base64.data(), base64.size(), output.data(),
53
3.05k
                               selected_option, last_chunk_option);
54
    // binary_length_from_base64 takes no last_chunk_handling option, so its
55
    // estimate only equals the decoded size under loose handling. Under strict
56
    // and stop_before_partial a partial final chunk is dropped, so a SUCCESS
57
    // can legitimately write fewer bytes than the estimate (e.g. "QQQ" with
58
    // stop_before_partial reports 2 but decodes 0). Only assert equality for
59
    // loose to avoid false positives.
60
3.05k
    if (last_chunk_option == simdutf::last_chunk_handling_options::loose &&
61
1.40k
        r.convertresult.error == simdutf::error_code::SUCCESS &&
62
654
        binarylength != r.convertresult.count) {
63
0
      std::cerr << "binary_length_from_base64 (" << binarylength
64
0
                << ") != decoded count (" << r.convertresult.count
65
0
                << ") for impl " << impl->name() << "\n";
66
0
      std::abort();
67
0
    }
68
3.05k
  }
69
1.01k
  auto neq = [](const auto& a, const auto& b) { return a != b; };
70
1.01k
  if (std::ranges::adjacent_find(results, neq) != results.end()) {
71
0
    std::cerr << "output differs between implementations for decode\n";
72
0
    const auto implementations = get_supported_implementations();
73
0
    std::size_t i = 0;
74
0
    for (const auto& r : results) {
75
0
      std::cerr << "impl " << implementations[i]->name()
76
0
                << " got maxbinarylength=" << r.maxbinarylength
77
0
                << " convertresult=" << r.convertresult << "\n";
78
0
      ++i;
79
0
    }
80
0
    std::cerr << "option: " << selected_option << '\n';
81
0
    std::cerr << "data: "
82
0
              << (std::is_same_v<FromChar, char> ? "char" : "char16_t") << "{";
83
0
    for (int v : base64) {
84
0
      std::cerr << v << ", ";
85
0
    }
86
0
    std::cerr << "}\n";
87
0
    std::abort();
88
0
  }
89
1.01k
}
void decode<char16_t, simdutf::base64_options, simdutf::last_chunk_handling_options>(std::__1::span<char16_t const, 18446744073709551615ul>, simdutf::base64_options, simdutf::last_chunk_handling_options)
Line
Count
Source
29
1.23k
            const auto last_chunk_option) {
30
1.23k
  std::vector<FromChar> base64(begin(base64_), end(base64_));
31
1.23k
  const auto implementations = get_supported_implementations();
32
1.23k
  std::vector<decoderesult> results;
33
1.23k
  results.reserve(implementations.size());
34
3.71k
  for (auto impl : implementations) {
35
3.71k
    auto& r = results.emplace_back();
36
3.71k
    r.maxbinarylength =
37
3.71k
        impl->maximal_binary_length_from_base64(base64.data(), base64.size());
38
    // binary_length_from_base64 is not compared across implementations: for
39
    // invalid input the implementations may legitimately return different
40
    // estimates, so keep it as a local rather than a field of decoderesult.
41
3.71k
    const std::size_t binarylength =
42
3.71k
        impl->binary_length_from_base64(base64.data(), base64.size());
43
    // binary_length_from_base64 must never exceed the maximal upper bound.
44
3.71k
    if (binarylength > r.maxbinarylength) {
45
0
      std::cerr << "binary_length_from_base64 (" << binarylength
46
0
                << ") > maximal_binary_length_from_base64 ("
47
0
                << r.maxbinarylength << ") for impl " << impl->name() << "\n";
48
0
      std::abort();
49
0
    }
50
3.71k
    std::vector<char> output(r.maxbinarylength);
51
3.71k
    r.convertresult =
52
3.71k
        impl->base64_to_binary(base64.data(), base64.size(), output.data(),
53
3.71k
                               selected_option, last_chunk_option);
54
    // binary_length_from_base64 takes no last_chunk_handling option, so its
55
    // estimate only equals the decoded size under loose handling. Under strict
56
    // and stop_before_partial a partial final chunk is dropped, so a SUCCESS
57
    // can legitimately write fewer bytes than the estimate (e.g. "QQQ" with
58
    // stop_before_partial reports 2 but decodes 0). Only assert equality for
59
    // loose to avoid false positives.
60
3.71k
    if (last_chunk_option == simdutf::last_chunk_handling_options::loose &&
61
1.62k
        r.convertresult.error == simdutf::error_code::SUCCESS &&
62
561
        binarylength != r.convertresult.count) {
63
0
      std::cerr << "binary_length_from_base64 (" << binarylength
64
0
                << ") != decoded count (" << r.convertresult.count
65
0
                << ") for impl " << impl->name() << "\n";
66
0
      std::abort();
67
0
    }
68
3.71k
  }
69
1.23k
  auto neq = [](const auto& a, const auto& b) { return a != b; };
70
1.23k
  if (std::ranges::adjacent_find(results, neq) != results.end()) {
71
0
    std::cerr << "output differs between implementations for decode\n";
72
0
    const auto implementations = get_supported_implementations();
73
0
    std::size_t i = 0;
74
0
    for (const auto& r : results) {
75
0
      std::cerr << "impl " << implementations[i]->name()
76
0
                << " got maxbinarylength=" << r.maxbinarylength
77
0
                << " convertresult=" << r.convertresult << "\n";
78
0
      ++i;
79
0
    }
80
0
    std::cerr << "option: " << selected_option << '\n';
81
0
    std::cerr << "data: "
82
0
              << (std::is_same_v<FromChar, char> ? "char" : "char16_t") << "{";
83
0
    for (int v : base64) {
84
0
      std::cerr << v << ", ";
85
0
    }
86
0
    std::cerr << "}\n";
87
0
    std::abort();
88
0
  }
89
1.23k
}
90
91
template <typename FromChar>
92
void decode_safe(std::span<const FromChar> base64_, const auto selected_option,
93
                 const std::size_t decode_buf_size,
94
913
                 const auto last_chunk_option) {
95
913
  std::vector<FromChar> base64(begin(base64_), end(base64_));
96
913
  std::vector<char> output(decode_buf_size);
97
913
  std::size_t outlen = decode_buf_size;
98
913
  const auto convertresult = simdutf::base64_to_binary_safe(
99
913
      base64.data(), base64.size(), output.data(), outlen, selected_option,
100
913
      last_chunk_option);
101
102
  // the number of written bytes must always be less than the supplied buffer
103
913
  assert(outlen <= decode_buf_size);
104
105
913
  switch (convertresult.error) {
106
111
  case simdutf::error_code::OUTPUT_BUFFER_TOO_SMALL: {
107
111
    if (!(convertresult.count <= base64.size())) {
108
0
      std::cerr << " decode_buf_size=" << decode_buf_size
109
0
                << " outlen=" << outlen << " and result=" << convertresult
110
0
                << '\n';
111
0
      std::abort();
112
0
    }
113
111
  } break;
114
269
  case simdutf::error_code::INVALID_BASE64_CHARACTER: {
115
269
    assert(convertresult.count < base64.size());
116
269
  } break;
117
269
  case simdutf::error_code::BASE64_INPUT_REMAINDER: {
118
51
    if (!(convertresult.count <= base64.size())) {
119
0
      std::cerr << "on input with size=" << base64.size()
120
0
                << ": got BASE64_INPUT_REMAINDER decode_buf_size="
121
0
                << decode_buf_size << " outlen=" << outlen
122
0
                << " and result=" << convertresult << '\n';
123
0
      std::abort();
124
0
    }
125
51
  } break;
126
465
  case simdutf::error_code::SUCCESS: {
127
    // possibility to compare with the normal function
128
465
  } break;
129
17
  default:;
130
913
  }
131
913
}
void decode_safe<char, simdutf::base64_options, simdutf::last_chunk_handling_options>(std::__1::span<char const, 18446744073709551615ul>, simdutf::base64_options, unsigned long, simdutf::last_chunk_handling_options)
Line
Count
Source
94
402
                 const auto last_chunk_option) {
95
402
  std::vector<FromChar> base64(begin(base64_), end(base64_));
96
402
  std::vector<char> output(decode_buf_size);
97
402
  std::size_t outlen = decode_buf_size;
98
402
  const auto convertresult = simdutf::base64_to_binary_safe(
99
402
      base64.data(), base64.size(), output.data(), outlen, selected_option,
100
402
      last_chunk_option);
101
102
  // the number of written bytes must always be less than the supplied buffer
103
402
  assert(outlen <= decode_buf_size);
104
105
402
  switch (convertresult.error) {
106
54
  case simdutf::error_code::OUTPUT_BUFFER_TOO_SMALL: {
107
54
    if (!(convertresult.count <= base64.size())) {
108
0
      std::cerr << " decode_buf_size=" << decode_buf_size
109
0
                << " outlen=" << outlen << " and result=" << convertresult
110
0
                << '\n';
111
0
      std::abort();
112
0
    }
113
54
  } break;
114
74
  case simdutf::error_code::INVALID_BASE64_CHARACTER: {
115
74
    assert(convertresult.count < base64.size());
116
74
  } break;
117
74
  case simdutf::error_code::BASE64_INPUT_REMAINDER: {
118
30
    if (!(convertresult.count <= base64.size())) {
119
0
      std::cerr << "on input with size=" << base64.size()
120
0
                << ": got BASE64_INPUT_REMAINDER decode_buf_size="
121
0
                << decode_buf_size << " outlen=" << outlen
122
0
                << " and result=" << convertresult << '\n';
123
0
      std::abort();
124
0
    }
125
30
  } break;
126
236
  case simdutf::error_code::SUCCESS: {
127
    // possibility to compare with the normal function
128
236
  } break;
129
8
  default:;
130
402
  }
131
402
}
void decode_safe<char16_t, simdutf::base64_options, simdutf::last_chunk_handling_options>(std::__1::span<char16_t const, 18446744073709551615ul>, simdutf::base64_options, unsigned long, simdutf::last_chunk_handling_options)
Line
Count
Source
94
511
                 const auto last_chunk_option) {
95
511
  std::vector<FromChar> base64(begin(base64_), end(base64_));
96
511
  std::vector<char> output(decode_buf_size);
97
511
  std::size_t outlen = decode_buf_size;
98
511
  const auto convertresult = simdutf::base64_to_binary_safe(
99
511
      base64.data(), base64.size(), output.data(), outlen, selected_option,
100
511
      last_chunk_option);
101
102
  // the number of written bytes must always be less than the supplied buffer
103
511
  assert(outlen <= decode_buf_size);
104
105
511
  switch (convertresult.error) {
106
57
  case simdutf::error_code::OUTPUT_BUFFER_TOO_SMALL: {
107
57
    if (!(convertresult.count <= base64.size())) {
108
0
      std::cerr << " decode_buf_size=" << decode_buf_size
109
0
                << " outlen=" << outlen << " and result=" << convertresult
110
0
                << '\n';
111
0
      std::abort();
112
0
    }
113
57
  } break;
114
195
  case simdutf::error_code::INVALID_BASE64_CHARACTER: {
115
195
    assert(convertresult.count < base64.size());
116
195
  } break;
117
195
  case simdutf::error_code::BASE64_INPUT_REMAINDER: {
118
21
    if (!(convertresult.count <= base64.size())) {
119
0
      std::cerr << "on input with size=" << base64.size()
120
0
                << ": got BASE64_INPUT_REMAINDER decode_buf_size="
121
0
                << decode_buf_size << " outlen=" << outlen
122
0
                << " and result=" << convertresult << '\n';
123
0
      std::abort();
124
0
    }
125
21
  } break;
126
229
  case simdutf::error_code::SUCCESS: {
127
    // possibility to compare with the normal function
128
229
  } break;
129
9
  default:;
130
511
  }
131
511
}
132
133
struct roundtripresult {
134
  std::size_t length{};
135
  std::size_t maxbinarylength{};
136
  std::string outputhash;
137
  std::size_t written{};
138
  simdutf::result convertbackresult{};
139
  auto operator<=>(const roundtripresult&) const = default;
140
};
141
142
/// verifies that base64 with lines is the same as without lines, but with
143
/// newlines every line_length:th byte
144
void verify_lines(std::span<const char> without_lines,
145
                  std::span<const char> with_lines,
146
3.37k
                  const std::size_t line_length) {
147
  // ensure we get the same as output, with a newline every line_length:th
148
  // byte
149
43.9M
  for (std::size_t i = 0, j = 0;;) {
150
    // check one line
151
293M
    for (int count = 0; count < line_length && j < with_lines.size(); ++count) {
152
249M
      if (without_lines[i++] != with_lines[j++]) {
153
        // unexpected - different content
154
0
        std::abort();
155
0
      }
156
249M
    }
157
43.9M
    if (j == with_lines.size()) {
158
      // we are at the end of with_lines
159
3.37k
      if (i != without_lines.size()) {
160
        // unexpected - we are not at the end of without_lines
161
0
        std::abort();
162
0
      }
163
3.37k
      break;
164
3.37k
    }
165
43.9M
    if (with_lines[j++] != '\n') {
166
      // unexpected - not a newline
167
0
      std::abort();
168
0
    }
169
43.9M
  }
170
3.37k
}
171
172
void roundtrip(std::span<const char> binary, const auto selected_option,
173
1.12k
               const auto last_chunk_option, const std::size_t line_length) {
174
1.12k
  if (last_chunk_option ==
175
1.12k
      simdutf::last_chunk_handling_options::stop_before_partial) {
176
1
    return; // this is not a valid option for roundtrip
177
1
  }
178
1.12k
  const auto inputhash = FNV1A_hash::as_str(binary);
179
1.12k
  const auto implementations = get_supported_implementations();
180
1.12k
  std::vector<roundtripresult> results;
181
1.12k
  results.reserve(implementations.size());
182
3.37k
  for (auto impl : implementations) {
183
3.37k
    auto& r = results.emplace_back();
184
3.37k
    r.length = impl->base64_length_from_binary(binary.size(), selected_option);
185
3.37k
    std::vector<char> output(r.length);
186
3.37k
    r.written = impl->binary_to_base64(binary.data(), binary.size(),
187
3.37k
                                       output.data(), selected_option);
188
3.37k
    if (r.length != r.written) {
189
0
      std::abort();
190
0
    }
191
192
    // make sure generating base64 with lines gives the expected result
193
3.37k
    const auto length_with_lines =
194
3.37k
        simdutf::base64_length_from_binary_with_lines(
195
3.37k
            binary.size(), selected_option, line_length);
196
3.37k
    assert(length_with_lines >= r.length);
197
3.37k
    std::string output_with_lines(length_with_lines, '\0');
198
3.37k
    const auto nwritten_with_lines = impl->binary_to_base64_with_lines(
199
3.37k
        binary.data(), binary.size(), output_with_lines.data(), line_length,
200
3.37k
        selected_option);
201
3.37k
    if (nwritten_with_lines != length_with_lines) {
202
0
      std::cerr << nwritten_with_lines << "!=" << length_with_lines << '\n';
203
0
      std::abort();
204
0
    }
205
3.37k
    verify_lines(output, output_with_lines, line_length);
206
207
3.37k
    r.outputhash = FNV1A_hash::as_str(output);
208
    // convert back to binary
209
3.37k
    r.maxbinarylength =
210
3.37k
        impl->maximal_binary_length_from_base64(output.data(), output.size());
211
3.37k
    std::vector<char> restored(r.maxbinarylength);
212
3.37k
    r.convertbackresult =
213
3.37k
        impl->base64_to_binary(output.data(), output.size(), restored.data(),
214
3.37k
                               selected_option, last_chunk_option);
215
3.37k
    if (const auto restoredhash = FNV1A_hash::as_str(restored);
216
3.37k
        inputhash != restoredhash) {
217
0
      std::abort();
218
0
    }
219
3.37k
    if (restored.size() != binary.size()) {
220
0
      std::abort();
221
0
    }
222
3.37k
  }
223
224
2.25k
  auto neq = [](const auto& a, const auto& b) { return a != b; };
225
1.12k
  if (std::ranges::adjacent_find(results, neq) != results.end()) {
226
0
    std::cerr << "output differs between implementations\n";
227
0
    for (const auto& r : results) {
228
0
      std::cout << "written=" << r.written << " maxlength=" << r.maxbinarylength
229
0
                << " length=" << r.length << '\n';
230
0
    }
231
0
    std::abort();
232
0
  }
233
1.12k
}
234
235
4.29k
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
236
  // pick one of the function pointers, based on the fuzz data
237
  // the first byte is which action to take. step forward
238
  // several bytes so the input is aligned.
239
4.29k
  constexpr auto optionbytes = 6u;
240
4.29k
  static_assert(optionbytes % 2 == 0,
241
4.29k
                "optionbytes must be even to avoid misaligned char16 pointers");
242
243
4.29k
  if (size < optionbytes) {
244
4
    return 0;
245
4
  }
246
4.29k
  constexpr auto Ncases = 5u;
247
4.29k
  constexpr auto actionmask = std::bit_ceil(Ncases) - 1;
248
4.29k
  const auto action = data[0] & actionmask;
249
250
  // pick a random option
251
4.29k
  const auto selected_option = [](auto index) {
252
4.29k
    if (index >= options.size())
253
0
      return options[0];
254
4.29k
    else {
255
4.29k
      return options[index];
256
4.29k
    }
257
4.29k
  }(data[1] & (std::bit_ceil(options.size()) - 1));
258
4.29k
  const auto selected_last_chunk =
259
4.29k
      (selected_option == simdutf::base64_url ||
260
3.64k
       selected_option == simdutf::base64_default_no_padding)
261
4.29k
          ? simdutf::last_chunk_handling_options::loose
262
4.29k
          : [](auto index) {
263
3.16k
              if (index >= last_chunk.size())
264
419
                return last_chunk[0];
265
2.74k
              else {
266
2.74k
                return last_chunk[index];
267
2.74k
              }
268
3.16k
            }(data[2] & (std::bit_ceil(last_chunk.size()) - 1));
269
270
  // decode buffer size
271
4.29k
  const std::size_t decode_buffer_size = (data[4] << 8) + data[3];
272
273
  // line length must be at least 4
274
4.29k
  const std::size_t line_length = unsigned{data[5]} + 4u;
275
276
4.29k
  data += optionbytes;
277
4.29k
  size -= optionbytes;
278
279
4.29k
  switch (action) {
280
1.12k
  case 0: {
281
1.12k
    const std::span<const char> chardata{(const char*)data, size};
282
1.12k
    roundtrip(chardata, selected_option, selected_last_chunk, line_length);
283
1.12k
  } break;
284
1.01k
  case 1: {
285
1.01k
    const std::span<const char> chardata{(const char*)data, size};
286
1.01k
    decode(chardata, selected_option, selected_last_chunk);
287
1.01k
  } break;
288
1.23k
  case 2: {
289
1.23k
    const std::span<const char16_t> chardata{(const char16_t*)data, size / 2};
290
1.23k
    decode(chardata, selected_option, selected_last_chunk);
291
1.23k
  } break;
292
402
  case 3: {
293
402
    const std::span<const char> chardata{(const char*)data, size};
294
402
    decode_safe(chardata, selected_option, decode_buffer_size,
295
402
                selected_last_chunk);
296
402
  } break;
297
511
  case 4: {
298
511
    const std::span<const char16_t> chardata{(const char16_t*)data, size / 2};
299
511
    decode_safe(chardata, selected_option, decode_buffer_size,
300
511
                selected_last_chunk);
301
511
  } break;
302
4.29k
  }
303
304
4.29k
  return 0;
305
4.29k
}