cpp-common/bt2: make setters return `*this`
[babeltrace.git] / src / cpp-common / bt2 / message-array.hpp
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
221 ConstMessageArray& append(ConstMessage::Shared message) noexcept
222 {
223 BT_ASSERT_DBG(!this->isFull());
224
225 /* Move reference to underlying array */
226 _mLibArrayPtr[_mLen] = message.release().libObjPtr();
227 ++_mLen;
228 return *this;
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 */
This page took 0.03584 seconds and 4 git commands to generate.