Commit | Line | Data |
---|---|---|
736675a4 PP |
1 | /* |
2 | * Copyright (c) 2023 Philippe Proulx <pproulx@efficios.com> | |
3 | * | |
4 | * SPDX-License-Identifier: MIT | |
5 | */ | |
6 | ||
7 | #ifndef BABELTRACE_CPP_COMMON_BT2_MESSAGE_ARRAY_HPP | |
8 | #define BABELTRACE_CPP_COMMON_BT2_MESSAGE_ARRAY_HPP | |
9 | ||
10 | #include <algorithm> | |
11 | ||
12 | #include <babeltrace2/babeltrace.h> | |
13 | ||
14 | #include "common/assert.h" | |
15 | ||
16 | #include "message.hpp" | |
17 | ||
18 | namespace bt2 { | |
19 | ||
20 | class ConstMessageArray; | |
21 | ||
22 | class ConstMessageArrayIterator final | |
23 | { | |
24 | friend class ConstMessageArray; | |
25 | ||
26 | public: | |
27 | using difference_type = std::ptrdiff_t; | |
28 | using value_type = ConstMessage; | |
29 | using pointer = value_type *; | |
30 | using reference = value_type&; | |
31 | using iterator_category = std::input_iterator_tag; | |
32 | ||
33 | private: | |
34 | explicit ConstMessageArrayIterator(const ConstMessageArray& msgArray, | |
35 | const uint64_t idx) noexcept : | |
36 | _mMsgArray {&msgArray}, | |
37 | _mIdx {idx} | |
38 | { | |
39 | } | |
40 | ||
41 | public: | |
42 | ConstMessageArrayIterator& operator++() noexcept | |
43 | { | |
44 | ++_mIdx; | |
45 | return *this; | |
46 | } | |
47 | ||
48 | ConstMessageArrayIterator operator++(int) noexcept | |
49 | { | |
50 | const auto tmp = *this; | |
51 | ||
52 | ++(*this); | |
53 | return tmp; | |
54 | } | |
55 | ||
56 | bool operator==(const ConstMessageArrayIterator& other) const noexcept | |
57 | { | |
58 | BT_ASSERT_DBG(_mMsgArray == other._mMsgArray); | |
59 | return _mIdx == other._mIdx; | |
60 | } | |
61 | ||
62 | bool operator!=(const ConstMessageArrayIterator& other) const noexcept | |
63 | { | |
64 | return !(*this == other); | |
65 | } | |
66 | ||
67 | ConstMessage operator*() noexcept; | |
68 | ||
69 | private: | |
70 | const ConstMessageArray *_mMsgArray; | |
71 | uint64_t _mIdx; | |
72 | }; | |
73 | ||
74 | /* | |
75 | * A wrapper of `bt_message_array_const`, either: | |
76 | * | |
77 | * Containing existing messages: | |
78 | * Use ConstMessageArray::wrapExisting(). | |
79 | * | |
80 | * Example: | |
81 | * | |
82 | * bt_message_array_const libMsgs; | |
83 | * uint64_t count; | |
84 | * | |
85 | * if (bt_message_iterator_next(myIter, &libMsgs, &count) != | |
86 | * BT_MESSAGE_ITERATOR_NEXT_STATUS_OK) { | |
87 | * // Handle special status | |
88 | * } | |
89 | * | |
90 | * const auto msgs = bt2::ConstMessageArray::wrapExisting(libMsgs, | |
91 | * count); | |
92 | * | |
93 | * // At this point `msgs` manages `libMsgs` | |
94 | * | |
95 | * An empty one which you need to fill: | |
96 | * Use ConstMessageArray::wrapEmpty(). | |
97 | * | |
98 | * Use the release() method to move the ownership of the wrapped | |
99 | * library array to you. | |
100 | * | |
101 | * Example: | |
102 | * | |
103 | * bt_message_iterator_class_next_method_status myNext( | |
104 | * bt_self_message_iterator * const libSelfMsgIter, | |
105 | * const bt_message_array_const libMsgs, | |
106 | * const uint64_t capacity, uint64_t * const count) | |
107 | * { | |
108 | * auto msgs = bt2::ConstMessageArray::wrapEmpty(libMsgs, | |
109 | * capacity); | |
110 | * | |
111 | * // Create messages and append with `msgs.append(...)` | |
112 | * | |
113 | * *count = msgs.release(); | |
114 | * return BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK; | |
115 | * } | |
116 | * | |
117 | * In both cases, the returned message array wrapper is always the sole | |
118 | * owner of the wrapped library array: it's not a simple passive view. | |
119 | * The destructor puts the references of the contained messages. | |
120 | */ | |
121 | class ConstMessageArray final | |
122 | { | |
123 | private: | |
124 | explicit ConstMessageArray(const bt_message_array_const libArrayPtr, const std::uint64_t length, | |
125 | const std::uint64_t capacity) noexcept : | |
126 | _mLibArrayPtr {libArrayPtr}, | |
127 | _mLen {length}, _mCap {capacity} | |
128 | { | |
129 | BT_ASSERT_DBG(length <= capacity); | |
130 | BT_ASSERT_DBG(capacity > 0); | |
131 | } | |
132 | ||
133 | public: | |
134 | using Iterator = ConstMessageArrayIterator; | |
135 | ||
136 | ~ConstMessageArray() | |
137 | { | |
138 | this->_putMsgRefs(); | |
139 | } | |
140 | ||
141 | /* | |
142 | * Not available because there's no underlying message array to | |
143 | * copy to. | |
144 | */ | |
145 | ConstMessageArray(const ConstMessageArray&) = delete; | |
146 | ||
147 | /* | |
148 | * Not available because we don't need it yet. | |
149 | */ | |
150 | ConstMessageArray& operator=(const ConstMessageArray&) = delete; | |
151 | ||
152 | ConstMessageArray(ConstMessageArray&& other) noexcept : | |
153 | ConstMessageArray {other._mLibArrayPtr, other._mLen, other._mCap} | |
154 | { | |
155 | other._reset(); | |
156 | } | |
157 | ||
158 | ConstMessageArray& operator=(ConstMessageArray&& other) noexcept | |
159 | { | |
160 | /* Put existing message references */ | |
161 | this->_putMsgRefs(); | |
162 | ||
163 | /* Move other members to this message array */ | |
164 | _mLibArrayPtr = other._mLibArrayPtr; | |
165 | _mLen = other._mLen; | |
166 | _mCap = other._mCap; | |
167 | ||
168 | /* Reset other message array */ | |
169 | other._reset(); | |
170 | ||
171 | return *this; | |
172 | } | |
173 | ||
174 | /* | |
175 | * Wraps an existing library array `libArrayPtr`, known to contain | |
176 | * `length` messages. | |
177 | * | |
178 | * CAUTION: The ownership of the existing messages contained in | |
179 | * `libArrayPtr` is _moved_ to the returned `ConstMessageArray` | |
180 | * instance. | |
181 | * | |
182 | * This is similar to what the constructor of `std::shared_ptr` | |
183 | * does. Do NOT wrap the same library array twice. | |
184 | */ | |
185 | static ConstMessageArray wrapExisting(const bt_message_array_const libArrayPtr, | |
186 | const std::uint64_t length) noexcept | |
187 | { | |
188 | return ConstMessageArray {libArrayPtr, length, length}; | |
189 | } | |
190 | ||
191 | /* | |
192 | * Wraps an existing library array `libArrayPtr`, known to be empty, | |
193 | * with a capacity of `capacity` messages. | |
194 | */ | |
195 | static ConstMessageArray wrapEmpty(const bt_message_array_const libArrayPtr, | |
196 | const std::uint64_t capacity) noexcept | |
197 | { | |
198 | return ConstMessageArray {libArrayPtr, 0, capacity}; | |
199 | } | |
200 | ||
201 | std::uint64_t length() const noexcept | |
202 | { | |
203 | return _mLen; | |
204 | } | |
205 | ||
206 | std::uint64_t capacity() const noexcept | |
207 | { | |
208 | return _mCap; | |
209 | } | |
210 | ||
211 | bool isEmpty() const noexcept | |
212 | { | |
213 | return _mLen == 0; | |
214 | } | |
215 | ||
216 | bool isFull() const noexcept | |
217 | { | |
218 | return _mLen == _mCap; | |
219 | } | |
220 | ||
2a24eba8 | 221 | ConstMessageArray& append(ConstMessage::Shared message) noexcept |
736675a4 PP |
222 | { |
223 | BT_ASSERT_DBG(!this->isFull()); | |
224 | ||
225 | /* Move reference to underlying array */ | |
226 | _mLibArrayPtr[_mLen] = message.release().libObjPtr(); | |
227 | ++_mLen; | |
2a24eba8 | 228 | return *this; |
736675a4 PP |
229 | } |
230 | ||
231 | /* | |
232 | * Transfers the ownership of the wrapped library array to the | |
233 | * caller, returning the number of contained messages (array | |
234 | * length). | |
235 | */ | |
236 | std::uint64_t release() noexcept | |
237 | { | |
238 | const auto len = _mLen; | |
239 | ||
240 | this->_reset(); | |
241 | return len; | |
242 | } | |
243 | ||
244 | ConstMessage operator[](const std::uint64_t index) const noexcept | |
245 | { | |
246 | BT_ASSERT_DBG(index < _mLen); | |
247 | return ConstMessage {_mLibArrayPtr[index]}; | |
248 | } | |
249 | ||
250 | Iterator begin() const noexcept | |
251 | { | |
252 | return Iterator {*this, 0}; | |
253 | } | |
254 | ||
255 | Iterator end() const noexcept | |
256 | { | |
257 | return Iterator {*this, this->length()}; | |
258 | } | |
259 | ||
260 | private: | |
261 | void _reset() noexcept | |
262 | { | |
263 | /* | |
264 | * This means this array is pretty much dead, and any call to | |
265 | * append() or operator[]() will make assertions fail. | |
266 | * | |
267 | * That being said, you may still move another array to this | |
268 | * one. | |
269 | */ | |
270 | _mLen = 0; | |
271 | _mCap = 0; | |
272 | } | |
273 | ||
274 | /* | |
275 | * Decrements the reference count of all the contained messages. | |
276 | */ | |
277 | void _putMsgRefs() noexcept | |
278 | { | |
279 | std::for_each(&_mLibArrayPtr[0], &_mLibArrayPtr[_mLen], [](const bt_message * const msg) { | |
280 | bt_message_put_ref(msg); | |
281 | }); | |
282 | } | |
283 | ||
284 | /* Underlying array which is generally owned by the library */ | |
285 | bt_message_array_const _mLibArrayPtr; | |
286 | ||
287 | /* Length (count of contained messages) */ | |
288 | std::uint64_t _mLen; | |
289 | ||
290 | /* Capacity (maximum length) */ | |
291 | std::uint64_t _mCap; | |
292 | }; | |
293 | ||
294 | inline ConstMessage ConstMessageArrayIterator::operator*() noexcept | |
295 | { | |
296 | return (*_mMsgArray)[_mIdx]; | |
297 | } | |
298 | ||
299 | } /* namespace bt2 */ | |
300 | ||
301 | #endif /* BABELTRACE_CPP_COMMON_BT2_MESSAGE_ARRAY_HPP */ |