blob: c2e1192e14a5a4cd2528d997ec1fc71eb55b0f12 [file] [log] [blame]
James Kuszmaulcf324122023-01-14 14:07:17 -08001// Copyright (c) FIRST and other WPILib contributors.
2// Open Source Software; you can modify and/or share it under the terms of
3// the WPILib BSD license file in the root directory of this project.
4
5#include "wpi/DataLogReader.h"
6
7#include "wpi/DataLog.h"
8#include "wpi/Endian.h"
9#include "wpi/MathExtras.h"
10
11using namespace wpi::log;
12
13static bool ReadString(std::span<const uint8_t>* buf, std::string_view* str) {
14 if (buf->size() < 4) {
15 *str = {};
16 return false;
17 }
18 uint32_t len = wpi::support::endian::read32le(buf->data());
19 if (len > (buf->size() - 4)) {
20 *str = {};
21 return false;
22 }
23 *str = {reinterpret_cast<const char*>(buf->data() + 4), len};
24 *buf = buf->subspan(len + 4);
25 return true;
26}
27
28bool DataLogRecord::IsStart() const {
29 return m_entry == 0 && m_data.size() >= 17 &&
30 m_data[0] == impl::kControlStart;
31}
32
33bool DataLogRecord::IsFinish() const {
34 return m_entry == 0 && m_data.size() == 5 &&
35 m_data[0] == impl::kControlFinish;
36}
37
38bool DataLogRecord::IsSetMetadata() const {
39 return m_entry == 0 && m_data.size() >= 9 &&
40 m_data[0] == impl::kControlSetMetadata;
41}
42
43bool DataLogRecord::GetStartData(StartRecordData* out) const {
44 if (!IsStart()) {
45 return false;
46 }
47 out->entry = wpi::support::endian::read32le(&m_data[1]);
48 auto buf = m_data.subspan(5);
49 if (!ReadString(&buf, &out->name)) {
50 return false;
51 }
52 if (!ReadString(&buf, &out->type)) {
53 return false;
54 }
55 if (!ReadString(&buf, &out->metadata)) {
56 return false;
57 }
58 return true;
59}
60
61bool DataLogRecord::GetFinishEntry(int* out) const {
62 if (!IsFinish()) {
63 return false;
64 }
65 *out = wpi::support::endian::read32le(&m_data[1]);
66 return true;
67}
68
69bool DataLogRecord::GetSetMetadataData(MetadataRecordData* out) const {
70 if (!IsSetMetadata()) {
71 return false;
72 }
73 out->entry = wpi::support::endian::read32le(&m_data[1]);
74 auto buf = m_data.subspan(5);
75 return ReadString(&buf, &out->metadata);
76}
77
78bool DataLogRecord::GetBoolean(bool* value) const {
79 if (m_data.size() != 1) {
80 return false;
81 }
82 *value = m_data[0] != 0;
83 return true;
84}
85
86bool DataLogRecord::GetInteger(int64_t* value) const {
87 if (m_data.size() != 8) {
88 return false;
89 }
90 *value = wpi::support::endian::read64le(m_data.data());
91 return true;
92}
93
94bool DataLogRecord::GetFloat(float* value) const {
95 if (m_data.size() != 4) {
96 return false;
97 }
James Kuszmaulb13e13f2023-11-22 20:44:04 -080098 *value = wpi::bit_cast<float>(wpi::support::endian::read32le(m_data.data()));
James Kuszmaulcf324122023-01-14 14:07:17 -080099 return true;
100}
101
102bool DataLogRecord::GetDouble(double* value) const {
103 if (m_data.size() != 8) {
104 return false;
105 }
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800106 *value = wpi::bit_cast<double>(wpi::support::endian::read64le(m_data.data()));
James Kuszmaulcf324122023-01-14 14:07:17 -0800107 return true;
108}
109
110bool DataLogRecord::GetString(std::string_view* value) const {
111 *value = {reinterpret_cast<const char*>(m_data.data()), m_data.size()};
112 return true;
113}
114
115bool DataLogRecord::GetBooleanArray(std::vector<int>* arr) const {
116 arr->clear();
117 arr->reserve(m_data.size());
118 for (auto v : m_data) {
119 arr->push_back(v);
120 }
121 return true;
122}
123
124bool DataLogRecord::GetIntegerArray(std::vector<int64_t>* arr) const {
125 arr->clear();
126 if ((m_data.size() % 8) != 0) {
127 return false;
128 }
129 arr->reserve(m_data.size() / 8);
130 for (size_t pos = 0; pos < m_data.size(); pos += 8) {
131 arr->push_back(wpi::support::endian::read64le(&m_data[pos]));
132 }
133 return true;
134}
135
136bool DataLogRecord::GetFloatArray(std::vector<float>* arr) const {
137 arr->clear();
138 if ((m_data.size() % 4) != 0) {
139 return false;
140 }
141 arr->reserve(m_data.size() / 4);
142 for (size_t pos = 0; pos < m_data.size(); pos += 4) {
143 arr->push_back(
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800144 wpi::bit_cast<float>(wpi::support::endian::read32le(&m_data[pos])));
James Kuszmaulcf324122023-01-14 14:07:17 -0800145 }
146 return true;
147}
148
149bool DataLogRecord::GetDoubleArray(std::vector<double>* arr) const {
150 arr->clear();
151 if ((m_data.size() % 8) != 0) {
152 return false;
153 }
154 arr->reserve(m_data.size() / 8);
155 for (size_t pos = 0; pos < m_data.size(); pos += 8) {
156 arr->push_back(
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800157 wpi::bit_cast<double>(wpi::support::endian::read64le(&m_data[pos])));
James Kuszmaulcf324122023-01-14 14:07:17 -0800158 }
159 return true;
160}
161
162bool DataLogRecord::GetStringArray(std::vector<std::string_view>* arr) const {
163 arr->clear();
164 if (m_data.size() < 4) {
165 return false;
166 }
167 uint32_t size = wpi::support::endian::read32le(m_data.data());
168 // sanity check size
169 if (size > ((m_data.size() - 4) / 4)) {
170 return false;
171 }
172 auto buf = m_data.subspan(4);
173 arr->reserve(size);
174 for (uint32_t i = 0; i < size; ++i) {
175 std::string_view str;
176 if (!ReadString(&buf, &str)) {
177 arr->clear();
178 return false;
179 }
180 arr->push_back(str);
181 }
182 // any left over? treat as corrupt
183 if (!buf.empty()) {
184 arr->clear();
185 return false;
186 }
187 return true;
188}
189
190DataLogReader::DataLogReader(std::unique_ptr<MemoryBuffer> buffer)
191 : m_buf{std::move(buffer)} {}
192
193bool DataLogReader::IsValid() const {
194 if (!m_buf) {
195 return false;
196 }
197 auto buf = m_buf->GetBuffer();
198 return buf.size() >= 12 &&
199 std::string_view{reinterpret_cast<const char*>(buf.data()), 6} ==
200 "WPILOG" &&
201 wpi::support::endian::read16le(&buf[6]) >= 0x0100;
202}
203
204uint16_t DataLogReader::GetVersion() const {
205 if (!m_buf) {
206 return 0;
207 }
208 auto buf = m_buf->GetBuffer();
209 if (buf.size() < 12) {
210 return 0;
211 }
212 return wpi::support::endian::read16le(&buf[6]);
213}
214
215std::string_view DataLogReader::GetExtraHeader() const {
216 if (!m_buf) {
217 return {};
218 }
219 auto buf = m_buf->GetBuffer();
220 if (buf.size() < 8) {
221 return {};
222 }
223 std::string_view rv;
224 buf = buf.subspan(8);
225 ReadString(&buf, &rv);
226 return rv;
227}
228
229DataLogReader::iterator DataLogReader::begin() const {
230 if (!m_buf) {
231 return end();
232 }
233 auto buf = m_buf->GetBuffer();
234 if (buf.size() < 12) {
235 return end();
236 }
237 uint32_t size = wpi::support::endian::read32le(&buf[8]);
238 if (buf.size() < (12 + size)) {
239 return end();
240 }
241 return DataLogIterator{this, 12 + size};
242}
243
244static uint64_t ReadVarInt(std::span<const uint8_t> buf) {
245 uint64_t val = 0;
246 int shift = 0;
247 for (auto v : buf) {
248 val |= static_cast<uint64_t>(v) << shift;
249 shift += 8;
250 }
251 return val;
252}
253
254bool DataLogReader::GetRecord(size_t* pos, DataLogRecord* out) const {
255 if (!m_buf) {
256 return false;
257 }
258 auto buf = m_buf->GetBuffer();
259 if (*pos >= buf.size()) {
260 return false;
261 }
262 buf = buf.subspan(*pos);
263 if (buf.size() < 4) { // minimum header length
264 return false;
265 }
266 unsigned int entryLen = (buf[0] & 0x3) + 1;
267 unsigned int sizeLen = ((buf[0] >> 2) & 0x3) + 1;
268 unsigned int timestampLen = ((buf[0] >> 4) & 0x7) + 1;
269 unsigned int headerLen = 1 + entryLen + sizeLen + timestampLen;
270 if (buf.size() < headerLen) {
271 return false;
272 }
273 int entry = ReadVarInt(buf.subspan(1, entryLen));
274 uint32_t size = ReadVarInt(buf.subspan(1 + entryLen, sizeLen));
275 if (size > (buf.size() - headerLen)) {
276 return false;
277 }
278 int64_t timestamp =
279 ReadVarInt(buf.subspan(1 + entryLen + sizeLen, timestampLen));
280 *out = DataLogRecord{entry, timestamp, buf.subspan(headerLen, size)};
281 *pos += headerLen + size;
282 return true;
283}
284
285bool DataLogReader::GetNextRecord(size_t* pos) const {
286 if (!m_buf) {
287 return false;
288 }
289 auto buf = m_buf->GetBuffer();
290 if (buf.size() < (*pos + 4)) { // minimum header length
291 return false;
292 }
293 unsigned int entryLen = (buf[*pos] & 0x3) + 1;
294 unsigned int sizeLen = ((buf[*pos] >> 2) & 0x3) + 1;
295 unsigned int timestampLen = ((buf[*pos] >> 4) & 0x7) + 1;
296 unsigned int headerLen = 1 + entryLen + sizeLen + timestampLen;
297 if (buf.size() < (*pos + headerLen)) {
298 return false;
299 }
300 uint32_t size = ReadVarInt(buf.subspan(*pos + 1 + entryLen, sizeLen));
301 // check this way to avoid overflow
302 if (size >= (buf.size() - *pos - headerLen)) {
303 return false;
304 }
305 *pos += headerLen + size;
306 return true;
307}