• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

devmarkusb / util / 26256902581

21 May 2026 10:30PM UTC coverage: 92.731% (+0.006%) from 92.725%
26256902581

push

github

MarkusB
lint

19 of 20 new or added lines in 5 files covered. (95.0%)

1 existing line in 1 file now uncovered.

6468 of 6975 relevant lines covered (92.73%)

125.82 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.72
/include/mb/ul/basiccodesupport/std/circular_buffer.hpp
1
//! Circular buffer structure(s).
2
/** They make a perfect replacement for queue if the needed capacity is known beforehand.
3
    The behavior for further pushes in case of a full buffer is simple overwriting of the oldest elements.
4
    There are two kinds of implementations provided:
5
        1) CircularBuffer<T>, which dynamically allocates the buffer memory (although only once of the capacity
6
        passed to the constructor then - like a vector::resize/reserve), and
7
        2) CircularBuffer<T, size_t> which does it statically (on stack ideally) with the capacity passed as template
8
        parameter (without the constructor expecting any arguments).
9
    The latter obviously should be preferred if the needed capacity is known at compile time.*/
10
//! \file
11

12
#ifndef CIRCULAR_BUFFER_H_GFHCIO489CGHX4M9GH39U8HX3GH
13
#define CIRCULAR_BUFFER_H_GFHCIO489CGHX4M9GH39U8HX3GH
14

15
#include "../assert.hpp"
16
#include <array>
17
#include <cstddef>
18
#include <memory>
19
#include <type_traits>
20
#include <vector>
21

22
namespace mb::ul {
23
namespace detail::circbuf_impl_container {
24
template <typename T, size_t static_capacity>
25
class Array {
26
protected:
27
    std::array<T, static_capacity> buf_{};
28

29
    template <typename U, typename Buffer>
30
    void emplace(U&& item, Buffer& buf, size_t head) noexcept {
31
        static_assert(std::is_convertible_v<U, T>);
32

33
        buf[head] = std::forward<U>(item);
34
    }
35
};
36

37
template <typename T>
38
class Vector {
39
protected:
40
    std::vector<T> buf_;
41
    size_t capacity_{};
42

43
    explicit Vector(size_t capacity)
17✔
44
        : capacity_{capacity} {
17✔
45
        UL_EXPECT(capacity > 0);
46

47
        buf_.resize(capacity);
17✔
48
    }
17✔
49

50
    template <typename U, typename Buffer>
51
    void emplace(U&& item, Buffer& buf, size_t head) {
1✔
52
        static_assert(std::is_convertible_v<U, T>);
53

54
        auto it = std::begin(buf);
1✔
55
        std::advance(it, head);
56
        buf.emplace(it, std::forward<U>(item));
2✔
57
    }
1✔
58
};
59

60
template <typename T, size_t static_capacity>
61
using Base = std::conditional_t<static_capacity == 0, Vector<T>, Array<T, static_capacity>>;
62
} // namespace detail::circbuf_impl_container
63

64
/** If you know the (always fixed) capacity of the circular buffer at compile time you should definitely prefer
65
    to pass it as template staticCapacity != 0. Then there is only a default constructor (no parameters).
66
    Otherwise for a capacity only known as soon as runtime, you keep staticCapacity at 0 and pass the desired
67
    capacity as constructor parameter.*/
68
template <typename T, size_t static_capacity = 0>
69
class CircularBuffer : private detail::circbuf_impl_container::Base<T, static_capacity> {
70
public:
71
    using Base = detail::circbuf_impl_container::Base<T, static_capacity>;
72

73
    //! Expects capacity > 0.
74
    template <size_t capacity_enabled = static_capacity>
75
        requires(capacity_enabled == 0)
76
    explicit CircularBuffer(size_t capacity)
17✔
77
        : Base{capacity} {
17✔
78
    }
17✔
79

80
    CircularBuffer() noexcept = default;
2✔
81

82
    template <typename U>
83
    void push(U&& item) noexcept {
172✔
84
        static_assert(std::is_convertible_v<U, T>);
85

86
        if (full_)
172✔
87
            tail_ = next(tail_);
20✔
88

89
        Base::buf_[head_] = std::forward<U>(item); // NOLINT
172✔
90

91
        head_ = next(head_);
172✔
92

93
        full_ = head_ == tail_;
172✔
94
    }
172✔
95

96
    //! Don't use! Doesn't work yet.
97
    /** (At least not working in the 'dynamic' staticCapacity == 0 case, whereas in the 'static' capacity != 0 case
98
        there is no difference to push.)*/
99
    template <typename U>
100
    void emplace(U&& item) {
1✔
101
        static_assert(std::is_convertible_v<U, T>);
102

103
        if (full_)
1✔
NEW
104
            tail_ = next(tail_);
×
105

106
        Base::emplace(std::forward<U>(item), Base::buf_, head_);
2✔
107

108
        head_ = next(head_);
1✔
109

110
        full_ = head_ == tail_;
1✔
111
    }
1✔
112

113
    bool try_front(T& front_item) const noexcept {
2✔
114
        if (empty())
2✔
115
            return false;
1✔
116

117
        front_item = Base::buf_[tail_];
1✔
118
        return true;
1✔
119
    }
120

121
    bool try_pop(T& popped_item) noexcept {
144✔
122
        if (empty())
144✔
123
            return false;
6✔
124

125
        popped_item = Base::buf_[tail_]; // NOLINT
138✔
126
        full_ = false;
138✔
127
        tail_ = next(tail_);
138✔
128

129
        return true;
138✔
130
    }
131

132
    void reset() noexcept {
6✔
133
        head_ = tail_;
6✔
134
        full_ = false;
6✔
135
    }
6✔
136

137
    [[nodiscard]] bool empty() const noexcept {
310✔
138
        return !full_ && (head_ == tail_);
310✔
139
    }
140

141
    [[nodiscard]] bool full() const noexcept {
164✔
142
        return full_;
164✔
143
    }
144

145
    [[nodiscard]] constexpr size_t capacity() const noexcept {
399✔
146
        if constexpr (static_capacity > 0)
147
            return static_capacity;
94✔
148
        else
149
            return Base::capacity_;
305✔
150
    }
151

152
    [[nodiscard]] size_t size() const noexcept {
56✔
153
        size_t size = capacity();
56✔
154

155
        if (!full_) {
56✔
156
            if (head_ >= tail_)
42✔
157
                size = head_ - tail_;
42✔
158
            else
159
                size = capacity() + head_ - tail_;
×
160
        }
161

162
        return size;
56✔
163
    }
164

165
private:
166
    [[nodiscard]] size_t next(size_t index) const noexcept {
331✔
167
        const auto next_index = index + 1;
331✔
168
        if (next_index == capacity())
331✔
169
            return 0;
52✔
170
        return next_index;
279✔
171
    }
172

173
    size_t head_{0};
174
    size_t tail_{0};
175
    bool full_{};
176
};
177
} // namespace mb::ul
178

179
#endif
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc