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>>
28 : std::true_type {};
29
30template <std::size_t N>
31struct IsBitStringCompatible<std::bitset<N>> : std::true_type {};
32
33template <std::size_t N>
34struct IsBitStringCompatible<std::array<bool, N>> : std::true_type {};
35
36template <typename T>
37inline constexpr bool kIsBitStringCompatible = IsBitStringCompatible<T>::value;
38
39template <typename BitContainer, typename Enable = void>
40struct BitContainerTraits;
41
42template <typename BitContainer>
45 static bool TestBit(const BitContainer& bits, std::uint8_t i) {
46 return bits & (1ull << i);
47 }
48 static void SetBit(BitContainer& bits, std::uint8_t i) {
49 bits |= (1ull << i);
50 }
51 static constexpr std::size_t BitCount() noexcept {
52 return sizeof(BitContainer) * 8;
53 }
54 static void Reset(BitContainer& bits) noexcept { bits = 0; }
55};
56
57template <std::size_t N>
58struct BitContainerTraits<std::array<bool, N>> {
59 static_assert(N > 0, "Length for bit container must be at least 1");
60 using BitContainer = std::array<bool, N>;
61 static bool TestBit(const BitContainer& bits, std::uint8_t i) {
62 return bits[i];
63 }
64 static void SetBit(BitContainer& bits, std::uint8_t i) { bits[i] = true; }
65 static constexpr std::size_t BitCount() noexcept { return N; }
66 static void Reset(BitContainer& bits) noexcept { bits.fill(false); }
67};
68
69template <std::size_t N>
71 static_assert(N > 0, "Length for bit container must be at least 1");
72 using BitContainer = std::bitset<N>;
73 static bool TestBit(const BitContainer& bits, std::uint8_t i) {
74 return bits.test(i);
75 }
76 static void SetBit(BitContainer& bits, std::uint8_t i) { bits.set(i); }
77 static constexpr std::size_t BitCount() noexcept { return N; }
78 static void Reset(BitContainer& bits) noexcept { bits.reset(); }
79};
80
81} // namespace io::traits
82
83enum class BitStringType { kBit, kBitVarying };
84
85namespace detail {
86
87enum class BitContainerInterface { kCommon, kFlags };
88
89template <typename BitContainerRef, BitContainerInterface, BitStringType>
90struct BitStringRefWrapper {
91 static_assert(std::is_reference<BitContainerRef>::value,
92 "The container must be passed by reference");
93
94 using BitContainer = std::decay_t<BitContainerRef>;
95 static_assert(io::traits::kIsBitStringCompatible<BitContainer>,
96 "This C++ type cannot be used with PostgreSQL 'bit' and 'bit "
97 "varying' data type");
98
99 BitContainerRef bits;
100};
101
102} // namespace detail
103
104template <typename BitContainer, BitStringType>
106 static_assert(!std::is_reference<BitContainer>::value,
107 "The container must not be passed by reference");
108
109 static_assert(io::traits::kIsBitStringCompatible<BitContainer>,
110 "This C++ type cannot be used with PostgreSQL 'bit' and 'bit "
111 "varying' data type");
112
113 BitContainer bits{};
114};
115
116template <BitStringType kBitStringType, typename BitContainer>
117constexpr detail::BitStringRefWrapper<
118 const BitContainer&, detail::BitContainerInterface::kCommon, kBitStringType>
119BitString(const BitContainer& bits) {
120 return {bits};
121}
122
123template <BitStringType kBitStringType, typename BitContainer>
124constexpr detail::BitStringRefWrapper<
125 BitContainer&, detail::BitContainerInterface::kCommon, kBitStringType>
126BitString(BitContainer& bits) {
127 return {bits};
128}
129
130template <BitStringType kBitStringType, typename Enum>
131constexpr detail::BitStringRefWrapper<
132 const USERVER_NAMESPACE::utils::Flags<Enum>&,
133 detail::BitContainerInterface::kFlags, kBitStringType>
134BitString(const USERVER_NAMESPACE::utils::Flags<Enum>& bits) {
135 return {bits};
136}
137
138template <BitStringType kBitStringType, typename Enum>
139constexpr detail::BitStringRefWrapper<USERVER_NAMESPACE::utils::Flags<Enum>&,
140 detail::BitContainerInterface::kFlags,
141 kBitStringType>
142BitString(USERVER_NAMESPACE::utils::Flags<Enum>& bits) {
143 return {bits};
144}
145
146template <typename BitContainer>
147constexpr auto Varbit(BitContainer&& bits) {
148 return BitString<BitStringType::kBitVarying>(
149 std::forward<BitContainer>(bits));
150}
151
152template <typename BitContainer>
153constexpr auto Bit(BitContainer&& bits) {
154 return BitString<BitStringType::kBit>(std::forward<BitContainer>(bits));
155}
156
157namespace io {
158
165 kBitStringType>&&> {
167 using BaseType =
170 kBitStringType>&&>;
171 using BaseType::BaseType;
172
173 void operator()(FieldBuffer buffer) {
176 if (static_cast<std::size_t>((bit_count + 7) / 8) > buffer.length)
178
179 auto& bits = this->value.bits;
180 if (const Integer target_bit_count =
184 }
185
186 // buffer contains a zero-padded bitstring, most significant bit first
188 for (Integer i = 0; i < bit_count; ++i) {
189 const auto* byte_cptr = buffer.buffer + (i / 8);
190 if ((*byte_cptr) & (0x80 >> (i % 8))) {
192 bit_count - i - 1);
193 }
194 }
195 }
196};
197
204 kBitStringType>&&> {
206 using BaseType =
209 kBitStringType>&&>;
210 using BaseType::BaseType;
211
212 void operator()(FieldBuffer buffer) {
213 typename BitContainer::ValueType bits{0};
215 this->value.bits.SetValue(bits);
216 }
217};
218
219template <typename BitContainer, BitStringType kBitStringType>
225 using BaseType::BaseType;
226
227 void operator()(const FieldBuffer& buffer) {
229 }
230};
231
232template <std::size_t N>
235 using BaseType::BaseType;
236
237 void operator()(const FieldBuffer& buffer) {
238 ReadBuffer(buffer, Varbit(this->value));
239 }
240};
241
250 using BaseType =
254 using BaseType::BaseType;
255
256 template <typename Buffer>
257 void operator()(const UserTypes& types, Buffer& buffer) const {
258 // convert bitcontainer to bytes and write into buffer,
259 // from most to least significant
260 const auto& bits = this->value.bits;
261 constexpr auto bit_count =
263
264 std::array<std::uint8_t, (bit_count + 7) / 8> data{};
265 for (std::size_t i = 0; i < bit_count; ++i) {
266 data[i / 8] |= static_cast<std::uint8_t>(
268 bits, bit_count - i - 1))
269 << (7 - i % 8);
270 }
271
272 buffer.reserve(buffer.size() + sizeof(Integer) + data.size());
273 WriteBuffer(types, buffer, static_cast<Integer>(bit_count));
275 }
276};
277
286 using BaseType =
290 using BaseType::BaseType;
291
292 template <typename Buffer>
293 void operator()(const UserTypes& types, Buffer& buffer) const {
296 }
297};
298
299template <typename BitContainer, BitStringType kBitStringType>
305 using BaseType::BaseType;
306
307 template <typename Buffer>
308 void operator()(const UserTypes& types, Buffer& buffer) const {
310 }
311};
312
313// std::bitset is saved as bit varying on default
314
315template <std::size_t N>
318 using BitContainer = std::bitset<N>;
320 using BaseType::BaseType;
321
322 template <typename Buffer>
323 void operator()(const UserTypes& types, Buffer& buffer) const {
325 }
326};
327
328template <typename BitContainer,
329 postgres::detail::BitContainerInterface kContainerInterface>
330struct CppToSystemPg<postgres::detail::BitStringRefWrapper<
331 BitContainer, kContainerInterface, postgres::BitStringType::kBitVarying>>
332 : PredefinedOid<PredefinedOids::kVarbit> {};
333template <typename BitContainer>
334struct CppToSystemPg<postgres::BitStringWrapper<
335 BitContainer, postgres::BitStringType::kBitVarying>>
336 : PredefinedOid<PredefinedOids::kVarbit> {};
337
338template <typename BitContainer,
339 postgres::detail::BitContainerInterface kContainerInterface>
340struct CppToSystemPg<postgres::detail::BitStringRefWrapper<
341 BitContainer, kContainerInterface, postgres::BitStringType::kBit>>
342 : PredefinedOid<PredefinedOids::kBit> {};
343template <typename BitContainer>
344struct CppToSystemPg<
345 postgres::BitStringWrapper<BitContainer, postgres::BitStringType::kBit>>
346 : PredefinedOid<PredefinedOids::kBit> {};
347
348template <std::size_t N>
349struct CppToSystemPg<std::bitset<N>> : PredefinedOid<PredefinedOids::kVarbit> {
350};
351
352} // namespace io
353} // namespace storages::postgres
354
355USERVER_NAMESPACE_END