sparrow-ipc 0.2.0
Loading...
Searching...
No Matches
any_output_stream.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <concepts>
4#include <cstddef>
5#include <cstdint>
6#include <functional>
7#include <memory>
8#include <span>
9
11
12namespace sparrow_ipc
13{
20 template <typename T>
21 concept writable_stream = requires(T& t, const char* s, std::streamsize count) {
22 { t.write(s, count) };
23 };
24
55 {
56 public:
57
67 template <writable_stream TStream>
68 any_output_stream(TStream& stream);
69
73 ~any_output_stream() = default;
74
75 any_output_stream(any_output_stream&&) noexcept = default;
76 any_output_stream& operator=(any_output_stream&&) noexcept = default;
77
79 any_output_stream& operator=(const any_output_stream&) = delete;
80
88 void write(std::span<const std::uint8_t> span);
89
97 void write(uint8_t value, std::size_t count = 1);
98
103
109 void reserve(std::size_t size);
110
116 void reserve(const std::function<std::size_t()>& calculate_reserve_size);
117
123 [[nodiscard]] size_t size() const;
124
132 template <typename TStream>
133 TStream& get();
134
142 template <typename TStream>
143 const TStream& get() const;
144
145 private:
146
150 struct stream_concept
151 {
152 virtual ~stream_concept() = default;
153 virtual void write(const char* s, std::streamsize count) = 0;
154 virtual void write(std::span<const std::uint8_t> span) = 0;
155 virtual void write(uint8_t value, std::size_t count) = 0;
156 virtual void put(uint8_t value) = 0;
157 virtual void add_padding() = 0;
158 virtual void reserve(std::size_t size) = 0;
159 virtual void reserve(const std::function<std::size_t()>& calculate_reserve_size) = 0;
160 [[nodiscard]] virtual size_t size() const = 0;
161 };
162
168 template <typename TStream>
169 class stream_model : public stream_concept
170 {
171 public:
172
173 stream_model(TStream& stream);
174
175 void write(const char* s, std::streamsize count) final;
176
177 void write(std::span<const std::uint8_t> span) final;
178
179 void write(uint8_t value, std::size_t count) final;
180
181 void put(uint8_t value) final;
182
183 void add_padding() final;
184
185 void reserve(std::size_t size) final;
186
187 void reserve(const std::function<std::size_t()>& calculate_reserve_size) final;
188
189 [[nodiscard]] size_t size() const final;
190
191 TStream& get_stream();
192
193 const TStream& get_stream() const;
194
195 private:
196
197 TStream* m_stream;
198 size_t m_size = 0;
199 };
200
201 std::unique_ptr<stream_concept> m_impl;
202 };
203
204 // Implementation
205
206 template <writable_stream TStream>
208 : m_impl(std::make_unique<stream_model<TStream>>(stream))
209 {
210 }
211
212 template <typename TStream>
214 {
215 auto* model = dynamic_cast<stream_model<TStream>*>(m_impl.get());
216 if (!model)
217 {
218 throw std::bad_cast();
219 }
220 return model->get_stream();
221 }
222
223 template <typename TStream>
224 const TStream& any_output_stream::get() const
225 {
226 const auto* model = dynamic_cast<const stream_model<TStream>*>(m_impl.get());
227 if (!model)
228 {
229 throw std::bad_cast();
230 }
231 return model->get_stream();
232 }
233
234 // stream_model implementation
235
236 template <typename TStream>
237 any_output_stream::stream_model<TStream>::stream_model(TStream& stream)
238 : m_stream(&stream)
239 {
240 }
241
242 template <typename TStream>
243 void any_output_stream::stream_model<TStream>::write(const char* s, std::streamsize count)
244 {
245 m_stream->write(s, count);
246 m_size += static_cast<size_t>(count);
247 }
248
249 template <typename TStream>
250 void any_output_stream::stream_model<TStream>::write(std::span<const std::uint8_t> span)
251 {
252 m_stream->write(reinterpret_cast<const char*>(span.data()), static_cast<std::streamsize>(span.size()));
253 m_size += span.size();
254 }
255
256 template <typename TStream>
257 void any_output_stream::stream_model<TStream>::write(uint8_t value, std::size_t count)
258 {
259 if constexpr (requires(TStream& t, uint8_t v, std::size_t c) { t.write(v, c); })
260 {
261 m_stream->write(value, count);
262 }
263 else
264 {
265 // Fallback: write one byte at a time
266 for (std::size_t i = 0; i < count; ++i)
267 {
268 m_stream->put(value);
269 }
270 }
271 m_size += count;
272 }
273
274 template <typename TStream>
275 void any_output_stream::stream_model<TStream>::put(uint8_t value)
276 {
277 m_stream->put(value);
278 m_size ++;
279 }
280
281 template <typename TStream>
282 void any_output_stream::stream_model<TStream>::add_padding()
283 {
284 const size_t current_size = size();
285 const size_t padding_needed = (8 - (current_size % 8)) % 8;
286 if (padding_needed > 0)
287 {
288 static constexpr char padding_value = 0;
289 for (size_t i = 0; i < padding_needed; ++i)
290 {
291 m_stream->write(&padding_value, 1);
292 }
293 m_size += padding_needed;
294 }
295 }
296
297 template <typename TStream>
298 void any_output_stream::stream_model<TStream>::reserve(std::size_t size)
299 {
300 if constexpr (requires(TStream& t, std::size_t s) { t.reserve(s); })
301 {
302 m_stream->reserve(size);
303 }
304 // If not reservable, do nothing
305 }
306
307 template <typename TStream>
308 void any_output_stream::stream_model<TStream>::reserve(const std::function<std::size_t()>& calculate_reserve_size)
309 {
310 if constexpr (requires(TStream& t, const std::function<std::size_t()>& func) {
311 { t.reserve(func) };
312 })
313 {
314 m_stream->reserve(calculate_reserve_size);
315 }
316 else if constexpr (requires(TStream& t, std::size_t s) { t.reserve(s); })
317 {
318 m_stream->reserve(calculate_reserve_size());
319 }
320 // If not reservable, do nothing
321 }
322
323 template <typename TStream>
324 size_t any_output_stream::stream_model<TStream>::size() const
325 {
326 if constexpr (requires(const TStream& t) {
327 { t.size() } -> std::convertible_to<size_t>;
328 })
329 {
330 return m_stream->size();
331 }
332 else
333 {
334 return m_size;
335 }
336 }
337
338 template <typename TStream>
339 TStream& any_output_stream::stream_model<TStream>::get_stream()
340 {
341 return *m_stream;
342 }
343
344 template <typename TStream>
345 const TStream& any_output_stream::stream_model<TStream>::get_stream() const
346 {
347 return *m_stream;
348 }
349} // namespace sparrow_ipc
~any_output_stream()=default
Default destructor.
void add_padding()
Adds padding to align to 8-byte boundary.
void reserve(std::size_t size)
Reserves capacity if supported by the underlying stream.
any_output_stream(TStream &stream)
Constructs a type-erased stream from any stream-like object.
void write(std::span< const std::uint8_t > span)
Writes a span of bytes to the underlying stream.
any_output_stream(any_output_stream &&) noexcept=default
TStream & get()
Gets a reference to the underlying stream cast to the specified type.
size_t size() const
Gets the current size of the stream.
Concept for stream-like types that support write operations.
#define SPARROW_IPC_API
Definition config.hpp:12