Coverage Report

Created: 2026-04-15 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/boringssl/pki/string_util.cc
Line
Count
Source
1
// Copyright 2022 The Chromium Authors
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     https://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
#include "string_util.h"
16
17
#include <algorithm>
18
#include <iomanip>
19
#include <sstream>
20
#include <string>
21
22
#include <openssl/base64.h>
23
#include <openssl/mem.h>
24
25
BSSL_NAMESPACE_BEGIN
26
namespace string_util {
27
28
10.8k
bool IsAscii(std::string_view str) {
29
10.8k
  for (unsigned char c : str) {
30
7.66k
    if (c > 127) {
31
61
      return false;
32
61
    }
33
7.66k
  }
34
10.7k
  return true;
35
10.8k
}
36
37
0
bool IsEqualNoCase(std::string_view str1, std::string_view str2) {
38
0
  return std::equal(str1.begin(), str1.end(), str2.begin(), str2.end(),
39
0
                    [](const unsigned char a, const unsigned char b) {
40
0
                      return OPENSSL_tolower(a) == OPENSSL_tolower(b);
41
0
                    });
42
0
}
43
44
0
bool EndsWithNoCase(std::string_view str, std::string_view suffix) {
45
0
  return suffix.size() <= str.size() &&
46
0
         IsEqualNoCase(suffix, str.substr(str.size() - suffix.size()));
47
0
}
48
49
0
bool StartsWithNoCase(std::string_view str, std::string_view prefix) {
50
0
  return prefix.size() <= str.size() &&
51
0
         IsEqualNoCase(prefix, str.substr(0, prefix.size()));
52
0
}
53
54
std::string FindAndReplace(std::string_view str, std::string_view find,
55
0
                           std::string_view replace) {
56
0
  std::string ret;
57
58
0
  if (find.empty()) {
59
0
    return std::string(str);
60
0
  }
61
0
  while (!str.empty()) {
62
0
    size_t index = str.find(find);
63
0
    if (index == std::string_view::npos) {
64
0
      ret.append(str);
65
0
      break;
66
0
    }
67
0
    ret.append(str.substr(0, index));
68
0
    ret.append(replace);
69
0
    str = str.substr(index + find.size());
70
0
  }
71
0
  return ret;
72
0
}
73
74
// TODO(bbe) get rid of this once we can c++20.
75
0
bool EndsWith(std::string_view str, std::string_view suffix) {
76
0
  return suffix.size() <= str.size() &&
77
0
         suffix == str.substr(str.size() - suffix.size());
78
0
}
79
80
// TODO(bbe) get rid of this once we can c++20.
81
0
bool StartsWith(std::string_view str, std::string_view prefix) {
82
0
  return prefix.size() <= str.size() && prefix == str.substr(0, prefix.size());
83
0
}
84
85
0
std::string HexEncode(Span<const uint8_t> data) {
86
0
  std::ostringstream out;
87
0
  for (uint8_t b : data) {
88
0
    out << std::hex << std::setfill('0') << std::setw(2) << std::uppercase
89
0
        << int{b};
90
0
  }
91
0
  return out.str();
92
0
}
93
94
// TODO(bbe) get rid of this once extracted to boringssl. Everything else
95
// in third_party uses std::to_string
96
0
std::string NumberToDecimalString(int i) {
97
0
  std::ostringstream out;
98
0
  out << std::dec << i;
99
0
  return out.str();
100
0
}
101
102
std::vector<std::string_view> SplitString(std::string_view str,
103
0
                                          char split_char) {
104
0
  std::vector<std::string_view> out;
105
106
0
  if (str.empty()) {
107
0
    return out;
108
0
  }
109
110
0
  while (true) {
111
    // Find end of current token
112
0
    size_t i = str.find(split_char);
113
114
    // Add current token
115
0
    out.push_back(str.substr(0, i));
116
117
0
    if (i == str.npos) {
118
      // That was the last token
119
0
      break;
120
0
    }
121
    // Continue to next
122
0
    str = str.substr(i + 1);
123
0
  }
124
125
0
  return out;
126
0
}
127
128
0
static bool IsUnicodeWhitespace(char c) {
129
0
  return c == 9 || c == 10 || c == 11 || c == 12 || c == 13 || c == ' ';
130
0
}
131
132
std::string CollapseWhitespaceASCII(std::string_view text,
133
0
                                    bool trim_sequences_with_line_breaks) {
134
0
  std::string result;
135
0
  result.resize(text.size());
136
137
  // Set flags to pretend we're already in a trimmed whitespace sequence, so we
138
  // will trim any leading whitespace.
139
0
  bool in_whitespace = true;
140
0
  bool already_trimmed = true;
141
142
0
  int chars_written = 0;
143
0
  for (auto i = text.begin(); i != text.end(); ++i) {
144
0
    if (IsUnicodeWhitespace(*i)) {
145
0
      if (!in_whitespace) {
146
        // Reduce all whitespace sequences to a single space.
147
0
        in_whitespace = true;
148
0
        result[chars_written++] = L' ';
149
0
      }
150
0
      if (trim_sequences_with_line_breaks && !already_trimmed &&
151
0
          ((*i == '\n') || (*i == '\r'))) {
152
        // Whitespace sequences containing CR or LF are eliminated entirely.
153
0
        already_trimmed = true;
154
0
        --chars_written;
155
0
      }
156
0
    } else {
157
      // Non-whitespace characters are copied straight across.
158
0
      in_whitespace = false;
159
0
      already_trimmed = false;
160
0
      result[chars_written++] = *i;
161
0
    }
162
0
  }
163
164
0
  if (in_whitespace && !already_trimmed) {
165
    // Any trailing whitespace is eliminated.
166
0
    --chars_written;
167
0
  }
168
169
0
  result.resize(chars_written);
170
0
  return result;
171
0
}
172
173
0
bool Base64Encode(const std::string_view &input, std::string *output) {
174
0
  size_t len;
175
0
  if (!EVP_EncodedLength(&len, input.size())) {
176
0
    return false;
177
0
  }
178
0
  std::vector<char> encoded(len);
179
0
  len = EVP_EncodeBlock(reinterpret_cast<uint8_t *>(encoded.data()),
180
0
                        reinterpret_cast<const uint8_t *>(input.data()),
181
0
                        input.size());
182
0
  if (!len) {
183
0
    return false;
184
0
  }
185
0
  output->assign(encoded.data(), len);
186
0
  return true;
187
0
}
188
189
0
bool Base64Decode(const std::string_view &input, std::string *output) {
190
0
  size_t len;
191
0
  if (!EVP_DecodedLength(&len, input.size())) {
192
0
    return false;
193
0
  }
194
0
  std::vector<char> decoded(len);
195
0
  if (!EVP_DecodeBase64(reinterpret_cast<uint8_t *>(decoded.data()), &len, len,
196
0
                        reinterpret_cast<const uint8_t *>(input.data()),
197
0
                        input.size())) {
198
0
    return false;
199
0
  }
200
0
  output->assign(decoded.data(), len);
201
0
  return true;
202
0
}
203
204
}  // namespace string_util
205
BSSL_NAMESPACE_END