master
cpp 154 lines 4.06 KB
Raw
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 }