rob@76: /* rob@76: oscpack -- Open Sound Control (OSC) packet manipulation library rob@76: http://www.rossbencina.com/code/oscpack rob@76: rob@76: Copyright (c) 2004-2013 Ross Bencina rob@76: rob@76: Permission is hereby granted, free of charge, to any person obtaining rob@76: a copy of this software and associated documentation files rob@76: (the "Software"), to deal in the Software without restriction, rob@76: including without limitation the rights to use, copy, modify, merge, rob@76: publish, distribute, sublicense, and/or sell copies of the Software, rob@76: and to permit persons to whom the Software is furnished to do so, rob@76: subject to the following conditions: rob@76: rob@76: The above copyright notice and this permission notice shall be rob@76: included in all copies or substantial portions of the Software. rob@76: rob@76: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, rob@76: EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF rob@76: MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. rob@76: IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR rob@76: ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF rob@76: CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION rob@76: WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. rob@76: */ rob@76: rob@76: /* rob@76: The text above constitutes the entire oscpack license; however, rob@76: the oscpack developer(s) also make the following non-binding requests: rob@76: rob@76: Any person wishing to distribute modifications to the Software is rob@76: requested to send the modifications to the original developer so that rob@76: they can be incorporated into the canonical version. It is also rob@76: requested that these non-binding requests be included whenever the rob@76: above license is reproduced. rob@76: */ rob@76: #include "OscReceivedElements.h" rob@76: rob@76: #include "OscHostEndianness.h" rob@76: rob@76: #include // ptrdiff_t rob@76: rob@76: namespace osc{ rob@76: rob@76: rob@76: // return the first 4 byte boundary after the end of a str4 rob@76: // be careful about calling this version if you don't know whether rob@76: // the string is terminated correctly. rob@76: static inline const char* FindStr4End( const char *p ) rob@76: { rob@76: if( p[0] == '\0' ) // special case for SuperCollider integer address pattern rob@76: return p + 4; rob@76: rob@76: p += 3; rob@76: rob@76: while( *p ) rob@76: p += 4; rob@76: rob@76: return p + 1; rob@76: } rob@76: rob@76: rob@76: // return the first 4 byte boundary after the end of a str4 rob@76: // returns 0 if p == end or if the string is unterminated rob@76: static inline const char* FindStr4End( const char *p, const char *end ) rob@76: { rob@76: if( p >= end ) rob@76: return 0; rob@76: rob@76: if( p[0] == '\0' ) // special case for SuperCollider integer address pattern rob@76: return p + 4; rob@76: rob@76: p += 3; rob@76: end -= 1; rob@76: rob@76: while( p < end && *p ) rob@76: p += 4; rob@76: rob@76: if( *p ) rob@76: return 0; rob@76: else rob@76: return p + 1; rob@76: } rob@76: rob@76: rob@76: // round up to the next highest multiple of 4. unless x is already a multiple of 4 rob@76: static inline uint32 RoundUp4( uint32 x ) rob@76: { rob@76: return (x + 3) & ~((uint32)0x03); rob@76: } rob@76: rob@76: rob@76: static inline int32 ToInt32( const char *p ) rob@76: { rob@76: #ifdef OSC_HOST_LITTLE_ENDIAN rob@76: union{ rob@76: osc::int32 i; rob@76: char c[4]; rob@76: } u; rob@76: rob@76: u.c[0] = p[3]; rob@76: u.c[1] = p[2]; rob@76: u.c[2] = p[1]; rob@76: u.c[3] = p[0]; rob@76: rob@76: return u.i; rob@76: #else rob@76: return *(int32*)p; rob@76: #endif rob@76: } rob@76: rob@76: rob@76: static inline uint32 ToUInt32( const char *p ) rob@76: { rob@76: #ifdef OSC_HOST_LITTLE_ENDIAN rob@76: union{ rob@76: osc::uint32 i; rob@76: char c[4]; rob@76: } u; rob@76: rob@76: u.c[0] = p[3]; rob@76: u.c[1] = p[2]; rob@76: u.c[2] = p[1]; rob@76: u.c[3] = p[0]; rob@76: rob@76: return u.i; rob@76: #else rob@76: return *(uint32*)p; rob@76: #endif rob@76: } rob@76: rob@76: rob@76: static inline int64 ToInt64( const char *p ) rob@76: { rob@76: #ifdef OSC_HOST_LITTLE_ENDIAN rob@76: union{ rob@76: osc::int64 i; rob@76: char c[8]; rob@76: } u; rob@76: rob@76: u.c[0] = p[7]; rob@76: u.c[1] = p[6]; rob@76: u.c[2] = p[5]; rob@76: u.c[3] = p[4]; rob@76: u.c[4] = p[3]; rob@76: u.c[5] = p[2]; rob@76: u.c[6] = p[1]; rob@76: u.c[7] = p[0]; rob@76: rob@76: return u.i; rob@76: #else rob@76: return *(int64*)p; rob@76: #endif rob@76: } rob@76: rob@76: rob@76: static inline uint64 ToUInt64( const char *p ) rob@76: { rob@76: #ifdef OSC_HOST_LITTLE_ENDIAN rob@76: union{ rob@76: osc::uint64 i; rob@76: char c[8]; rob@76: } u; rob@76: rob@76: u.c[0] = p[7]; rob@76: u.c[1] = p[6]; rob@76: u.c[2] = p[5]; rob@76: u.c[3] = p[4]; rob@76: u.c[4] = p[3]; rob@76: u.c[5] = p[2]; rob@76: u.c[6] = p[1]; rob@76: u.c[7] = p[0]; rob@76: rob@76: return u.i; rob@76: #else rob@76: return *(uint64*)p; rob@76: #endif rob@76: } rob@76: rob@76: //------------------------------------------------------------------------------ rob@76: rob@76: bool ReceivedPacket::IsBundle() const rob@76: { rob@76: return (Size() > 0 && Contents()[0] == '#'); rob@76: } rob@76: rob@76: //------------------------------------------------------------------------------ rob@76: rob@76: bool ReceivedBundleElement::IsBundle() const rob@76: { rob@76: return (Size() > 0 && Contents()[0] == '#'); rob@76: } rob@76: rob@76: rob@76: osc_bundle_element_size_t ReceivedBundleElement::Size() const rob@76: { rob@76: return ToInt32( sizePtr_ ); rob@76: } rob@76: rob@76: //------------------------------------------------------------------------------ rob@76: rob@76: bool ReceivedMessageArgument::AsBool() const rob@76: { rob@76: if( !typeTagPtr_ ) rob@76: throw MissingArgumentException(); rob@76: else if( *typeTagPtr_ == TRUE_TYPE_TAG ) rob@76: return true; rob@76: else if( *typeTagPtr_ == FALSE_TYPE_TAG ) rob@76: return false; rob@76: else rob@76: throw WrongArgumentTypeException(); rob@76: } rob@76: rob@76: rob@76: bool ReceivedMessageArgument::AsBoolUnchecked() const rob@76: { rob@76: if( !typeTagPtr_ ) rob@76: throw MissingArgumentException(); rob@76: else if( *typeTagPtr_ == TRUE_TYPE_TAG ) rob@76: return true; rob@76: else rob@76: return false; rob@76: } rob@76: rob@76: rob@76: int32 ReceivedMessageArgument::AsInt32() const rob@76: { rob@76: if( !typeTagPtr_ ) rob@76: throw MissingArgumentException(); rob@76: else if( *typeTagPtr_ == INT32_TYPE_TAG ) rob@76: return AsInt32Unchecked(); rob@76: else rob@76: throw WrongArgumentTypeException(); rob@76: } rob@76: rob@76: rob@76: int32 ReceivedMessageArgument::AsInt32Unchecked() const rob@76: { rob@76: #ifdef OSC_HOST_LITTLE_ENDIAN rob@76: union{ rob@76: osc::int32 i; rob@76: char c[4]; rob@76: } u; rob@76: rob@76: u.c[0] = argumentPtr_[3]; rob@76: u.c[1] = argumentPtr_[2]; rob@76: u.c[2] = argumentPtr_[1]; rob@76: u.c[3] = argumentPtr_[0]; rob@76: rob@76: return u.i; rob@76: #else rob@76: return *(int32*)argument_; rob@76: #endif rob@76: } rob@76: rob@76: rob@76: float ReceivedMessageArgument::AsFloat() const rob@76: { rob@76: if( !typeTagPtr_ ) rob@76: throw MissingArgumentException(); rob@76: else if( *typeTagPtr_ == FLOAT_TYPE_TAG ) rob@76: return AsFloatUnchecked(); rob@76: else rob@76: throw WrongArgumentTypeException(); rob@76: } rob@76: rob@76: rob@76: float ReceivedMessageArgument::AsFloatUnchecked() const rob@76: { rob@76: #ifdef OSC_HOST_LITTLE_ENDIAN rob@76: union{ rob@76: float f; rob@76: char c[4]; rob@76: } u; rob@76: rob@76: u.c[0] = argumentPtr_[3]; rob@76: u.c[1] = argumentPtr_[2]; rob@76: u.c[2] = argumentPtr_[1]; rob@76: u.c[3] = argumentPtr_[0]; rob@76: rob@76: return u.f; rob@76: #else rob@76: return *(float*)argument_; rob@76: #endif rob@76: } rob@76: rob@76: rob@76: char ReceivedMessageArgument::AsChar() const rob@76: { rob@76: if( !typeTagPtr_ ) rob@76: throw MissingArgumentException(); rob@76: else if( *typeTagPtr_ == CHAR_TYPE_TAG ) rob@76: return AsCharUnchecked(); rob@76: else rob@76: throw WrongArgumentTypeException(); rob@76: } rob@76: rob@76: rob@76: char ReceivedMessageArgument::AsCharUnchecked() const rob@76: { rob@76: return (char)ToInt32( argumentPtr_ ); rob@76: } rob@76: rob@76: rob@76: uint32 ReceivedMessageArgument::AsRgbaColor() const rob@76: { rob@76: if( !typeTagPtr_ ) rob@76: throw MissingArgumentException(); rob@76: else if( *typeTagPtr_ == RGBA_COLOR_TYPE_TAG ) rob@76: return AsRgbaColorUnchecked(); rob@76: else rob@76: throw WrongArgumentTypeException(); rob@76: } rob@76: rob@76: rob@76: uint32 ReceivedMessageArgument::AsRgbaColorUnchecked() const rob@76: { rob@76: return ToUInt32( argumentPtr_ ); rob@76: } rob@76: rob@76: rob@76: uint32 ReceivedMessageArgument::AsMidiMessage() const rob@76: { rob@76: if( !typeTagPtr_ ) rob@76: throw MissingArgumentException(); rob@76: else if( *typeTagPtr_ == MIDI_MESSAGE_TYPE_TAG ) rob@76: return AsMidiMessageUnchecked(); rob@76: else rob@76: throw WrongArgumentTypeException(); rob@76: } rob@76: rob@76: rob@76: uint32 ReceivedMessageArgument::AsMidiMessageUnchecked() const rob@76: { rob@76: return ToUInt32( argumentPtr_ ); rob@76: } rob@76: rob@76: rob@76: int64 ReceivedMessageArgument::AsInt64() const rob@76: { rob@76: if( !typeTagPtr_ ) rob@76: throw MissingArgumentException(); rob@76: else if( *typeTagPtr_ == INT64_TYPE_TAG ) rob@76: return AsInt64Unchecked(); rob@76: else rob@76: throw WrongArgumentTypeException(); rob@76: } rob@76: rob@76: rob@76: int64 ReceivedMessageArgument::AsInt64Unchecked() const rob@76: { rob@76: return ToInt64( argumentPtr_ ); rob@76: } rob@76: rob@76: rob@76: uint64 ReceivedMessageArgument::AsTimeTag() const rob@76: { rob@76: if( !typeTagPtr_ ) rob@76: throw MissingArgumentException(); rob@76: else if( *typeTagPtr_ == TIME_TAG_TYPE_TAG ) rob@76: return AsTimeTagUnchecked(); rob@76: else rob@76: throw WrongArgumentTypeException(); rob@76: } rob@76: rob@76: rob@76: uint64 ReceivedMessageArgument::AsTimeTagUnchecked() const rob@76: { rob@76: return ToUInt64( argumentPtr_ ); rob@76: } rob@76: rob@76: rob@76: double ReceivedMessageArgument::AsDouble() const rob@76: { rob@76: if( !typeTagPtr_ ) rob@76: throw MissingArgumentException(); rob@76: else if( *typeTagPtr_ == DOUBLE_TYPE_TAG ) rob@76: return AsDoubleUnchecked(); rob@76: else rob@76: throw WrongArgumentTypeException(); rob@76: } rob@76: rob@76: rob@76: double ReceivedMessageArgument::AsDoubleUnchecked() const rob@76: { rob@76: #ifdef OSC_HOST_LITTLE_ENDIAN rob@76: union{ rob@76: double d; rob@76: char c[8]; rob@76: } u; rob@76: rob@76: u.c[0] = argumentPtr_[7]; rob@76: u.c[1] = argumentPtr_[6]; rob@76: u.c[2] = argumentPtr_[5]; rob@76: u.c[3] = argumentPtr_[4]; rob@76: u.c[4] = argumentPtr_[3]; rob@76: u.c[5] = argumentPtr_[2]; rob@76: u.c[6] = argumentPtr_[1]; rob@76: u.c[7] = argumentPtr_[0]; rob@76: rob@76: return u.d; rob@76: #else rob@76: return *(double*)argument_; rob@76: #endif rob@76: } rob@76: rob@76: rob@76: const char* ReceivedMessageArgument::AsString() const rob@76: { rob@76: if( !typeTagPtr_ ) rob@76: throw MissingArgumentException(); rob@76: else if( *typeTagPtr_ == STRING_TYPE_TAG ) rob@76: return argumentPtr_; rob@76: else rob@76: throw WrongArgumentTypeException(); rob@76: } rob@76: rob@76: rob@76: const char* ReceivedMessageArgument::AsSymbol() const rob@76: { rob@76: if( !typeTagPtr_ ) rob@76: throw MissingArgumentException(); rob@76: else if( *typeTagPtr_ == SYMBOL_TYPE_TAG ) rob@76: return argumentPtr_; rob@76: else rob@76: throw WrongArgumentTypeException(); rob@76: } rob@76: rob@76: rob@76: void ReceivedMessageArgument::AsBlob( const void*& data, osc_bundle_element_size_t& size ) const rob@76: { rob@76: if( !typeTagPtr_ ) rob@76: throw MissingArgumentException(); rob@76: else if( *typeTagPtr_ == BLOB_TYPE_TAG ) rob@76: AsBlobUnchecked( data, size ); rob@76: else rob@76: throw WrongArgumentTypeException(); rob@76: } rob@76: rob@76: rob@76: void ReceivedMessageArgument::AsBlobUnchecked( const void*& data, osc_bundle_element_size_t& size ) const rob@76: { rob@76: // read blob size as an unsigned int then validate rob@76: osc_bundle_element_size_t sizeResult = (osc_bundle_element_size_t)ToUInt32( argumentPtr_ ); rob@76: if( !IsValidElementSizeValue(sizeResult) ) rob@76: throw MalformedMessageException("invalid blob size"); rob@76: rob@76: size = sizeResult; rob@76: data = (void*)(argumentPtr_+ osc::OSC_SIZEOF_INT32); rob@76: } rob@76: rob@76: std::size_t ReceivedMessageArgument::ComputeArrayItemCount() const rob@76: { rob@76: // it is only valid to call ComputeArrayItemCount when the argument is the array start marker rob@76: if( !IsArrayBegin() ) rob@76: throw WrongArgumentTypeException(); rob@76: rob@76: std::size_t result = 0; rob@76: unsigned int level = 0; rob@76: const char *typeTag = typeTagPtr_ + 1; rob@76: rob@76: // iterate through all type tags. note that ReceivedMessage::Init rob@76: // has already checked that the message is well formed. rob@76: while( *typeTag ) { rob@76: switch( *typeTag++ ) { rob@76: case ARRAY_BEGIN_TYPE_TAG: rob@76: level += 1; rob@76: break; rob@76: rob@76: case ARRAY_END_TYPE_TAG: rob@76: if(level == 0) rob@76: return result; rob@76: level -= 1; rob@76: break; rob@76: rob@76: default: rob@76: if( level == 0 ) // only count items at level 0 rob@76: ++result; rob@76: } rob@76: } rob@76: rob@76: return result; rob@76: } rob@76: rob@76: //------------------------------------------------------------------------------ rob@76: rob@76: void ReceivedMessageArgumentIterator::Advance() rob@76: { rob@76: if( !value_.typeTagPtr_ ) rob@76: return; rob@76: rob@76: switch( *value_.typeTagPtr_++ ){ rob@76: case '\0': rob@76: // don't advance past end rob@76: --value_.typeTagPtr_; rob@76: break; rob@76: rob@76: case TRUE_TYPE_TAG: rob@76: case FALSE_TYPE_TAG: rob@76: case NIL_TYPE_TAG: rob@76: case INFINITUM_TYPE_TAG: rob@76: rob@76: // zero length rob@76: break; rob@76: rob@76: case INT32_TYPE_TAG: rob@76: case FLOAT_TYPE_TAG: rob@76: case CHAR_TYPE_TAG: rob@76: case RGBA_COLOR_TYPE_TAG: rob@76: case MIDI_MESSAGE_TYPE_TAG: rob@76: rob@76: value_.argumentPtr_ += 4; rob@76: break; rob@76: rob@76: case INT64_TYPE_TAG: rob@76: case TIME_TAG_TYPE_TAG: rob@76: case DOUBLE_TYPE_TAG: rob@76: rob@76: value_.argumentPtr_ += 8; rob@76: break; rob@76: rob@76: case STRING_TYPE_TAG: rob@76: case SYMBOL_TYPE_TAG: rob@76: rob@76: // we use the unsafe function FindStr4End(char*) here because all of rob@76: // the arguments have already been validated in rob@76: // ReceivedMessage::Init() below. rob@76: rob@76: value_.argumentPtr_ = FindStr4End( value_.argumentPtr_ ); rob@76: break; rob@76: rob@76: case BLOB_TYPE_TAG: rob@76: { rob@76: // treat blob size as an unsigned int for the purposes of this calculation rob@76: uint32 blobSize = ToUInt32( value_.argumentPtr_ ); rob@76: value_.argumentPtr_ = value_.argumentPtr_ + osc::OSC_SIZEOF_INT32 + RoundUp4( blobSize ); rob@76: } rob@76: break; rob@76: rob@76: case ARRAY_BEGIN_TYPE_TAG: rob@76: case ARRAY_END_TYPE_TAG: rob@76: rob@76: // [ Indicates the beginning of an array. The tags following are for rob@76: // data in the Array until a close brace tag is reached. rob@76: // ] Indicates the end of an array. rob@76: rob@76: // zero length, don't advance argument ptr rob@76: break; rob@76: rob@76: default: // unknown type tag rob@76: // don't advance rob@76: --value_.typeTagPtr_; rob@76: break; rob@76: } rob@76: } rob@76: rob@76: //------------------------------------------------------------------------------ rob@76: rob@76: ReceivedMessage::ReceivedMessage( const ReceivedPacket& packet ) rob@76: : addressPattern_( packet.Contents() ) rob@76: { rob@76: Init( packet.Contents(), packet.Size() ); rob@76: } rob@76: rob@76: rob@76: ReceivedMessage::ReceivedMessage( const ReceivedBundleElement& bundleElement ) rob@76: : addressPattern_( bundleElement.Contents() ) rob@76: { rob@76: Init( bundleElement.Contents(), bundleElement.Size() ); rob@76: } rob@76: rob@76: rob@76: bool ReceivedMessage::AddressPatternIsUInt32() const rob@76: { rob@76: return (addressPattern_[0] == '\0'); rob@76: } rob@76: rob@76: rob@76: uint32 ReceivedMessage::AddressPatternAsUInt32() const rob@76: { rob@76: return ToUInt32( addressPattern_ ); rob@76: } rob@76: rob@76: rob@76: void ReceivedMessage::Init( const char *message, osc_bundle_element_size_t size ) rob@76: { rob@76: if( !IsValidElementSizeValue(size) ) rob@76: throw MalformedMessageException( "invalid message size" ); rob@76: rob@76: if( size == 0 ) rob@76: throw MalformedMessageException( "zero length messages not permitted" ); rob@76: rob@76: if( !IsMultipleOf4(size) ) rob@76: throw MalformedMessageException( "message size must be multiple of four" ); rob@76: rob@76: const char *end = message + size; rob@76: rob@76: typeTagsBegin_ = FindStr4End( addressPattern_, end ); rob@76: if( typeTagsBegin_ == 0 ){ rob@76: // address pattern was not terminated before end rob@76: throw MalformedMessageException( "unterminated address pattern" ); rob@76: } rob@76: rob@76: if( typeTagsBegin_ == end ){ rob@76: // message consists of only the address pattern - no arguments or type tags. rob@76: typeTagsBegin_ = 0; rob@76: typeTagsEnd_ = 0; rob@76: arguments_ = 0; rob@76: rob@76: }else{ rob@76: if( *typeTagsBegin_ != ',' ) rob@76: throw MalformedMessageException( "type tags not present" ); rob@76: rob@76: if( *(typeTagsBegin_ + 1) == '\0' ){ rob@76: // zero length type tags rob@76: typeTagsBegin_ = 0; rob@76: typeTagsEnd_ = 0; rob@76: arguments_ = 0; rob@76: rob@76: }else{ rob@76: // check that all arguments are present and well formed rob@76: rob@76: arguments_ = FindStr4End( typeTagsBegin_, end ); rob@76: if( arguments_ == 0 ){ rob@76: throw MalformedMessageException( "type tags were not terminated before end of message" ); rob@76: } rob@76: rob@76: ++typeTagsBegin_; // advance past initial ',' rob@76: rob@76: const char *typeTag = typeTagsBegin_; rob@76: const char *argument = arguments_; rob@76: unsigned int arrayLevel = 0; rob@76: rob@76: do{ rob@76: switch( *typeTag ){ rob@76: case TRUE_TYPE_TAG: rob@76: case FALSE_TYPE_TAG: rob@76: case NIL_TYPE_TAG: rob@76: case INFINITUM_TYPE_TAG: rob@76: // zero length rob@76: break; rob@76: rob@76: // [ Indicates the beginning of an array. The tags following are for rob@76: // data in the Array until a close brace tag is reached. rob@76: // ] Indicates the end of an array. rob@76: case ARRAY_BEGIN_TYPE_TAG: rob@76: ++arrayLevel; rob@76: // (zero length argument data) rob@76: break; rob@76: rob@76: case ARRAY_END_TYPE_TAG: rob@76: --arrayLevel; rob@76: // (zero length argument data) rob@76: break; rob@76: rob@76: case INT32_TYPE_TAG: rob@76: case FLOAT_TYPE_TAG: rob@76: case CHAR_TYPE_TAG: rob@76: case RGBA_COLOR_TYPE_TAG: rob@76: case MIDI_MESSAGE_TYPE_TAG: rob@76: rob@76: if( argument == end ) rob@76: throw MalformedMessageException( "arguments exceed message size" ); rob@76: argument += 4; rob@76: if( argument > end ) rob@76: throw MalformedMessageException( "arguments exceed message size" ); rob@76: break; rob@76: rob@76: case INT64_TYPE_TAG: rob@76: case TIME_TAG_TYPE_TAG: rob@76: case DOUBLE_TYPE_TAG: rob@76: rob@76: if( argument == end ) rob@76: throw MalformedMessageException( "arguments exceed message size" ); rob@76: argument += 8; rob@76: if( argument > end ) rob@76: throw MalformedMessageException( "arguments exceed message size" ); rob@76: break; rob@76: rob@76: case STRING_TYPE_TAG: rob@76: case SYMBOL_TYPE_TAG: rob@76: rob@76: if( argument == end ) rob@76: throw MalformedMessageException( "arguments exceed message size" ); rob@76: argument = FindStr4End( argument, end ); rob@76: if( argument == 0 ) rob@76: throw MalformedMessageException( "unterminated string argument" ); rob@76: break; rob@76: rob@76: case BLOB_TYPE_TAG: rob@76: { rob@76: if( argument + osc::OSC_SIZEOF_INT32 > end ) rob@76: MalformedMessageException( "arguments exceed message size" ); rob@76: rob@76: // treat blob size as an unsigned int for the purposes of this calculation rob@76: uint32 blobSize = ToUInt32( argument ); rob@76: argument = argument + osc::OSC_SIZEOF_INT32 + RoundUp4( blobSize ); rob@76: if( argument > end ) rob@76: MalformedMessageException( "arguments exceed message size" ); rob@76: } rob@76: break; rob@76: rob@76: default: rob@76: throw MalformedMessageException( "unknown type tag" ); rob@76: } rob@76: rob@76: }while( *++typeTag != '\0' ); rob@76: typeTagsEnd_ = typeTag; rob@76: rob@76: if( arrayLevel != 0 ) rob@76: throw MalformedMessageException( "array was not terminated before end of message (expected ']' end of array tag)" ); rob@76: } rob@76: rob@76: // These invariants should be guaranteed by the above code. rob@76: // we depend on them in the implementation of ArgumentCount() rob@76: #ifndef NDEBUG rob@76: std::ptrdiff_t argumentCount = typeTagsEnd_ - typeTagsBegin_; rob@76: assert( argumentCount >= 0 ); rob@76: assert( argumentCount <= OSC_INT32_MAX ); rob@76: #endif rob@76: } rob@76: } rob@76: rob@76: //------------------------------------------------------------------------------ rob@76: rob@76: ReceivedBundle::ReceivedBundle( const ReceivedPacket& packet ) rob@76: : elementCount_( 0 ) rob@76: { rob@76: Init( packet.Contents(), packet.Size() ); rob@76: } rob@76: rob@76: rob@76: ReceivedBundle::ReceivedBundle( const ReceivedBundleElement& bundleElement ) rob@76: : elementCount_( 0 ) rob@76: { rob@76: Init( bundleElement.Contents(), bundleElement.Size() ); rob@76: } rob@76: rob@76: rob@76: void ReceivedBundle::Init( const char *bundle, osc_bundle_element_size_t size ) rob@76: { rob@76: rob@76: if( !IsValidElementSizeValue(size) ) rob@76: throw MalformedBundleException( "invalid bundle size" ); rob@76: rob@76: if( size < 16 ) rob@76: throw MalformedBundleException( "packet too short for bundle" ); rob@76: rob@76: if( !IsMultipleOf4(size) ) rob@76: throw MalformedBundleException( "bundle size must be multiple of four" ); rob@76: rob@76: if( bundle[0] != '#' rob@76: || bundle[1] != 'b' rob@76: || bundle[2] != 'u' rob@76: || bundle[3] != 'n' rob@76: || bundle[4] != 'd' rob@76: || bundle[5] != 'l' rob@76: || bundle[6] != 'e' rob@76: || bundle[7] != '\0' ) rob@76: throw MalformedBundleException( "bad bundle address pattern" ); rob@76: rob@76: end_ = bundle + size; rob@76: rob@76: timeTag_ = bundle + 8; rob@76: rob@76: const char *p = timeTag_ + 8; rob@76: rob@76: while( p < end_ ){ rob@76: if( p + osc::OSC_SIZEOF_INT32 > end_ ) rob@76: throw MalformedBundleException( "packet too short for elementSize" ); rob@76: rob@76: // treat element size as an unsigned int for the purposes of this calculation rob@76: uint32 elementSize = ToUInt32( p ); rob@76: if( (elementSize & ((uint32)0x03)) != 0 ) rob@76: throw MalformedBundleException( "bundle element size must be multiple of four" ); rob@76: rob@76: p += osc::OSC_SIZEOF_INT32 + elementSize; rob@76: if( p > end_ ) rob@76: throw MalformedBundleException( "packet too short for bundle element" ); rob@76: rob@76: ++elementCount_; rob@76: } rob@76: rob@76: if( p != end_ ) rob@76: throw MalformedBundleException( "bundle contents " ); rob@76: } rob@76: rob@76: rob@76: uint64 ReceivedBundle::TimeTag() const rob@76: { rob@76: return ToUInt64( timeTag_ ); rob@76: } rob@76: rob@76: rob@76: } // namespace osc rob@76: