File: base64.cc

package info (click to toggle)
chromium 145.0.7632.159-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 5,976,224 kB
  • sloc: cpp: 36,198,469; ansic: 7,634,080; javascript: 3,564,060; python: 1,649,622; xml: 838,470; asm: 717,087; pascal: 185,708; sh: 88,786; perl: 88,718; objc: 79,984; sql: 59,811; cs: 42,452; fortran: 24,101; makefile: 21,144; tcl: 15,277; php: 14,022; yacc: 9,066; ruby: 7,553; awk: 3,720; lisp: 3,233; lex: 1,328; ada: 727; jsp: 228; sed: 36
file content (168 lines) | stat: -rw-r--r-- 5,878 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/base64.h"

#include <stddef.h>

#include <string_view>
#include <utility>

#include "base/check.h"
#include "base/features.h"
#include "base/numerics/checked_math.h"
#include "base/simdutf_shim.h"
#include "base/strings/string_util.h"
#include "base/strings/string_view_util.h"
#include "third_party/modp_b64/modp_b64.h"

namespace base {

namespace {

ModpDecodePolicy GetModpPolicy(Base64DecodePolicy policy) {
  switch (policy) {
    case Base64DecodePolicy::kStrict:
      return ModpDecodePolicy::kStrict;
    case Base64DecodePolicy::kForgiving:
      return ModpDecodePolicy::kForgiving;
  }
}

void Base64EncodeAppendModpB64(span<const uint8_t> input, std::string* output) {
  // Ensure `modp_b64_encode_data_len` will not overflow.
  CHECK_LE(input.size(), MODP_B64_MAX_INPUT_LEN);

  size_t encode_data_len = modp_b64_encode_data_len(input.size());

  const size_t after_size =
      CheckAdd(encode_data_len, output->size()).ValueOrDie();
  output->resize(after_size);

  span<const char> read = as_chars(input);
  span<char> write = span(*output).last(encode_data_len);

  const size_t written_size = modp_b64_encode_data(
      write.data(),  // This must point to `encode_data_len` many chars.
      read.data(), read.size());
  // If this failed, it would indicate modp_b64_encode_data() wrote OOB or left
  // bytes uninitialized. It's possible for this to be elided by the compiler,
  // since writing OOB is UB.
  CHECK_EQ(written_size, write.size());
}

void Base64EncodeAppendSimdutf(span<const uint8_t> input, std::string* output) {
  // Ensure `modp_b64_encode_data_len` will not overflow.
  // For now, use this with simdutf as well for consistency.
  CHECK_LE(input.size(), MODP_B64_MAX_INPUT_LEN);

  size_t encode_data_len =
      internal::simdutf_base64_length_from_binary(input.size());

  const size_t after_size =
      CheckAdd(encode_data_len, output->size()).ValueOrDie();
  output->resize(after_size);

  span<char> write = span(*output).last(encode_data_len);

  const size_t written_size = internal::simdutf_binary_to_base64(input, write);
  // If this failed, it would indicate simdutf wrote OOB or left bytes
  // uninitialized. It's possible for this to be elided by the compiler, since
  // writing OOB is UB.
  CHECK_EQ(written_size, write.size());
}

}  // namespace

std::string Base64Encode(span<const uint8_t> input) {
  std::string output;
  Base64EncodeAppend(input, &output);
  return output;
}

void Base64EncodeAppend(span<const uint8_t> input, std::string* output) {
  if (FeatureList::IsEnabled(features::kSimdutfBase64Encode)) {
    Base64EncodeAppendSimdutf(input, output);
    return;
  }

  Base64EncodeAppendModpB64(input, output);
}

std::string Base64Encode(std::string_view input) {
  return Base64Encode(as_byte_span(input));
}

BASE_EXPORT std::string Base64EncodeEarlyStartup(span<const uint8_t> input) {
  std::string output;
  Base64EncodeAppendModpB64(input, &output);
  return output;
}

bool Base64Decode(std::string_view input,
                  std::string* output,
                  Base64DecodePolicy policy) {
  std::string decode_buf;
  decode_buf.resize(modp_b64_decode_len(input.size()));

  // Does not NUL-terminate result since result is binary data!
  size_t written_size = modp_b64_decode(decode_buf.data(), input.data(),
                                        input.size(), GetModpPolicy(policy));

  // Forgiving mode requires whitespace to be stripped prior to decoding.
  // We don't do that in the above code to ensure that the "happy path" of
  // input without whitespace is as fast as possible. Since whitespace in input
  // will always cause `modp_b64_decode` to fail, just handle whitespace
  // stripping on failure. This is not much slower than just scanning for
  // whitespace first, even for input with whitespace.
  if (written_size == MODP_B64_ERROR &&
      policy == Base64DecodePolicy::kForgiving) {
    // We could use `output` here to avoid an allocation when decoding is done
    // in-place, but it violates the API contract that `output` is only modified
    // on success.
    std::string input_without_whitespace;
    RemoveChars(input, base::as_string_view(kInfraAsciiWhitespace),
                &input_without_whitespace);
    // This means that the required size to decode is at most what was needed
    // above, which means `decode_buf` will fit the decoded bytes at its current
    // size and we don't need to call `modp_b64_decode_len()` again.
    CHECK_LE(input_without_whitespace.size(), input.size());
    written_size =
        modp_b64_decode(decode_buf.data(), input_without_whitespace.data(),
                        input_without_whitespace.size(), GetModpPolicy(policy));
  }

  if (written_size == MODP_B64_ERROR) {
    return false;
  }

  // If this failed it would indicate we wrote OOB. It's possible for this to be
  // elided by the compiler, since writing OOB is UB.
  CHECK_LE(written_size, decode_buf.size());

  // Shrinks the buffer and makes it NUL-terminated.
  decode_buf.resize(written_size);
  *output = std::move(decode_buf);
  return true;
}

std::optional<std::vector<uint8_t>> Base64Decode(std::string_view input) {
  std::vector<uint8_t> write_buf(modp_b64_decode_len(input.size()));
  span<char> write = base::as_writable_chars(base::span(write_buf));

  size_t written_size =
      modp_b64_decode(write.data(), input.data(), input.size());
  if (written_size == MODP_B64_ERROR) {
    return std::nullopt;
  }

  // If this failed it would indicate we wrote OOB. It's possible for this to be
  // elided by the compiler, since writing OOB is UB.
  CHECK_LE(written_size, write.size());

  write_buf.resize(written_size);
  return write_buf;
}

}  // namespace base