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: #ifndef INCLUDED_OSCPACK_OSCRECEIVEDELEMENTS_H rob@76: #define INCLUDED_OSCPACK_OSCRECEIVEDELEMENTS_H rob@76: rob@76: #include rob@76: #include rob@76: #include // size_t rob@76: rob@76: #include "OscTypes.h" rob@76: #include "OscException.h" rob@76: rob@76: rob@76: namespace osc{ rob@76: rob@76: rob@76: class MalformedPacketException : public Exception{ rob@76: public: rob@76: MalformedPacketException( const char *w="malformed packet" ) rob@76: : Exception( w ) {} rob@76: }; rob@76: rob@76: class MalformedMessageException : public Exception{ rob@76: public: rob@76: MalformedMessageException( const char *w="malformed message" ) rob@76: : Exception( w ) {} rob@76: }; rob@76: rob@76: class MalformedBundleException : public Exception{ rob@76: public: rob@76: MalformedBundleException( const char *w="malformed bundle" ) rob@76: : Exception( w ) {} rob@76: }; rob@76: rob@76: class WrongArgumentTypeException : public Exception{ rob@76: public: rob@76: WrongArgumentTypeException( const char *w="wrong argument type" ) rob@76: : Exception( w ) {} rob@76: }; rob@76: rob@76: class MissingArgumentException : public Exception{ rob@76: public: rob@76: MissingArgumentException( const char *w="missing argument" ) rob@76: : Exception( w ) {} rob@76: }; rob@76: rob@76: class ExcessArgumentException : public Exception{ rob@76: public: rob@76: ExcessArgumentException( const char *w="too many arguments" ) rob@76: : Exception( w ) {} rob@76: }; rob@76: rob@76: rob@76: class ReceivedPacket{ rob@76: public: rob@76: // Although the OSC spec is not entirely clear on this, we only support rob@76: // packets up to 0x7FFFFFFC bytes long (the maximum 4-byte aligned value rob@76: // representable by an int32). An exception will be raised if you pass a rob@76: // larger value to the ReceivedPacket() constructor. rob@76: rob@76: ReceivedPacket( const char *contents, osc_bundle_element_size_t size ) rob@76: : contents_( contents ) rob@76: , size_( ValidateSize(size) ) {} rob@76: rob@76: ReceivedPacket( const char *contents, std::size_t size ) rob@76: : contents_( contents ) rob@76: , size_( ValidateSize( (osc_bundle_element_size_t)size ) ) {} rob@76: rob@76: #if !(defined(__x86_64__) || defined(_M_X64)) rob@76: ReceivedPacket( const char *contents, int size ) rob@76: : contents_( contents ) rob@76: , size_( ValidateSize( (osc_bundle_element_size_t)size ) ) {} rob@76: #endif rob@76: rob@76: bool IsMessage() const { return !IsBundle(); } rob@76: bool IsBundle() const; rob@76: rob@76: osc_bundle_element_size_t Size() const { return size_; } rob@76: const char *Contents() const { return contents_; } rob@76: rob@76: private: rob@76: const char *contents_; rob@76: osc_bundle_element_size_t size_; rob@76: rob@76: static osc_bundle_element_size_t ValidateSize( osc_bundle_element_size_t size ) rob@76: { rob@76: // sanity check integer types declared in OscTypes.h rob@76: // you'll need to fix OscTypes.h if any of these asserts fail rob@76: assert( sizeof(osc::int32) == 4 ); rob@76: assert( sizeof(osc::uint32) == 4 ); rob@76: assert( sizeof(osc::int64) == 8 ); rob@76: assert( sizeof(osc::uint64) == 8 ); rob@76: rob@76: if( !IsValidElementSizeValue(size) ) rob@76: throw MalformedPacketException( "invalid packet size" ); rob@76: rob@76: if( size == 0 ) rob@76: throw MalformedPacketException( "zero length elements not permitted" ); rob@76: rob@76: if( !IsMultipleOf4(size) ) rob@76: throw MalformedPacketException( "element size must be multiple of four" ); rob@76: rob@76: return size; rob@76: } rob@76: }; rob@76: rob@76: rob@76: class ReceivedBundleElement{ rob@76: public: rob@76: ReceivedBundleElement( const char *sizePtr ) rob@76: : sizePtr_( sizePtr ) {} rob@76: rob@76: friend class ReceivedBundleElementIterator; rob@76: rob@76: bool IsMessage() const { return !IsBundle(); } rob@76: bool IsBundle() const; rob@76: rob@76: osc_bundle_element_size_t Size() const; rob@76: const char *Contents() const { return sizePtr_ + osc::OSC_SIZEOF_INT32; } rob@76: rob@76: private: rob@76: const char *sizePtr_; rob@76: }; rob@76: rob@76: rob@76: class ReceivedBundleElementIterator{ rob@76: public: rob@76: ReceivedBundleElementIterator( const char *sizePtr ) rob@76: : value_( sizePtr ) {} rob@76: rob@76: ReceivedBundleElementIterator operator++() rob@76: { rob@76: Advance(); rob@76: return *this; rob@76: } rob@76: rob@76: ReceivedBundleElementIterator operator++(int) rob@76: { rob@76: ReceivedBundleElementIterator old( *this ); rob@76: Advance(); rob@76: return old; rob@76: } rob@76: rob@76: const ReceivedBundleElement& operator*() const { return value_; } rob@76: rob@76: const ReceivedBundleElement* operator->() const { return &value_; } rob@76: rob@76: friend bool operator==(const ReceivedBundleElementIterator& lhs, rob@76: const ReceivedBundleElementIterator& rhs ); rob@76: rob@76: private: rob@76: ReceivedBundleElement value_; rob@76: rob@76: void Advance() { value_.sizePtr_ = value_.Contents() + value_.Size(); } rob@76: rob@76: bool IsEqualTo( const ReceivedBundleElementIterator& rhs ) const rob@76: { rob@76: return value_.sizePtr_ == rhs.value_.sizePtr_; rob@76: } rob@76: }; rob@76: rob@76: inline bool operator==(const ReceivedBundleElementIterator& lhs, rob@76: const ReceivedBundleElementIterator& rhs ) rob@76: { rob@76: return lhs.IsEqualTo( rhs ); rob@76: } rob@76: rob@76: inline bool operator!=(const ReceivedBundleElementIterator& lhs, rob@76: const ReceivedBundleElementIterator& rhs ) rob@76: { rob@76: return !( lhs == rhs ); rob@76: } rob@76: rob@76: rob@76: class ReceivedMessageArgument{ rob@76: public: rob@76: ReceivedMessageArgument( const char *typeTagPtr, const char *argumentPtr ) rob@76: : typeTagPtr_( typeTagPtr ) rob@76: , argumentPtr_( argumentPtr ) {} rob@76: rob@76: friend class ReceivedMessageArgumentIterator; rob@76: rob@76: char TypeTag() const { return *typeTagPtr_; } rob@76: rob@76: // the unchecked methods below don't check whether the argument actually rob@76: // is of the specified type. they should only be used if you've already rob@76: // checked the type tag or the associated IsType() method. rob@76: rob@76: bool IsBool() const rob@76: { return *typeTagPtr_ == TRUE_TYPE_TAG || *typeTagPtr_ == FALSE_TYPE_TAG; } rob@76: bool AsBool() const; rob@76: bool AsBoolUnchecked() const; rob@76: rob@76: bool IsNil() const { return *typeTagPtr_ == NIL_TYPE_TAG; } rob@76: bool IsInfinitum() const { return *typeTagPtr_ == INFINITUM_TYPE_TAG; } rob@76: rob@76: bool IsInt32() const { return *typeTagPtr_ == INT32_TYPE_TAG; } rob@76: int32 AsInt32() const; rob@76: int32 AsInt32Unchecked() const; rob@76: rob@76: bool IsFloat() const { return *typeTagPtr_ == FLOAT_TYPE_TAG; } rob@76: float AsFloat() const; rob@76: float AsFloatUnchecked() const; rob@76: rob@76: bool IsChar() const { return *typeTagPtr_ == CHAR_TYPE_TAG; } rob@76: char AsChar() const; rob@76: char AsCharUnchecked() const; rob@76: rob@76: bool IsRgbaColor() const { return *typeTagPtr_ == RGBA_COLOR_TYPE_TAG; } rob@76: uint32 AsRgbaColor() const; rob@76: uint32 AsRgbaColorUnchecked() const; rob@76: rob@76: bool IsMidiMessage() const { return *typeTagPtr_ == MIDI_MESSAGE_TYPE_TAG; } rob@76: uint32 AsMidiMessage() const; rob@76: uint32 AsMidiMessageUnchecked() const; rob@76: rob@76: bool IsInt64() const { return *typeTagPtr_ == INT64_TYPE_TAG; } rob@76: int64 AsInt64() const; rob@76: int64 AsInt64Unchecked() const; rob@76: rob@76: bool IsTimeTag() const { return *typeTagPtr_ == TIME_TAG_TYPE_TAG; } rob@76: uint64 AsTimeTag() const; rob@76: uint64 AsTimeTagUnchecked() const; rob@76: rob@76: bool IsDouble() const { return *typeTagPtr_ == DOUBLE_TYPE_TAG; } rob@76: double AsDouble() const; rob@76: double AsDoubleUnchecked() const; rob@76: rob@76: bool IsString() const { return *typeTagPtr_ == STRING_TYPE_TAG; } rob@76: const char* AsString() const; rob@76: const char* AsStringUnchecked() const { return argumentPtr_; } rob@76: rob@76: bool IsSymbol() const { return *typeTagPtr_ == SYMBOL_TYPE_TAG; } rob@76: const char* AsSymbol() const; rob@76: const char* AsSymbolUnchecked() const { return argumentPtr_; } rob@76: rob@76: bool IsBlob() const { return *typeTagPtr_ == BLOB_TYPE_TAG; } rob@76: void AsBlob( const void*& data, osc_bundle_element_size_t& size ) const; rob@76: void AsBlobUnchecked( const void*& data, osc_bundle_element_size_t& size ) const; rob@76: rob@76: bool IsArrayBegin() const { return *typeTagPtr_ == ARRAY_BEGIN_TYPE_TAG; } rob@76: bool IsArrayEnd() const { return *typeTagPtr_ == ARRAY_END_TYPE_TAG; } rob@76: // Calculate the number of top-level items in the array. Nested arrays count as one item. rob@76: // Only valid at array start. Will throw an exception if IsArrayStart() == false. rob@76: std::size_t ComputeArrayItemCount() const; rob@76: rob@76: private: rob@76: const char *typeTagPtr_; rob@76: const char *argumentPtr_; rob@76: }; rob@76: rob@76: rob@76: class ReceivedMessageArgumentIterator{ rob@76: public: rob@76: ReceivedMessageArgumentIterator( const char *typeTags, const char *arguments ) rob@76: : value_( typeTags, arguments ) {} rob@76: rob@76: ReceivedMessageArgumentIterator operator++() rob@76: { rob@76: Advance(); rob@76: return *this; rob@76: } rob@76: rob@76: ReceivedMessageArgumentIterator operator++(int) rob@76: { rob@76: ReceivedMessageArgumentIterator old( *this ); rob@76: Advance(); rob@76: return old; rob@76: } rob@76: rob@76: const ReceivedMessageArgument& operator*() const { return value_; } rob@76: rob@76: const ReceivedMessageArgument* operator->() const { return &value_; } rob@76: rob@76: friend bool operator==(const ReceivedMessageArgumentIterator& lhs, rob@76: const ReceivedMessageArgumentIterator& rhs ); rob@76: rob@76: private: rob@76: ReceivedMessageArgument value_; rob@76: rob@76: void Advance(); rob@76: rob@76: bool IsEqualTo( const ReceivedMessageArgumentIterator& rhs ) const rob@76: { rob@76: return value_.typeTagPtr_ == rhs.value_.typeTagPtr_; rob@76: } rob@76: }; rob@76: rob@76: inline bool operator==(const ReceivedMessageArgumentIterator& lhs, rob@76: const ReceivedMessageArgumentIterator& rhs ) rob@76: { rob@76: return lhs.IsEqualTo( rhs ); rob@76: } rob@76: rob@76: inline bool operator!=(const ReceivedMessageArgumentIterator& lhs, rob@76: const ReceivedMessageArgumentIterator& rhs ) rob@76: { rob@76: return !( lhs == rhs ); rob@76: } rob@76: rob@76: rob@76: class ReceivedMessageArgumentStream{ rob@76: friend class ReceivedMessage; rob@76: ReceivedMessageArgumentStream( const ReceivedMessageArgumentIterator& begin, rob@76: const ReceivedMessageArgumentIterator& end ) rob@76: : p_( begin ) rob@76: , end_( end ) {} rob@76: rob@76: ReceivedMessageArgumentIterator p_, end_; rob@76: rob@76: public: rob@76: rob@76: // end of stream rob@76: bool Eos() const { return p_ == end_; } rob@76: rob@76: ReceivedMessageArgumentStream& operator>>( bool& rhs ) rob@76: { rob@76: if( Eos() ) rob@76: throw MissingArgumentException(); rob@76: rob@76: rhs = (*p_++).AsBool(); rob@76: return *this; rob@76: } rob@76: rob@76: // not sure if it would be useful to stream Nil and Infinitum rob@76: // for now it's not possible rob@76: // same goes for array boundaries rob@76: rob@76: ReceivedMessageArgumentStream& operator>>( int32& rhs ) rob@76: { rob@76: if( Eos() ) rob@76: throw MissingArgumentException(); rob@76: rob@76: rhs = (*p_++).AsInt32(); rob@76: return *this; rob@76: } rob@76: rob@76: ReceivedMessageArgumentStream& operator>>( float& rhs ) rob@76: { rob@76: if( Eos() ) rob@76: throw MissingArgumentException(); rob@76: rob@76: rhs = (*p_++).AsFloat(); rob@76: return *this; rob@76: } rob@76: rob@76: ReceivedMessageArgumentStream& operator>>( char& rhs ) rob@76: { rob@76: if( Eos() ) rob@76: throw MissingArgumentException(); rob@76: rob@76: rhs = (*p_++).AsChar(); rob@76: return *this; rob@76: } rob@76: rob@76: ReceivedMessageArgumentStream& operator>>( RgbaColor& rhs ) rob@76: { rob@76: if( Eos() ) rob@76: throw MissingArgumentException(); rob@76: rob@76: rhs.value = (*p_++).AsRgbaColor(); rob@76: return *this; rob@76: } rob@76: rob@76: ReceivedMessageArgumentStream& operator>>( MidiMessage& rhs ) rob@76: { rob@76: if( Eos() ) rob@76: throw MissingArgumentException(); rob@76: rob@76: rhs.value = (*p_++).AsMidiMessage(); rob@76: return *this; rob@76: } rob@76: rob@76: ReceivedMessageArgumentStream& operator>>( int64& rhs ) rob@76: { rob@76: if( Eos() ) rob@76: throw MissingArgumentException(); rob@76: rob@76: rhs = (*p_++).AsInt64(); rob@76: return *this; rob@76: } rob@76: rob@76: ReceivedMessageArgumentStream& operator>>( TimeTag& rhs ) rob@76: { rob@76: if( Eos() ) rob@76: throw MissingArgumentException(); rob@76: rob@76: rhs.value = (*p_++).AsTimeTag(); rob@76: return *this; rob@76: } rob@76: rob@76: ReceivedMessageArgumentStream& operator>>( double& rhs ) rob@76: { rob@76: if( Eos() ) rob@76: throw MissingArgumentException(); rob@76: rob@76: rhs = (*p_++).AsDouble(); rob@76: return *this; rob@76: } rob@76: rob@76: ReceivedMessageArgumentStream& operator>>( Blob& rhs ) rob@76: { rob@76: if( Eos() ) rob@76: throw MissingArgumentException(); rob@76: rob@76: (*p_++).AsBlob( rhs.data, rhs.size ); rob@76: return *this; rob@76: } rob@76: rob@76: ReceivedMessageArgumentStream& operator>>( const char*& rhs ) rob@76: { rob@76: if( Eos() ) rob@76: throw MissingArgumentException(); rob@76: rob@76: rhs = (*p_++).AsString(); rob@76: return *this; rob@76: } rob@76: rob@76: ReceivedMessageArgumentStream& operator>>( Symbol& rhs ) rob@76: { rob@76: if( Eos() ) rob@76: throw MissingArgumentException(); rob@76: rob@76: rhs.value = (*p_++).AsSymbol(); rob@76: return *this; rob@76: } rob@76: rob@76: ReceivedMessageArgumentStream& operator>>( MessageTerminator& rhs ) rob@76: { rob@76: (void) rhs; // suppress unused parameter warning rob@76: rob@76: if( !Eos() ) rob@76: throw ExcessArgumentException(); rob@76: rob@76: return *this; rob@76: } rob@76: }; rob@76: rob@76: rob@76: class ReceivedMessage{ rob@76: void Init( const char *bundle, osc_bundle_element_size_t size ); rob@76: public: rob@76: explicit ReceivedMessage( const ReceivedPacket& packet ); rob@76: explicit ReceivedMessage( const ReceivedBundleElement& bundleElement ); rob@76: rob@76: const char *AddressPattern() const { return addressPattern_; } rob@76: rob@76: // Support for non-standard SuperCollider integer address patterns: rob@76: bool AddressPatternIsUInt32() const; rob@76: uint32 AddressPatternAsUInt32() const; rob@76: rob@76: uint32 ArgumentCount() const { return static_cast(typeTagsEnd_ - typeTagsBegin_); } rob@76: rob@76: const char *TypeTags() const { return typeTagsBegin_; } rob@76: rob@76: rob@76: typedef ReceivedMessageArgumentIterator const_iterator; rob@76: rob@76: ReceivedMessageArgumentIterator ArgumentsBegin() const rob@76: { rob@76: return ReceivedMessageArgumentIterator( typeTagsBegin_, arguments_ ); rob@76: } rob@76: rob@76: ReceivedMessageArgumentIterator ArgumentsEnd() const rob@76: { rob@76: return ReceivedMessageArgumentIterator( typeTagsEnd_, 0 ); rob@76: } rob@76: rob@76: ReceivedMessageArgumentStream ArgumentStream() const rob@76: { rob@76: return ReceivedMessageArgumentStream( ArgumentsBegin(), ArgumentsEnd() ); rob@76: } rob@76: rob@76: private: rob@76: const char *addressPattern_; rob@76: const char *typeTagsBegin_; rob@76: const char *typeTagsEnd_; rob@76: const char *arguments_; rob@76: }; rob@76: rob@76: rob@76: class ReceivedBundle{ rob@76: void Init( const char *message, osc_bundle_element_size_t size ); rob@76: public: rob@76: explicit ReceivedBundle( const ReceivedPacket& packet ); rob@76: explicit ReceivedBundle( const ReceivedBundleElement& bundleElement ); rob@76: rob@76: uint64 TimeTag() const; rob@76: rob@76: uint32 ElementCount() const { return elementCount_; } rob@76: rob@76: typedef ReceivedBundleElementIterator const_iterator; rob@76: rob@76: ReceivedBundleElementIterator ElementsBegin() const rob@76: { rob@76: return ReceivedBundleElementIterator( timeTag_ + 8 ); rob@76: } rob@76: rob@76: ReceivedBundleElementIterator ElementsEnd() const rob@76: { rob@76: return ReceivedBundleElementIterator( end_ ); rob@76: } rob@76: rob@76: private: rob@76: const char *timeTag_; rob@76: const char *end_; rob@76: uint32 elementCount_; rob@76: }; rob@76: rob@76: rob@76: } // namespace osc rob@76: rob@76: rob@76: #endif /* INCLUDED_OSCPACK_OSCRECEIVEDELEMENTS_H */