userver: userver/storages/postgres/io/field_buffer.hpp Source File
Loading...
Searching...
No Matches
field_buffer.hpp
1#pragma once
2
3#include <userver/storages/postgres/exceptions.hpp>
4#include <userver/storages/postgres/io/buffer_io.hpp>
5#include <userver/storages/postgres/io/integral_types.hpp>
6#include <userver/storages/postgres/io/nullable_traits.hpp>
7
8USERVER_NAMESPACE_BEGIN
9
10namespace storages::postgres::io {
11
12inline constexpr FieldBuffer FieldBuffer::GetSubBuffer(
13 std::size_t offset, std::size_t size, BufferCategory cat) const {
14 const auto* new_buffer_start = buffer + offset;
15 if (offset > length) {
16 throw InvalidInputBufferSize(
17 length, ". Offset requested " + std::to_string(offset));
18 }
19 size = size == npos ? length - offset : size;
20 if (offset + size > length) {
21 throw InvalidInputBufferSize(
22 size, ". Buffer remaininig size is " + std::to_string(length - offset));
23 }
25 cat = category;
26 }
27 return {is_null, cat, size, new_buffer_start};
28}
29
30template <typename T>
31std::size_t FieldBuffer::Read(T&& value, BufferCategory cat, std::size_t len) {
32 io::ReadBuffer(GetSubBuffer(0, len, cat), std::forward<T>(value));
33 length -= len;
34 buffer += len;
35 return len;
36}
37
38template <typename T>
39std::size_t FieldBuffer::Read(T&& value, const TypeBufferCategory& categories,
40 std::size_t len, BufferCategory cat) {
41 io::ReadBuffer(GetSubBuffer(0, len, cat), std::forward<T>(value), categories);
42 length -= len;
43 buffer += len;
44 return len;
45}
46
47template <typename T>
48std::size_t FieldBuffer::ReadRaw(T&& value,
49 const TypeBufferCategory& categories,
50 BufferCategory cat) {
51 using ValueType = std::decay_t<T>;
52 Integer field_length{0};
53 auto consumed = Read(field_length, BufferCategory::kPlainBuffer);
54 if (field_length == kPgNullBufferSize) {
55 // NULL value
56 traits::GetSetNull<ValueType>::SetNull(std::forward<T>(value));
57 return consumed;
58 } else if (field_length < 0) {
59 // invalid length value
60 throw InvalidInputBufferSize(0, "Negative buffer size value");
61 } else if (field_length == 0) {
62 traits::GetSetNull<ValueType>::SetDefault(std::forward<T>(value));
63 return consumed;
64 } else {
65 return consumed + Read(value, categories, field_length, cat);
66 }
67}
68
69template <typename T>
70std::size_t ReadRawBinary(FieldBuffer buffer, T& value,
71 const TypeBufferCategory& categories) {
72 return buffer.ReadRaw(value, categories);
73}
74
75namespace detail {
76
77template <typename T, typename Buffer,
78 typename Enable = USERVER_NAMESPACE::utils::void_t<>>
79struct FormatterAcceptsReplacementOid : std::false_type {};
80
81template <typename T, typename Buffer>
82struct FormatterAcceptsReplacementOid<
83 T, Buffer,
84 USERVER_NAMESPACE::utils::void_t<decltype(std::declval<T&>()(
85 std::declval<const UserTypes&>(), std::declval<Buffer&>(),
86 std::declval<Oid>()))>> : std::true_type {};
87
88} // namespace detail
89
90template <typename T, typename Buffer>
91void WriteRawBinary(const UserTypes& types, Buffer& buffer, const T& value,
92 [[maybe_unused]] Oid replace_oid = kInvalidOid) {
93 traits::CheckFormatter<T>();
94 static constexpr auto size_len = sizeof(Integer);
95 if (traits::GetSetNull<T>::IsNull(value)) {
96 io::WriteBuffer(types, buffer, kPgNullBufferSize);
97 } else {
98 using BufferFormatter = typename traits::IO<T>::FormatterType;
99 using AcceptsReplacementOid =
100 detail::FormatterAcceptsReplacementOid<BufferFormatter, Buffer>;
101 auto len_start = buffer.size();
102 buffer.resize(buffer.size() + size_len);
103 auto size_before = buffer.size();
104 if constexpr (AcceptsReplacementOid{}) {
105 BufferFormatter{value}(types, buffer, replace_oid);
106 } else {
107 io::WriteBuffer(types, buffer, value);
108 }
109 Integer bytes = buffer.size() - size_before;
110 BufferWriter(bytes)(buffer.begin() + len_start);
111 }
112}
113
114} // namespace storages::postgres::io
115
116USERVER_NAMESPACE_END