Lightweight 0.20250627.0
Loading...
Searching...
No Matches
SqlFixedString.hpp
1// SPDX-License-Identifier: Apache-2.0
2
3#pragma once
4
5#include "../SqlColumnTypeDefinitions.hpp"
6#include "Core.hpp"
7#include "UnicodeConverter.hpp"
8
9#include <format>
10#include <ranges>
11#include <stdexcept>
12#include <utility>
13
14enum class SqlFixedStringMode : uint8_t
15{
16 FIXED_SIZE,
17 FIXED_SIZE_RIGHT_TRIMMED,
18 VARIABLE_SIZE,
19};
20
21/// SQL fixed-capacity string that mimmicks standard library string/string_view with a fixed-size underlying
22/// buffer.
23///
24/// The underlying storage will not be guaranteed to be `\0`-terminated unless
25/// a call to mutable/const c_str() has been performed.
26///
27/// @ingroup DataTypes
28template <std::size_t N, typename T = char, SqlFixedStringMode Mode = SqlFixedStringMode::FIXED_SIZE>
30{
31 private:
32 T _data[N + 1] {};
33 std::size_t _size = 0;
34
35 public:
36 using value_type = T;
37 using iterator = T*;
38 using const_iterator = T const*;
39 using pointer_type = T*;
40 using const_pointer_type = T const*;
41
42 static constexpr std::size_t Capacity = N;
43 static constexpr SqlFixedStringMode PostRetrieveOperation = Mode;
44
45 /// Constructs a fixed-size string from a string literal.
46 template <std::size_t SourceSize>
47 constexpr LIGHTWEIGHT_FORCE_INLINE SqlFixedString(T const (&text)[SourceSize]):
48 _size { SourceSize - 1 }
49 {
50 static_assert(SourceSize <= N + 1, "RHS string size must not exceed target string's capacity.");
51 std::copy_n(text, SourceSize, _data);
52 }
53
54 LIGHTWEIGHT_FORCE_INLINE constexpr SqlFixedString() noexcept = default;
55 LIGHTWEIGHT_FORCE_INLINE constexpr SqlFixedString(SqlFixedString const&) noexcept = default;
56 LIGHTWEIGHT_FORCE_INLINE constexpr SqlFixedString& operator=(SqlFixedString const&) noexcept = default;
57 LIGHTWEIGHT_FORCE_INLINE constexpr SqlFixedString(SqlFixedString&&) noexcept = default;
58 LIGHTWEIGHT_FORCE_INLINE constexpr SqlFixedString& operator=(SqlFixedString&&) noexcept = default;
59 LIGHTWEIGHT_FORCE_INLINE constexpr ~SqlFixedString() noexcept = default;
60
61 /// Constructs a fixed-size string from a string view.
62 LIGHTWEIGHT_FORCE_INLINE constexpr SqlFixedString(std::basic_string_view<T> s) noexcept:
63 _size { (std::min) (N, s.size()) }
64 {
65 std::copy_n(s.data(), _size, _data); // NOLINT(bugprone-suspicious-stringview-data-usage)
66 }
67
68 /// Constructs a fixed-size string from a string.
69 LIGHTWEIGHT_FORCE_INLINE constexpr SqlFixedString(std::basic_string<T> const& s) noexcept:
70 _size { (std::min) (N, s.size()) }
71 {
72 std::copy_n(s.data(), _size, _data);
73 }
74
75 /// Constructs a fixed-size string from a string pointer and length.
76 LIGHTWEIGHT_FORCE_INLINE constexpr SqlFixedString(T const* s, std::size_t len) noexcept:
77 _size { (std::min) (N, len) }
78 {
79 std::copy_n(s, _size, _data);
80 }
81
82 /// Constructs a fixed-size string from a string pointer and end pointer.
83 LIGHTWEIGHT_FORCE_INLINE constexpr SqlFixedString(T const* s, T const* e) noexcept:
84 _size { (std::min) (N, static_cast<std::size_t>(e - s)) }
85 {
86 std::copy(s, e, _data);
87 }
88
89 LIGHTWEIGHT_FORCE_INLINE void reserve(std::size_t capacity)
90 {
91 if (capacity > N)
92 throw std::length_error(std::format("SqlFixedString: capacity {} exceeds maximum capacity {}", capacity, N));
93 }
94
95 /// Tests if the string is empty.
96 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr bool empty() const noexcept
97 {
98 return _size == 0;
99 }
100
101 /// Returns the size of the string.
102 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr std::size_t size() const noexcept
103 {
104 return _size;
105 }
106
107 LIGHTWEIGHT_FORCE_INLINE /*TODO constexpr*/ void setsize(std::size_t n) noexcept
108 {
109 auto const newSize = (std::min) (n, N);
110 _size = newSize;
111 _data[newSize] = '\0';
112 }
113
114 /// Resizes the string.
115 ///
116 /// This sets the size of the string to `n`. If `n` is greater than the current size,
117 /// capped at the maximum capacity.
118 LIGHTWEIGHT_FORCE_INLINE constexpr void resize(std::size_t n) noexcept
119 {
120 auto const newSize = (std::min) (n, N);
121 _size = newSize;
122 _data[newSize] = '\0';
123 }
124
125 /// Returns the maximum capacity of the string.
126 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr std::size_t capacity() const noexcept
127 {
128 return N;
129 }
130
131 /// Clears the string.
132 LIGHTWEIGHT_FORCE_INLINE constexpr void clear() noexcept
133 {
134 _size = 0;
135 }
136
137 /// Assigns a string literal to the string.
138 template <std::size_t SourceSize>
139 LIGHTWEIGHT_FORCE_INLINE constexpr void assign(T const (&source)[SourceSize]) noexcept
140 {
141 static_assert(SourceSize <= N + 1, "Source string must not overflow the target string's capacity.");
142 _size = SourceSize - 1;
143 std::copy_n(source, SourceSize, _data);
144 }
145
146 /// Assigns a string view to the string.
147 LIGHTWEIGHT_FORCE_INLINE constexpr void assign(std::basic_string_view<T> s) noexcept
148 {
149 _size = (std::min) (N, s.size());
150 std::copy_n(s.data(), _size, _data); // NOLINT(bugprone-suspicious-stringview-data-usage)
151 }
152
153 /// Appends a character to the string.
154 LIGHTWEIGHT_FORCE_INLINE constexpr void push_back(T c) noexcept
155 {
156 if (_size < N)
157 {
158 _data[_size] = c;
159 ++_size;
160 }
161 }
162
163 /// Removes the last character from the string.
164 LIGHTWEIGHT_FORCE_INLINE constexpr void pop_back() noexcept
165 {
166 if (_size > 0)
167 --_size;
168 }
169
170 /// Returns a sub string of the string.
171 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr std::basic_string_view<T> substr(
172 std::size_t offset = 0, std::size_t count = (std::numeric_limits<std::size_t>::max)()) const noexcept
173 {
174 if (offset >= _size)
175 return {};
176 if (count == (std::numeric_limits<std::size_t>::max)())
177 return std::basic_string_view<T>(_data + offset, _size - offset);
178 if (offset + count > _size)
179 return std::basic_string_view<T>(_data + offset, _size - offset);
180 return std::basic_string_view<T>(_data + offset, count);
181 }
182
183 /// Returns a string view of the string.
184 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr std::basic_string_view<T> str() const noexcept
185 {
186 return std::basic_string_view<T> { _data, _size };
187 }
188
189 // clang-format off
190 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr pointer_type c_str() noexcept { _data[_size] = '\0'; return _data; }
191 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr pointer_type data() noexcept { return _data; }
192 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr iterator begin() noexcept { return _data; }
193 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr iterator end() noexcept { return _data + size(); }
194 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr T& at(std::size_t i) noexcept { return _data[i]; }
195 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr T& operator[](std::size_t i) noexcept { return _data[i]; }
196
197 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr const_pointer_type c_str() const noexcept { return _data; }
198 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr const_pointer_type data() const noexcept { return _data; }
199 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr const_iterator begin() const noexcept { return _data; }
200 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr const_iterator end() const noexcept { return _data + size(); }
201 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr T const& at(std::size_t i) const noexcept { return _data[i]; }
202 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr T const& operator[](std::size_t i) const noexcept { return _data[i]; }
203 // clang-format on
204
205 /// Returns a std::basic_string<T> from the string.
206 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr std::basic_string<T> ToString() const noexcept
207 {
208 return { _data, _size };
209 }
210
211 /// Returns a std::basic_string<T> from the string.
212 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr explicit operator std::basic_string<T>() const noexcept
213 {
214 return ToString();
215 }
216
217 /// Returns a std::basic_string_view<T> from the string.
218 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr std::basic_string_view<T> ToStringView() const noexcept
219 {
220 return { _data, _size };
221 }
222
223 /// Returns a std::basic_string_view<T> from the string.
224 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr explicit operator std::basic_string_view<T>() const noexcept
225 {
226 return ToStringView();
227 }
228
229 template <std::size_t OtherSize, SqlFixedStringMode OtherMode>
230 LIGHTWEIGHT_FORCE_INLINE std::weak_ordering operator<=>(
231 SqlFixedString<OtherSize, T, OtherMode> const& other) const noexcept
232 {
233 if ((void*) this == (void*) &other) [[unlikely]]
234 return std::weak_ordering::equivalent;
235
236 for (auto const i: std::views::iota(0U, (std::min) (size(), other.size())))
237 if (auto const cmp = _data[i] <=> other._data[i]; cmp != std::weak_ordering::equivalent) [[unlikely]]
238 return cmp;
239 return size() <=> other.size();
240 }
241
242 template <std::size_t OtherSize, SqlFixedStringMode OtherMode>
243 LIGHTWEIGHT_FORCE_INLINE constexpr bool operator==(SqlFixedString<OtherSize, T, OtherMode> const& other) const noexcept
244 {
245 return (*this <=> other) == std::weak_ordering::equivalent;
246 }
247
248 template <std::size_t OtherSize, SqlFixedStringMode OtherMode>
249 LIGHTWEIGHT_FORCE_INLINE constexpr bool operator!=(SqlFixedString<OtherSize, T, OtherMode> const& other) const noexcept
250 {
251 return !(*this == other);
252 }
253
254 LIGHTWEIGHT_FORCE_INLINE constexpr bool operator==(std::basic_string_view<T> other) const noexcept
255 {
256 return (substr() <=> other) == std::weak_ordering::equivalent;
257 }
258
259 LIGHTWEIGHT_FORCE_INLINE constexpr bool operator!=(std::basic_string_view<T> other) const noexcept
260 {
261 return !(*this == other);
262 }
263};
264
265template <std::size_t N, typename CharT, SqlFixedStringMode Mode>
266struct detail::SqlViewHelper<SqlFixedString<N, CharT, Mode>>
267{
268 static LIGHTWEIGHT_FORCE_INLINE std::basic_string_view<CharT> View(SqlFixedString<N, CharT, Mode> const& str) noexcept
269 {
270 return { str.data(), str.size() };
271 }
272};
273
274namespace detail
275{
276
277template <typename>
278struct IsSqlFixedStringTypeImpl: std::false_type
279{
280};
281
282template <std::size_t N, typename T, SqlFixedStringMode Mode>
283struct IsSqlFixedStringTypeImpl<SqlFixedString<N, T, Mode>>: std::true_type
284{
285};
286
287} // namespace detail
288
289template <typename T>
290constexpr bool IsSqlFixedString = detail::IsSqlFixedStringTypeImpl<T>::value;
291
292/// Fixed-size string of element type `char` with a capacity of `N` characters.
293///
294/// @ingroup DataTypes
295template <std::size_t N>
297
298/// Fixed-size string of element type `char16_t` with a capacity of `N` characters.
299///
300/// @ingroup DataTypes
301template <std::size_t N>
303
304/// Fixed-size string of element type `char32_t` with a capacity of `N` characters.
305///
306/// @ingroup DataTypes
307template <std::size_t N>
309
310/// Fixed-size string of element type `wchar_t` with a capacity of `N` characters.
311///
312/// @ingroup DataTypes
313template <std::size_t N>
315
316/// Fixed-size (right-trimmed) string of element type `char` with a capacity of `N` characters.
317///
318/// @ingroup DataTypes
319template <std::size_t N, typename T = char>
321
322template <std::size_t N, typename T = char>
324
325template <std::size_t N, typename T, SqlFixedStringMode Mode>
326struct SqlBasicStringOperations<SqlFixedString<N, T, Mode>>
327{
328 using CharType = T;
329 using ValueType = SqlFixedString<N, CharType, Mode>;
330 static constexpr auto ColumnType = []() constexpr -> SqlColumnTypeDefinition {
331 if constexpr (std::same_as<CharType, char>)
332 {
333 if constexpr (Mode == SqlFixedStringMode::VARIABLE_SIZE)
334 return SqlColumnTypeDefinitions::Varchar { N };
335 else
336 return SqlColumnTypeDefinitions::Char { N };
337 }
338 else
339 {
340 if constexpr (Mode == SqlFixedStringMode::VARIABLE_SIZE)
341 return SqlColumnTypeDefinitions::NVarchar { N };
342 else
343 return SqlColumnTypeDefinitions::NChar { N };
344 }
345 }();
346
347 static CharType const* Data(ValueType const* str) noexcept
348 {
349 return str->data();
350 }
351
352 static CharType* Data(ValueType* str) noexcept
353 {
354 return str->data();
355 }
356
357 static SQLULEN Size(ValueType const* str) noexcept
358 {
359 return str->size();
360 }
361
362 static void Clear(ValueType* str) noexcept
363 {
364 str->clear();
365 }
366
367 static void Reserve(ValueType* str, size_t capacity) noexcept
368 {
369 str->reserve((std::min) (N, capacity));
370 str->resize((std::min) (N, capacity));
371 }
372
373 static void Resize(ValueType* str, SQLLEN indicator) noexcept
374 {
375 str->resize(indicator);
376 }
377
378 static void PostProcessOutputColumn(ValueType* result, SQLLEN indicator)
379 {
380 switch (indicator)
381 {
382 case SQL_NULL_DATA:
383 result->clear();
384 break;
385 case SQL_NO_TOTAL:
386 result->resize(N);
387 break;
388 default: {
389 auto const len = (std::min) (N, static_cast<std::size_t>(indicator) / sizeof(CharType));
390 result->setsize(len);
391
392 if constexpr (Mode == SqlFixedStringMode::FIXED_SIZE_RIGHT_TRIMMED)
393 {
394 TrimRight(result, indicator);
395 }
396 break;
397 }
398 }
399 }
400
401 LIGHTWEIGHT_FORCE_INLINE static void TrimRight(ValueType* boundOutputString, SQLLEN indicator) noexcept
402 {
403 size_t n = (std::min) ((size_t) indicator, N - 1);
404 while (n > 0 && std::isspace((*boundOutputString)[n - 1]))
405 --n;
406 boundOutputString->setsize(n);
407 }
408};
409
410template <std::size_t N, typename T, SqlFixedStringMode P>
411struct std::formatter<SqlFixedString<N, T, P>>: std::formatter<std::string>
412{
413 using value_type = SqlFixedString<N, T, P>;
414 auto format(value_type const& text, format_context& ctx) const -> format_context::iterator
415 {
416 if constexpr (std::same_as<T, wchar_t>)
417 return std::formatter<std::string>::format((char const*) ToUtf8(text.ToStringView()).c_str(), ctx);
418 else
419 return std::formatter<std::string>::format(text.c_str(), ctx);
420 }
421};
LIGHTWEIGHT_FORCE_INLINE constexpr std::basic_string_view< T > substr(std::size_t offset=0, std::size_t count=(std::numeric_limits< std::size_t >::max)()) const noexcept
Returns a sub string of the string.
LIGHTWEIGHT_FORCE_INLINE constexpr void assign(T const (&source)[SourceSize]) noexcept
Assigns a string literal to the string.
LIGHTWEIGHT_FORCE_INLINE constexpr void resize(std::size_t n) noexcept
LIGHTWEIGHT_FORCE_INLINE constexpr void assign(std::basic_string_view< T > s) noexcept
Assigns a string view to the string.
LIGHTWEIGHT_FORCE_INLINE constexpr bool empty() const noexcept
Tests if the string is empty.
LIGHTWEIGHT_FORCE_INLINE constexpr std::basic_string_view< T > str() const noexcept
Returns a string view of the string.
LIGHTWEIGHT_FORCE_INLINE constexpr void clear() noexcept
Clears the string.
LIGHTWEIGHT_FORCE_INLINE constexpr std::size_t size() const noexcept
Returns the size of the string.
LIGHTWEIGHT_FORCE_INLINE constexpr void push_back(T c) noexcept
Appends a character to the string.
constexpr LIGHTWEIGHT_FORCE_INLINE SqlFixedString(T const (&text)[SourceSize])
Constructs a fixed-size string from a string literal.
LIGHTWEIGHT_FORCE_INLINE constexpr void pop_back() noexcept
Removes the last character from the string.
LIGHTWEIGHT_FORCE_INLINE constexpr SqlFixedString(T const *s, T const *e) noexcept
Constructs a fixed-size string from a string pointer and end pointer.
LIGHTWEIGHT_FORCE_INLINE constexpr std::size_t capacity() const noexcept
Returns the maximum capacity of the string.
LIGHTWEIGHT_FORCE_INLINE constexpr SqlFixedString(std::basic_string< T > const &s) noexcept
Constructs a fixed-size string from a string.
LIGHTWEIGHT_FORCE_INLINE constexpr std::basic_string< T > ToString() const noexcept
Returns a std::basic_string<T> from the string.
LIGHTWEIGHT_FORCE_INLINE constexpr std::basic_string_view< T > ToStringView() const noexcept
Returns a std::basic_string_view<T> from the string.
LIGHTWEIGHT_FORCE_INLINE constexpr SqlFixedString(T const *s, std::size_t len) noexcept
Constructs a fixed-size string from a string pointer and length.
LIGHTWEIGHT_API std::u8string ToUtf8(std::u32string_view u32InputString)