iceberg-cpp
Loading...
Searching...
No Matches
string_util.h
Go to the documentation of this file.
1/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20#pragma once
21
24
25#include <algorithm>
26#include <cerrno>
27#include <charconv>
28#include <optional>
29#include <string>
30#include <string_view>
31#include <type_traits>
32#include <typeinfo>
33#include <utility>
34#include <vector>
35
38#include "iceberg/result.h"
39
40namespace iceberg {
41
42template <typename T>
43concept FromChars = requires(const char* p, T& v) { std::from_chars(p, p, v); };
44
45class ICEBERG_EXPORT StringUtils {
46 public:
62 static std::string ToLower(std::string_view str);
63
72 static std::string ToUpper(std::string_view str);
73
79 static bool EqualsIgnoreCase(std::string_view lhs, std::string_view rhs) {
80 const std::optional<bool> fast = AsciiEqualsIgnoreCase(lhs, rhs);
81 return fast.has_value() ? *fast : (ToLower(lhs) == ToLower(rhs));
82 }
83
90 static bool StartsWithIgnoreCase(std::string_view str, std::string_view prefix) {
91 if (prefix.size() <= str.size()) {
92 const std::optional<bool> fast =
93 AsciiEqualsIgnoreCase(str.substr(0, prefix.size()), prefix);
94 if (fast.has_value()) {
95 return *fast;
96 }
97 }
98 return ToLower(str).starts_with(ToLower(prefix));
99 }
100
102 static size_t CodePointCount(std::string_view str) {
103 size_t count = 0;
104 for (char i : str) {
105 if ((i & 0xC0) != 0x80) {
106 count++;
107 }
108 }
109 return count;
110 }
111
112 template <typename T>
113 requires std::is_arithmetic_v<T> && FromChars<T> && (!std::same_as<T, bool>)
114 static Result<T> ParseNumber(std::string_view str) {
115 T value = 0;
116 auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), value);
117 if (ec == std::errc()) [[likely]] {
118 if (ptr != str.data() + str.size()) {
119 return InvalidArgument("Failed to parse {} from string '{}': trailing characters",
120 typeid(T).name(), str);
121 }
122 return value;
123 }
124 if (ec == std::errc::invalid_argument) {
125 return InvalidArgument("Failed to parse {} from string '{}': invalid argument",
126 typeid(T).name(), str);
127 }
128 if (ec == std::errc::result_out_of_range) {
129 return InvalidArgument("Failed to parse {} from string '{}': value out of range",
130 typeid(T).name(), str);
131 }
133 }
134
137 static Result<std::vector<uint8_t>> HexStringToBytes(std::string_view hex);
138
139 template <typename T>
140 requires std::is_floating_point_v<T> && (!FromChars<T>)
141 static Result<T> ParseNumber(std::string_view str) {
142 T value{};
143 // strto* require null-terminated input; string_view does not guarantee it.
144 std::string owned(str);
145 const char* start = owned.c_str();
146 char* end = nullptr;
147 errno = 0;
148
149 if constexpr (std::same_as<T, float>) {
150 value = std::strtof(start, &end);
151 } else if constexpr (std::same_as<T, double>) {
152 value = std::strtod(start, &end);
153 } else {
154 value = std::strtold(start, &end);
155 }
156
157 if (end == start || end != start + static_cast<std::ptrdiff_t>(owned.size())) {
158 return InvalidArgument("Failed to parse {} from string '{}': invalid argument",
159 typeid(T).name(), str);
160 }
161 if (errno == ERANGE) {
162 return InvalidArgument("Failed to parse {} from string '{}': value out of range",
163 typeid(T).name(), str);
164 }
165 return value;
166 }
167
175 static bool ParseBoolean(std::string_view str) { return EqualsIgnoreCase(str, "true"); }
176
177 private:
178 // ASCII-only case mappings. These avoid std::toupper/std::tolower, which are
179 // locale-dependent and have undefined behavior for negative char values.
180 static constexpr char ToUpperAscii(char c) noexcept {
181 return (c >= 'a' && c <= 'z') ? static_cast<char>(c - 'a' + 'A') : c;
182 }
183 static constexpr char ToLowerAscii(char c) noexcept {
184 return (c >= 'A' && c <= 'Z') ? static_cast<char>(c - 'A' + 'a') : c;
185 }
186
187 // True if c is a 7-bit ASCII byte (< 0x80). The cast is required because char may be
188 // signed, which would make bytes >= 0x80 compare as negative.
189 static constexpr bool IsAsciiByte(char c) noexcept {
190 return (static_cast<unsigned char>(c) & 0x80) == 0;
191 }
192
193 // Case-insensitive equality decided in a single byte-wise pass, without allocating.
194 // Returns nullopt once a byte of either operand is non-ASCII, because folding can then
195 // be non-ASCII and length-changing (e.g. "İ" (U+0130) -> "i"), which only ToLower
196 // knows.
197 static std::optional<bool> AsciiEqualsIgnoreCase(std::string_view a,
198 std::string_view b) {
199 const size_t n = std::min(a.size(), b.size());
200 for (size_t i = 0; i < n; ++i) {
201 if (!IsAsciiByte(a[i]) || !IsAsciiByte(b[i])) {
202 return std::nullopt;
203 }
204 if (ToLowerAscii(a[i]) != ToLowerAscii(b[i])) {
205 return false;
206 }
207 }
208 return a.size() == b.size();
209 }
210};
211
216struct ICEBERG_EXPORT StringHash {
217 using hash_type = std::hash<std::string_view>;
218 using is_transparent = void;
219
220 std::size_t operator()(std::string_view str) const { return hash_type{}(str); }
221 std::size_t operator()(const char* str) const { return hash_type{}(str); }
222 std::size_t operator()(const std::string& str) const { return hash_type{}(str); }
223};
224
226struct ICEBERG_EXPORT StringEqual {
227 using is_transparent = void;
228
229 bool operator()(std::string_view lhs, std::string_view rhs) const { return lhs == rhs; }
230 bool operator()(const std::string& lhs, const std::string& rhs) const {
231 return lhs == rhs;
232 }
233};
234
236struct ICEBERG_EXPORT StringLess {
237 using is_transparent = void;
238
239 bool operator()(std::string_view lhs, std::string_view rhs) const { return lhs < rhs; }
240};
241
242} // namespace iceberg
Definition string_util.h:45
static bool StartsWithIgnoreCase(std::string_view str, std::string_view prefix)
Case-insensitive prefix test using Unicode simple (1:1) case mapping.
Definition string_util.h:90
static size_t CodePointCount(std::string_view str)
Count the number of code points in a UTF-8 string.
Definition string_util.h:102
static std::string ToUpper(std::string_view str)
Upper-case the ASCII letters (a-z) in a string; all other bytes, including multi-byte UTF-8 sequences...
static bool ParseBoolean(std::string_view str)
Parse a boolean from its string representation, ignoring case.
Definition string_util.h:175
static std::string ToLower(std::string_view str)
Lower-case a UTF-8 string using Unicode simple (1:1) case mapping.
static Result< std::vector< uint8_t > > HexStringToBytes(std::string_view hex)
Decode a hex string (upper or lower case) into bytes. Returns an error if the string has odd length o...
static bool EqualsIgnoreCase(std::string_view lhs, std::string_view rhs)
Case-insensitive equality using Unicode simple (1:1) case mapping.
Definition string_util.h:79
Definition expected.h:1199
Definition string_util.h:43
Define symbol visibility macros for core Iceberg APIs.
Core Apache Iceberg C++ APIs.
Definition arrow_io_util.h:34
void unreachable() noexcept
Marks a branch the program can never take.
Definition unreachable.h:35
Define Result, Status, and error helpers.
Transparent equality function that supports std::string_view as lookup key.
Definition string_util.h:226
Transparent hash function that supports std::string_view as lookup key.
Definition string_util.h:216
Transparent less function that supports std::string_view as lookup key.
Definition string_util.h:236
Portable stand-in for std::unreachable.