MessageSerializer.cpp
Go to the documentation of this file.
10 #include <ros/package.h>
11 #include <external/md5.h>
12 #include <fstream>
13 #include <iomanip>
14 #include <sstream>
15 #include <regex>
16 
17 using namespace swarmros;
18 using namespace swarmros::introspection;
19 
20 // Map of message serializers
21 std::map<std::string, std::unique_ptr<MessageSerializer>> MessageSerializer::_messageSerializers;
22 
23 // Regex to match the name against
24 static const std::regex NamePattern("^(?:([a-zA-Z][\\w|/]*)\\/)?([a-zA-Z]\\w*)$");
25 
26 // Regex to match an entire definition line against
27 static const std::regex LinePattern("^\\s*(?:([^\\s#]+)\\s+([a-zA-Z][a-zA-Z0-9_]*)\\s*(?:=\\s*(.*\\S)\\s*)?)?(?:#.*)?\\r?$");
28 
29 // Regex to match the type against
30 static const std::regex TypePattern("^([^\\[]+)(\\[(\\d+)?\\])?$");
31 
32 const MessageSerializer& MessageSerializer::MessageSerializerForType(const std::string& messageType, const std::string& parentPackage)
33 {
34  // Check if it exists
35  auto found = _messageSerializers.find(messageType);
36  if (found != _messageSerializers.end())
37  {
38  return *found->second;
39  }
40 
41  // If not, process name
42  std::smatch match;
43  if (std::regex_match(messageType, match, NamePattern))
44  {
45  // Get parts
46  auto package = match[1].str();
47  auto type = match[2].str();
48 
49  // Handle the special case of the Header field
50  if (package.size() == 0)
51  {
52  if (parentPackage.size() > 0)
53  {
54  package = parentPackage;
55  }
56  else
57  {
58  throw UnqualifiedException("Relative package name with no parent package");
59  }
60  }
61 
62  // Check if we have a cached helper
63  auto key = package + "/" + type;
64  auto found = _messageSerializers.find(key);
65  if (found != _messageSerializers.end())
66  {
67  return *found->second;
68  }
69 
70  // Get base path
71  auto path = ros::package::getPath(package);
72 
73  // Append message file path
74  if (path.size() > 0)
75  {
76  path += "/msg/" + type + ".msg";
77  }
78  else
79  {
80  throw UnqualifiedException("Package for message not found");
81  }
82 
83  // Build definition
84  _messageSerializers[key] = std::make_unique<MessageSerializer>(package, type, path);
85 
86  // Return value
87  return *_messageSerializers[key];
88  }
89  else
90  {
91  throw UnqualifiedException("Invalid message name");
92  }
93 }
94 
95 MessageSerializer::MessageSerializer(const std::string& package, const std::string& name, const std::string& path)
96  : Serializer(name), _package(package), _fullName(package + "/" + name), _shortcut(nullptr), _hasHeader(false)
97 {
98  // Read file line-by-line
99  std::ifstream stream(path);
100  std::string line;
101  int currentLine = 0;
102  while (std::getline(stream, line))
103  {
104  // Increment line counter and wrap exception qualifier
105  ++currentLine;
106  try
107  {
108  // Perform match
109  std::smatch lineMatch;
110  if (std::regex_match(line, lineMatch, LinePattern))
111  {
112  if (lineMatch[1].matched)
113  {
114  // Get parts
115  auto type = lineMatch[1].str();
116  auto name = lineMatch[2].str();
117 
118  // Determine if this is a constant field
119  if (lineMatch[3].matched)
120  {
121  // Extract literal value
122  auto value = lineMatch[3].str();
123 
124  // Fetch serializer
125  const Serializer& serializer = Serializer::SerializerForType(type, package);
126 
127  // Add constant field
128  _constants.emplace_back(name, serializer, value);
129  }
130  else
131  {
132  // Perform match
133  std::smatch typeMatch;
134  if (std::regex_match(type, typeMatch, TypePattern))
135  {
136  // Override parent package for header fields
137  const char* parentPackage = package.c_str();
138  if (typeMatch[1].str() == "Header" && _fields.empty())
139  {
140  parentPackage = "std_msgs";
141  }
142 
143  // Fetch serializer
144  const Serializer& serializer = Serializer::SerializerForType(typeMatch[1].str(), parentPackage);
145 
146  // Determine if this is an array
147  if (typeMatch[2].matched)
148  {
149  // Determine if dynamic or fixed
150  if (typeMatch[3].matched)
151  {
152  // Fixed array
153  auto length = std::stoul(typeMatch[3].str());
154  if (length > 0)
155  {
156  _fields.push_back(std::make_unique<FixedArrayField>(name, serializer, length));
157  }
158  else
159  {
160  throw UnqualifiedException("Invalid length for array");
161  }
162  }
163  else
164  {
165  // Dynamic array
166  _fields.push_back(std::make_unique<ArrayField>(name, serializer));
167  }
168  }
169  else
170  {
171  // Simple type
172  _fields.push_back(std::make_unique<Field>(name, serializer));
173  }
174  }
175  else
176  {
177  throw UnqualifiedException("Invalid type definition");
178  }
179  }
180  }
181  else
182  {
183  // Empty line, ignore
184  }
185  }
186  else
187  {
188  throw UnqualifiedException("Invalid line in message definition");
189  }
190  }
191  catch (const UnqualifiedException& e)
192  {
193  // Rethrow with location information
194  throw MessageDefinitionParserException(e.what(), path, currentLine);
195  }
196  }
197 
198  // Build canonical definition
200 
201  // Calculate MD5 hash
202  _hash = CalculateHash();
203 
204  // For primitives in the std_msgs package, and for
205  // the parameter types in this package, a shortcut
206  // is used: the value / data field is deserialized
207  // and used directly instead of being placed into
208  // a map.
209  if (_fields.size() == 1)
210  {
211  const auto& name = _fields.front()->GetName();
212  if ((package == "swarmros" && name == "value") || (package == "std_msgs" && name == "data"))
213  {
214  _shortcut = _fields.front().get();
215  }
216  }
217 
218  // Check if we have a standard message header
219  if (!_fields.empty() && _fields.front()->GetName() == "header" && _fields.front()->GetSerializer().GetFullName() == "std_msgs/Header")
220  {
221  _hasHeader = true;
222  }
223 }
224 
226 {
227  // Just use fields as is
228  std::stringstream stream;
229  for (const auto& field : _fields)
230  {
231  field->WriteDefinition(stream, false);
232  stream << std::endl;
233  }
234 
235  // Return definition
236  return stream.str();
237 }
238 
240 {
241  // For the first round, we only take the constant fields
242  bool firstLine = true;
243  std::stringstream stream;
244  for (const auto& constant : _constants)
245  {
246  if (firstLine)
247  {
248  firstLine = false;
249  }
250  else
251  {
252  stream << std::endl;
253  }
254  constant.WriteDefinition(stream, true);
255  }
256 
257  // For the second round, we only take the dynamic fields
258  for (const auto& field : _fields)
259  {
260  if (firstLine)
261  {
262  firstLine = false;
263  }
264  else
265  {
266  stream << std::endl;
267  }
268  field->WriteDefinition(stream, true);
269  }
270 
271  // Calculate hash
272  unsigned char rawHash[16];
273  std::string base = stream.str();
274  MD5_CTX context;
275  MD5_Init(&context);
276  MD5_Update(&context, base.c_str(), base.size());
277  MD5_Final(rawHash, &context);
278 
279  // Convert to string
280  std::stringstream hashStream;
281  hashStream << std::hex << std::setfill('0');
282  for (int i = 0; i < sizeof(rawHash); ++i)
283  {
284  hashStream << std::setw(2) << (int)rawHash[i];
285  }
286  return hashStream.str();
287 }
288 
289 uint32_t MessageSerializer::CalculateSerializedLength(const swarmio::data::Variant& value, const FieldStack& fieldStack) const
290 {
291  return CalculateSerializedLength(value, 0, fieldStack);
292 }
293 
294 uint32_t MessageSerializer::CalculateSerializedLength(const swarmio::data::Variant& value, unsigned skipCount, const FieldStack& fieldStack) const
295 {
296  if (_shortcut != nullptr)
297  {
298  return _shortcut->CalculateSerializedLength(value, fieldStack);
299  }
300  else if (value.value_case() == swarmio::data::Variant::ValueCase::kMapValue)
301  {
302  return CalculateSerializedLength(value.map_value(), skipCount, fieldStack);
303  }
304  else
305  {
306  throw TypeMismatchException(
307  "Unexpected source type for MessageSerializer",
308  fieldStack.GetLocation(),
309  swarmio::data::Variant::ValueCase::kMapValue,
310  value.value_case());
311  }
312 }
313 
314 uint32_t MessageSerializer::GetDefaultLength(const FieldStack& fieldStack) const
315 {
316  uint32_t length = 0;
317  for (const auto& field : _fields)
318  {
319  length += field->GetDefaultLength(fieldStack);
320  }
321  return length;
322 }
323 
324 uint32_t MessageSerializer::CalculateSerializedLength(const swarmio::data::Map& value, unsigned skipCount, const FieldStack& fieldStack) const
325 {
326  uint32_t length = 0;
327  auto it = _fields.begin();
328 
329  // Skip fields
330  while (skipCount > 0)
331  {
332  ++it;
333  --skipCount;
334  }
335 
336  // Fill the rest of the fields
337  while (it != _fields.end())
338  {
339  auto& field = *it;
340  auto entry = value.pairs().find(field->GetName());
341  if (entry != value.pairs().end())
342  {
343  length += field->CalculateSerializedLength(entry->second, fieldStack);
344  }
345  else
346  {
347  length += field->GetDefaultLength(fieldStack);
348  }
349  ++it;
350  }
351 
352  // Return length
353  return length;
354 }
355 
356 void MessageSerializer::Serialize(ros::serialization::OStream& stream, const swarmio::data::Variant& value, const FieldStack& fieldStack) const
357 {
358  // Check for an array
359  if (value.value_case() == swarmio::data::Variant::ValueCase::kMapArray)
360  {
361  IndexedFieldStack current(fieldStack);
362  for (const auto& element : value.map_array().elements())
363  {
364  // Serialize each element
365  Serialize(stream, element, 0, current);
366  ++current;
367  }
368  }
369  else
370  {
371  // Serialize as a single item
372  Serialize(stream, value, 0, fieldStack);
373  }
374 }
375 
376 void MessageSerializer::Serialize(ros::serialization::OStream& stream, const swarmio::data::Variant& value, unsigned skipCount, const FieldStack& fieldStack) const
377 {
378  // Check for a shortcut
379  if (_shortcut != nullptr)
380  {
381  _shortcut->Serialize(stream, value, fieldStack);
382  }
383  else if (value.value_case() == swarmio::data::Variant::ValueCase::kMapValue)
384  {
385  // Serialize as a map value
386  Serialize(stream, value.map_value(), skipCount, fieldStack);
387  }
388  else
389  {
390  throw TypeMismatchException(
391  "Unexpected source type for MessageSerializer",
392  fieldStack.GetLocation(),
393  swarmio::data::Variant::ValueCase::kMapValue,
394  value.value_case());
395  }
396 }
397 
398 void MessageSerializer::Serialize(ros::serialization::OStream& stream, const swarmio::data::Map& value, unsigned skipCount, const FieldStack& fieldStack) const
399 {
400  const auto& pairs = value.pairs();
401  auto it = _fields.begin();
402 
403  // Skip fields
404  while (skipCount > 0)
405  {
406  ++it;
407  --skipCount;
408  }
409 
410  // Fill the rest of the fields
411  while (it != _fields.end())
412  {
413  auto& field = *it;
414  auto value = pairs.find(field->GetName());
415  if (value != pairs.end())
416  {
417  field->Serialize(stream, value->second, fieldStack);
418  }
419  else
420  {
421  field->EmitDefault(stream, fieldStack);
422  }
423  ++it;
424  }
425 }
426 
428 {
429  // Emit default value for each field
430  for (const auto& field : _fields)
431  {
432  field->EmitDefault(stream, fieldStack);
433  }
434 }
435 
436 swarmio::data::Variant MessageSerializer::DeserializeArray(ros::serialization::IStream& stream, uint32_t count, const FieldStack& fieldStack) const
437 {
438  // Check for a shortcut
439  if (_shortcut != nullptr)
440  {
441  return _shortcut->DeserializeArray(stream, count, fieldStack);
442  }
443  else
444  {
445  // Perform array deserialization
446  IndexedFieldStack current(fieldStack);
447  swarmio::data::Variant value;
448  for (uint32_t i = 0; i < count; ++i)
449  {
450  Deserialize(stream, *value.mutable_map_array()->add_elements(), 0, current);
451  }
452  return value;
453  }
454 }
455 
456 swarmio::data::Variant MessageSerializer::Deserialize(ros::serialization::IStream& stream, const FieldStack& fieldStack) const
457 {
458  return Deserialize(stream, 0, fieldStack);
459 }
460 
461 swarmio::data::Variant MessageSerializer::Deserialize(ros::serialization::IStream& stream, unsigned skipCount, const FieldStack& fieldStack) const
462 {
463  // Check for a shortcut
464  if (_shortcut != nullptr)
465  {
466  return _shortcut->Deserialize(stream, fieldStack);
467  }
468  else
469  {
470  // Deserialize as a map
471  swarmio::data::Variant value;
472  Deserialize(stream, *value.mutable_map_value(), skipCount, fieldStack);
473  return value;
474  }
475 }
476 
477 void MessageSerializer::Deserialize(ros::serialization::IStream& stream, swarmio::data::Map& value, unsigned skipCount, const FieldStack& fieldStack) const
478 {
479  auto& map = *value.mutable_pairs();
480  auto it = _fields.begin();
481 
482  // Skip fields
483  while (skipCount > 0)
484  {
485  ++it;
486  --skipCount;
487  }
488 
489  // Fill the rest of the fields
490  while (it != _fields.end())
491  {
492  auto& field = *it;
493  map[field->GetName()] = field->Deserialize(stream, fieldStack);
494  ++it;
495  }
496 }
497 
498 swarmio::data::discovery::Field MessageSerializer::GetFieldDescriptor() const
499 {
500  if (_shortcut != nullptr)
501  {
502  return _shortcut->GetFieldDescriptor();
503 
504  }
505  else
506  {
507  swarmio::data::discovery::Field field;
508  *field.mutable_schema() = GetSchemaDescriptor(0);
509  return field;
510  }
511 }
512 
513 swarmio::data::discovery::Schema MessageSerializer::GetSchemaDescriptor(unsigned skipCount) const
514 {
515  swarmio::data::discovery::Schema schema;
516  auto& map = *schema.mutable_fields();
517  auto it = _fields.begin();
518 
519  // Skip fields
520  while (skipCount > 0)
521  {
522  ++it;
523  --skipCount;
524  }
525 
526  // Fill the rest of the fields
527  while (it != _fields.end())
528  {
529  auto& field = *it;
530  map[field->GetName()] = field->GetFieldDescriptor();
531  ++it;
532  }
533 
534  // Return schema
535  return schema;
536 }
537 
538 void MessageSerializer::EnumerateFields(std::function<bool(unsigned, const Field&)> enumerator) const
539 {
540  unsigned n = 0;
541  for (auto& field : _fields)
542  {
543  if (!enumerator(n++, *field))
544  {
545  break;
546  }
547  }
548 }
void MD5_Init(MD5_CTX *ctx)
Init MD5 context.
Definition: md5.cpp:207
void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size)
Update MD5 context.
Definition: md5.cpp:218
MessageSerializer(const std::string &package, const std::string &name, const std::string &path)
Construct a new MessageSerializer object.
static const std::regex TypePattern("^([^\\[]+)(\\[(\\d+)?\\])?$")
virtual swarmio::data::Variant DeserializeArray(ros::serialization::IStream &stream, uint32_t count, const FieldStack &fieldStack) const override
Deserialize a stream into a variant array.
virtual void Serialize(ros::serialization::OStream &stream, const swarmio::data::Variant &value, const FieldStack &fieldStack) const override
Serialize a variant onto a stream.
Definition: Field.cpp:57
string package
std::string _hash
MD5 hash of the message.
virtual swarmio::data::discovery::Field GetFieldDescriptor() const override
Build a field descriptor for the underlying type.
Definition: Field.cpp:87
void EnumerateFields(std::function< bool(unsigned, const Field &)> enumerator) const
Enumerate the fields of the message with a function.
static const MessageSerializer & MessageSerializerForType(const std::string &type, const std::string &parentPackage)
Look up or build a reader for a message type.
const Field * _shortcut
For certain messages, the normal field processing is skipped and a single field is used to deserializ...
static std::map< std::string, std::unique_ptr< MessageSerializer > > _messageSerializers
Map of message serializers.
virtual std::string GetLocation() const =0
Construct the current location.
Serializer for full-fledged message types.
A Field represents an entry in a message reader that can read one of its fields.
Definition: Field.h:15
bool _hasHeader
Whether the message begins with the standard header.
Exception thrown while parsing a message definition file.
static const std::regex NamePattern("^(?:([a-zA-Z][\\w|/]*)\\/)?([a-zA-Z]\\w*)$")
std::list< ConstantField > _constants
Constant fields.
virtual swarmio::data::Variant Deserialize(ros::serialization::IStream &stream, const FieldStack &fieldStack) const override
Deserialize a stream into a variant.
std::list< std::unique_ptr< Field > > _fields
Variable fields.
virtual void Serialize(ros::serialization::OStream &stream, const swarmio::data::Variant &value, const FieldStack &fieldStack) const override
Serialize a variant onto a stream.
ROSLIB_DECL std::string getPath(const std::string &package_name)
virtual uint32_t GetDefaultLength(const FieldStack &fieldStack) const override
Get the length of the default value.
virtual void EmitDefault(ros::serialization::OStream &stream, const FieldStack &fieldStack) const override
Write the default value to the stream.
Exception thrown when the expected type of the Variant does not match the actual type.
virtual uint32_t CalculateSerializedLength(const swarmio::data::Variant &value, const FieldStack &fieldStack) const override
Calculate the length of a serialized message in bytes.
Definition: Field.cpp:44
Serializer is the base class for all binary message interpreters.
Definition: Serializer.h:17
static const Serializer & SerializerForType(const std::string &type, const std::string &parentPackage)
Look up or build a reader for a message type.
Definition: Serializer.cpp:40
static const std::regex LinePattern("^\\s*(?:([^\\s#]+)\\s+([a-zA-Z][a-zA-Z0-9_]*)\\s*(?:=\\s*(.*\\S)\\s*)?)?(?:#.*)?\\r?$")
std::string CalculateHash()
Calculate the MD5 hash used to identify the message type.
std::string BuildCanonicalDefinition()
Build a canonical definition for the message.
virtual swarmio::data::discovery::Field GetFieldDescriptor() const override
Build a field descriptor for the underlying type.
const char * what() const noexceptoverride
Get the error message.
virtual swarmio::data::Variant Deserialize(ros::serialization::IStream &stream, const FieldStack &fieldStack) const override
Deserialize a stream into a variant.
Definition: Field.cpp:76
virtual swarmio::data::Variant DeserializeArray(ros::serialization::IStream &stream, uint32_t count, const FieldStack &fieldStack) const override
Deserialize a stream into a variant array - not supported on Fields.
Definition: Field.cpp:82
An exception that is expected to be caught and translated into another exception. ...
void MD5_Final(unsigned char *result, MD5_CTX *ctx)
Finalize MD5 context.
Definition: md5.cpp:258
MD5 context.
Definition: md5.h:40
virtual uint32_t CalculateSerializedLength(const swarmio::data::Variant &value, const FieldStack &fieldStack) const override
Calculate the length of a serialized message in bytes.
std::string _canonicalDefinition
Canonical definition of the message.
swarmio::data::discovery::Schema GetSchemaDescriptor(unsigned skipCount) const
Build a field descriptor for the underlying type, with or without its header.


swarmros
Author(s):
autogenerated on Fri Apr 3 2020 03:42:48