blob: f511a95ab9edb2d39bf5accee5b1bb04b8178eb8 [file] [log] [blame]
Artem Senicheve8837d52020-06-07 11:59:04 +03001// SPDX-License-Identifier: Apache-2.0
2// Copyright (C) 2020 YADRO
3
4#include "log_buffer.hpp"
5
6/** @brief Check if a character is EOL symbol. */
7constexpr bool isEol(char c)
8{
9 return c == '\r' || c == '\n';
10}
11
12LogBuffer::LogBuffer(size_t maxSize, size_t maxTime) :
13 lastComplete(true), sizeLimit(maxSize), timeLimit(maxTime)
14{}
15
16void LogBuffer::append(const char* data, size_t sz)
17{
18 // Split raw data into separate messages by EOL symbols (\r or \n).
19 // Stream may not be ended with EOL, so we handle this situation by
20 // lastComplete flag.
21 size_t pos = 0;
22 while (pos < sz)
23 {
24 // Search for EOL ('\r' or '\n')
25 size_t eol = pos;
26 while (eol < sz)
27 {
28 if (isEol(data[eol]))
29 {
30 break;
31 }
32 ++eol;
33 }
34 const bool eolFound = eol < sz;
35 const char* msgText = data + pos;
36 const size_t msgLen = (eolFound ? eol : sz) - pos;
37
38 // Append message to the container
39 if (!lastComplete && !messages.empty())
40 {
41 // The last message is incomplete, add data as part of it
42 messages.back().text.append(msgText, msgLen);
43 }
44 else
45 {
46 Message msg;
47 time(&msg.timeStamp);
48 msg.text.assign(msgText, msgLen);
49 messages.push_back(msg);
50 }
51 lastComplete = eolFound;
52
53 // Move current position and skip EOL character
54 pos = eol + 1;
55 // Handle EOL sequences '\r\n' or '\n\r' as one delimiter
56 if (eolFound && pos < sz && isEol(data[pos]) && data[eol] != data[pos])
57 {
58 ++pos;
59 }
60 }
61
62 shrink();
63}
64
65void LogBuffer::setFullHandler(std::function<void()> cb)
66{
67 fullHandler = cb;
68}
69
70void LogBuffer::clear()
71{
72 messages.clear();
73 lastComplete = true;
74}
75
76bool LogBuffer::empty() const
77{
78 return messages.empty();
79}
80
81LogBuffer::container_t::const_iterator LogBuffer::begin() const
82{
83 return messages.begin();
84}
85
86LogBuffer::container_t::const_iterator LogBuffer::end() const
87{
88 return messages.end();
89}
90
91void LogBuffer::shrink()
92{
93 if (sizeLimit && messages.size() > sizeLimit)
94 {
95 if (fullHandler)
96 {
97 fullHandler();
98 }
99 while (messages.size() > sizeLimit)
100 {
101 messages.pop_front();
102 }
103 }
104 if (timeLimit && !messages.empty())
105 {
106 time_t oldest;
107 time(&oldest);
108 oldest -= timeLimit * 60 /* sec */;
109 if (messages.begin()->timeStamp < oldest)
110 {
111 if (fullHandler)
112 {
113 fullHandler();
114 }
115 while (!messages.empty() && messages.begin()->timeStamp < oldest)
116 {
117 messages.pop_front();
118 }
119 }
120 }
121}