| 191 | */ |
| 192 | template<typename T, Concepts::ByteLike StorageType> |
| 193 | void IO::CircularBuffer<T, StorageType>::append(const T& data) |
| 194 | { |
| 195 | const qsizetype dataSize = data.size(); |
| 196 | if (dataSize == 0) [[unlikely]] |
| 197 | return; |
| 198 | |
| 199 | const uint8_t* src = reinterpret_cast<const uint8_t*>(data.data()); |
| 200 | |
| 201 | qsizetype copySize = dataSize; |
| 202 | if (copySize > m_capacity) [[unlikely]] { |
| 203 | src += (copySize - m_capacity); |
| 204 | copySize = m_capacity; |
| 205 | m_overflowCount.fetch_add(dataSize - m_capacity, std::memory_order_relaxed); |
| 206 | } |
| 207 | |
| 208 | const qsizetype head = m_head.load(std::memory_order_acquire); |
| 209 | const qsizetype tail = m_tail.load(std::memory_order_relaxed); |
| 210 | |
| 211 | qsizetype current_size = (tail >= head) ? (tail - head) : (m_capacity - head + tail); |
| 212 | qsizetype free_space = m_capacity - current_size; |
| 213 | |
| 214 | if (copySize > free_space) [[unlikely]] { |
| 215 | const qsizetype overwrite = copySize - free_space; |
| 216 | m_overflowCount.fetch_add(overwrite, std::memory_order_relaxed); |
| 217 | |
| 218 | const qsizetype new_head = (head + overwrite) & m_capacityMask; |
| 219 | m_head.store(new_head, std::memory_order_release); |
| 220 | } |
| 221 | |
| 222 | const qsizetype firstChunk = std::min(copySize, m_capacity - tail); |
| 223 | std::memcpy(&m_buffer[tail], src, firstChunk); |
| 224 | |
| 225 | if (copySize > firstChunk) [[unlikely]] |
| 226 | std::memcpy(&m_buffer[0], src + firstChunk, copySize - firstChunk); |
| 227 | |
| 228 | const qsizetype new_tail = (tail + copySize) & m_capacityMask; |
| 229 | m_tail.store(new_tail, std::memory_order_release); |
| 230 | } |
| 231 | |
| 232 | /** |
| 233 | * @brief Clears the buffer and modifies its maximum capacity. |
no test coverage detected