ChimeraTK-DeviceAccess 03.31.00
Loading...
Searching...
No Matches
ReadCoordinator.h
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Deutsches Elektronen-Synchrotron DESY, MSK, ChimeraTK Project <chimeratk-support@desy.de>
2// SPDX-License-Identifier: LGPL-3.0-or-later
3#pragma once
4
6#include "RawConverter.h"
8
9#include <cstring>
10#include <list>
11#include <map>
12#include <memory>
13#include <typeindex>
14#include <vector>
15
16namespace ChimeraTK::detail {
17
18 /********************************************************************************************************************/
19
27 class ReadCoordinator {
28 public:
32 struct GroupKey {
34 uint32_t width{};
35 int32_t nFractionalBits{};
36 bool signedFlag{};
37 DataType rawType;
38 std::type_index userType{typeid(std::nullptr_t)};
39 bool isRaw{};
40
41 auto operator<=>(const GroupKey&) const = default;
42 };
43
47 template<typename UserType>
48 struct Consumer {
49 Consumer(size_t offset, size_t pitch, size_t count, std::vector<UserType>& stagingBuffer);
50
51 size_t byteOffset{}; // offset of first sample within the raw buffer
52 size_t stride{}; // elementPitchBits / 8 (equal to the element data width for a non-strided consumer)
53 size_t nSamples{};
54 std::vector<UserType>& staging;
55 };
56
62 struct GroupBase {
64 [[nodiscard]] virtual bool consumersEmpty() const = 0;
65
67 virtual void rawCopy(const std::vector<uint8_t>& rawBuffer) = 0;
68
69 virtual ~GroupBase() = default;
70
71 bool isRaw{};
72 std::unique_ptr<RawConverter::ConverterLoopHelper> converterLoopHelper;
73 };
74
79 template<typename UserType>
80 struct Group : GroupBase {
81 [[nodiscard]] bool consumersEmpty() const override;
82
84 void rawCopy(const std::vector<uint8_t>& rawBuffer) override;
85
86 std::list<Consumer<UserType>> consumers;
87 };
88
95 template<typename UserType>
96 class Registration {
97 public:
98 Registration() = default;
99
106 Registration(ReadCoordinator* owner, const NumericAddressedRegisterInfo& info, bool isRaw, size_t byteOffset,
107 size_t stride, size_t nSamples);
108
109 ~Registration();
110
111 Registration(const Registration&) = delete;
112 Registration& operator=(const Registration&) = delete;
113
114 Registration(Registration&& other) noexcept;
115 Registration& operator=(Registration&& other) noexcept;
116
118 void reset();
119
121 [[nodiscard]] std::vector<UserType>& staging();
122
123 private:
124 friend class ReadCoordinator;
125
126 ReadCoordinator* _owner{nullptr};
127 size_t _groupId{}; // the consumer's group, see _groups
128 std::list<Consumer<UserType>>::iterator _it;
129
130 // Heap-allocated so its address (which the registered Consumer references) stays stable when the registration is
131 // moved; the moveable handle owns it, the buffer's data pointer changes on each swap, never its address.
132 std::unique_ptr<std::vector<UserType>> _staging;
133 };
134
135 ReadCoordinator() = default;
136
137 ReadCoordinator(const ReadCoordinator&) = delete;
138 ReadCoordinator(ReadCoordinator&&) noexcept = delete;
139 ReadCoordinator& operator=(const ReadCoordinator&) = delete;
140 ReadCoordinator& operator=(ReadCoordinator&&) noexcept = delete;
141
142 ~ReadCoordinator() = default;
143
145 [[nodiscard]] bool empty() const;
146
151 void run(const std::vector<uint8_t>& rawBuffer);
152
158 template<class UserType, typename RawType, RawConverter::SignificantBitsCase sc, RawConverter::FractionalCase fc,
159 bool isSigned>
160 void doPostReadImpl(RawConverter::Converter<UserType, RawType, sc, fc, isSigned> converter, size_t groupId);
161
166 template<class UserType, typename RawType, RawConverter::SignificantBitsCase sc, RawConverter::FractionalCase fc,
167 bool isSigned>
168 void doPreWriteImpl(RawConverter::Converter<UserType, RawType, sc, fc, isSigned> converter, size_t groupId);
169
170 private:
171 template<typename>
172 friend class Registration;
173
174 // _groups stores the type-erased groups keyed by their stable, monotonically increasing groupId; _groupIndex maps
175 // each group key to that groupId. The groupId is a non-positional identity handed to the group's
176 // ConverterLoopHelper, so it can never go stale. A group is removed only at teardown (it is never erased while the
177 // element lives, it just loses its consumers), so the registry only ever grows or vanishes wholesale with the
178 // element and needs no freed-slot tracking.
179 std::map<size_t, std::unique_ptr<GroupBase>> _groups;
180 std::map<GroupKey, size_t> _groupIndex;
181 size_t _nextGroupId{};
182 // the current read's raw buffer, set only while run() iterates the groups; null between reads
183 const std::vector<uint8_t>* _rawBuffer{nullptr};
184 };
185
186 /********************************************************************************************************************/
187
188 template<typename UserType>
189 inline ReadCoordinator::Consumer<UserType>::Consumer(
190 size_t offset, size_t pitch, size_t count, std::vector<UserType>& stagingBuffer)
191 : byteOffset(offset), stride(pitch), nSamples(count), staging(stagingBuffer) {}
192
193 /********************************************************************************************************************/
194
195 template<typename UserType>
196 inline bool ReadCoordinator::Group<UserType>::consumersEmpty() const {
197 return consumers.empty();
198 }
199
200 /********************************************************************************************************************/
201
202 template<typename UserType>
203 inline void ReadCoordinator::Group<UserType>::rawCopy(const std::vector<uint8_t>& rawBuffer) {
204 for(const auto& consumer : consumers) {
205 if(consumer.nSamples == 0) {
206 continue;
207 }
208 auto* cooked = consumer.staging.data();
209 const auto* raw = rawBuffer.data() + consumer.byteOffset;
210 // A raw-mode group holds consumers whose UserType is the register's raw data type: an integer of the matching
211 // size and signedness (signed or unsigned). The nested if constexpr is a compile-time gate: the memcpy below is
212 // instantiated only for such types and never for any other UserType (which would be undefined behaviour). The
213 // outer integral guard comes first because std::make_unsigned_t is defined only for integral and enum types, so
214 // the unsigned counterpart may be formed only behind it; the remaining non-integral UserTypes are excluded by it.
215 // isRawType covers unsigned raw types only, while a raw-mode UserType may be signed, hence the unsigned
216 // counterpart is checked. Such a use cannot be formed through the library, so no runtime fallback exists.
217 if constexpr(std::is_integral_v<UserType>) {
218 if constexpr(ChimeraTK::isRawType<std::make_unsigned_t<UserType>>) {
219 for(size_t i = 0; i < consumer.nSamples; ++i) {
220 std::memcpy(cooked + i, raw + i * consumer.stride, sizeof(UserType));
221 }
222 }
223 }
224 }
225 }
226
227 /********************************************************************************************************************/
228
229 DECLARE_TEMPLATE_FOR_CHIMERATK_USER_TYPES(ReadCoordinator::Registration);
230
231 /********************************************************************************************************************/
232
233} // namespace ChimeraTK::detail
#define DECLARE_TEMPLATE_FOR_CHIMERATK_USER_TYPES(TemplateClass)
@ raw
Raw access: disable any possible conversion from the original hardware data type into the given UserT...
STL namespace.
constexpr size_t nSamples