data_tamer_parser.hpp
Go to the documentation of this file.
1 #pragma once
2 
3 #include <array>
4 #include <cstdint>
5 #include <cstring>
6 #include <iostream>
7 #include <functional>
8 #include <limits>
9 #include <map>
10 #include <optional>
11 #include <sstream>
12 #include <string>
13 #include <unordered_map>
14 #include <variant>
15 #include <vector>
16 
17 namespace DataTamerParser
18 {
19 
20 constexpr int SCHEMA_VERSION = 4;
21 
22 enum class BasicType
23 {
24  BOOL,
25  CHAR,
26  INT8,
27  UINT8,
28 
29  INT16,
30  UINT16,
31 
32  INT32,
33  UINT32,
34 
35  INT64,
36  UINT64,
37 
38  FLOAT32,
39  FLOAT64,
40  OTHER
41 };
42 
43 constexpr size_t TypesCount = 13;
44 
45 using VarNumber = std::variant<bool, char, int8_t, uint8_t, int16_t, uint16_t, int32_t,
46  uint32_t, int64_t, uint64_t, float, double>;
47 
48 struct BufferSpan
49 {
50  const uint8_t* data = nullptr;
51  size_t size = 0;
52 
53  void trimFront(size_t n)
54  {
55  data += n;
56  size -= n;
57  }
58 };
59 
60 VarNumber DeserializeToVarNumber(BasicType type, BufferSpan& buffer);
61 
62 //---------------------------------------------------------
63 struct TypeField
64 {
65  std::string field_name;
67  std::string type_name;
68  bool is_vector = 0;
69  uint32_t array_size = 0;
70 
71  bool operator==(const TypeField& other) const;
72  bool operator!=(const TypeField& other) const;
73 };
74 
75 using FieldsVector = std::vector<TypeField>;
76 
80 struct Schema
81 {
82  uint64_t hash = 0;
84  std::string channel_name;
85 
86  std::map<std::string, FieldsVector> custom_types;
87 };
88 
89 Schema BuilSchemaFromText(const std::string& txt);
90 
92 {
94  size_t schema_hash;
95 
97  uint64_t timestamp;
98 
103 
106 };
107 
108 bool GetBit(BufferSpan mask, size_t index);
109 
110 constexpr auto NullCustomCallback = [](const std::string&, const BufferSpan,
111  const std::string&) {};
112 
113 // Callback must be a std::function or lambda with signature:
114 //
115 // void(const std::string& name_field, const VarNumber& value)
116 //
117 // void(const std::string& name_field, const BufferSpan payload, const std::string& type_name)
118 //
119 template <typename NumberCallback, typename CustomCallback = decltype(NullCustomCallback)>
120 bool ParseSnapshot(const Schema& schema, SnapshotView snapshot,
121  const NumberCallback& callback_number,
122  const CustomCallback& callback_custom = NullCustomCallback);
123 
124 //---------------------------------------------------------
125 //---------------------------------------------------------
126 //---------------------------------------------------------
127 
128 template <typename T>
129 inline T Deserialize(BufferSpan& buffer)
130 {
131  T var;
132  const auto N = sizeof(T);
133  std::memcpy(&var, buffer.data, N);
134  buffer.data += N;
135  if (N > buffer.size)
136  {
137  throw std::runtime_error("Buffer overflow");
138  }
139  buffer.size -= N;
140  return var;
141 }
142 
144 {
145  switch (type)
146  {
147  case BasicType::BOOL:
148  return Deserialize<bool>(buffer);
149  case BasicType::CHAR:
150  return Deserialize<char>(buffer);
151 
152  case BasicType::INT8:
153  return Deserialize<int8_t>(buffer);
154  case BasicType::UINT8:
155  return Deserialize<uint8_t>(buffer);
156 
157  case BasicType::INT16:
158  return Deserialize<int16_t>(buffer);
159  case BasicType::UINT16:
160  return Deserialize<uint16_t>(buffer);
161 
162  case BasicType::INT32:
163  return Deserialize<int32_t>(buffer);
164  case BasicType::UINT32:
165  return Deserialize<uint32_t>(buffer);
166 
167  case BasicType::INT64:
168  return Deserialize<int64_t>(buffer);
169  case BasicType::UINT64:
170  return Deserialize<uint64_t>(buffer);
171 
172  case BasicType::FLOAT32:
173  return Deserialize<float>(buffer);
174  case BasicType::FLOAT64:
175  return Deserialize<double>(buffer);
176 
177  case BasicType::OTHER:
178  return double(std::numeric_limits<double>::quiet_NaN());
179  }
180  return {};
181 }
182 
183 inline bool GetBit(BufferSpan mask, size_t index)
184 {
185  const uint8_t& byte = mask.data[index >> 3];
186  return 0 != (byte & uint8_t(1 << (index % 8)));
187 }
188 
189 [[nodiscard]] inline uint64_t AddFieldToHash(const TypeField& field, uint64_t hash)
190 {
191  // https://stackoverflow.com/questions/2590677/how-do-i-combine-hash-values-in-c0x
192  const std::hash<std::string> str_hasher;
193  const std::hash<BasicType> type_hasher;
194  const std::hash<bool> bool_hasher;
195  const std::hash<uint32_t> uint_hasher;
196 
197  auto combine = [&hash](const auto& hasher, const auto& val) {
198  hash ^= hasher(val) + 0x9e3779b9 + (hash << 6) + (hash >> 2);
199  };
200 
201  combine(str_hasher, field.field_name);
202  combine(type_hasher, field.type);
203  if (field.type == BasicType::OTHER)
204  {
205  combine(str_hasher, field.type_name);
206  }
207  combine(bool_hasher, field.is_vector);
208  combine(uint_hasher, field.array_size);
209  return hash;
210 }
211 
212 bool TypeField::operator==(const TypeField& other) const
213 {
214  return is_vector == other.is_vector && type == other.type &&
215  array_size == other.array_size && field_name == other.field_name &&
216  type_name == other.type_name;
217 }
218 
219 inline Schema BuilSchemaFromText(const std::string& txt)
220 {
221  auto trimString = [](std::string& str) {
222  while (str.back() == ' ' || str.back() == '\r')
223  {
224  str.pop_back();
225  }
226  while (str.front() == ' ' || str.front() == '\r')
227  {
228  str.erase(0, 1);
229  }
230  };
231 
232  std::istringstream ss(txt);
233  std::string line;
234  Schema schema;
235  uint64_t declared_schema = 0;
236 
237  std::vector<TypeField>* field_vector = &schema.fields;
238 
239  while (std::getline(ss, line))
240  {
241  trimString(line);
242  if (line.empty())
243  {
244  continue;
245  }
246  if (line.find("==============================") != std::string::npos)
247  {
248  // get "MSG:" in the next line
249  std::getline(ss, line);
250  auto msg_pos = line.find("MSG: ");
251  if (msg_pos == std::string::npos)
252  {
253  throw std::runtime_error("Expecting \"MSG: \" at the beginning of line: " + line);
254  }
255  line.erase(0, 5);
256  trimString(line);
257  field_vector = &schema.custom_types[line];
258  continue;
259  }
260 
261  // a single space is expected
262  auto space_pos = line.find(' ');
263  if (space_pos == std::string::npos)
264  {
265  throw std::runtime_error("Unexpected line: " + line);
266  }
267  if (line.find("### ") == 0)
268  {
269  space_pos = line.find(' ', 5);
270  }
271 
272  std::string str_left = line.substr(0, space_pos);
273  std::string str_right = line.substr(space_pos + 1, line.size() - (space_pos + 1));
274  trimString(str_left);
275  trimString(str_right);
276 
277  const std::string* str_type = &str_left;
278  const std::string* str_name = &str_right;
279 
280  if (str_left == "### version:" || str_left == "__version__:")
281  {
282  continue;
283  }
284  if (str_left == "### hash:" || str_left == "__hash__:")
285  {
286  // check compatibility
287  declared_schema = std::stoul(str_right);
288  continue;
289  }
290 
291  if (str_left == "### channel_name:" || str_left == "__channel_name__:")
292  {
293  // check compatibility
294  schema.channel_name = str_right;
295  schema.hash = std::hash<std::string>()(schema.channel_name);
296  continue;
297  }
298 
300 
301  static const std::array<std::string, TypesCount> kNamesNew = {
302  "bool", "char", "int8", "uint8", "int16", "uint16", "int32",
303  "uint32", "int64", "uint64", "float32", "float64", "other"};
304  // backcompatibility to old format
305  static const std::array<std::string, TypesCount> kNamesOld = {
306  "BOOL", "CHAR", "INT8", "UINT8", "INT16", "UINT16", "INT32",
307  "UINT32", "INT64", "UINT64", "FLOAT", "DOUBLE", "OTHER"};
308 
309  for (size_t i = 0; i < TypesCount; i++)
310  {
311  if (str_left.find(kNamesNew[i]) == 0)
312  {
313  field.type = static_cast<BasicType>(i);
314  break;
315  }
316  if (str_right.find(kNamesOld[i]) == 0)
317  {
318  field.type = static_cast<BasicType>(i);
319  std::swap(str_type, str_name);
320  break;
321  }
322  }
323 
324  auto offset = str_type->find_first_of(" [");
325  if (field.type != BasicType::OTHER)
326  {
327  field.type_name = kNamesNew[static_cast<size_t>(field.type)];
328  }
329  else {
330  field.type_name = str_type->substr(0, offset);
331  }
332 
333  if (offset != std::string::npos && str_type->at(offset) == '[')
334  {
335  field.is_vector = true;
336  auto pos = str_type->find(']', offset);
337  if (pos != offset + 1)
338  {
339  // get number
340  std::string number_string = line.substr(offset + 1, pos - offset - 1);
341  field.array_size = static_cast<uint16_t>(std::stoi(number_string));
342  }
343  }
344 
345  field.field_name = *str_name;
346  trimString(field.field_name);
347 
348  // update the hash
349  if (field_vector == &schema.fields)
350  {
351  schema.hash = AddFieldToHash(field, schema.hash);
352  }
353 
354  field_vector->push_back(field);
355  }
356  if (declared_schema != 0 && declared_schema != schema.hash)
357  {
358  throw std::runtime_error("Error in hash calculation");
359  }
360  return schema;
361 }
362 
363 template <typename NumberCallback>
365  const std::map<std::string, FieldsVector>& types_list,
366  BufferSpan& buffer,
367  const NumberCallback& callback_number,
368  const std::string& prefix)
369 {
370 
371  [[maybe_unused]] uint32_t vect_size = field.array_size;
372  if (field.is_vector && field.array_size == 0)
373  {
374  // dynamic vector
375  vect_size = Deserialize<uint32_t>(buffer);
376  }
377 
378  auto new_prefix = (prefix.empty()) ? field.field_name : (prefix + "/" + field.field_name);
379 
380  auto doParse = [&](const std::string& var_name)
381  {
382  if(field.type != BasicType::OTHER)
383  {
384  const auto var = DeserializeToVarNumber(field.type, buffer);
385  callback_number(var_name, var);
386  }
387  else {
388  const FieldsVector& fields = types_list.at(field.type_name);
389  for(const auto& sub_field: fields)
390  {
391  ParseSnapshotRecursive(sub_field, types_list, buffer, callback_number, var_name);
392  }
393  }
394  };
395 
396  if(!field.is_vector)
397  {
398  doParse(new_prefix);
399  }
400  else
401  {
402  for(uint32_t a=0; a < vect_size; a++)
403  {
404  const auto& name = new_prefix + "[" + std::to_string(a) + "]";
405  doParse(name);
406  }
407  }
408  return true;
409 }
410 
411 
412 template <typename NumberCallback, typename CustomCallback>
413 inline bool ParseSnapshot(const Schema& schema, SnapshotView snapshot,
414  const NumberCallback& callback_number,
415  const CustomCallback& callback_custom)
416 {
417  if (schema.hash != snapshot.schema_hash)
418  {
419  return false;
420  }
421  BufferSpan buffer = snapshot.payload;
422 
423  for (size_t i = 0; i < schema.fields.size(); i++)
424  {
425  const auto& field = schema.fields[i];
426  if (GetBit(snapshot.active_mask, i))
427  {
428  ParseSnapshotRecursive(field, schema.custom_types, buffer, callback_number, {});
429  }
430  }
431  return true;
432 }
433 
434 } // namespace DataTamerParser
DataTamerParser::TypeField::type_name
std::string type_name
Definition: data_tamer_parser.hpp:67
DataTamerParser::Schema
DataTamer uses a simple "flat" schema of key/value pairs (each pair is a "field").
Definition: data_tamer_parser.hpp:80
DataTamerParser::BasicType::INT8
@ INT8
DataTamerParser::BasicType::INT16
@ INT16
DataTamerParser::BasicType::FLOAT64
@ FLOAT64
DataTamerParser::SCHEMA_VERSION
constexpr int SCHEMA_VERSION
Definition: data_tamer_parser.hpp:20
DataTamerParser::BasicType::UINT8
@ UINT8
DataTamerParser::TypeField::type
BasicType type
Definition: data_tamer_parser.hpp:66
DataTamerParser::DeserializeToVarNumber
VarNumber DeserializeToVarNumber(BasicType type, BufferSpan &buffer)
Definition: data_tamer_parser.hpp:143
DataTamerParser::BasicType::UINT64
@ UINT64
DataTamerParser::Schema::hash
uint64_t hash
Definition: data_tamer_parser.hpp:82
DataTamerParser::SnapshotView::payload
BufferSpan payload
serialized data containing all the values, ordered as in the schema
Definition: data_tamer_parser.hpp:105
DataTamerParser::BasicType
BasicType
Definition: data_tamer_parser.hpp:22
DataTamerParser::AddFieldToHash
uint64_t AddFieldToHash(const TypeField &field, uint64_t hash)
Definition: data_tamer_parser.hpp:189
DataTamerParser::BasicType::UINT32
@ UINT32
DataTamerParser::TypeField::is_vector
bool is_vector
Definition: data_tamer_parser.hpp:68
DataTamerParser::BasicType::BOOL
@ BOOL
DataTamerParser::Schema::channel_name
std::string channel_name
Definition: data_tamer_parser.hpp:84
DataTamerParser::SnapshotView::timestamp
uint64_t timestamp
snapshot timestamp
Definition: data_tamer_parser.hpp:97
DataTamerParser::BasicType::FLOAT32
@ FLOAT32
DataTamerParser::BasicType::CHAR
@ CHAR
DataTamerParser::ParseSnapshotRecursive
bool ParseSnapshotRecursive(const TypeField &field, const std::map< std::string, FieldsVector > &types_list, BufferSpan &buffer, const NumberCallback &callback_number, const std::string &prefix)
Definition: data_tamer_parser.hpp:364
DataTamerParser::VarNumber
std::variant< bool, char, int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t, float, double > VarNumber
Definition: data_tamer_parser.hpp:46
DataTamerParser::GetBit
bool GetBit(BufferSpan mask, size_t index)
Definition: data_tamer_parser.hpp:183
sol::var
auto var(V &&v)
Definition: sol.hpp:18020
DataTamerParser::BasicType::OTHER
@ OTHER
DataTamerParser::FieldsVector
std::vector< TypeField > FieldsVector
Definition: data_tamer_parser.hpp:75
DataTamerParser::TypeField::operator==
bool operator==(const TypeField &other) const
Definition: data_tamer_parser.hpp:212
DataTamerParser::BuilSchemaFromText
Schema BuilSchemaFromText(const std::string &txt)
Definition: data_tamer_parser.hpp:219
DataTamerParser::TypeField::field_name
std::string field_name
Definition: data_tamer_parser.hpp:65
std::swap
NLOHMANN_BASIC_JSON_TPL_DECLARATION void swap(nlohmann::NLOHMANN_BASIC_JSON_TPL &j1, nlohmann::NLOHMANN_BASIC_JSON_TPL &j2) noexcept(//NOLINT(readability-inconsistent-declaration-parameter-name) is_nothrow_move_constructible< nlohmann::NLOHMANN_BASIC_JSON_TPL >::value &&//NOLINT(misc-redundant-expression) is_nothrow_move_assignable< nlohmann::NLOHMANN_BASIC_JSON_TPL >::value)
exchanges the values of two JSON objects
Definition: json.hpp:21884
DataTamerParser::TypeField::operator!=
bool operator!=(const TypeField &other) const
DataTamerParser::TypeField::array_size
uint32_t array_size
Definition: data_tamer_parser.hpp:69
DataTamerParser::TypesCount
constexpr size_t TypesCount
Definition: data_tamer_parser.hpp:43
DataTamerParser::SnapshotView::active_mask
BufferSpan active_mask
Definition: data_tamer_parser.hpp:102
DataTamerParser::BufferSpan::trimFront
void trimFront(size_t n)
Definition: data_tamer_parser.hpp:53
DataTamerParser::Deserialize
T Deserialize(BufferSpan &buffer)
Definition: data_tamer_parser.hpp:129
DataTamerParser
Definition: data_tamer_parser.hpp:17
DataTamerParser::BasicType::INT64
@ INT64
DataTamerParser::Schema::custom_types
std::map< std::string, FieldsVector > custom_types
Definition: data_tamer_parser.hpp:86
DataTamerParser::BufferSpan::data
const uint8_t * data
Definition: data_tamer_parser.hpp:50
field
static void field(LexState *ls, ConsControl *cc)
Definition: lparser.c:891
DataTamerParser::TypeField
Definition: data_tamer_parser.hpp:63
DataTamerParser::BufferSpan
Definition: data_tamer_parser.hpp:48
DataTamerParser::ParseSnapshot
bool ParseSnapshot(const Schema &schema, SnapshotView snapshot, const NumberCallback &callback_number, const CustomCallback &callback_custom=NullCustomCallback)
Definition: data_tamer_parser.hpp:413
DataTamerParser::NullCustomCallback
constexpr auto NullCustomCallback
Definition: data_tamer_parser.hpp:110
combine
static const Proto * combine(lua_State *L, int n)
Definition: luac.c:143
DataTamerParser::SnapshotView
Definition: data_tamer_parser.hpp:91
DataTamerParser::BasicType::UINT16
@ UINT16
DataTamerParser::BufferSpan::size
size_t size
Definition: data_tamer_parser.hpp:51
DataTamerParser::SnapshotView::schema_hash
size_t schema_hash
Unique identifier of the schema.
Definition: data_tamer_parser.hpp:94
DataTamerParser::Schema::fields
FieldsVector fields
Definition: data_tamer_parser.hpp:83
DataTamerParser::BasicType::INT32
@ INT32


plotjuggler
Author(s): Davide Faconti
autogenerated on Sun Aug 11 2024 02:24:22