]> git.sesse.net Git - casparcg/blob - protocol/osc/oscpack/OscReceivedElements.cpp
9554cc43330ab93a7c12382f9247a540133bd375
[casparcg] / protocol / osc / oscpack / OscReceivedElements.cpp
1 /*\r
2         oscpack -- Open Sound Control packet manipulation library\r
3         http://www.audiomulch.com/~rossb/oscpack\r
4 \r
5         Copyright (c) 2004-2005 Ross Bencina <rossb@audiomulch.com>\r
6 \r
7         Permission is hereby granted, free of charge, to any person obtaining\r
8         a copy of this software and associated documentation files\r
9         (the "Software"), to deal in the Software without restriction,\r
10         including without limitation the rights to use, copy, modify, merge,\r
11         publish, distribute, sublicense, and/or sell copies of the Software,\r
12         and to permit persons to whom the Software is furnished to do so,\r
13         subject to the following conditions:\r
14 \r
15         The above copyright notice and this permission notice shall be\r
16         included in all copies or substantial portions of the Software.\r
17 \r
18         Any person wishing to distribute modifications to the Software is\r
19         requested to send the modifications to the original developer so that\r
20         they can be incorporated into the canonical version.\r
21 \r
22         THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
23         EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
24         MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\r
25         IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR\r
26         ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\r
27         CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
28         WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
29 */\r
30 #undef _CRT_SECURE_NO_WARNINGS\r
31 #define _CRT_SECURE_NO_WARNINGS\r
32 \r
33 #include "OscReceivedElements.h"\r
34 \r
35 #include <cassert>\r
36 \r
37 #include "OscHostEndianness.h"\r
38 \r
39 \r
40 namespace osc{\r
41 \r
42 \r
43 // return the first 4 byte boundary after the end of a str4\r
44 // be careful about calling this version if you don't know whether\r
45 // the string is terminated correctly.\r
46 static inline const char* FindStr4End( const char *p )\r
47 {\r
48         if( p[0] == '\0' )    // special case for SuperCollider integer address pattern\r
49                 return p + 4;\r
50 \r
51     p += 3;\r
52 \r
53     while( *p )\r
54         p += 4;\r
55 \r
56     return p + 1;\r
57 }\r
58 \r
59 \r
60 // return the first 4 byte boundary after the end of a str4\r
61 // returns 0 if p == end or if the string is unterminated\r
62 static inline const char* FindStr4End( const char *p, const char *end )\r
63 {\r
64     if( p >= end )\r
65         return 0;\r
66 \r
67         if( p[0] == '\0' )    // special case for SuperCollider integer address pattern\r
68                 return p + 4;\r
69 \r
70     p += 3;\r
71     end -= 1;\r
72 \r
73     while( p < end && *p )\r
74         p += 4;\r
75 \r
76     if( *p )\r
77         return 0;\r
78     else\r
79         return p + 1;\r
80 }\r
81 \r
82 \r
83 static inline unsigned long RoundUp4( unsigned long x )\r
84 {\r
85     unsigned long remainder = x & 0x3UL;\r
86     if( remainder )\r
87         return x + (4 - remainder);\r
88     else\r
89         return x;\r
90 }\r
91 \r
92 \r
93 static inline int32 ToInt32( const char *p )\r
94 {\r
95 #ifdef OSC_HOST_LITTLE_ENDIAN\r
96     union{\r
97         osc::int32 i;\r
98         char c[4];\r
99     } u;\r
100 \r
101     u.c[0] = p[3];\r
102     u.c[1] = p[2];\r
103     u.c[2] = p[1];\r
104     u.c[3] = p[0];\r
105 \r
106     return u.i;\r
107 #else\r
108         return *(int32*)p;\r
109 #endif\r
110 }\r
111 \r
112 \r
113 static inline uint32 ToUInt32( const char *p )\r
114 {\r
115 #ifdef OSC_HOST_LITTLE_ENDIAN\r
116     union{\r
117         osc::uint32 i;\r
118         char c[4];\r
119     } u;\r
120 \r
121     u.c[0] = p[3];\r
122     u.c[1] = p[2];\r
123     u.c[2] = p[1];\r
124     u.c[3] = p[0];\r
125 \r
126     return u.i;\r
127 #else\r
128         return *(uint32*)p;\r
129 #endif\r
130 }\r
131 \r
132 \r
133 int64 ToInt64( const char *p )\r
134 {\r
135 #ifdef OSC_HOST_LITTLE_ENDIAN\r
136     union{\r
137         osc::int64 i;\r
138         char c[8];\r
139     } u;\r
140 \r
141     u.c[0] = p[7];\r
142     u.c[1] = p[6];\r
143     u.c[2] = p[5];\r
144     u.c[3] = p[4];\r
145     u.c[4] = p[3];\r
146     u.c[5] = p[2];\r
147     u.c[6] = p[1];\r
148     u.c[7] = p[0];\r
149 \r
150     return u.i;\r
151 #else\r
152         return *(int64*)p;\r
153 #endif\r
154 }\r
155 \r
156 \r
157 uint64 ToUInt64( const char *p )\r
158 {\r
159 #ifdef OSC_HOST_LITTLE_ENDIAN\r
160     union{\r
161         osc::uint64 i;\r
162         char c[8];\r
163     } u;\r
164 \r
165     u.c[0] = p[7];\r
166     u.c[1] = p[6];\r
167     u.c[2] = p[5];\r
168     u.c[3] = p[4];\r
169     u.c[4] = p[3];\r
170     u.c[5] = p[2];\r
171     u.c[6] = p[1];\r
172     u.c[7] = p[0];\r
173 \r
174     return u.i;\r
175 #else\r
176         return *(uint64*)p;\r
177 #endif\r
178 }\r
179 \r
180 //------------------------------------------------------------------------------\r
181 \r
182 bool ReceivedPacket::IsBundle() const\r
183 {\r
184     return (Size() > 0 && Contents()[0] == '#');\r
185 }\r
186 \r
187 //------------------------------------------------------------------------------\r
188 \r
189 bool ReceivedBundleElement::IsBundle() const\r
190 {\r
191     return (Size() > 0 && Contents()[0] == '#');\r
192 }\r
193 \r
194 \r
195 int32 ReceivedBundleElement::Size() const\r
196 {\r
197     return ToUInt32( size_ );\r
198 }\r
199 \r
200 //------------------------------------------------------------------------------\r
201 \r
202 bool ReceivedMessageArgument::AsBool() const\r
203 {\r
204     if( !typeTag_ )\r
205         throw MissingArgumentException();\r
206         else if( *typeTag_ == TRUE_TYPE_TAG )\r
207                 return true;\r
208         else if( *typeTag_ == FALSE_TYPE_TAG )\r
209                 return false;\r
210         else\r
211                 throw WrongArgumentTypeException();\r
212 }\r
213 \r
214 \r
215 bool ReceivedMessageArgument::AsBoolUnchecked() const\r
216 {\r
217     if( !typeTag_ )\r
218         throw MissingArgumentException();\r
219         else if( *typeTag_ == TRUE_TYPE_TAG )\r
220                 return true;\r
221     else\r
222             return false;\r
223 }\r
224 \r
225 \r
226 int32 ReceivedMessageArgument::AsInt32() const\r
227 {\r
228     if( !typeTag_ )\r
229         throw MissingArgumentException();\r
230         else if( *typeTag_ == INT32_TYPE_TAG )\r
231                 return AsInt32Unchecked();\r
232         else\r
233                 throw WrongArgumentTypeException();\r
234 }\r
235 \r
236 \r
237 int32 ReceivedMessageArgument::AsInt32Unchecked() const\r
238 {\r
239 #ifdef OSC_HOST_LITTLE_ENDIAN\r
240     union{\r
241         osc::int32 i;\r
242         char c[4];\r
243     } u;\r
244 \r
245     u.c[0] = argument_[3];\r
246     u.c[1] = argument_[2];\r
247     u.c[2] = argument_[1];\r
248     u.c[3] = argument_[0];\r
249 \r
250     return u.i;\r
251 #else\r
252         return *(int32*)argument_;\r
253 #endif\r
254 }\r
255 \r
256 \r
257 float ReceivedMessageArgument::AsFloat() const\r
258 {\r
259     if( !typeTag_ )\r
260         throw MissingArgumentException();\r
261         else if( *typeTag_ == FLOAT_TYPE_TAG )\r
262                 return AsFloatUnchecked();\r
263         else\r
264                 throw WrongArgumentTypeException();\r
265 }\r
266 \r
267 \r
268 float ReceivedMessageArgument::AsFloatUnchecked() const\r
269 {\r
270 #ifdef OSC_HOST_LITTLE_ENDIAN\r
271     union{\r
272         float f;\r
273         char c[4];\r
274     } u;\r
275 \r
276     u.c[0] = argument_[3];\r
277     u.c[1] = argument_[2];\r
278     u.c[2] = argument_[1];\r
279     u.c[3] = argument_[0];\r
280 \r
281     return u.f;\r
282 #else\r
283         return *(float*)argument_;\r
284 #endif\r
285 }\r
286 \r
287 \r
288 char ReceivedMessageArgument::AsChar() const\r
289 {\r
290     if( !typeTag_ )\r
291         throw MissingArgumentException();\r
292         else if( *typeTag_ == CHAR_TYPE_TAG )\r
293                 return AsCharUnchecked();\r
294         else\r
295                 throw WrongArgumentTypeException();\r
296 }\r
297 \r
298 \r
299 char ReceivedMessageArgument::AsCharUnchecked() const\r
300 {\r
301     return (char)ToInt32( argument_ );\r
302 }\r
303 \r
304 \r
305 uint32 ReceivedMessageArgument::AsRgbaColor() const\r
306 {\r
307     if( !typeTag_ )\r
308         throw MissingArgumentException();\r
309         else if( *typeTag_ == RGBA_COLOR_TYPE_TAG )\r
310                 return AsRgbaColorUnchecked();\r
311         else\r
312                 throw WrongArgumentTypeException();\r
313 }\r
314 \r
315 \r
316 uint32 ReceivedMessageArgument::AsRgbaColorUnchecked() const\r
317 {\r
318         return ToUInt32( argument_ );\r
319 }\r
320 \r
321 \r
322 uint32 ReceivedMessageArgument::AsMidiMessage() const\r
323 {\r
324     if( !typeTag_ )\r
325         throw MissingArgumentException();\r
326         else if( *typeTag_ == MIDI_MESSAGE_TYPE_TAG )\r
327                 return AsMidiMessageUnchecked();\r
328         else\r
329                 throw WrongArgumentTypeException();\r
330 }\r
331 \r
332 \r
333 uint32 ReceivedMessageArgument::AsMidiMessageUnchecked() const\r
334 {\r
335         return ToUInt32( argument_ );\r
336 }\r
337 \r
338 \r
339 int64 ReceivedMessageArgument::AsInt64() const\r
340 {\r
341     if( !typeTag_ )\r
342         throw MissingArgumentException();\r
343         else if( *typeTag_ == INT64_TYPE_TAG )\r
344                 return AsInt64Unchecked();\r
345         else\r
346                 throw WrongArgumentTypeException();\r
347 }\r
348 \r
349 \r
350 int64 ReceivedMessageArgument::AsInt64Unchecked() const\r
351 {\r
352     return ToInt64( argument_ );\r
353 }\r
354 \r
355 \r
356 uint64 ReceivedMessageArgument::AsTimeTag() const\r
357 {\r
358     if( !typeTag_ )\r
359         throw MissingArgumentException();\r
360         else if( *typeTag_ == TIME_TAG_TYPE_TAG )\r
361                 return AsTimeTagUnchecked();\r
362         else\r
363                 throw WrongArgumentTypeException();\r
364 }\r
365 \r
366 \r
367 uint64 ReceivedMessageArgument::AsTimeTagUnchecked() const\r
368 {\r
369     return ToUInt64( argument_ );\r
370 }\r
371 \r
372 \r
373 double ReceivedMessageArgument::AsDouble() const\r
374 {\r
375     if( !typeTag_ )\r
376         throw MissingArgumentException();\r
377         else if( *typeTag_ == DOUBLE_TYPE_TAG )\r
378                 return AsDoubleUnchecked();\r
379         else\r
380                 throw WrongArgumentTypeException();\r
381 }\r
382 \r
383 \r
384 double ReceivedMessageArgument::AsDoubleUnchecked() const\r
385 {\r
386 #ifdef OSC_HOST_LITTLE_ENDIAN\r
387     union{\r
388         double d;\r
389         char c[8];\r
390     } u;\r
391 \r
392     u.c[0] = argument_[7];\r
393     u.c[1] = argument_[6];\r
394     u.c[2] = argument_[5];\r
395     u.c[3] = argument_[4];\r
396     u.c[4] = argument_[3];\r
397     u.c[5] = argument_[2];\r
398     u.c[6] = argument_[1];\r
399     u.c[7] = argument_[0];\r
400 \r
401     return u.d;\r
402 #else\r
403         return *(double*)argument_;\r
404 #endif\r
405 }\r
406 \r
407 \r
408 const char* ReceivedMessageArgument::AsString() const\r
409 {\r
410     if( !typeTag_ )\r
411         throw MissingArgumentException();\r
412         else if( *typeTag_ == STRING_TYPE_TAG )\r
413                 return argument_;\r
414         else\r
415                 throw WrongArgumentTypeException();\r
416 }\r
417 \r
418 \r
419 const char* ReceivedMessageArgument::AsSymbol() const\r
420 {\r
421     if( !typeTag_ )\r
422         throw MissingArgumentException();\r
423         else if( *typeTag_ == SYMBOL_TYPE_TAG )\r
424                 return argument_;\r
425         else\r
426                 throw WrongArgumentTypeException();\r
427 }\r
428 \r
429 \r
430 void ReceivedMessageArgument::AsBlob( const void*& data, unsigned long& size ) const\r
431 {\r
432     if( !typeTag_ )\r
433         throw MissingArgumentException();\r
434         else if( *typeTag_ == BLOB_TYPE_TAG )\r
435                 AsBlobUnchecked( data, size );\r
436         else\r
437                 throw WrongArgumentTypeException();\r
438 }\r
439 \r
440 \r
441 void ReceivedMessageArgument::AsBlobUnchecked( const void*& data, unsigned long& size ) const\r
442 {\r
443     size = ToUInt32( argument_ );\r
444         data = (void*)(argument_+4);\r
445 }\r
446 \r
447 //------------------------------------------------------------------------------\r
448 \r
449 void ReceivedMessageArgumentIterator::Advance()\r
450 {\r
451     if( !value_.typeTag_ )\r
452         return;\r
453         \r
454     switch( *value_.typeTag_++ ){\r
455         case '\0':\r
456             // don't advance past end\r
457             --value_.typeTag_;\r
458             break;\r
459             \r
460         case TRUE_TYPE_TAG:\r
461         case FALSE_TYPE_TAG:\r
462         case NIL_TYPE_TAG:\r
463         case INFINITUM_TYPE_TAG:\r
464 \r
465             // zero length\r
466             break;\r
467 \r
468         case INT32_TYPE_TAG:\r
469         case FLOAT_TYPE_TAG:                                    \r
470         case CHAR_TYPE_TAG:\r
471         case RGBA_COLOR_TYPE_TAG:\r
472         case MIDI_MESSAGE_TYPE_TAG:\r
473 \r
474             value_.argument_ += 4;\r
475             break;\r
476 \r
477         case INT64_TYPE_TAG:\r
478         case TIME_TAG_TYPE_TAG:\r
479         case DOUBLE_TYPE_TAG:\r
480                                 \r
481             value_.argument_ += 8;\r
482             break;\r
483 \r
484         case STRING_TYPE_TAG: \r
485         case SYMBOL_TYPE_TAG:\r
486 \r
487             // we use the unsafe function FindStr4End(char*) here because all of\r
488             // the arguments have already been validated in\r
489             // ReceivedMessage::Init() below.\r
490             \r
491             value_.argument_ = FindStr4End( value_.argument_ );\r
492             break;\r
493 \r
494         case BLOB_TYPE_TAG:\r
495             {\r
496                 uint32 blobSize = ToUInt32( value_.argument_ );\r
497                 value_.argument_ = value_.argument_ + 4 + RoundUp4( (unsigned long)blobSize );\r
498             }\r
499             break;\r
500 \r
501         default:    // unknown type tag\r
502             // don't advance\r
503             --value_.typeTag_;\r
504             break;\r
505             \r
506 \r
507         //    not handled:\r
508         //    [ Indicates the beginning of an array. The tags following are for\r
509         //        data in the Array until a close brace tag is reached.\r
510         //    ] Indicates the end of an array.\r
511     }\r
512 }\r
513 \r
514 //------------------------------------------------------------------------------\r
515 \r
516 ReceivedMessage::ReceivedMessage( const ReceivedPacket& packet )\r
517     : addressPattern_( packet.Contents() )\r
518 {\r
519     Init( packet.Contents(), packet.Size() );\r
520 }\r
521 \r
522 \r
523 ReceivedMessage::ReceivedMessage( const ReceivedBundleElement& bundleElement )\r
524     : addressPattern_( bundleElement.Contents() )\r
525 {\r
526     Init( bundleElement.Contents(), bundleElement.Size() );\r
527 }\r
528 \r
529 \r
530 bool ReceivedMessage::AddressPatternIsUInt32() const\r
531 {\r
532         return (addressPattern_[0] == '\0');\r
533 }\r
534 \r
535 \r
536 uint32 ReceivedMessage::AddressPatternAsUInt32() const\r
537 {\r
538     return ToUInt32( addressPattern_ );\r
539 }\r
540 \r
541 \r
542 void ReceivedMessage::Init( const char *message, unsigned long size )\r
543 {\r
544     if( size == 0 )\r
545         throw MalformedMessageException( "zero length messages not permitted" );\r
546 \r
547     if( (size & 0x03L) != 0 )\r
548         throw MalformedMessageException( "message size must be multiple of four" );\r
549 \r
550     const char *end = message + size;\r
551 \r
552     typeTagsBegin_ = FindStr4End( addressPattern_, end );\r
553     if( typeTagsBegin_ == 0 ){\r
554         // address pattern was not terminated before end\r
555         throw MalformedMessageException( "unterminated address pattern" );\r
556     }\r
557 \r
558     if( typeTagsBegin_ == end ){\r
559         // message consists of only the address pattern - no arguments or type tags.\r
560         typeTagsBegin_ = 0;\r
561         typeTagsEnd_ = 0;\r
562         arguments_ = 0;\r
563             \r
564     }else{\r
565         if( *typeTagsBegin_ != ',' )\r
566             throw MalformedMessageException( "type tags not present" );\r
567 \r
568         if( *(typeTagsBegin_ + 1) == '\0' ){\r
569             // zero length type tags\r
570             typeTagsBegin_ = 0;\r
571             typeTagsEnd_ = 0;\r
572             arguments_ = 0;\r
573 \r
574         }else{\r
575             // check that all arguments are present and well formed\r
576                 \r
577             arguments_ = FindStr4End( typeTagsBegin_, end );\r
578             if( arguments_ == 0 ){\r
579                 throw MalformedMessageException( "type tags were not terminated before end of message" );\r
580             }\r
581 \r
582             ++typeTagsBegin_; // advance past initial ','\r
583             \r
584             const char *typeTag = typeTagsBegin_;\r
585             const char *argument = arguments_;\r
586                         \r
587             do{\r
588                 switch( *typeTag ){\r
589                     case TRUE_TYPE_TAG:\r
590                     case FALSE_TYPE_TAG:\r
591                     case NIL_TYPE_TAG:\r
592                     case INFINITUM_TYPE_TAG:\r
593 \r
594                         // zero length\r
595                         break;\r
596 \r
597                     case INT32_TYPE_TAG:\r
598                     case FLOAT_TYPE_TAG:\r
599                     case CHAR_TYPE_TAG:\r
600                     case RGBA_COLOR_TYPE_TAG:\r
601                     case MIDI_MESSAGE_TYPE_TAG:\r
602 \r
603                         if( argument == end )\r
604                             throw MalformedMessageException( "arguments exceed message size" );\r
605                         argument += 4;\r
606                         if( argument > end )\r
607                             throw MalformedMessageException( "arguments exceed message size" );\r
608                         break;\r
609 \r
610                     case INT64_TYPE_TAG:\r
611                     case TIME_TAG_TYPE_TAG:\r
612                     case DOUBLE_TYPE_TAG:\r
613 \r
614                         if( argument == end )\r
615                             throw MalformedMessageException( "arguments exceed message size" );\r
616                         argument += 8;\r
617                         if( argument > end )\r
618                             throw MalformedMessageException( "arguments exceed message size" );\r
619                         break;\r
620 \r
621                     case STRING_TYPE_TAG: \r
622                     case SYMBOL_TYPE_TAG:\r
623                     \r
624                         if( argument == end )\r
625                             throw MalformedMessageException( "arguments exceed message size" );\r
626                         argument = FindStr4End( argument, end );\r
627                         if( argument == 0 )\r
628                             throw MalformedMessageException( "unterminated string argument" );\r
629                         break;\r
630 \r
631                     case BLOB_TYPE_TAG:\r
632                         {\r
633                             if( argument + 4 > end )\r
634                                 MalformedMessageException( "arguments exceed message size" );\r
635                                 \r
636                             uint32 blobSize = ToUInt32( argument );\r
637                             argument = argument + 4 + RoundUp4( (unsigned long)blobSize );\r
638                             if( argument > end )\r
639                                 MalformedMessageException( "arguments exceed message size" );\r
640                         }\r
641                         break;\r
642                         \r
643                     default:\r
644                         throw MalformedMessageException( "unknown type tag" );\r
645 \r
646                     //    not handled:\r
647                     //    [ Indicates the beginning of an array. The tags following are for\r
648                     //        data in the Array until a close brace tag is reached.\r
649                     //    ] Indicates the end of an array.\r
650                 }\r
651 \r
652             }while( *++typeTag != '\0' );\r
653             typeTagsEnd_ = typeTag;\r
654         }\r
655     }\r
656 }\r
657 \r
658 //------------------------------------------------------------------------------\r
659 \r
660 ReceivedBundle::ReceivedBundle( const ReceivedPacket& packet )\r
661     : elementCount_( 0 )\r
662 {\r
663     Init( packet.Contents(), packet.Size() );\r
664 }\r
665 \r
666 \r
667 ReceivedBundle::ReceivedBundle( const ReceivedBundleElement& bundleElement )\r
668     : elementCount_( 0 )\r
669 {\r
670     Init( bundleElement.Contents(), bundleElement.Size() );\r
671 }\r
672 \r
673 \r
674 void ReceivedBundle::Init( const char *bundle, unsigned long size )\r
675 {\r
676     if( size < 16 )\r
677         throw MalformedBundleException( "packet too short for bundle" );\r
678 \r
679     if( (size & 0x03L) != 0 )\r
680         throw MalformedBundleException( "bundle size must be multiple of four" );\r
681 \r
682     if( bundle[0] != '#'\r
683         || bundle[1] != 'b'\r
684         || bundle[2] != 'u'\r
685         || bundle[3] != 'n'\r
686         || bundle[4] != 'd'\r
687         || bundle[5] != 'l'\r
688         || bundle[6] != 'e'\r
689         || bundle[7] != '\0' )\r
690             throw MalformedBundleException( "bad bundle address pattern" );    \r
691 \r
692     end_ = bundle + size;\r
693 \r
694     timeTag_ = bundle + 8;\r
695 \r
696     const char *p = timeTag_ + 8;\r
697         \r
698     while( p < end_ ){\r
699         if( p + 4 > end_ )\r
700             throw MalformedBundleException( "packet too short for elementSize" );\r
701 \r
702         uint32 elementSize = ToUInt32( p );\r
703         if( (elementSize & 0x03L) != 0 )\r
704             throw MalformedBundleException( "bundle element size must be multiple of four" );\r
705 \r
706         p += 4 + elementSize;\r
707         if( p > end_ )\r
708             throw MalformedBundleException( "packet too short for bundle element" );\r
709 \r
710         ++elementCount_;\r
711     }\r
712 \r
713     if( p != end_ )\r
714         throw MalformedBundleException( "bundle contents " );\r
715 }\r
716 \r
717 \r
718 uint64 ReceivedBundle::TimeTag() const\r
719 {\r
720     return ToUInt64( timeTag_ );\r
721 }\r
722 \r
723 \r
724 } // namespace osc\r
725 \r