summaryrefslogtreecommitdiff
path: root/src/util/string.hpp
blob: 3b23fa4ed4df010c590a8018de83d8f91b92a521 (plain)
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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
// Copyright (C) 2021-2023 Joel Rosdahl and other contributors
//
// See doc/AUTHORS.adoc for a complete list of contributors.
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 3 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the Free Software Foundation, Inc., 51
// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA

#pragma once

#include <util/Bytes.hpp>

#include <third_party/nonstd/expected.hpp>
#include <third_party/nonstd/span.hpp>

#include <sys/stat.h> // for mode_t

#include <cstdint>
#include <cstring>
#include <optional>
#include <string>
#include <string_view>
#include <utility>

namespace util {

// --- Interface ---

// Return true if `suffix` is a suffix of `string`.
bool ends_with(std::string_view string, std::string_view suffix);

// Format `diff` as a human-readable string.
std::string format_human_readable_diff(int64_t diff);

// Format `size` as a human-readable string.
std::string format_human_readable_size(uint64_t size);

// Format `size` as a parsable string.
std::string format_parsable_size_with_suffix(uint64_t size);

// Join stringified elements of `container` delimited by `delimiter` into a
// string. There must exist an `std::string to_string(T::value_type)` function.
template<typename T>
std::string join(const T& container, const std::string_view delimiter);

// Join stringified elements between input iterators `begin` and `end` delimited
// by `delimiter` into a string. There must exist an `std::string
// to_string(T::value_type)` function.
template<typename T>
std::string
join(const T& begin, const T& end, const std::string_view delimiter);

// Parse a string into a double.
//
// Returns an error string if `value` cannot be parsed as a double.
nonstd::expected<double, std::string> parse_double(const std::string& value);

// Parse a string into a signed integer.
//
// Returns an error string if `value` cannot be parsed as an int64_t or if the
// value falls out of the range [`min_value`, `max_value`]. `min_value` and
// `max_value` default to min and max values of int64_t. `description` is
// included in the error message for range violations.
nonstd::expected<int64_t, std::string>
parse_signed(std::string_view value,
             std::optional<int64_t> min_value = std::nullopt,
             std::optional<int64_t> max_value = std::nullopt,
             std::string_view description = "integer");

// Parse a "size value", i.e. a string that can end in k, M, G, T (10-based
// suffixes) or Ki, Mi, Gi, Ti (2-based suffixes). For backward compatibility, K
// is also recognized as a synonym of k.
nonstd::expected<uint64_t, std::string> parse_size(const std::string& value);

// Parse `value` (an octal integer).
nonstd::expected<mode_t, std::string> parse_umask(std::string_view value);

// Parse a string into an unsigned integer.
//
// Returns an error string if `value` cannot be parsed as an uint64_t with base
// `base`, or if the value falls out of the range [`min_value`, `max_value`].
// `min_value` and `max_value` default to min and max values of uint64_t.
// `description` is included in the error message for range violations.
nonstd::expected<uint64_t, std::string>
parse_unsigned(std::string_view value,
               std::optional<uint64_t> min_value = std::nullopt,
               std::optional<uint64_t> max_value = std::nullopt,
               std::string_view description = "integer",
               int base = 10);

// Percent-decode[1] `string`.
//
// [1]: https://en.wikipedia.org/wiki/Percent-encoding
nonstd::expected<std::string, std::string>
percent_decode(std::string_view string);

// Replace the all occurrences of `from` to `to` in `string`.
std::string replace_all(std::string_view string,
                        std::string_view from,
                        std::string_view to);

// Replace the first occurrence of `from` to `to` in `string`.
std::string replace_first(std::string_view string,
                          std::string_view from,
                          std::string_view to);

// Split `string` into two parts using `split_char` as the delimiter. The second
// part will be `nullopt` if there is no `split_char` in `string.`
std::pair<std::string_view, std::optional<std::string_view>>
split_once(const char* string, char split_char);
std::pair<std::string, std::optional<std::string>>
split_once(std::string&& string, char split_char);
std::pair<std::string_view, std::optional<std::string_view>>
split_once(std::string_view string, char split_char);

// Return true if `prefix` is a prefix of `string`.
bool starts_with(const char* string, std::string_view prefix);

// Return true if `prefix` is a prefix of `string`.
bool starts_with(std::string_view string, std::string_view prefix);

// Strip whitespace from left and right side of a string.
[[nodiscard]] std::string strip_whitespace(std::string_view string);

// Convert `value` to a `nonstd::span<const uint8_t>`.
nonstd::span<const uint8_t> to_span(std::string_view value);

// Convert `value` to a string. This function is used when joining
// `std::string`s with `util::join`.
template<typename T> std::string to_string(const T& value);

// Convert `data` to a `std::string_view`.
std::string_view to_string_view(nonstd::span<const uint8_t> data);

// --- Inline implementations ---

inline bool
ends_with(const std::string_view string, const std::string_view suffix)
{
  return string.length() >= suffix.length()
         && string.substr(string.length() - suffix.length()) == suffix;
}

template<typename T>
inline std::string
join(const T& container, const std::string_view delimiter)
{
  return join(container.begin(), container.end(), delimiter);
}

template<typename T>
inline std::string
join(const T& begin, const T& end, const std::string_view delimiter)
{
  std::string result;
  for (auto it = begin; it != end; ++it) {
    if (it != begin) {
      result.append(delimiter.data(), delimiter.length());
    }
    result += to_string(*it);
  }
  return result;
}

inline bool
starts_with(const char* const string, const std::string_view prefix)
{
  // Optimized version of starts_with(string_view, string_view): avoid computing
  // the length of the string argument.
  return std::strncmp(string, prefix.data(), prefix.length()) == 0;
}

inline bool
starts_with(const std::string_view string, const std::string_view prefix)
{
  return string.substr(0, prefix.size()) == prefix;
}

inline nonstd::span<const uint8_t>
to_span(std::string_view data)
{
  return nonstd::span<const uint8_t>(
    reinterpret_cast<const uint8_t*>(data.data()), data.size());
}

template<typename T>
inline std::string
to_string(const T& t)
{
  using std::to_string;
  return to_string(std::forward<T>(t));
}

template<>
inline std::string
to_string(const std::string& string)
{
  return std::string(string);
}

template<>
inline std::string
to_string(const std::string_view& sv)
{
  return std::string(sv);
}

template<>
inline std::string
to_string(const nonstd::span<const uint8_t>& bytes)
{
  return std::string(to_string_view(bytes));
}

template<>
inline std::string
to_string(const util::Bytes& bytes)
{
  return std::string(to_string_view(bytes));
}

inline std::string_view
to_string_view(nonstd::span<const uint8_t> data)
{
  return std::string_view(reinterpret_cast<const char*>(data.data()),
                          data.size());
}

} // namespace util