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