This repository has been archived on 2025-09-14. You can view files and clone it, but cannot push or open issues or pull requests.
Files
zhangyang-zerotierone/ext/libpqxx-7.7.3/src/util.cxx
2022-06-24 10:12:36 -07:00

195 lines
5.2 KiB
C++

/** Various utility functions.
*
* Copyright (c) 2000-2022, Jeroen T. Vermeulen.
*
* See COPYING for copyright license. If you did not receive a file called
* COPYING with this source code, please notify the distributor of this
* mistake, or contact the author.
*/
#include "pqxx-source.hxx"
#include <cerrno>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <new>
extern "C"
{
#include <libpq-fe.h>
}
#include "pqxx/internal/header-pre.hxx"
#include "pqxx/except.hxx"
#include "pqxx/internal/concat.hxx"
#include "pqxx/util.hxx"
#include "pqxx/internal/header-post.hxx"
using namespace std::literals;
pqxx::thread_safety_model PQXX_COLD pqxx::describe_thread_safety()
{
thread_safety_model model;
model.safe_libpq = (PQisthreadsafe() != 0);
// Sadly I'm not aware of any way to avoid this just yet.
model.safe_kerberos = false;
model.description = internal::concat(
(model.safe_libpq ? ""sv :
"Using a libpq build that is not thread-safe.\n"sv),
(model.safe_kerberos ?
""sv :
"Kerberos is not thread-safe. If your application uses Kerberos, "
"protect all calls to Kerberos or libpqxx using a global lock.\n"sv));
return model;
}
std::string pqxx::internal::describe_object(
std::string_view class_name, std::string_view obj_name)
{
if (std::empty(obj_name))
return std::string{class_name};
else
return pqxx::internal::concat(class_name, " '", obj_name, "'");
}
void pqxx::internal::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)
{
if (new_guest == nullptr)
throw internal_error{"Null pointer registered."};
if (old_guest != nullptr)
throw usage_error{
(old_guest == new_guest) ?
concat("Started twice: ", describe_object(old_class, old_name), ".") :
concat(
"Started new ", describe_object(new_class, new_name), " while ",
describe_object(new_class, new_name), " was still active.")};
}
void pqxx::internal::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)
{
if (new_guest != old_guest)
{
PQXX_UNLIKELY
if (new_guest == nullptr)
throw usage_error{concat(
"Expected to close ", describe_object(old_class, old_name),
", but got null pointer instead.")};
if (old_guest == nullptr)
throw usage_error{concat(
"Closed while not open: ", describe_object(new_class, new_name))};
else
throw usage_error{concat(
"Closed ", describe_object(new_class, new_name),
"; expected to close ", describe_object(old_class, old_name))};
}
}
namespace
{
constexpr char hex_digits[] = {'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
/// Translate a number (must be between 0 and 16 exclusive) to a hex digit.
constexpr char hex_digit(int c) noexcept
{
return hex_digits[c];
}
/// Translate a hex digit to a nibble. Return -1 if it's not a valid digit.
constexpr int nibble(int c) noexcept
{
if (c >= '0' and c <= '9')
PQXX_LIKELY
return c - '0';
else if (c >= 'a' and c <= 'f') return 10 + (c - 'a');
else if (c >= 'A' and c <= 'F') return 10 + (c - 'A');
else return -1;
}
} // namespace
void pqxx::internal::esc_bin(
std::basic_string_view<std::byte> binary_data, char buffer[]) noexcept
{
auto here{buffer};
*here++ = '\\';
*here++ = 'x';
for (auto const byte : binary_data)
{
auto uc{static_cast<unsigned char>(byte)};
*here++ = hex_digit(uc >> 4);
*here++ = hex_digit(uc & 0x0f);
}
// (No need to increment further. Facebook's "infer" complains if we do.)
*here = '\0';
}
std::string
pqxx::internal::esc_bin(std::basic_string_view<std::byte> binary_data)
{
auto const bytes{size_esc_bin(std::size(binary_data))};
std::string buf;
buf.resize(bytes);
esc_bin(binary_data, buf.data());
// Strip off the trailing zero.
buf.resize(bytes - 1);
return buf;
}
void pqxx::internal::unesc_bin(
std::string_view escaped_data, std::byte buffer[])
{
auto const in_size{std::size(escaped_data)};
if (in_size < 2)
throw pqxx::failure{"Binary data appears truncated."};
if ((in_size % 2) != 0)
throw pqxx::failure{"Invalid escaped binary length."};
char const *in{escaped_data.data()};
char const *const end{in + in_size};
if (*in++ != '\\' or *in++ != 'x')
throw pqxx::failure(
"Escaped binary data did not start with '\\x'`. Is the server or libpq "
"too old?");
auto out{buffer};
while (in != end)
{
int hi{nibble(*in++)};
if (hi < 0)
throw pqxx::failure{"Invalid hex-escaped data."};
int lo{nibble(*in++)};
if (lo < 0)
throw pqxx::failure{"Invalid hex-escaped data."};
*out++ = static_cast<std::byte>((hi << 4) | lo);
}
}
std::basic_string<std::byte>
pqxx::internal::unesc_bin(std::string_view escaped_data)
{
auto const bytes{size_unesc_bin(std::size(escaped_data))};
std::basic_string<std::byte> buf;
buf.resize(bytes);
unesc_bin(escaped_data, buf.data());
return buf;
}