MySQL 9.6.0
Source Code Documentation
tag.h
Go to the documentation of this file.
1// Copyright (c) 2025, Oracle and/or its affiliates.
2//
3// This program is free software; you can redistribute it and/or modify
4// it under the terms of the GNU General Public License, version 2.0,
5// as published by the Free Software Foundation.
6//
7// This program is designed to work with certain software (including
8// but not limited to OpenSSL) that is licensed under separate terms,
9// as designated in a particular file or component or in included license
10// documentation. The authors of MySQL hereby grant you an additional
11// permission to link the program and your derivative works with the
12// separately licensed software that they have either included with
13// the program or referenced in the documentation.
14//
15// This program is distributed in the hope that it will be useful,
16// but WITHOUT ANY WARRANTY; without even the implied warranty of
17// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18// GNU General Public License, version 2.0, for more details.
19//
20// You should have received a copy of the GNU General Public License
21// along with this program; if not, write to the Free Software
22// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23
24#ifndef MYSQL_GTIDS_TAG_H
25#define MYSQL_GTIDS_TAG_H
26
27/// @file
28/// Experimental API header
29
30#include <array> // array
31#include <cstdint> // size_t
32#include <initializer_list> // initializer_list
33#include <stdexcept> // domain_error
34#include <string_view> // string_view
35#include "mysql/ranges/buffer_interface.h" // Buffer_interface
36#include "mysql/strconv/strconv.h" // Is_string_target
37#include "mysql/utils/call_and_catch.h" // call_and_catch
38#include "mysql/utils/return_status.h" // Return_status
39
40/// @addtogroup GroupLibsMysqlGtids
41/// @{
42
43// ==== class Char_table ====
44
45namespace mysql::gtids::detail {
46
47/// Helper class to hold lookup tables indexed by ascii characters.
48///
49/// @tparam Transform_tp Function that transforms letters. It should accept a
50/// char argument and return the type of values stored in the table.
51///
52/// @tparam char_ranges_tp An even number of char constants, each pair defining
53/// the beginning and end of a range of characters included in the table. Both
54/// beginning and end are *inclusive*.
55template <std::invocable<unsigned char> Transform_tp,
56 unsigned char... char_ranges_tp>
57 requires(sizeof...(char_ranges_tp) % 2 == 0)
59 public:
60 using Transform_t = Transform_tp;
61 using Element_t = std::invoke_result_t<Transform_t, unsigned char>;
62 using Table_t = std::array<Element_t, 256>;
63
64 /// Return reference to the table.
65 static Table_t &table() {
66 // Compute the table on the first invocation of this function.
67 static Table_t &tbl = build_table({char_ranges_tp...});
68 return tbl;
69 }
70
71 private:
72 /// Compute the table.
74 std::initializer_list<unsigned char> char_ranges) {
75 static Table_t tbl;
76 // Initialize with zero bytes.
77 tbl.fill({});
78 // Fill with the specified ranges.
79 for (const auto *it = char_ranges.begin(); it != char_ranges.end();
80 it += 2) {
81 auto first = *it;
82 auto last = *std::next(it);
83 for (unsigned char ch = first; ch <= last; ++ch)
84 tbl[ch] = Transform_t{}(ch);
85 }
86 return tbl;
87 }
88};
89
90} // namespace mysql::gtids::detail
91
92// ==== Base class and concept ====
93
94namespace mysql::gtids::detail {
95/// Top of the hierarchy, defining the tag format.
96class Tag_base {
97 public:
98 /// The maximum number of characters a tag.
99 static constexpr std::size_t max_size = 32;
100 static constexpr std::ptrdiff_t max_ssize = 32;
101
102 private:
103 /// Lambda function that converts a character to lower case.
104 using Tolower_t = decltype([](unsigned char ch) { return std::tolower(ch); });
105
106 /// Table of characters allowed as the first letter in a tag.
109
110 /// Table of characters allowed in tags at other positions than first.
113
114 public:
115 /// Return the character converted to lowercase, if it is allowed as the first
116 /// character in a tag; otherwise return 0.
117 [[nodiscard]] static int get_normalized_first_char(int ch) {
118 assert(ch >= 0);
119 assert(ch < 256);
120 return First_char_table_t::table()[ch];
121 }
122
123 /// Return true if the given character is allowed as the first character in a
124 /// tag.
125 [[nodiscard]] static bool is_valid_first_char(int ch) {
126 return get_normalized_first_char(ch) != 0;
127 }
128
129 /// Return the character converted to lowercase, if it is allowed in a tag at
130 /// other positions than the first; otherwise return 0.
131 [[nodiscard]] static int get_normalized_nonfirst_char(int ch) {
132 assert(ch >= 0);
133 assert(ch < 256);
134 return Nonfirst_char_table_t::table()[ch];
135 }
136
137 /// Return true if the given character is allowed in a tag, at other positions
138 /// than the first.
139 [[nodiscard]] static bool is_valid_nonfirst_char(int ch) {
140 return get_normalized_nonfirst_char(ch) != 0;
141 }
142
143 /// Return true if the given string is a valid tag.
144 ///
145 /// To be a valid tag, it must be between 0 and max_size characters long, and
146 /// if it is not empty, the first character must be 'a'-'z', 'A-Z', or '_',
147 /// and any remaining characters must be either that or '0'-'9'.
148 [[nodiscard]] static bool is_valid(const std::string_view &sv) {
149 if (sv.empty()) return true;
150 if (sv.size() > max_size) return false;
151 if (!is_valid_first_char(sv[0])) return false;
152 // Not on all platforms is `it` a pointer and can be declared as `auto *`
153 // NOLINTNEXTLINE(llvm-qualified-auto,readability-qualified-auto)
154 for (auto it = std::next(sv.begin()); it != sv.end(); ++it) {
155 if (!is_valid_nonfirst_char(*it)) return false;
156 }
157 return true;
158 }
159
160 /// Return the length of a tag that appears as an initial prefix of the given
161 /// string, or no value if there is a string of tag characters that does not
162 /// meet the requiremet of maximum length or the requirement that first
163 /// character must be a non-digit.
164 ///
165 /// To be a valid tag, it must be between 0 and max_size characters long, and
166 /// if it is not empty, the first character must be 'a'-'z', 'A-Z', or '_',
167 /// and any remaining characters must be either that or '0'-'9'. Moreover, if
168 /// the tag does not extend to the end of the string, the following character
169 /// must not be any of the tag characters 'a-z', 'A-Z', '_', or '0'-'9'.
170 [[nodiscard]] static std::optional<std::size_t> valid_prefix_length(
171 const std::string_view &sv) {
172 if (sv.empty()) return 0;
173 if (!is_valid_first_char(sv[0])) {
174 if (is_valid_nonfirst_char(sv[0])) return {};
175 return 0;
176 }
177 std::size_t end = std::min(sv.size(), max_size);
178 for (std::size_t pos = 1; pos != end; ++pos) {
179 if (!is_valid_nonfirst_char(sv[pos])) return pos;
180 }
181 if (sv.size() > end) {
182 if (is_valid_nonfirst_char(sv[end])) return {};
183 }
184 return end;
185 }
186};
187} // namespace mysql::gtids::detail
188
189namespace mysql::gtids {
190/// True if Test is one of the tag classes.
191template <class Test>
192concept Is_tag = std::derived_from<Test, detail::Tag_base>;
193} // namespace mysql::gtids
194
195// ==== Tag_interface ====
196
197namespace mysql::gtids::detail {
198
199/// CRTP base class defining a tag class given an implementation that provides
200/// a contiguous buffer as storage, through a `out_str` member.
201///
202/// Subclasses should define the following member to access the tag data:
203/// @code
204/// std::size_t size() const;
205/// char *data() const;
206/// /*Output String Wrapper type*/ out_str();
207/// @endcode
208// "nolint": Problem not worth fixing, and workaround too intrusive.
209// NOLINTBEGIN(bugprone-crtp-constructor-accessibility)
210template <class Self_tp>
211class Tag_interface : public Tag_base,
212 public mysql::ranges::Buffer_interface<Self_tp> {
213 // NOLINTEND(bugprone-crtp-constructor-accessibility)
214 public:
215 /// Copy from the given tag into this tag.
216 ///
217 /// @param other Tag to copy from.
218 ///
219 /// @return If the tag implementation allocates, returns Return_status::ok on
220 /// success and Return_status::error on out-of-memory errors. Otherwise this
221 /// function cannot fail and returns void.
222 [[nodiscard]] auto assign(const Is_tag auto &other) {
223 assert(is_valid(other.string_view()));
224 return assign_and_normalize(other.string_view());
225 }
226
227 /// Copy from the given std::string_view into this tag.
228 ///
229 /// Use in exception-free code only if the tag format has been validated
230 /// already.
231 ///
232 /// @param sv string_view to copy from.
233 ///
234 /// @throws std::domain_error if `sv` does not have the correct tag format.
235 void throwing_assign(const std::string_view &sv) {
236 if (!is_valid(sv)) throw std::domain_error("Invalid tag format.");
238 }
239
240 /// Copy from the given std::string_view into this tag.
241 ///
242 /// @param sv string_view to copy from.
243 ///
244 /// @return Return_status::ok on success, Return_status::error if the given
245 /// string was not a valid tag or an out-of-memory condition occurred when
246 /// allocating a character buffer.
247 [[nodiscard]] mysql::utils::Return_status assign(const std::string_view &sv) {
248 return mysql::utils::call_and_catch(
249 [&sv, this] { this->throwing_assign(sv); });
250 }
251
252 /// Set the tag to empty.
253 void clear() {
254 // Can't fail when assigning empty string.
255 [[maybe_unused]] auto ret = assign("");
256 assert(ret == mysql::utils::Return_status::ok);
257 }
258
259 private:
260 /// Copy from the given string_view, changing character case to lower case.
261 [[nodiscard]] auto assign_and_normalize(const std::string_view &sv) {
263 counter.advance(sv.size());
264 };
265 auto write = [&](mysql::strconv::String_writer &writer) {
266 if (sv.empty()) return;
267 writer.write_char(get_normalized_first_char(sv[0]));
268 for (std::size_t index = 1; index != sv.size(); ++index) {
269 auto ch = get_normalized_nonfirst_char(sv[index]);
270 assert(ch != 0);
271 writer.write_char(ch);
272 }
273 };
274 return mysql::strconv::out_str_write(self().out_str(), count, write);
275 }
276
277 [[nodiscard]] Self_tp &self() { return static_cast<Self_tp &>(*this); }
278 [[nodiscard]] const Self_tp &self() const {
279 return static_cast<const Self_tp &>(*this);
280 }
281}; // class Tag_interface
282
283} // namespace mysql::gtids::detail
284
285namespace mysql::gtids {
286
287// ==== Class Tag_trivial ====
288
289/// Class representing a tag by storing the characters in a member array. This
290/// never allocates, and is trivially default constructible and trivially
291/// copyable.
292///
293/// Note that trivially default constructible implies that a default-constructed
294/// object is *uninitialized*, so it is in an invalid state rather than a valid
295/// empty tag.
296class Tag_trivial : public detail::Tag_interface<Tag_trivial> {
298
299 public:
300 /// Default constructor: This does not initialize the tag! Use Tag
301 /// unless you need std::is_trivially_default_constructible.
303
304 /// Copy from any other tag
305 explicit Tag_trivial(const Is_tag auto &other) noexcept : Base_t() {
306 assign(other);
307 }
308 // Default rule-of-5
309
310 /// Return a new Tag, initialized with the given string.
311 ///
312 /// Use in exception-free code only if the tag format has been validated
313 /// already.
314 ///
315 /// @param sv string_view to copy from.
316 ///
317 /// @throws std::domain_error if `sv` does not have the correct tag format.
318 static auto throwing_make(const std::string_view &sv) {
319 Tag_trivial ret;
320 ret.throwing_assign(sv);
321 return ret;
322 }
323
324 /// Return the number of characters of the tag: an integer between 0 and
325 /// max_size.
326 [[nodiscard]] std::size_t size() const { return m_size; }
327
328 /// Return a const pointer to the data.
329 [[nodiscard]] const char *data() const { return m_data; }
330
331 private:
332 /// Return an Output String Wrapper writing to this Tag.
333 [[nodiscard]] auto out_str() {
335 }
336
337 /// The base class needs to call `out_str()`.
338 friend Base_t;
339
340 /// Tag represented as data + size.
342 std::size_t m_size;
343};
344
345// ==== Class Tag ====
346
347/// Class representing a tag by storing the characters in a member array. This
348/// never allocates and is trivially copyable. A default-constructed tag is
349/// initialized to empty.
350///
351/// The initialization at construction time implies that the tag is not
352/// trivially default constructible.
353class Tag : public Tag_trivial {
355
356 public:
357 /// Construct a new empty tag.
359
360 /// Construct a new tag by copying the other tag
361 explicit Tag(const Is_tag auto &other) noexcept : Base_t(other) {}
362 // Default rule-of-5
363
364 /// Return a new Tag, initialized with the given string. This raises an
365 /// exception if the string does not have the correct tag format: use in
366 /// exception-free code only if the sequence_number has been validated
367 /// already.
368 static auto throwing_make(const std::string_view &sv) {
369 Tag ret;
370 ret.throwing_assign(sv);
371 return ret;
372 }
373};
374
375/// Enable operator== for mixed tag types.
376[[nodiscard]] bool operator==(const Is_tag auto &tag1,
377 const Is_tag auto &tag2) {
378 return tag1.string_view() == tag2.string_view();
379}
380
381/// Enable operator!= for mixed tag types.
382[[nodiscard]] bool operator!=(const Is_tag auto &tag1,
383 const Is_tag auto &tag2) {
384 return !(tag1 == tag2);
385}
386
387} // namespace mysql::gtids
388
389// addtogroup GroupLibsMysqlGtids
390/// @}
391
392#endif // ifndef MYSQL_GTIDS_TAG_H
Experimental API header.
Experimental API header.
Class representing a tag by storing the characters in a member array.
Definition: tag.h:296
friend Base_t
The base class needs to call out_str().
Definition: tag.h:338
std::size_t size() const
Return the number of characters of the tag: an integer between 0 and max_size.
Definition: tag.h:326
auto out_str()
Return an Output String Wrapper writing to this Tag.
Definition: tag.h:333
Tag_trivial() noexcept=default
Default constructor: This does not initialize the tag! Use Tag unless you need std::is_trivially_defa...
static auto throwing_make(const std::string_view &sv)
Return a new Tag, initialized with the given string.
Definition: tag.h:318
std::size_t m_size
Definition: tag.h:342
const char * data() const
Return a const pointer to the data.
Definition: tag.h:329
char m_data[max_size]
Tag represented as data + size.
Definition: tag.h:341
Class representing a tag by storing the characters in a member array.
Definition: tag.h:353
Tag() noexcept
Construct a new empty tag.
Definition: tag.h:358
static auto throwing_make(const std::string_view &sv)
Return a new Tag, initialized with the given string.
Definition: tag.h:368
Tag(const Is_tag auto &other) noexcept
Construct a new tag by copying the other tag.
Definition: tag.h:361
Tag_trivial Base_t
Definition: tag.h:354
Helper class to hold lookup tables indexed by ascii characters.
Definition: tag.h:58
std::array< Element_t, 256 > Table_t
Definition: tag.h:62
static Table_t & table()
Return reference to the table.
Definition: tag.h:65
Transform_tp Transform_t
Definition: tag.h:60
std::invoke_result_t< Transform_t, unsigned char > Element_t
Definition: tag.h:61
static Table_t & build_table(std::initializer_list< unsigned char > char_ranges)
Compute the table.
Definition: tag.h:73
Top of the hierarchy, defining the tag format.
Definition: tag.h:96
static constexpr std::ptrdiff_t max_ssize
Definition: tag.h:100
static constexpr std::size_t max_size
The maximum number of characters a tag.
Definition: tag.h:99
static bool is_valid(const std::string_view &sv)
Return true if the given string is a valid tag.
Definition: tag.h:148
static bool is_valid_nonfirst_char(int ch)
Return true if the given character is allowed in a tag, at other positions than the first.
Definition: tag.h:139
static bool is_valid_first_char(int ch)
Return true if the given character is allowed as the first character in a tag.
Definition: tag.h:125
static int get_normalized_first_char(int ch)
Return the character converted to lowercase, if it is allowed as the first character in a tag; otherw...
Definition: tag.h:117
static std::optional< std::size_t > valid_prefix_length(const std::string_view &sv)
Return the length of a tag that appears as an initial prefix of the given string, or no value if ther...
Definition: tag.h:170
static int get_normalized_nonfirst_char(int ch)
Return the character converted to lowercase, if it is allowed in a tag at other positions than the fi...
Definition: tag.h:131
decltype([](unsigned char ch) { return std::tolower(ch) Tolower_t
Lambda function that converts a character to lower case.
Definition: tag.h:104
CRTP base class defining a tag class given an implementation that provides a contiguous buffer as sto...
Definition: tag.h:212
mysql::utils::Return_status assign(const std::string_view &sv)
Copy from the given std::string_view into this tag.
Definition: tag.h:247
auto assign(const Is_tag auto &other)
Copy from the given tag into this tag.
Definition: tag.h:222
void throwing_assign(const std::string_view &sv)
Copy from the given std::string_view into this tag.
Definition: tag.h:235
void clear()
Set the tag to empty.
Definition: tag.h:253
auto assign_and_normalize(const std::string_view &sv)
Copy from the given string_view, changing character case to lower case.
Definition: tag.h:261
CRTP base class that provides a rich API for classes that behave like byte buffers.
Definition: buffer_interface.h:102
Class that serves as the target for encode(..., Is_string_target), which never writes anything and on...
Definition: string_counter.h:40
Class that serves as the target for encode(..., Is_string_target), which writes to a char * buffer wi...
Definition: string_writer.h:43
True if Test is one of the tag classes.
Definition: tag.h:192
Experimental API header.
static int count
Definition: myisam_ftdump.cc:45
uint counter
Definition: mysqlimport.cc:58
void build_table(const std::string &schema, const std::string &table, const std::string &partition, bool is_tmp, bool convert, std::string &dict_name)
Definition: dict0dd.cc:7567
bool index(const std::string &value, const String &search_for, uint32_t *idx)
Definition: contains.h:76
char tolower(const char &ch)
Definition: parsing_helpers.h:41
Definition: gtid.h:47
Definition: gtid.h:45
bool operator!=(const Is_gtid auto &gtid1, const Is_gtid auto &gtid2)
Definition: gtid.h:328
bool operator==(const Is_gtid auto &gtid1, const Is_gtid auto &gtid2)
Definition: gtid.h:323
auto out_str_fixed_nz(mysql::meta::Is_pointer_to_charlike auto first, Size_t &length, Size_t capacity=0)
Return a wrapper around a non-growable, non-null-terminated output buffer, represented using a raw po...
Definition: out_str.h:818
mysql::utils::Return_status out_str_write(const Out_str_t &out_str, const Producer_counter_t &producer_counter, const Producer_writer_t &producer_writer, const Oom_action_t &oom_action=detail::nop)
Given an Is_out_str object, a String_producer_counter, and a String_producer_writer,...
Definition: out_str_write.h:178
Return_status
Simple, strongly-typed enumeration to indicate internal status: ok, error.
Definition: return_status.h:40
@ ok
operation succeeded
noexcept
The return type for any call_and_catch(f, args...) call where f(args...) returns Type.
Definition: call_and_catch.h:76
Experimental API header.