userver: userver/storages/postgres/io/bitstring.hpp Source File
⚠️ This is the documentation for an old userver version. Click here to switch to the latest version.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
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