qb  2.0.0.0
C++17 Actor Framework
qb Issue Watch Star Fork Follow @isndev
Loading...
Searching...
No Matches
pipe.h
Go to the documentation of this file.
1
14
15#ifndef QB_PIPE_H
16#define QB_PIPE_H
17#include <array>
18#include <cstring>
19#include <iostream>
20#include <memory>
21#include <qb/string.h>
23#include <qb/utility/nocopy.h>
24#include <qb/utility/prefix.h>
25#include <string_view>
26#include <vector>
27
28namespace qb::allocator {
29
38template <typename T, typename U>
39constexpr auto
41 return sizeof(T) / sizeof(U) + static_cast<bool>(sizeof(T) % sizeof(U));
42}
43
56template <typename T>
57class base_pipe : std::allocator<T> {
58 using base_type = std::allocator<T>;
59 constexpr static const std::size_t _SIZE = 4096;
60
61protected:
62 std::size_t _begin;
63 std::size_t _end;
65 std::size_t _capacity;
66 std::size_t _factor;
67 T *_data;
68
69public:
76 : _begin(0)
77 , _end(0)
78 , _flag_front(false)
79 , _capacity(_SIZE)
80 , _factor(1)
81 , _data(base_type::allocate(_SIZE)) {}
82
90 base_pipe(base_pipe const &rhs)
91 : base_type()
92 , _begin(0)
93 , _end(0)
94 , _flag_front(false)
95 , _capacity(0)
96 , _factor(1)
97 , _data(nullptr) {
98 std::memcpy(allocate_back(rhs.size()), rhs._data, rhs.size() * sizeof(T));
99 }
100
108 base_pipe(base_pipe &&rhs) noexcept
109 : base_type()
110 , _begin(rhs._begin)
111 , _end(rhs._end)
112 , _flag_front(rhs._flag_front)
113 , _capacity(rhs._capacity)
114 , _factor(rhs._factor)
115 , _data(rhs._data) {
116 rhs._begin = rhs._end = 0;
117 rhs._flag_front = false;
118 rhs._capacity = 0;
119 rhs._factor = 1;
120 rhs._data = nullptr;
121 }
122
127 if (this != &rhs) {
128 reset();
129 std::memcpy(allocate_back(rhs.size()), rhs._data, rhs.size() * sizeof(T));
130 }
131 return *this;
132 };
133
140 base_pipe &
141 operator=(base_pipe &&rhs) noexcept {
142 base_type::deallocate(_data, _capacity);
143 _begin = rhs._begin;
144 _end = rhs._end;
145 _flag_front = rhs._flag_front;
146 _capacity = rhs._capacity;
147 _factor = rhs._factor;
148 _data = rhs._data;
149 rhs._begin = rhs._end = 0;
150 rhs._flag_front = false;
151 rhs._capacity = 0;
152 rhs._factor = 1;
153 rhs._data = nullptr;
154 return *this;
155 }
156
163 if (_capacity)
164 base_type::deallocate(_data, _capacity);
165 }
166
172 [[nodiscard]] inline std::size_t
173 capacity() const noexcept {
174 return _capacity;
175 }
176
182 [[nodiscard]] inline T *
183 data() const noexcept {
184 return _data;
185 }
186
192 [[nodiscard]] inline T *
193 begin() const noexcept {
194 return _data + _begin;
195 }
196
202 [[nodiscard]] inline T *
203 end() const noexcept {
204 return _data + _end;
205 }
206
212 [[nodiscard]] inline const T *
213 cbegin() const noexcept {
214 return _data + _begin;
215 }
216
222 [[nodiscard]] inline const T *
223 cend() const noexcept {
224 return _data + _end;
225 }
226
232 [[nodiscard]] inline std::size_t
233 size() const noexcept {
234 return _end - _begin;
235 }
236
245 void
246 resize(std::size_t new_size) {
247 if (new_size <= size())
248 _end -= size() - new_size;
249 else
250 allocate_back(new_size - size());
251 }
252
258 inline void
259 free_front(std::size_t const size) noexcept {
260 _begin += size;
261 }
262
268 inline void
269 free_back(std::size_t const size) noexcept {
270 _end -= size;
271 }
272
281 inline void
282 reset(std::size_t const begin) noexcept {
283 if (begin != _end)
284 _begin = begin;
285 else {
286 _begin = 0;
287 _end = 0;
288 }
289 }
290
296 inline void
297 reset() noexcept {
298 _begin = 0;
299 _end = 0;
300 _flag_front = false;
301 }
302
306 inline void
307 clear() noexcept {
308 reset();
309 }
310
316 inline bool
317 empty() const noexcept {
318 return _begin == _end;
319 }
320
328 inline void
329 free(std::size_t const size) noexcept {
330 if (_flag_front)
331 _begin += size;
332 else
333 _end -= size;
334 }
335
344 inline auto *
345 allocate_back(std::size_t const size) {
346 if (likely(_end + size <= _capacity)) {
347 const auto save_index = _end;
348 _end += size;
349 return _data + save_index;
350 }
351 const auto nb_item = _end - _begin;
352 const auto half = _capacity / 2;
353 if (_begin > half && size < half) {
354 reorder();
355 _end += size;
356 return _data + nb_item;
357 } else {
358 std::size_t new_capacity;
359 do {
360 _factor <<= 1u;
361 new_capacity = _factor * _SIZE;
362 } while (new_capacity - nb_item < size);
363
364 const auto new_data = base_type::allocate(new_capacity);
365 std::memcpy(new_data, _data + _begin, nb_item * sizeof(T));
366 if (_capacity)
367 base_type::deallocate(_data, _capacity);
368
369 _begin = 0;
370 _end = nb_item + size;
371 _capacity = new_capacity;
372 _data = new_data;
373 }
374 return _data + nb_item;
375 }
376
385 template <typename U, typename... _Init>
386 inline U &
387 allocate_back(_Init &&...init) {
388 constexpr std::size_t BUCKET_SIZE = getItemSize<U, T>();
389 return *(new (reinterpret_cast<U *>(allocate_back(BUCKET_SIZE)))
390 U(std::forward<_Init>(init)...));
391 }
392
402 template <typename U, typename... _Init>
403 inline U &
404 allocate_size(std::size_t const size, _Init &&...init) {
405 constexpr std::size_t BUCKET_SIZE = getItemSize<U, T>();
406 return *(new (reinterpret_cast<U *>(allocate_back(size + BUCKET_SIZE)))
407 U(std::forward<_Init>(init)...));
408 }
409
418 inline auto
419 allocate(std::size_t const size) {
420 if (_begin - (size + 1) < _end) {
421 _begin -= size;
422 _flag_front = true;
423 return _data + _begin;
424 }
425 _flag_front = false;
426 return allocate_back(size);
427 }
428
437 template <typename U, typename... _Init>
438 inline U &
439 allocate(_Init &&...init) {
440 constexpr std::size_t BUCKET_SIZE = getItemSize<U, T>();
441 return *(new (reinterpret_cast<U *>(allocate(BUCKET_SIZE)))
442 U(std::forward<_Init>(init)...));
443 }
444
452 template <typename U>
453 inline U &
454 recycle_back(U const &data) {
455 constexpr std::size_t BUCKET_SIZE = getItemSize<U, T>();
456 return *reinterpret_cast<U *>(
457 std::memcpy(allocate_back(BUCKET_SIZE), &data, sizeof(U)));
458 }
459
468 template <typename U>
469 inline U &
470 recycle_back(U const &data, std::size_t const size) {
471 return *reinterpret_cast<U *>(
472 std::memcpy(allocate_back(size), &data, size * sizeof(T)));
473 }
474
482 template <typename U>
483 inline U &
484 recycle(U const &data) {
485 constexpr std::size_t BUCKET_SIZE = getItemSize<U, T>();
486 return *reinterpret_cast<U *>(
487 std::memcpy(allocate(BUCKET_SIZE), &data, sizeof(U)));
488 }
489
498 template <typename U>
499 inline U &
500 recycle(U const &data, std::size_t const size) {
501 return *reinterpret_cast<U *>(
502 std::memcpy(allocate(size), &data, size * sizeof(T)));
503 }
504
510 inline void
511 reorder() noexcept {
512 if (!_begin)
513 return;
514 const auto nb_item = _end - _begin;
515 // std::cout << "Start reorder " << _begin << ":" << _end << "|" << nb_item;
516 std::memmove(_data, _data + _begin, nb_item * sizeof(T));
517 _begin = 0;
518 _end = nb_item;
519 // std::cout << "End reorder " << _begin << ":" << _end << "|" << _end - _begin;
520 }
521
525 inline void
526 flush() const noexcept {}
527
535 void
536 reserve(std::size_t const size) {
539 }
540};
541
551template <typename T>
552class QB_LOCKFREE_CACHELINE_ALIGNMENT pipe : public base_pipe<T> {
553public:
559 inline void
560 swap(pipe &rhs) noexcept {
561 std::swap(*reinterpret_cast<CacheLine *>(this),
562 *reinterpret_cast<CacheLine *>(&rhs));
563 }
564
573 template <typename _It>
574 pipe &
575 put(_It begin, _It const &end) {
576 auto out = this->allocate_back(end - begin);
577 while (begin != end) {
578 *out = *begin;
579 ++out;
580 ++begin;
581 }
582 return *this;
583 }
584
592 pipe &
593 put(T const *data, std::size_t const size) {
594 memcpy(this->allocate_back(size), data, size * sizeof(T));
595 return *this;
596 }
597
605 template <typename U>
607 operator<<(U &&rhs) noexcept {
608 this->template allocate_back<U>(std::forward<U>(rhs));
609 return *this;
610 }
611};
612
619template <>
620class pipe<char> : public base_pipe<char> {
621public:
622 pipe() = default;
623 pipe(pipe const &) = default;
624 pipe(pipe &&) noexcept = default;
625 pipe &operator=(pipe const &) = default;
626 pipe &operator=(pipe &&) noexcept = default;
627
635 template <typename U>
636 pipe &
637 put(U &rhs) {
638 return put(static_cast<const U &>(rhs));
639 }
640
648 template <typename U>
649 pipe &
650 put(const U &rhs) {
651 return put(std::to_string(rhs));
652 }
653
661 template <std::size_t _Size>
662 pipe &
663 put(const char (&str)[_Size]) {
664 memcpy(allocate_back(_Size - 1), str, _Size - 1);
665 return *this;
666 }
667
675 template <std::size_t _Size>
676 pipe &
677 put(const qb::string<_Size> &str) {
678 memcpy(allocate_back(str.size()), str.c_str(), str.size());
679 return *this;
680 }
681
689 template <typename T>
690 pipe &
691 put(std::vector<T> const &vec) {
692 memcpy(allocate_back(vec.size()), reinterpret_cast<const char *>(vec.data()),
693 vec.size());
694 return *this;
695 }
696
705 template <typename T, std::size_t _Size>
706 pipe &
707 put(std::array<T, _Size> const &arr) {
708 memcpy(allocate_back(arr.size()), reinterpret_cast<const char *>(arr.data()),
709 arr.size());
710 return *this;
711 }
712
721 template <typename _It>
722 pipe &
723 put(_It begin, _It const &end) {
724 auto out = allocate_back(end - begin);
725 while (begin != end) {
726 *out = *begin;
727 ++out;
728 ++begin;
729 }
730 return *this;
731 }
732
740 template <typename U>
741 pipe &
742 operator<<(const U &rhs) {
743 return put(rhs);
744 }
745
753 template <typename U>
754 pipe &
755 operator<<(U &rhs) {
756 return put(rhs);
757 }
758
766 pipe &put(char const *data, std::size_t size) noexcept;
767
775 pipe &write(const char *data, std::size_t size) noexcept;
776
782 std::string str() const noexcept;
783
789 std::string_view view() const noexcept;
790};
791
792// Explicit specialization declarations
793template <>
794pipe<char> &pipe<char>::put<char>(const char &c);
795
796template <>
797pipe<char> &pipe<char>::put<unsigned char>(const unsigned char &c);
798
799template <>
800pipe<char> &pipe<char>::put<const char *>(const char *const &c);
801
802template <>
803pipe<char> &pipe<char>::put<std::string>(std::string const &str);
804
805template <>
806pipe<char> &pipe<char>::put<std::string_view>(std::string_view const &str);
807
808template <>
809pipe<char> &pipe<char>::put<pipe<char>>(pipe<char> const &rhs);
810
811} // namespace qb::allocator
812
821template <typename stream, typename = std::enable_if_t<
822 !std::is_same_v<stream, qb::allocator::pipe<char>>>>
823stream &
824operator<<(stream &os, qb::allocator::pipe<char> const &p) {
825 os << std::string_view(p.begin(), p.size());
826 return os;
827}
828
829#endif // QB_PIPE_H
Branch prediction hint utilities for performance optimization.
Base class for the extensible buffer.
Definition pipe.h:57
void reset() noexcept
Completely resets the buffer.
Definition pipe.h:297
void clear() noexcept
Clears the buffer content (alias for reset)
Definition pipe.h:307
T * end() const noexcept
Returns a pointer just after the last valid element.
Definition pipe.h:203
T * _data
Buffer data.
Definition pipe.h:67
void free_back(std::size_t const size) noexcept
Frees elements at the end of the buffer.
Definition pipe.h:269
U & recycle_back(U const &data)
Copies an object to the end of the buffer.
Definition pipe.h:454
std::size_t _begin
Index of first valid element.
Definition pipe.h:62
U & allocate_back(_Init &&...init)
Allocates and constructs an object of type U at the end of the buffer.
Definition pipe.h:387
void reorder() noexcept
Reorganizes the buffer to consolidate free space.
Definition pipe.h:511
std::size_t _factor
Buffer expansion factor.
Definition pipe.h:66
void reset(std::size_t const begin) noexcept
Resets the buffer to a specific position.
Definition pipe.h:282
base_pipe & operator=(base_pipe &&rhs) noexcept
Move assignment operator.
Definition pipe.h:141
std::size_t capacity() const noexcept
Returns the total capacity of the buffer.
Definition pipe.h:173
base_pipe & operator=(base_pipe const &rhs)
Copy assignment operator.
Definition pipe.h:126
std::size_t size() const noexcept
Returns the number of valid elements in the buffer.
Definition pipe.h:233
bool _flag_front
Indicates if the last allocation was at the beginning.
Definition pipe.h:64
base_pipe()
Default constructor.
Definition pipe.h:75
bool empty() const noexcept
Checks if the buffer is empty.
Definition pipe.h:317
void flush() const noexcept
Flushes the buffer (synchronization operation, no-op in this class)
Definition pipe.h:526
std::size_t _capacity
Total buffer capacity.
Definition pipe.h:65
U & allocate(_Init &&...init)
Allocates space and constructs an object.
Definition pipe.h:439
U & recycle(U const &data)
Copies an object to the beginning or end of the buffer.
Definition pipe.h:484
~base_pipe()
Destructor.
Definition pipe.h:162
T * data() const noexcept
Returns a pointer to the buffer data.
Definition pipe.h:183
void resize(std::size_t new_size)
Resizes the buffer.
Definition pipe.h:246
void free(std::size_t const size) noexcept
Frees a specified number of elements.
Definition pipe.h:329
T * begin() const noexcept
Returns a pointer to the first valid element.
Definition pipe.h:193
void reserve(std::size_t const size)
Reserves space in the buffer.
Definition pipe.h:536
auto * allocate_back(std::size_t const size)
Allocates space at the end of the buffer.
Definition pipe.h:345
const T * cend() const noexcept
Returns a constant pointer just after the last valid element.
Definition pipe.h:223
base_pipe(base_pipe const &rhs)
Copy constructor.
Definition pipe.h:90
auto allocate(std::size_t const size)
Allocates space at the beginning or end of the buffer.
Definition pipe.h:419
U & allocate_size(std::size_t const size, _Init &&...init)
Allocates space with a custom size and constructs an object.
Definition pipe.h:404
const T * cbegin() const noexcept
Returns a constant pointer to the first valid element.
Definition pipe.h:213
base_pipe(base_pipe &&rhs) noexcept
Move constructor.
Definition pipe.h:108
std::size_t _end
Index just after the last valid element.
Definition pipe.h:63
U & recycle_back(U const &data, std::size_t const size)
Copies data to the end of the buffer.
Definition pipe.h:470
U & recycle(U const &data, std::size_t const size)
Copies data to the beginning or end of the buffer.
Definition pipe.h:500
void free_front(std::size_t const size) noexcept
Frees elements at the beginning of the buffer.
Definition pipe.h:259
Extensible buffer optimized for performance.
Definition pipe.h:552
void swap(pipe &rhs) noexcept
Swaps content with another buffer.
Definition pipe.h:560
pipe & put(_It begin, _It const &end)
Adds elements from an iterator range.
Definition pipe.h:575
pipe & put(T const *data, std::size_t const size)
Adds elements from an array.
Definition pipe.h:593
const_pointer c_str() const noexcept
Get C-style string representation.
Definition string.h:435
constexpr std::size_t size() const noexcept
Get the string length.
Definition string.h:509
std::ostream & operator<<(std::ostream &os, qb::Actor const &actor)
Stream output operator for Actor objects.
std::ostream & operator<<(std::ostream &os, const qb::jsonb &j)
Stream output operator for qb::jsonb.
Definition json.h:324
json::string_t string
JSON string type from nlohmann::json.
Definition json.h:67
bool likely(bool expr)
Hint for branch prediction when a condition is expected to be true.
Definition branch_hints.h:49
Pipe pipe
Alias for the Pipe class.
Definition Pipe.h:117
Defines a base class to make derived classes non-copyable.
Platform-specific alignment macros, cache-line definitions, and related utilities.
Fixed-size string implementation optimized for performance.
A structure automatically aligned to cache line boundaries.
Definition prefix.h:108
constexpr auto getItemSize()
Calculates the number of elements of type U needed to store an element of type T.
Definition pipe.h:40