userver: userver/storages/postgres/io/integral_types.hpp Source File
Loading...
Searching...
No Matches
integral_types.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/storages/postgres/io/integral_types.hpp
4/// @brief Integral types I/O support
5/// @ingroup userver_postgres_parse_and_format
6
7#include <boost/endian/conversion.hpp>
8
9#include <algorithm>
10#include <type_traits>
11
12#include <userver/storages/postgres/exceptions.hpp>
13#include <userver/storages/postgres/io/buffer_io_base.hpp>
14#include <userver/storages/postgres/io/traits.hpp>
15#include <userver/storages/postgres/io/type_mapping.hpp>
16#include <userver/utils/numeric_cast.hpp>
17
18USERVER_NAMESPACE_BEGIN
19
20namespace storages::postgres::io {
21
22namespace detail {
23
24template <std::size_t Size>
25struct IntegralType;
26
27template <>
28struct IntegralType<2> {
29 using type = Smallint;
30};
31
32template <>
33struct IntegralType<4> {
34 using type = Integer;
35};
36
37template <>
38struct IntegralType<8> {
39 using type = Bigint;
40};
41
42template <std::size_t Size>
43struct IntegralBySizeParser {
44 using IntType = typename IntegralType<Size>::type;
45 constexpr static std::size_t size = Size;
46
47 static IntType ParseBuffer(const FieldBuffer& buf) {
48 IntType i;
49 std::memcpy(&i, buf.buffer, size);
50 return boost::endian::big_to_native(i);
51 }
52};
53
54template <typename To, typename From>
55To NumericCast(From value) {
56 return USERVER_NAMESPACE::utils::numeric_cast<To, NarrowingOverflow>(value);
57}
58
59template <typename T>
60struct IntegralBinaryParser : BufferParserBase<T> {
61 using BaseType = BufferParserBase<T>;
62 using BaseType::BaseType;
63 using ValueType = typename BaseType::ValueType;
64
65 void operator()(const FieldBuffer& buf) {
66 switch (buf.length) {
67 case 2:
68 this->value = NumericCast<ValueType>(IntegralBySizeParser<2>::ParseBuffer(buf));
69 break;
70 case 4:
71 this->value = NumericCast<ValueType>(IntegralBySizeParser<4>::ParseBuffer(buf));
72 break;
73 case 8:
74 this->value = NumericCast<ValueType>(IntegralBySizeParser<8>::ParseBuffer(buf));
75 break;
76 default:
77 throw InvalidInputBufferSize{fmt::format(
78 "Invalid buffer size {} "
79 "for an integral value type (expecting size 2, 4, or 8). Not an "
80 "integer was returned from query.",
81 buf.length
82 )};
83 }
84 }
85};
86
87template <typename T>
88struct IntegralBinaryFormatter {
89 static constexpr std::size_t size = sizeof(T);
90 using BySizeType = typename IntegralType<size>::type;
91
92 T value;
93
94 explicit IntegralBinaryFormatter(T val)
95 : value{val}
96 {}
97 template <typename Buffer>
98 void operator()(const UserTypes&, Buffer& buf) const {
99 buf.reserve(buf.size() + size);
100 auto tmp = boost::endian::native_to_big(static_cast<BySizeType>(value));
101 const char* p = reinterpret_cast<char const*>(&tmp);
102 const char* e = p + size;
103 std::copy(p, e, std::back_inserter(buf));
104 }
105
106 /// Write the value to char buffer, the buffer MUST be already resized
107 template <typename Iterator>
108 void operator()(Iterator buffer) const {
109 auto tmp = boost::endian::native_to_big(static_cast<BySizeType>(value));
110 const char* p = reinterpret_cast<char const*>(&tmp);
111 const char* e = p + size;
112 std::copy(p, e, buffer);
113 }
114};
115
116#if defined(__x86_64__) || defined(__aarch64__)
117// 64bit architectures have two types for 64bit integers, this is the second one
118using AltInteger = std::conditional_t<std::is_same_v<Bigint, long>, long long, long>;
119static_assert(sizeof(AltInteger) == sizeof(Bigint));
120#else
121// 32bit architectures have two types for 32bit integers, this is the second one
122using AltInteger = std::conditional_t<std::is_same_v<Integer, int>, long int, int>;
123static_assert(sizeof(AltInteger) == sizeof(Integer));
124#endif
125
126} // namespace detail
127
128//@{
129/** @name 2 byte integer */
130template <>
131struct BufferParser<Smallint> : detail::IntegralBinaryParser<Smallint> {
132 explicit BufferParser(Smallint& val)
133 : IntegralBinaryParser(val)
134 {}
135};
136
137template <>
138struct BufferFormatter<Smallint> : detail::IntegralBinaryFormatter<Smallint> {
139 explicit BufferFormatter(Smallint val)
140 : IntegralBinaryFormatter(val)
141 {}
142};
143//@}
144
145//@{
146/** @name 4 byte integer */
147template <>
148struct BufferParser<Integer> : detail::IntegralBinaryParser<Integer> {
149 explicit BufferParser(Integer& val)
150 : IntegralBinaryParser(val)
151 {}
152};
153
154template <>
155struct BufferFormatter<Integer> : detail::IntegralBinaryFormatter<Integer> {
156 explicit BufferFormatter(Integer val)
157 : IntegralBinaryFormatter(val)
158 {}
159};
160//@}
161
162//@{
163/** @name 8 byte integer */
164template <>
165struct BufferParser<Bigint> : detail::IntegralBinaryParser<Bigint> {
166 explicit BufferParser(Bigint& val)
167 : IntegralBinaryParser(val)
168 {}
169};
170
171template <>
172struct BufferFormatter<Bigint> : detail::IntegralBinaryFormatter<Bigint> {
173 explicit BufferFormatter(Bigint val)
174 : IntegralBinaryFormatter(val)
175 {}
176};
177
178/// @cond
179template <>
180struct BufferParser<detail::AltInteger> : detail::IntegralBinaryParser<detail::AltInteger> {
181 explicit BufferParser(detail::AltInteger& val)
182 : IntegralBinaryParser(val)
183 {}
184};
185
186template <>
187struct BufferFormatter<detail::AltInteger> : detail::IntegralBinaryFormatter<detail::AltInteger> {
188 explicit BufferFormatter(detail::AltInteger val)
189 : IntegralBinaryFormatter(val)
190 {}
191};
192/// @endcond
193
194//@}
195
196//@{
197/** @name boolean */
198template <>
199struct BufferParser<bool> {
200 bool& value;
201 explicit BufferParser(bool& val)
202 : value{val}
203 {}
204 void operator()(const FieldBuffer& buf);
205};
206
207template <>
208struct BufferFormatter<bool> {
209 bool value;
210 explicit BufferFormatter(bool val)
211 : value(val)
212 {}
213 template <typename Buffer>
214 void operator()(const UserTypes&, Buffer& buf) const {
215 buf.push_back(value ? 1 : 0);
216 }
217};
218//@}
219
220//@{
221/** @name C++ to PostgreSQL mapping for integral types */
222template <>
223struct CppToSystemPg<Smallint> : PredefinedOid<PredefinedOids::kInt2> {};
224template <>
225struct CppToSystemPg<Integer> : PredefinedOid<PredefinedOids::kInt4> {};
226template <>
227struct CppToSystemPg<Bigint> : PredefinedOid<PredefinedOids::kInt8> {};
228template <>
229struct CppToSystemPg<bool> : PredefinedOid<PredefinedOids::kBoolean> {};
230
231/// @cond
232template <>
233struct CppToSystemPg<detail::AltInteger>
234 : PredefinedOid<sizeof(detail::AltInteger) == 8 ? PredefinedOids::kInt8 : PredefinedOids::kInt4> {};
235/// @endcond
236
237//@}
238
239} // namespace storages::postgres::io
240
241USERVER_NAMESPACE_END