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