userver: userver/storages/postgres/io/bitstring.hpp Source File
Loading...
Searching...
No Matches
bitstring.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/storages/postgres/io/bitstring.hpp
4/// @brief storages::postgres::BitString I/O support
5/// @ingroup userver_postgres_parse_and_format
6
7#include <bitset>
8#include <vector>
9
10#include <userver/storages/postgres/exceptions.hpp>
11#include <userver/storages/postgres/io/buffer_io.hpp>
12#include <userver/storages/postgres/io/buffer_io_base.hpp>
13#include <userver/storages/postgres/io/type_mapping.hpp>
14
15#include <userver/utils/flags.hpp>
16
17USERVER_NAMESPACE_BEGIN
18
19namespace storages::postgres {
20
21namespace io::traits {
22
23template <typename BitContainer>
24struct IsBitStringCompatible : std::is_integral<BitContainer> {};
25
26template <typename Enum>
27struct IsBitStringCompatible<USERVER_NAMESPACE::utils::Flags<Enum>> : std::true_type {};
28
29template <std::size_t N>
30struct IsBitStringCompatible<std::bitset<N>> : std::true_type {};
31
32template <std::size_t N>
33struct IsBitStringCompatible<std::array<bool, N>> : std::true_type {};
34
35template <typename T>
36concept kIsBitStringCompatible = IsBitStringCompatible<T>::value; // NOLINT(readability-identifier-naming)
37
38template <typename BitContainer, typename Enable = void>
40
41template <typename BitContainer>
42struct BitContainerTraits<BitContainer, std::enable_if_t<std::is_integral_v<BitContainer>>> {
43 static bool TestBit(const BitContainer& bits, std::uint8_t i) { return bits & (1ull << i); }
44 static void SetBit(BitContainer& bits, std::uint8_t i) { bits |= (1ull << i); }
45 static constexpr std::size_t BitCount() noexcept { return sizeof(BitContainer) * 8; }
46 static void Reset(BitContainer& bits) noexcept { bits = 0; }
47};
48
49template <std::size_t N>
50struct BitContainerTraits<std::array<bool, N>> {
51 static_assert(N > 0, "Length for bit container must be at least 1");
52 using BitContainer = std::array<bool, N>;
53 static bool TestBit(const BitContainer& bits, std::uint8_t i) { return bits[i]; }
54 static void SetBit(BitContainer& bits, std::uint8_t i) { bits[i] = true; }
55 static constexpr std::size_t BitCount() noexcept { return N; }
56 static void Reset(BitContainer& bits) noexcept { bits.fill(false); }
57};
58
59template <std::size_t N>
60struct BitContainerTraits<std::bitset<N>> {
61 static_assert(N > 0, "Length for bit container must be at least 1");
62 using BitContainer = std::bitset<N>;
63 static bool TestBit(const BitContainer& bits, std::uint8_t i) { return bits.test(i); }
64 static void SetBit(BitContainer& bits, std::uint8_t i) { bits.set(i); }
65 static constexpr std::size_t BitCount() noexcept { return N; }
66 static void Reset(BitContainer& bits) noexcept { bits.reset(); }
67};
68
69} // namespace io::traits
70
71enum class BitStringType { kBit, kBitVarying };
72
73namespace detail {
74
75enum class BitContainerInterface { kCommon, kFlags };
76
77template <typename BitContainerRef, BitContainerInterface, BitStringType>
78struct BitStringRefWrapper {
79 static_assert(std::is_reference<BitContainerRef>::value, "The container must be passed by reference");
80
81 using BitContainer = std::remove_cvref_t<BitContainerRef>;
82 static_assert(
83 io::traits::kIsBitStringCompatible<BitContainer>,
84 "This C++ type cannot be used with PostgreSQL 'bit' and 'bit "
85 "varying' data type"
86 );
87
88 BitContainerRef bits;
89};
90
91} // namespace detail
92
93template <typename BitContainer, BitStringType>
95 static_assert(!std::is_reference<BitContainer>::value, "The container must not be passed by reference");
96
97 static_assert(
98 io::traits::kIsBitStringCompatible<BitContainer>,
99 "This C++ type cannot be used with PostgreSQL 'bit' and 'bit "
100 "varying' data type"
101 );
102
103 BitContainer bits{};
104};
105
106template <BitStringType kBitStringType, typename BitContainer>
107constexpr detail::BitStringRefWrapper<const BitContainer&, detail::BitContainerInterface::kCommon, kBitStringType>
108BitString(const BitContainer& bits) {
109 return {bits};
110}
111
112template <BitStringType kBitStringType, typename BitContainer>
113constexpr detail::BitStringRefWrapper<BitContainer&, detail::BitContainerInterface::kCommon, kBitStringType> BitString(
114 BitContainer& bits
115) {
116 return {bits};
117}
118
119template <BitStringType kBitStringType, typename Enum>
120constexpr detail::BitStringRefWrapper<
121 const USERVER_NAMESPACE::utils::Flags<Enum>&,
122 detail::BitContainerInterface::kFlags,
123 kBitStringType>
124BitString(const USERVER_NAMESPACE::utils::Flags<Enum>& bits) {
125 return {bits};
126}
127
128template <BitStringType kBitStringType, typename Enum>
129constexpr detail::BitStringRefWrapper<
130 USERVER_NAMESPACE::utils::Flags<Enum>&,
131 detail::BitContainerInterface::kFlags,
132 kBitStringType>
133BitString(USERVER_NAMESPACE::utils::Flags<Enum>& bits) {
134 return {bits};
135}
136
137template <typename BitContainer>
138constexpr auto Varbit(BitContainer&& bits) {
139 return BitString<BitStringType::kBitVarying>(std::forward<BitContainer>(bits));
140}
141
142template <typename BitContainer>
143constexpr auto Bit(BitContainer&& bits) {
144 return BitString<BitStringType::kBit>(std::forward<BitContainer>(bits));
145}
146
147namespace io {
148
149template <typename BitContainerRef, BitStringType kBitStringType>
150struct BufferParser<postgres::detail::BitStringRefWrapper<
151 BitContainerRef,
152 postgres::detail::BitContainerInterface::kCommon,
153 kBitStringType>>
154 : detail::BufferParserBase<postgres::detail::BitStringRefWrapper<
155 BitContainerRef,
156 postgres::detail::BitContainerInterface::kCommon,
157 kBitStringType>&&> {
158 using BitContainer = std::remove_cvref_t<BitContainerRef>;
159 using BaseType = detail::BufferParserBase<postgres::detail::BitStringRefWrapper<
160 BitContainerRef,
161 postgres::detail::BitContainerInterface::kCommon,
162 kBitStringType>&&>;
163 using BaseType::BaseType;
164
165 void operator()(FieldBuffer buffer) {
166 Integer bit_count{0};
167 buffer.Read(bit_count, BufferCategory::kPlainBuffer);
168 if (bit_count < 0) {
170 }
171
172 static_assert(sizeof(bit_count) < sizeof(std::size_t) || std::is_signed_v<decltype(bit_count)>);
173 if ((static_cast<std::size_t>(bit_count) + 7) / 8 > buffer.length) {
175 }
176
177 auto& bits = this->value.bits;
178 if (const Integer target_bit_count = io::traits::BitContainerTraits<BitContainer>::BitCount();
179 target_bit_count < bit_count)
180 {
181 throw BitStringOverflow(bit_count, target_bit_count);
182 }
183
184 // buffer contains a zero-padded bitstring, most significant bit first
185 io::traits::BitContainerTraits<BitContainer>::Reset(bits);
186 for (Integer i = 0; i < bit_count; ++i) {
187 const auto* byte_cptr = buffer.buffer + (i / 8);
188 if ((*byte_cptr) & (0x80 >> (i % 8))) {
189 io::traits::BitContainerTraits<BitContainer>::SetBit(bits, bit_count - i - 1);
190 }
191 }
192 }
193};
194
195template <typename BitContainerRef, BitStringType kBitStringType>
196struct BufferParser<postgres::detail::BitStringRefWrapper<
197 BitContainerRef,
198 postgres::detail::BitContainerInterface::kFlags,
199 kBitStringType>>
200 : detail::BufferParserBase<postgres::detail::BitStringRefWrapper<
201 BitContainerRef,
202 postgres::detail::BitContainerInterface::kFlags,
203 kBitStringType>&&> {
204 using BitContainer = std::remove_cvref_t<BitContainerRef>;
205 using BaseType = detail::BufferParserBase<postgres::detail::BitStringRefWrapper<
206 BitContainerRef,
207 postgres::detail::BitContainerInterface::kFlags,
208 kBitStringType>&&>;
209 using BaseType::BaseType;
210
211 void operator()(FieldBuffer buffer) {
212 typename BitContainer::ValueType bits{0};
213 ReadBuffer(buffer, BitString<kBitStringType>(bits));
214 this->value.bits.SetValue(bits);
215 }
216};
217
218template <typename BitContainer, BitStringType kBitStringType>
219struct BufferParser<postgres::BitStringWrapper<BitContainer, kBitStringType>>
220 : detail::BufferParserBase<postgres::BitStringWrapper<BitContainer, kBitStringType>> {
221 using BaseType = detail::BufferParserBase<postgres::BitStringWrapper<BitContainer, kBitStringType>>;
222 using BaseType::BaseType;
223
224 void operator()(const FieldBuffer& buffer) { ReadBuffer(buffer, BitString<kBitStringType>(this->value.bits)); }
225};
226
227template <std::size_t N>
228struct BufferParser<std::bitset<N>> : detail::BufferParserBase<std::bitset<N>> {
229 using BaseType = detail::BufferParserBase<std::bitset<N>>;
230 using BaseType::BaseType;
231
232 void operator()(const FieldBuffer& buffer) { ReadBuffer(buffer, Varbit(this->value)); }
233};
234
235template <typename BitContainerRef, BitStringType kBitStringType>
236struct BufferFormatter<postgres::detail::BitStringRefWrapper<
237 BitContainerRef,
238 postgres::detail::BitContainerInterface::kCommon,
239 kBitStringType>>
240 : detail::BufferFormatterBase<postgres::detail::BitStringRefWrapper<
241 BitContainerRef,
242 postgres::detail::BitContainerInterface::kCommon,
243 kBitStringType>> {
244 using BitContainer = std::remove_cvref_t<BitContainerRef>;
245 using BaseType = detail::BufferFormatterBase<postgres::detail::BitStringRefWrapper<
246 BitContainerRef,
247 postgres::detail::BitContainerInterface::kCommon,
248 kBitStringType>>;
249 using BaseType::BaseType;
250
251 template <typename Buffer>
252 void operator()(const UserTypes& types, Buffer& buffer) const {
253 // convert bitcontainer to bytes and write into buffer,
254 // from most to least significant
255 const auto& bits = this->value.bits;
256 constexpr auto bit_count = io::traits::BitContainerTraits<BitContainer>::BitCount();
257
258 std::array<std::uint8_t, (bit_count + 7) / 8> data{};
259 for (std::size_t i = 0; i < bit_count; ++i) {
260 data[i / 8] |=
261 static_cast<std::uint8_t>(io::traits::BitContainerTraits<BitContainer>::TestBit(bits, bit_count - i - 1)
262 )
263 << (7 - i % 8);
264 }
265
266 buffer.reserve(buffer.size() + sizeof(Integer) + data.size());
267 WriteBuffer(types, buffer, static_cast<Integer>(bit_count));
268 buffer.insert(buffer.end(), data.begin(), data.end());
269 }
270};
271
272template <typename BitContainerRef, BitStringType kBitStringType>
273struct BufferFormatter<postgres::detail::BitStringRefWrapper<
274 BitContainerRef,
275 postgres::detail::BitContainerInterface::kFlags,
276 kBitStringType>>
277 : detail::BufferFormatterBase<postgres::detail::BitStringRefWrapper<
278 BitContainerRef,
279 postgres::detail::BitContainerInterface::kFlags,
280 kBitStringType>> {
281 using BitContainer = std::remove_cvref_t<BitContainerRef>;
282 using BaseType = detail::BufferFormatterBase<postgres::detail::BitStringRefWrapper<
283 BitContainerRef,
284 postgres::detail::BitContainerInterface::kFlags,
285 kBitStringType>>;
286 using BaseType::BaseType;
287
288 template <typename Buffer>
289 void operator()(const UserTypes& types, Buffer& buffer) const {
290 WriteBuffer(types, buffer, BitString<kBitStringType>(this->value.bits.GetValue()));
291 }
292};
293
294template <typename BitContainer, BitStringType kBitStringType>
295struct BufferFormatter<postgres::BitStringWrapper<BitContainer, kBitStringType>>
296 : detail::BufferFormatterBase<postgres::BitStringWrapper<BitContainer, kBitStringType>> {
297 using BaseType = detail::BufferFormatterBase<postgres::BitStringWrapper<BitContainer, kBitStringType>>;
298 using BaseType::BaseType;
299
300 template <typename Buffer>
301 void operator()(const UserTypes& types, Buffer& buffer) const {
302 WriteBuffer(types, buffer, BitString<kBitStringType>(this->value.bits));
303 }
304};
305
306// std::bitset is saved as bit varying on default
307
308template <std::size_t N>
309struct BufferFormatter<std::bitset<N>> : detail::BufferFormatterBase<std::bitset<N>> {
310 using BitContainer = std::bitset<N>;
311 using BaseType = detail::BufferFormatterBase<std::bitset<N>>;
312 using BaseType::BaseType;
313
314 template <typename Buffer>
315 void operator()(const UserTypes& types, Buffer& buffer) const {
316 WriteBuffer(types, buffer, Varbit(this->value));
317 }
318};
319
320template <typename BitContainer, postgres::detail::BitContainerInterface ContainerInterface>
321struct CppToSystemPg<
322 postgres::detail::BitStringRefWrapper<BitContainer, ContainerInterface, postgres::BitStringType::kBitVarying>>
323 : PredefinedOid<PredefinedOids::kVarbit> {};
324template <typename BitContainer>
325struct CppToSystemPg<postgres::BitStringWrapper<BitContainer, postgres::BitStringType::kBitVarying>>
326 : PredefinedOid<PredefinedOids::kVarbit> {};
327
328template <typename BitContainer, postgres::detail::BitContainerInterface ContainerInterface>
329struct CppToSystemPg<
330 postgres::detail::BitStringRefWrapper<BitContainer, ContainerInterface, postgres::BitStringType::kBit>>
331 : PredefinedOid<PredefinedOids::kBit> {};
332template <typename BitContainer>
333struct CppToSystemPg<postgres::BitStringWrapper<BitContainer, postgres::BitStringType::kBit>>
334 : PredefinedOid<PredefinedOids::kBit> {};
335
336template <std::size_t N>
337struct CppToSystemPg<std::bitset<N>> : PredefinedOid<PredefinedOids::kVarbit> {};
338
339} // namespace io
340} // namespace storages::postgres
341
342USERVER_NAMESPACE_END