| 1 | /*++ |
| 2 | |
| 3 | Copyright (c) Microsoft. All rights reserved. |
| 4 | |
| 5 | Module Name: |
| 6 | |
| 7 | RingBuffer.cpp |
| 8 | |
| 9 | Abstract: |
| 10 | |
| 11 | This file contains definitions for the RingBuffer class. |
| 12 | |
| 13 | --*/ |
| 14 | |
| 15 | #include "precomp.h" |
| 16 | #include "RingBuffer.h" |
| 17 | |
| 18 | RingBuffer::RingBuffer(size_t size) : m_maxSize(size), m_offset(0) |
| 19 | { |
| 20 | m_buffer.reserve(size); |
| 21 | } |
| 22 | |
| 23 | void RingBuffer::Insert(std::string_view data) |
| 24 | { |
| 25 | auto lock = m_lock.lock_exclusive(); |
| 26 | auto remainingData = gsl::make_span(data.data(), data.size()); |
| 27 | if (remainingData.size() > m_maxSize) |
| 28 | { |
| 29 | remainingData = remainingData.subspan(remainingData.size() - m_maxSize); |
| 30 | } |
| 31 | |
| 32 | const auto bytesAtEnd = std::min(m_maxSize - m_offset, remainingData.size()); |
| 33 | if (m_offset + bytesAtEnd > m_buffer.size()) |
| 34 | { |
| 35 | m_buffer.resize(m_offset + bytesAtEnd); |
| 36 | WI_ASSERT(m_buffer.size() <= m_maxSize); |
| 37 | } |
| 38 | |
| 39 | const auto allBuffer = gsl::make_span(m_buffer); |
| 40 | const auto beginCopyBuffer = allBuffer.subspan(m_offset, bytesAtEnd); |
| 41 | copy(remainingData.subspan(0, bytesAtEnd), beginCopyBuffer); |
| 42 | remainingData = remainingData.subspan(bytesAtEnd); |
| 43 | if (!remainingData.empty()) |
| 44 | { |
| 45 | copy(remainingData, allBuffer); |
| 46 | m_offset = remainingData.size(); |
| 47 | } |
| 48 | else |
| 49 | { |
| 50 | m_offset += bytesAtEnd; |
| 51 | } |
| 52 | } |
| 53 | |
| 54 | std::vector<std::string> RingBuffer::GetLastDelimitedStrings(char Delimiter, size_t Count) const |
| 55 | { |
| 56 | auto lock = m_lock.lock_shared(); |
| 57 | auto [begin, end] = Contents(); |
| 58 | std::vector<std::string> results; |
| 59 | std::optional<size_t> endIndex; |
| 60 | for (size_t i = end.size(); i > 0; i--) |
| 61 | { |
| 62 | if (results.size() == Count) |
| 63 | { |
| 64 | break; |
| 65 | } |
| 66 | |
| 67 | if (Delimiter == end[i - 1]) |
| 68 | { |
| 69 | if (endIndex.has_value()) |
| 70 | { |
| 71 | results.emplace(results.begin(), &end[i], endIndex.value() - i); |
| 72 | endIndex.reset(); |
| 73 | } |
| 74 | else |
| 75 | { |
| 76 | endIndex = i - 1; |
| 77 | } |
| 78 | } |
| 79 | } |
| 80 | |
| 81 | if (results.size() == Count) |
| 82 | { |
| 83 | return results; |
| 84 | } |
| 85 | |
| 86 | std::string partial; |
| 87 | if (endIndex.has_value()) |
| 88 | { |
| 89 | partial = std::string{&end[0], endIndex.value()}; |
| 90 | endIndex.reset(); |
| 91 | } |
| 92 | |
| 93 | for (size_t i = begin.size(); i > 0; i--) |
| 94 | { |
| 95 | if (results.size() == Count) |
| 96 | { |
| 97 | break; |
| 98 | } |
| 99 | |
| 100 | if (Delimiter == begin[i - 1]) |
| 101 | { |
| 102 | if (!partial.empty()) |
| 103 | { |
| 104 | // The debug CRT will fastfail if begin[size] is accessed |
| 105 | // But in this case it's not a problem because begin.size() - i would be == 0 |
| 106 | std::string partial_begin{&begin.data()[i], begin.size() - i}; |
| 107 | results.emplace(results.begin(), partial_begin + partial); |
| 108 | partial.clear(); |
| 109 | } |
| 110 | else if (endIndex.has_value()) |
| 111 | { |
| 112 | results.emplace(results.begin(), &begin.data()[i], endIndex.value() - i); |
| 113 | endIndex.reset(); |
| 114 | } |
| 115 | else |
| 116 | { |
| 117 | endIndex = i - 1; |
| 118 | } |
| 119 | } |
| 120 | } |
| 121 | |
| 122 | if (results.size() < Count) |
| 123 | { |
| 124 | // May have lost some data, or this could be the very first line logged. |
| 125 | if (!partial.empty()) |
| 126 | { |
| 127 | results.emplace(results.begin(), partial); |
| 128 | } |
| 129 | else if (endIndex.has_value()) |
| 130 | { |
| 131 | results.emplace(results.begin(), &begin[0], endIndex.value()); |
| 132 | } |
| 133 | } |
| 134 | |
| 135 | return results; |
| 136 | } |
| 137 | |
| 138 | std::string RingBuffer::Get() const |
| 139 | { |
| 140 | auto lock = m_lock.lock_shared(); |
| 141 | auto [begin, end] = Contents(); |
| 142 | std::string data; |
| 143 | data.reserve(begin.size() + end.size()); |
| 144 | data.append(begin.data(), begin.size()); |
| 145 | data.append(end.data(), end.size()); |
| 146 | return data; |
| 147 | } |
| 148 | |
| 149 | std::pair<std::string_view, std::string_view> RingBuffer::Contents() const |
| 150 | { |
| 151 | std::string_view beginView(m_buffer.data() + m_offset, m_buffer.size() - m_offset); |
| 152 | std::string_view endView(m_buffer.data(), m_offset); |
| 153 | return {beginView, endView}; |
| 154 | } |