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


plotjuggler
Author(s): Davide Faconti
autogenerated on Mon Nov 11 2024 03:23:43