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