iceberg-cpp
Loading...
Searching...
No Matches
string_util.h
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
22#include <algorithm>
23#include <cerrno>
24#include <charconv>
25#include <ranges>
26#include <string>
27#include <string_view>
28#include <type_traits>
29#include <typeinfo>
30#include <utility>
31#include <vector>
32
33#include "iceberg/iceberg_export.h"
34#include "iceberg/result.h"
35
36namespace iceberg {
37
38template <typename T>
39concept FromChars = requires(const char* p, T& v) { std::from_chars(p, p, v); };
40
41class ICEBERG_EXPORT StringUtils {
42 public:
43 static std::string ToLower(std::string_view str) {
44 return str | std::ranges::views::transform([](char c) { return std::tolower(c); }) |
45 std::ranges::to<std::string>();
46 }
47
48 static std::string ToUpper(std::string_view str) {
49 return str | std::ranges::views::transform([](char c) { return std::toupper(c); }) |
50 std::ranges::to<std::string>();
51 }
52
53 static bool EqualsIgnoreCase(std::string_view lhs, std::string_view rhs) {
54 return std::ranges::equal(
55 lhs, rhs, [](char lc, char rc) { return std::tolower(lc) == std::tolower(rc); });
56 }
57
58 static bool StartsWithIgnoreCase(std::string_view str, std::string_view prefix) {
59 if (str.size() < prefix.size()) {
60 return false;
61 }
62 return EqualsIgnoreCase(str.substr(0, prefix.size()), prefix);
63 }
64
66 static size_t CodePointCount(std::string_view str) {
67 size_t count = 0;
68 for (char i : str) {
69 if ((i & 0xC0) != 0x80) {
70 count++;
71 }
72 }
73 return count;
74 }
75
76 template <typename T>
77 requires std::is_arithmetic_v<T> && FromChars<T> && (!std::same_as<T, bool>)
78 static Result<T> ParseNumber(std::string_view str) {
79 T value = 0;
80 auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), value);
81 if (ec == std::errc()) [[likely]] {
82 if (ptr != str.data() + str.size()) {
83 return InvalidArgument("Failed to parse {} from string '{}': trailing characters",
84 typeid(T).name(), str);
85 }
86 return value;
87 }
88 if (ec == std::errc::invalid_argument) {
89 return InvalidArgument("Failed to parse {} from string '{}': invalid argument",
90 typeid(T).name(), str);
91 }
92 if (ec == std::errc::result_out_of_range) {
93 return InvalidArgument("Failed to parse {} from string '{}': value out of range",
94 typeid(T).name(), str);
95 }
96 std::unreachable();
97 }
98
101 static Result<std::vector<uint8_t>> HexStringToBytes(std::string_view hex);
102
103 template <typename T>
104 requires std::is_floating_point_v<T> && (!FromChars<T>)
105 static Result<T> ParseNumber(std::string_view str) {
106 T value{};
107 // strto* require null-terminated input; string_view does not guarantee it.
108 std::string owned(str);
109 const char* start = owned.c_str();
110 char* end = nullptr;
111 errno = 0;
112
113 if constexpr (std::same_as<T, float>) {
114 value = std::strtof(start, &end);
115 } else if constexpr (std::same_as<T, double>) {
116 value = std::strtod(start, &end);
117 } else {
118 value = std::strtold(start, &end);
119 }
120
121 if (end == start || end != start + static_cast<std::ptrdiff_t>(owned.size())) {
122 return InvalidArgument("Failed to parse {} from string '{}': invalid argument",
123 typeid(T).name(), str);
124 }
125 if (errno == ERANGE) {
126 return InvalidArgument("Failed to parse {} from string '{}': value out of range",
127 typeid(T).name(), str);
128 }
129 return value;
130 }
131};
132
137struct ICEBERG_EXPORT StringHash {
138 using hash_type = std::hash<std::string_view>;
139 using is_transparent = void;
140
141 std::size_t operator()(std::string_view str) const { return hash_type{}(str); }
142 std::size_t operator()(const char* str) const { return hash_type{}(str); }
143 std::size_t operator()(const std::string& str) const { return hash_type{}(str); }
144};
145
147struct ICEBERG_EXPORT StringEqual {
148 using is_transparent = void;
149
150 bool operator()(std::string_view lhs, std::string_view rhs) const { return lhs == rhs; }
151 bool operator()(const std::string& lhs, const std::string& rhs) const {
152 return lhs == rhs;
153 }
154};
155
156} // namespace iceberg
Definition string_util.h:41
static size_t CodePointCount(std::string_view str)
Count the number of code points in a UTF-8 string.
Definition string_util.h:66
Definition string_util.h:39
Transparent equality function that supports std::string_view as lookup key.
Definition string_util.h:147
Transparent hash function that supports std::string_view as lookup key.
Definition string_util.h:137