2 oscpack -- Open Sound Control packet manipulation library
3 http://www.audiomulch.com/~rossb/oscpack
5 Copyright (c) 2004-2005 Ross Bencina <rossb@audiomulch.com>
7 Permission is hereby granted, free of charge, to any person obtaining
8 a copy of this software and associated documentation files
9 (the "Software"), to deal in the Software without restriction,
10 including without limitation the rights to use, copy, modify, merge,
11 publish, distribute, sublicense, and/or sell copies of the Software,
12 and to permit persons to whom the Software is furnished to do so,
13 subject to the following conditions:
15 The above copyright notice and this permission notice shall be
16 included in all copies or substantial portions of the Software.
18 Any person wishing to distribute modifications to the Software is
19 requested to send the modifications to the original developer so that
20 they can be incorporated into the canonical version.
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
25 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
26 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
27 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 #undef _CRT_SECURE_NO_WARNINGS
31 #define _CRT_SECURE_NO_WARNINGS
33 #include "OscReceivedElements.h"
37 #include "OscHostEndianness.h"
43 // return the first 4 byte boundary after the end of a str4
44 // be careful about calling this version if you don't know whether
45 // the string is terminated correctly.
46 static inline const char* FindStr4End( const char *p )
48 if( p[0] == '\0' ) // special case for SuperCollider integer address pattern
60 // return the first 4 byte boundary after the end of a str4
61 // returns 0 if p == end or if the string is unterminated
62 static inline const char* FindStr4End( const char *p, const char *end )
67 if( p[0] == '\0' ) // special case for SuperCollider integer address pattern
73 while( p < end && *p )
83 static inline unsigned long RoundUp4( unsigned long x )
85 unsigned long remainder = x & 0x3UL;
87 return x + (4 - remainder);
93 static inline int32 ToInt32( const char *p )
95 #ifdef OSC_HOST_LITTLE_ENDIAN
113 static inline uint32 ToUInt32( const char *p )
115 #ifdef OSC_HOST_LITTLE_ENDIAN
133 int64 ToInt64( const char *p )
135 #ifdef OSC_HOST_LITTLE_ENDIAN
157 uint64 ToUInt64( const char *p )
159 #ifdef OSC_HOST_LITTLE_ENDIAN
180 //------------------------------------------------------------------------------
182 bool ReceivedPacket::IsBundle() const
184 return (Size() > 0 && Contents()[0] == '#');
187 //------------------------------------------------------------------------------
189 bool ReceivedBundleElement::IsBundle() const
191 return (Size() > 0 && Contents()[0] == '#');
195 int32 ReceivedBundleElement::Size() const
197 return ToUInt32( size_ );
200 //------------------------------------------------------------------------------
202 bool ReceivedMessageArgument::AsBool() const
205 throw MissingArgumentException();
206 else if( *typeTag_ == TRUE_TYPE_TAG )
208 else if( *typeTag_ == FALSE_TYPE_TAG )
211 throw WrongArgumentTypeException();
215 bool ReceivedMessageArgument::AsBoolUnchecked() const
218 throw MissingArgumentException();
219 else if( *typeTag_ == TRUE_TYPE_TAG )
226 int32 ReceivedMessageArgument::AsInt32() const
229 throw MissingArgumentException();
230 else if( *typeTag_ == INT32_TYPE_TAG )
231 return AsInt32Unchecked();
233 throw WrongArgumentTypeException();
237 int32 ReceivedMessageArgument::AsInt32Unchecked() const
239 #ifdef OSC_HOST_LITTLE_ENDIAN
245 u.c[0] = argument_[3];
246 u.c[1] = argument_[2];
247 u.c[2] = argument_[1];
248 u.c[3] = argument_[0];
252 return *(int32*)argument_;
257 float ReceivedMessageArgument::AsFloat() const
260 throw MissingArgumentException();
261 else if( *typeTag_ == FLOAT_TYPE_TAG )
262 return AsFloatUnchecked();
264 throw WrongArgumentTypeException();
268 float ReceivedMessageArgument::AsFloatUnchecked() const
270 #ifdef OSC_HOST_LITTLE_ENDIAN
276 u.c[0] = argument_[3];
277 u.c[1] = argument_[2];
278 u.c[2] = argument_[1];
279 u.c[3] = argument_[0];
283 return *(float*)argument_;
288 char ReceivedMessageArgument::AsChar() const
291 throw MissingArgumentException();
292 else if( *typeTag_ == CHAR_TYPE_TAG )
293 return AsCharUnchecked();
295 throw WrongArgumentTypeException();
299 char ReceivedMessageArgument::AsCharUnchecked() const
301 return (char)ToInt32( argument_ );
305 uint32 ReceivedMessageArgument::AsRgbaColor() const
308 throw MissingArgumentException();
309 else if( *typeTag_ == RGBA_COLOR_TYPE_TAG )
310 return AsRgbaColorUnchecked();
312 throw WrongArgumentTypeException();
316 uint32 ReceivedMessageArgument::AsRgbaColorUnchecked() const
318 return ToUInt32( argument_ );
322 uint32 ReceivedMessageArgument::AsMidiMessage() const
325 throw MissingArgumentException();
326 else if( *typeTag_ == MIDI_MESSAGE_TYPE_TAG )
327 return AsMidiMessageUnchecked();
329 throw WrongArgumentTypeException();
333 uint32 ReceivedMessageArgument::AsMidiMessageUnchecked() const
335 return ToUInt32( argument_ );
339 int64 ReceivedMessageArgument::AsInt64() const
342 throw MissingArgumentException();
343 else if( *typeTag_ == INT64_TYPE_TAG )
344 return AsInt64Unchecked();
346 throw WrongArgumentTypeException();
350 int64 ReceivedMessageArgument::AsInt64Unchecked() const
352 return ToInt64( argument_ );
356 uint64 ReceivedMessageArgument::AsTimeTag() const
359 throw MissingArgumentException();
360 else if( *typeTag_ == TIME_TAG_TYPE_TAG )
361 return AsTimeTagUnchecked();
363 throw WrongArgumentTypeException();
367 uint64 ReceivedMessageArgument::AsTimeTagUnchecked() const
369 return ToUInt64( argument_ );
373 double ReceivedMessageArgument::AsDouble() const
376 throw MissingArgumentException();
377 else if( *typeTag_ == DOUBLE_TYPE_TAG )
378 return AsDoubleUnchecked();
380 throw WrongArgumentTypeException();
384 double ReceivedMessageArgument::AsDoubleUnchecked() const
386 #ifdef OSC_HOST_LITTLE_ENDIAN
392 u.c[0] = argument_[7];
393 u.c[1] = argument_[6];
394 u.c[2] = argument_[5];
395 u.c[3] = argument_[4];
396 u.c[4] = argument_[3];
397 u.c[5] = argument_[2];
398 u.c[6] = argument_[1];
399 u.c[7] = argument_[0];
403 return *(double*)argument_;
408 const char* ReceivedMessageArgument::AsString() const
411 throw MissingArgumentException();
412 else if( *typeTag_ == STRING_TYPE_TAG )
415 throw WrongArgumentTypeException();
419 const char* ReceivedMessageArgument::AsSymbol() const
422 throw MissingArgumentException();
423 else if( *typeTag_ == SYMBOL_TYPE_TAG )
426 throw WrongArgumentTypeException();
430 void ReceivedMessageArgument::AsBlob( const void*& data, unsigned long& size ) const
433 throw MissingArgumentException();
434 else if( *typeTag_ == BLOB_TYPE_TAG )
435 AsBlobUnchecked( data, size );
437 throw WrongArgumentTypeException();
441 void ReceivedMessageArgument::AsBlobUnchecked( const void*& data, unsigned long& size ) const
443 size = ToUInt32( argument_ );
444 data = (void*)(argument_+4);
447 //------------------------------------------------------------------------------
449 void ReceivedMessageArgumentIterator::Advance()
451 if( !value_.typeTag_ )
454 switch( *value_.typeTag_++ ){
456 // don't advance past end
463 case INFINITUM_TYPE_TAG:
471 case RGBA_COLOR_TYPE_TAG:
472 case MIDI_MESSAGE_TYPE_TAG:
474 value_.argument_ += 4;
478 case TIME_TAG_TYPE_TAG:
479 case DOUBLE_TYPE_TAG:
481 value_.argument_ += 8;
484 case STRING_TYPE_TAG:
485 case SYMBOL_TYPE_TAG:
487 // we use the unsafe function FindStr4End(char*) here because all of
488 // the arguments have already been validated in
489 // ReceivedMessage::Init() below.
491 value_.argument_ = FindStr4End( value_.argument_ );
496 uint32 blobSize = ToUInt32( value_.argument_ );
497 value_.argument_ = value_.argument_ + 4 + RoundUp4( (unsigned long)blobSize );
501 default: // unknown type tag
508 // [ Indicates the beginning of an array. The tags following are for
509 // data in the Array until a close brace tag is reached.
510 // ] Indicates the end of an array.
514 //------------------------------------------------------------------------------
516 ReceivedMessage::ReceivedMessage( const ReceivedPacket& packet )
517 : addressPattern_( packet.Contents() )
519 Init( packet.Contents(), packet.Size() );
523 ReceivedMessage::ReceivedMessage( const ReceivedBundleElement& bundleElement )
524 : addressPattern_( bundleElement.Contents() )
526 Init( bundleElement.Contents(), bundleElement.Size() );
530 bool ReceivedMessage::AddressPatternIsUInt32() const
532 return (addressPattern_[0] == '\0');
536 uint32 ReceivedMessage::AddressPatternAsUInt32() const
538 return ToUInt32( addressPattern_ );
542 void ReceivedMessage::Init( const char *message, unsigned long size )
545 throw MalformedMessageException( "zero length messages not permitted" );
547 if( (size & 0x03L) != 0 )
548 throw MalformedMessageException( "message size must be multiple of four" );
550 const char *end = message + size;
552 typeTagsBegin_ = FindStr4End( addressPattern_, end );
553 if( typeTagsBegin_ == 0 ){
554 // address pattern was not terminated before end
555 throw MalformedMessageException( "unterminated address pattern" );
558 if( typeTagsBegin_ == end ){
559 // message consists of only the address pattern - no arguments or type tags.
565 if( *typeTagsBegin_ != ',' )
566 throw MalformedMessageException( "type tags not present" );
568 if( *(typeTagsBegin_ + 1) == '\0' ){
569 // zero length type tags
575 // check that all arguments are present and well formed
577 arguments_ = FindStr4End( typeTagsBegin_, end );
578 if( arguments_ == 0 ){
579 throw MalformedMessageException( "type tags were not terminated before end of message" );
582 ++typeTagsBegin_; // advance past initial ','
584 const char *typeTag = typeTagsBegin_;
585 const char *argument = arguments_;
592 case INFINITUM_TYPE_TAG:
600 case RGBA_COLOR_TYPE_TAG:
601 case MIDI_MESSAGE_TYPE_TAG:
603 if( argument == end )
604 throw MalformedMessageException( "arguments exceed message size" );
607 throw MalformedMessageException( "arguments exceed message size" );
611 case TIME_TAG_TYPE_TAG:
612 case DOUBLE_TYPE_TAG:
614 if( argument == end )
615 throw MalformedMessageException( "arguments exceed message size" );
618 throw MalformedMessageException( "arguments exceed message size" );
621 case STRING_TYPE_TAG:
622 case SYMBOL_TYPE_TAG:
624 if( argument == end )
625 throw MalformedMessageException( "arguments exceed message size" );
626 argument = FindStr4End( argument, end );
628 throw MalformedMessageException( "unterminated string argument" );
633 if( argument + 4 > end )
634 throw MalformedMessageException( "arguments exceed message size" );
636 uint32 blobSize = ToUInt32( argument );
637 argument = argument + 4 + RoundUp4( (unsigned long)blobSize );
639 throw MalformedMessageException( "arguments exceed message size" );
644 throw MalformedMessageException( "unknown type tag" );
647 // [ Indicates the beginning of an array. The tags following are for
648 // data in the Array until a close brace tag is reached.
649 // ] Indicates the end of an array.
652 }while( *++typeTag != '\0' );
653 typeTagsEnd_ = typeTag;
658 //------------------------------------------------------------------------------
660 ReceivedBundle::ReceivedBundle( const ReceivedPacket& packet )
663 Init( packet.Contents(), packet.Size() );
667 ReceivedBundle::ReceivedBundle( const ReceivedBundleElement& bundleElement )
670 Init( bundleElement.Contents(), bundleElement.Size() );
674 void ReceivedBundle::Init( const char *bundle, unsigned long size )
677 throw MalformedBundleException( "packet too short for bundle" );
679 if( (size & 0x03L) != 0 )
680 throw MalformedBundleException( "bundle size must be multiple of four" );
689 || bundle[7] != '\0' )
690 throw MalformedBundleException( "bad bundle address pattern" );
692 end_ = bundle + size;
694 timeTag_ = bundle + 8;
696 const char *p = timeTag_ + 8;
700 throw MalformedBundleException( "packet too short for elementSize" );
702 uint32 elementSize = ToUInt32( p );
703 if( (elementSize & 0x03L) != 0 )
704 throw MalformedBundleException( "bundle element size must be multiple of four" );
706 p += 4 + elementSize;
708 throw MalformedBundleException( "packet too short for bundle element" );
714 throw MalformedBundleException( "bundle contents " );
718 uint64 ReceivedBundle::TimeTag() const
720 return ToUInt64( timeTag_ );