libpqxx  7.7.0
util.hxx
1 /* Various utility definitions for libpqxx.
2  *
3  * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/util instead.
4  *
5  * Copyright (c) 2000-2022, Jeroen T. Vermeulen.
6  *
7  * See COPYING for copyright license. If you did not receive a file called
8  * COPYING with this source code, please notify the distributor of this
9  * mistake, or contact the author.
10  */
11 #ifndef PQXX_H_UTIL
12 #define PQXX_H_UTIL
13 
14 #include <cctype>
15 #include <cstdio>
16 #include <functional>
17 #include <iterator>
18 #include <limits>
19 #include <memory>
20 #include <stdexcept>
21 #include <string>
22 #include <string_view>
23 #include <type_traits>
24 #include <typeinfo>
25 #include <utility>
26 #include <vector>
27 
28 #if __has_include(<version>)
29 # include <version>
30 #endif
31 
32 #include "pqxx/except.hxx"
33 #include "pqxx/internal/encodings.hxx"
34 #include "pqxx/types.hxx"
35 #include "pqxx/version.hxx"
36 
37 
39 namespace pqxx
40 {}
41 
42 #include <pqxx/internal/libpq-forward.hxx>
43 
44 
46 namespace pqxx::internal
47 {
48 
49 
50 // C++20: Use concept to express LEFT and RIGHT must be integral types.
52 template<typename LEFT, typename RIGHT>
53 inline constexpr bool cmp_less(LEFT lhs, RIGHT rhs)
54 {
55 #if defined(PQXX_HAVE_CMP)
56  return std::cmp_less(lhs, rhs);
57 #else
58  if constexpr (std::is_signed_v<LEFT> == std::is_signed_v<RIGHT>)
59  return lhs < rhs;
60  else if constexpr (std::is_signed_v<LEFT>)
61  return (lhs <= 0) ? true : (std::make_unsigned_t<LEFT>(lhs) < rhs);
62  else
63  return (rhs <= 0) ? false : (lhs < std::make_unsigned_t<RIGHT>(rhs));
64 #endif
65 }
66 
67 
68 // C++20: Use concept to express LEFT and RIGHT must be integral types.
70 template<typename LEFT, typename RIGHT>
71 inline constexpr bool cmp_greater(LEFT lhs, RIGHT rhs)
72 {
73 #if defined(PQXX_HAVE_CMP)
74  return std::cmp_greater(lhs, rhs);
75 #else
76  return cmp_less(rhs, lhs);
77 #endif
78 }
79 
80 
81 // C++20: Use concept to express LEFT and RIGHT must be integral types.
83 template<typename LEFT, typename RIGHT>
84 inline constexpr bool cmp_less_equal(LEFT lhs, RIGHT rhs)
85 {
86 #if defined(PQXX_HAVE_CMP)
87  return std::cmp_less_equal(lhs, rhs);
88 #else
89  return not cmp_less(rhs, lhs);
90 #endif
91 }
92 
93 
94 // C++20: Use concept to express LEFT and RIGHT must be integral types.
96 template<typename LEFT, typename RIGHT>
97 inline constexpr bool cmp_greater_equal(LEFT lhs, RIGHT rhs)
98 {
99 #if defined(PQXX_HAVE_CMP)
100  return std::cmp_greater_equal(lhs, rhs);
101 #else
102  return not cmp_less(lhs, rhs);
103 #endif
104 }
105 
106 
108 
111 [[nodiscard]] inline std::string cat2(std::string_view x, std::string_view y)
112 {
113  std::string buf;
114  auto const xs{std::size(x)}, ys{std::size(y)};
115  buf.resize(xs + ys);
116  x.copy(std::data(buf), xs);
117  y.copy(std::data(buf) + xs, ys);
118  return buf;
119 }
120 } // namespace pqxx::internal
121 
122 
123 namespace pqxx
124 {
125 using namespace std::literals;
126 
128 template<typename... T> inline void ignore_unused(T &&...) {}
129 
130 
132 
135 template<typename TO, typename FROM>
136 inline TO check_cast(FROM value, std::string_view description)
137 {
138  static_assert(std::is_arithmetic_v<FROM>);
139  static_assert(std::is_arithmetic_v<TO>);
140  static_assert(std::is_integral_v<FROM> == std::is_integral_v<TO>);
141 
142  // The rest of this code won't quite work for bool, but bool is trivially
143  // convertible to other arithmetic types as far as I can see.
144  if constexpr (std::is_same_v<FROM, bool>)
145  return static_cast<TO>(value);
146 
147  // Depending on our "if constexpr" conditions, this parameter may not be
148  // needed. Some compilers will warn.
149  ignore_unused(description);
150 
151  using from_limits = std::numeric_limits<decltype(value)>;
152  using to_limits = std::numeric_limits<TO>;
153  if constexpr (std::is_signed_v<FROM>)
154  {
155  if constexpr (std::is_signed_v<TO>)
156  {
157  if (value < to_limits::lowest())
158  throw range_error{internal::cat2("Cast underflow: "sv, description)};
159  }
160  else
161  {
162  // FROM is signed, but TO is not. Treat this as a special case, because
163  // there may not be a good broader type in which the compiler can even
164  // perform our check.
165  if (value < 0)
167  "Casting negative value to unsigned type: "sv, description)};
168  }
169  }
170  else
171  {
172  // No need to check: the value is unsigned so can't fall below the range
173  // of the TO type.
174  }
175 
176  if constexpr (std::is_integral_v<FROM>)
177  {
178  using unsigned_from = std::make_unsigned_t<FROM>;
179  using unsigned_to = std::make_unsigned_t<TO>;
180  // C++20: constinit.
181  constexpr auto from_max{static_cast<unsigned_from>((from_limits::max)())};
182  // C++20: constinit.
183  constexpr auto to_max{static_cast<unsigned_to>((to_limits::max)())};
184  if constexpr (from_max > to_max)
185  {
186  if (internal::cmp_greater(value, to_max))
187  throw range_error{internal::cat2("Cast overflow: "sv, description)};
188  }
189  }
190  else if constexpr ((from_limits::max)() > (to_limits::max)())
191  {
192  if (value > (to_limits::max)())
193  throw range_error{internal::cat2("Cast overflow: ", description)};
194  }
195 
196  return static_cast<TO>(value);
197 }
198 
199 
221 inline PQXX_PRIVATE void check_version()
222 {
223  // There is no particular reason to do this here in @ref connection, except
224  // to ensure that every meaningful libpqxx client will execute it. The call
225  // must be in the execution path somewhere or the compiler won't try to link
226  // it. We can't use it to initialise a global or class-static variable,
227  // because a smart compiler might resolve it at compile time.
228  //
229  // On the other hand, we don't want to make a useless function call too
230  // often for performance reasons. A local static variable is initialised
231  // only on the definition's first execution. Compilers will be well
232  // optimised for this behaviour, so there's a minimal one-time cost.
233  static auto const version_ok{internal::PQXX_VERSION_CHECK()};
234  ignore_unused(version_ok);
235 }
236 
237 
239 
241 struct PQXX_LIBEXPORT thread_safety_model
242 {
244  bool safe_libpq = false;
245 
247 
253  bool safe_kerberos = false;
254 
256  std::string description;
257 };
258 
259 
261 [[nodiscard]] PQXX_LIBEXPORT thread_safety_model describe_thread_safety();
262 
263 
264 #if defined(PQXX_HAVE_CONCEPTS)
265 # define PQXX_POTENTIAL_BINARY_ARG pqxx::potential_binary
266 #else
267 # define PQXX_POTENTIAL_BINARY_ARG typename
268 #endif
269 
271 
288 template<PQXX_POTENTIAL_BINARY_ARG TYPE>
289 std::basic_string_view<std::byte> binary_cast(TYPE const &data)
290 {
291  static_assert(sizeof(value_type<TYPE>) == 1);
292  return {
293  reinterpret_cast<std::byte const *>(
294  const_cast<strip_t<decltype(*std::data(data))> const *>(
295  std::data(data))),
296  std::size(data)};
297 }
298 
299 
300 #if defined(PQXX_HAVE_CONCEPTS)
301 template<typename CHAR>
302 concept char_sized = (sizeof(CHAR) == 1);
303 # define PQXX_CHAR_SIZED_ARG char_sized
304 #else
305 # define PQXX_CHAR_SIZED_ARG typename
306 #endif
307 
309 
316 template<PQXX_CHAR_SIZED_ARG CHAR, typename SIZE>
317 std::basic_string_view<std::byte> binary_cast(CHAR const *data, SIZE size)
318 {
319  static_assert(sizeof(CHAR) == 1);
320  return {
321  reinterpret_cast<std::byte const *>(data),
322  check_cast<std::size_t>(size, "binary data size")};
323 }
324 
325 
326 // C++20: constinit.
328 constexpr oid oid_none{0};
329 } // namespace pqxx
330 
331 
333 
342 namespace pqxx::internal
343 {
344 using namespace std::literals;
345 
346 
348 
352 template<typename CHAR> bool is_digit(CHAR c)
353 {
354  return (c >= '0') and (c <= '9');
355 }
356 
357 
359 
361 [[nodiscard]] std::string
362 describe_object(std::string_view class_name, std::string_view name);
363 
364 
366 
378  void const *old_guest, std::string_view old_class, std::string_view old_name,
379  void const *new_guest, std::string_view new_class,
380  std::string_view new_name);
381 
382 
384 
388  void const *old_guest, std::string_view old_class, std::string_view old_name,
389  void const *new_guest, std::string_view new_class,
390  std::string_view new_name);
391 
392 
394 
397 constexpr std::size_t size_esc_bin(std::size_t binary_bytes) noexcept
398 {
399  return 2 + (2 * binary_bytes) + 1;
400 }
401 
402 
404 
406 constexpr std::size_t size_unesc_bin(std::size_t escaped_bytes) noexcept
407 {
408  return (escaped_bytes - 2) / 2;
409 }
410 
411 
412 // TODO: Use actual binary type for "data".
414 
419 void PQXX_LIBEXPORT
420 esc_bin(std::basic_string_view<std::byte> binary_data, char buffer[]) noexcept;
421 
422 
424 std::string PQXX_LIBEXPORT
425 esc_bin(std::basic_string_view<std::byte> binary_data);
426 
427 
429 void PQXX_LIBEXPORT
430 unesc_bin(std::string_view escaped_data, std::byte buffer[]);
431 
432 
434 std::basic_string<std::byte>
435  PQXX_LIBEXPORT unesc_bin(std::string_view escaped_data);
436 
437 
439 template<typename T> auto ssize(T const &c)
440 {
441 #if defined(__cpp_lib_ssize) && __cplusplus >= __cpp_lib_ssize
442  return std::ssize(c);
443 #else
444  using signed_t = std::make_signed_t<decltype(std::size(c))>;
445  return static_cast<signed_t>(std::size(c));
446 #endif // __cpp_lib_ssize
447 }
448 
449 
451 
454 void PQXX_LIBEXPORT wait_for(unsigned int microseconds);
455 } // namespace pqxx::internal
456 #endif
void esc_bin(std::basic_string_view< std::byte > binary_data, char buffer[]) noexcept
Hex-escape binary data into a buffer.
Definition: util.cxx:148
std::remove_cv_t< std::remove_reference_t< TYPE > > strip_t
Remove any constness, volatile, and reference-ness from a type.
Definition: types.hxx:87
int PQXX_VERSION_CHECK() noexcept
Library version check stub.
Definition: version.cxx:18
void unesc_bin(std::string_view escaped_data, std::byte buffer[])
Reconstitute binary data from its escaped version.
Definition: util.cxx:179
auto ssize(T const &c)
Transitional: std::ssize(), or custom implementation if not available.
Definition: util.hxx:439
bool is_digit(CHAR c)
A safer and more generic replacement for std::isdigit.
Definition: util.hxx:352
constexpr std::size_t size_unesc_bin(std::size_t escaped_bytes) noexcept
Compute binary size from the size of its escaped version.
Definition: util.hxx:406
std::string description
A human-readable description of any thread-safety issues.
Definition: util.hxx:256
thread_safety_model describe_thread_safety()
Describe thread safety available in this build.
Definition: util.cxx:54
TO check_cast(FROM value, std::string_view description)
Cast a numeric value to another type, or throw if it underflows/overflows.
Definition: util.hxx:136
void wait_for(unsigned int microseconds)
Wait.
Definition: util.cxx:218
constexpr bool cmp_less(LEFT lhs, RIGHT rhs)
C++20 std::cmp_less, or workaround if not available.
Definition: util.hxx:53
std::string cat2(std::string_view x, std::string_view y)
Efficiently concatenate two strings.
Definition: util.hxx:111
std::string describe_object(std::string_view class_name, std::string_view name)
Describe an object for humans, based on class name and optional name.
Definition: util.cxx:72
void check_unique_unregister(void const *old_guest, std::string_view old_class, std::string_view old_name, void const *new_guest, std::string_view new_class, std::string_view new_name)
Like check_unique_register, but for un-registering a guest.
Definition: util.cxx:99
constexpr std::size_t size_esc_bin(std::size_t binary_bytes) noexcept
Compute buffer size needed to escape binary data for use as a BYTEA.
Definition: util.hxx:397
constexpr bool cmp_greater_equal(LEFT lhs, RIGHT rhs)
C++20 std::cmp_greater_equal, or workaround if not available.
Definition: util.hxx:97
void ignore_unused(T &&...)
Suppress compiler warning about an unused item.
Definition: util.hxx:128
std::basic_string_view< std::byte > binary_cast(TYPE const &data)
Cast binary data to a type that libpqxx will recognise as binary.
Definition: util.hxx:289
void check_unique_register(void const *old_guest, std::string_view old_class, std::string_view old_name, void const *new_guest, std::string_view new_class, std::string_view new_name)
Check validity of registering a new "guest" in a "host.".
Definition: util.cxx:82
constexpr bool cmp_less_equal(LEFT lhs, RIGHT rhs)
C++20 std::cmp_less_equal, or workaround if not available.
Definition: util.hxx:84
Internal items for libpqxx&#39; own use. Do not use these yourself.
Definition: composite.hxx:79
constexpr oid oid_none
The "null" oid.
Definition: util.hxx:328
void check_version()
Definition: util.hxx:221
The home of all libpqxx classes, functions, templates, etc.
Definition: array.hxx:22
Something is out of range, similar to std::out_of_range.
Definition: except.hxx:189
Descriptor of library&#39;s thread-safety model.
Definition: util.hxx:241
decltype(*std::begin(std::declval< CONTAINER >())) value_type
The type of a container&#39;s elements.
Definition: types.hxx:103
constexpr bool cmp_greater(LEFT lhs, RIGHT rhs)
C++20 std::cmp_greater, or workaround if not available.
Definition: util.hxx:71