Mercurial > hg > nodescore
diff oscpack/osc/OscReceivedElements.h @ 76:0ae87af84e2f
added oscgroups
author | Rob Canning <rob@foo.net> |
---|---|
date | Sun, 13 Jul 2014 10:07:41 +0100 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/oscpack/osc/OscReceivedElements.h Sun Jul 13 10:07:41 2014 +0100 @@ -0,0 +1,548 @@ +/* + oscpack -- Open Sound Control (OSC) packet manipulation library + http://www.rossbencina.com/code/oscpack + + Copyright (c) 2004-2013 Ross Bencina <rossb@audiomulch.com> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +/* + The text above constitutes the entire oscpack license; however, + the oscpack developer(s) also make the following non-binding requests: + + Any person wishing to distribute modifications to the Software is + requested to send the modifications to the original developer so that + they can be incorporated into the canonical version. It is also + requested that these non-binding requests be included whenever the + above license is reproduced. +*/ +#ifndef INCLUDED_OSCPACK_OSCRECEIVEDELEMENTS_H +#define INCLUDED_OSCPACK_OSCRECEIVEDELEMENTS_H + +#include <cassert> +#include <cstddef> +#include <cstring> // size_t + +#include "OscTypes.h" +#include "OscException.h" + + +namespace osc{ + + +class MalformedPacketException : public Exception{ +public: + MalformedPacketException( const char *w="malformed packet" ) + : Exception( w ) {} +}; + +class MalformedMessageException : public Exception{ +public: + MalformedMessageException( const char *w="malformed message" ) + : Exception( w ) {} +}; + +class MalformedBundleException : public Exception{ +public: + MalformedBundleException( const char *w="malformed bundle" ) + : Exception( w ) {} +}; + +class WrongArgumentTypeException : public Exception{ +public: + WrongArgumentTypeException( const char *w="wrong argument type" ) + : Exception( w ) {} +}; + +class MissingArgumentException : public Exception{ +public: + MissingArgumentException( const char *w="missing argument" ) + : Exception( w ) {} +}; + +class ExcessArgumentException : public Exception{ +public: + ExcessArgumentException( const char *w="too many arguments" ) + : Exception( w ) {} +}; + + +class ReceivedPacket{ +public: + // Although the OSC spec is not entirely clear on this, we only support + // packets up to 0x7FFFFFFC bytes long (the maximum 4-byte aligned value + // representable by an int32). An exception will be raised if you pass a + // larger value to the ReceivedPacket() constructor. + + ReceivedPacket( const char *contents, osc_bundle_element_size_t size ) + : contents_( contents ) + , size_( ValidateSize(size) ) {} + + ReceivedPacket( const char *contents, std::size_t size ) + : contents_( contents ) + , size_( ValidateSize( (osc_bundle_element_size_t)size ) ) {} + +#if !(defined(__x86_64__) || defined(_M_X64)) + ReceivedPacket( const char *contents, int size ) + : contents_( contents ) + , size_( ValidateSize( (osc_bundle_element_size_t)size ) ) {} +#endif + + bool IsMessage() const { return !IsBundle(); } + bool IsBundle() const; + + osc_bundle_element_size_t Size() const { return size_; } + const char *Contents() const { return contents_; } + +private: + const char *contents_; + osc_bundle_element_size_t size_; + + static osc_bundle_element_size_t ValidateSize( osc_bundle_element_size_t size ) + { + // sanity check integer types declared in OscTypes.h + // you'll need to fix OscTypes.h if any of these asserts fail + assert( sizeof(osc::int32) == 4 ); + assert( sizeof(osc::uint32) == 4 ); + assert( sizeof(osc::int64) == 8 ); + assert( sizeof(osc::uint64) == 8 ); + + if( !IsValidElementSizeValue(size) ) + throw MalformedPacketException( "invalid packet size" ); + + if( size == 0 ) + throw MalformedPacketException( "zero length elements not permitted" ); + + if( !IsMultipleOf4(size) ) + throw MalformedPacketException( "element size must be multiple of four" ); + + return size; + } +}; + + +class ReceivedBundleElement{ +public: + ReceivedBundleElement( const char *sizePtr ) + : sizePtr_( sizePtr ) {} + + friend class ReceivedBundleElementIterator; + + bool IsMessage() const { return !IsBundle(); } + bool IsBundle() const; + + osc_bundle_element_size_t Size() const; + const char *Contents() const { return sizePtr_ + osc::OSC_SIZEOF_INT32; } + +private: + const char *sizePtr_; +}; + + +class ReceivedBundleElementIterator{ +public: + ReceivedBundleElementIterator( const char *sizePtr ) + : value_( sizePtr ) {} + + ReceivedBundleElementIterator operator++() + { + Advance(); + return *this; + } + + ReceivedBundleElementIterator operator++(int) + { + ReceivedBundleElementIterator old( *this ); + Advance(); + return old; + } + + const ReceivedBundleElement& operator*() const { return value_; } + + const ReceivedBundleElement* operator->() const { return &value_; } + + friend bool operator==(const ReceivedBundleElementIterator& lhs, + const ReceivedBundleElementIterator& rhs ); + +private: + ReceivedBundleElement value_; + + void Advance() { value_.sizePtr_ = value_.Contents() + value_.Size(); } + + bool IsEqualTo( const ReceivedBundleElementIterator& rhs ) const + { + return value_.sizePtr_ == rhs.value_.sizePtr_; + } +}; + +inline bool operator==(const ReceivedBundleElementIterator& lhs, + const ReceivedBundleElementIterator& rhs ) +{ + return lhs.IsEqualTo( rhs ); +} + +inline bool operator!=(const ReceivedBundleElementIterator& lhs, + const ReceivedBundleElementIterator& rhs ) +{ + return !( lhs == rhs ); +} + + +class ReceivedMessageArgument{ +public: + ReceivedMessageArgument( const char *typeTagPtr, const char *argumentPtr ) + : typeTagPtr_( typeTagPtr ) + , argumentPtr_( argumentPtr ) {} + + friend class ReceivedMessageArgumentIterator; + + char TypeTag() const { return *typeTagPtr_; } + + // the unchecked methods below don't check whether the argument actually + // is of the specified type. they should only be used if you've already + // checked the type tag or the associated IsType() method. + + bool IsBool() const + { return *typeTagPtr_ == TRUE_TYPE_TAG || *typeTagPtr_ == FALSE_TYPE_TAG; } + bool AsBool() const; + bool AsBoolUnchecked() const; + + bool IsNil() const { return *typeTagPtr_ == NIL_TYPE_TAG; } + bool IsInfinitum() const { return *typeTagPtr_ == INFINITUM_TYPE_TAG; } + + bool IsInt32() const { return *typeTagPtr_ == INT32_TYPE_TAG; } + int32 AsInt32() const; + int32 AsInt32Unchecked() const; + + bool IsFloat() const { return *typeTagPtr_ == FLOAT_TYPE_TAG; } + float AsFloat() const; + float AsFloatUnchecked() const; + + bool IsChar() const { return *typeTagPtr_ == CHAR_TYPE_TAG; } + char AsChar() const; + char AsCharUnchecked() const; + + bool IsRgbaColor() const { return *typeTagPtr_ == RGBA_COLOR_TYPE_TAG; } + uint32 AsRgbaColor() const; + uint32 AsRgbaColorUnchecked() const; + + bool IsMidiMessage() const { return *typeTagPtr_ == MIDI_MESSAGE_TYPE_TAG; } + uint32 AsMidiMessage() const; + uint32 AsMidiMessageUnchecked() const; + + bool IsInt64() const { return *typeTagPtr_ == INT64_TYPE_TAG; } + int64 AsInt64() const; + int64 AsInt64Unchecked() const; + + bool IsTimeTag() const { return *typeTagPtr_ == TIME_TAG_TYPE_TAG; } + uint64 AsTimeTag() const; + uint64 AsTimeTagUnchecked() const; + + bool IsDouble() const { return *typeTagPtr_ == DOUBLE_TYPE_TAG; } + double AsDouble() const; + double AsDoubleUnchecked() const; + + bool IsString() const { return *typeTagPtr_ == STRING_TYPE_TAG; } + const char* AsString() const; + const char* AsStringUnchecked() const { return argumentPtr_; } + + bool IsSymbol() const { return *typeTagPtr_ == SYMBOL_TYPE_TAG; } + const char* AsSymbol() const; + const char* AsSymbolUnchecked() const { return argumentPtr_; } + + bool IsBlob() const { return *typeTagPtr_ == BLOB_TYPE_TAG; } + void AsBlob( const void*& data, osc_bundle_element_size_t& size ) const; + void AsBlobUnchecked( const void*& data, osc_bundle_element_size_t& size ) const; + + bool IsArrayBegin() const { return *typeTagPtr_ == ARRAY_BEGIN_TYPE_TAG; } + bool IsArrayEnd() const { return *typeTagPtr_ == ARRAY_END_TYPE_TAG; } + // Calculate the number of top-level items in the array. Nested arrays count as one item. + // Only valid at array start. Will throw an exception if IsArrayStart() == false. + std::size_t ComputeArrayItemCount() const; + +private: + const char *typeTagPtr_; + const char *argumentPtr_; +}; + + +class ReceivedMessageArgumentIterator{ +public: + ReceivedMessageArgumentIterator( const char *typeTags, const char *arguments ) + : value_( typeTags, arguments ) {} + + ReceivedMessageArgumentIterator operator++() + { + Advance(); + return *this; + } + + ReceivedMessageArgumentIterator operator++(int) + { + ReceivedMessageArgumentIterator old( *this ); + Advance(); + return old; + } + + const ReceivedMessageArgument& operator*() const { return value_; } + + const ReceivedMessageArgument* operator->() const { return &value_; } + + friend bool operator==(const ReceivedMessageArgumentIterator& lhs, + const ReceivedMessageArgumentIterator& rhs ); + +private: + ReceivedMessageArgument value_; + + void Advance(); + + bool IsEqualTo( const ReceivedMessageArgumentIterator& rhs ) const + { + return value_.typeTagPtr_ == rhs.value_.typeTagPtr_; + } +}; + +inline bool operator==(const ReceivedMessageArgumentIterator& lhs, + const ReceivedMessageArgumentIterator& rhs ) +{ + return lhs.IsEqualTo( rhs ); +} + +inline bool operator!=(const ReceivedMessageArgumentIterator& lhs, + const ReceivedMessageArgumentIterator& rhs ) +{ + return !( lhs == rhs ); +} + + +class ReceivedMessageArgumentStream{ + friend class ReceivedMessage; + ReceivedMessageArgumentStream( const ReceivedMessageArgumentIterator& begin, + const ReceivedMessageArgumentIterator& end ) + : p_( begin ) + , end_( end ) {} + + ReceivedMessageArgumentIterator p_, end_; + +public: + + // end of stream + bool Eos() const { return p_ == end_; } + + ReceivedMessageArgumentStream& operator>>( bool& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + rhs = (*p_++).AsBool(); + return *this; + } + + // not sure if it would be useful to stream Nil and Infinitum + // for now it's not possible + // same goes for array boundaries + + ReceivedMessageArgumentStream& operator>>( int32& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + rhs = (*p_++).AsInt32(); + return *this; + } + + ReceivedMessageArgumentStream& operator>>( float& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + rhs = (*p_++).AsFloat(); + return *this; + } + + ReceivedMessageArgumentStream& operator>>( char& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + rhs = (*p_++).AsChar(); + return *this; + } + + ReceivedMessageArgumentStream& operator>>( RgbaColor& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + rhs.value = (*p_++).AsRgbaColor(); + return *this; + } + + ReceivedMessageArgumentStream& operator>>( MidiMessage& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + rhs.value = (*p_++).AsMidiMessage(); + return *this; + } + + ReceivedMessageArgumentStream& operator>>( int64& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + rhs = (*p_++).AsInt64(); + return *this; + } + + ReceivedMessageArgumentStream& operator>>( TimeTag& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + rhs.value = (*p_++).AsTimeTag(); + return *this; + } + + ReceivedMessageArgumentStream& operator>>( double& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + rhs = (*p_++).AsDouble(); + return *this; + } + + ReceivedMessageArgumentStream& operator>>( Blob& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + (*p_++).AsBlob( rhs.data, rhs.size ); + return *this; + } + + ReceivedMessageArgumentStream& operator>>( const char*& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + rhs = (*p_++).AsString(); + return *this; + } + + ReceivedMessageArgumentStream& operator>>( Symbol& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + rhs.value = (*p_++).AsSymbol(); + return *this; + } + + ReceivedMessageArgumentStream& operator>>( MessageTerminator& rhs ) + { + (void) rhs; // suppress unused parameter warning + + if( !Eos() ) + throw ExcessArgumentException(); + + return *this; + } +}; + + +class ReceivedMessage{ + void Init( const char *bundle, osc_bundle_element_size_t size ); +public: + explicit ReceivedMessage( const ReceivedPacket& packet ); + explicit ReceivedMessage( const ReceivedBundleElement& bundleElement ); + + const char *AddressPattern() const { return addressPattern_; } + + // Support for non-standard SuperCollider integer address patterns: + bool AddressPatternIsUInt32() const; + uint32 AddressPatternAsUInt32() const; + + uint32 ArgumentCount() const { return static_cast<uint32>(typeTagsEnd_ - typeTagsBegin_); } + + const char *TypeTags() const { return typeTagsBegin_; } + + + typedef ReceivedMessageArgumentIterator const_iterator; + + ReceivedMessageArgumentIterator ArgumentsBegin() const + { + return ReceivedMessageArgumentIterator( typeTagsBegin_, arguments_ ); + } + + ReceivedMessageArgumentIterator ArgumentsEnd() const + { + return ReceivedMessageArgumentIterator( typeTagsEnd_, 0 ); + } + + ReceivedMessageArgumentStream ArgumentStream() const + { + return ReceivedMessageArgumentStream( ArgumentsBegin(), ArgumentsEnd() ); + } + +private: + const char *addressPattern_; + const char *typeTagsBegin_; + const char *typeTagsEnd_; + const char *arguments_; +}; + + +class ReceivedBundle{ + void Init( const char *message, osc_bundle_element_size_t size ); +public: + explicit ReceivedBundle( const ReceivedPacket& packet ); + explicit ReceivedBundle( const ReceivedBundleElement& bundleElement ); + + uint64 TimeTag() const; + + uint32 ElementCount() const { return elementCount_; } + + typedef ReceivedBundleElementIterator const_iterator; + + ReceivedBundleElementIterator ElementsBegin() const + { + return ReceivedBundleElementIterator( timeTag_ + 8 ); + } + + ReceivedBundleElementIterator ElementsEnd() const + { + return ReceivedBundleElementIterator( end_ ); + } + +private: + const char *timeTag_; + const char *end_; + uint32 elementCount_; +}; + + +} // namespace osc + + +#endif /* INCLUDED_OSCPACK_OSCRECEIVEDELEMENTS_H */