diff oscpack/osc/OscReceivedElements.cpp @ 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.cpp	Sun Jul 13 10:07:41 2014 +0100
@@ -0,0 +1,796 @@
+/*
+	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.
+*/
+#include "OscReceivedElements.h"
+
+#include "OscHostEndianness.h"
+
+#include <cstddef> // ptrdiff_t
+
+namespace osc{
+
+
+// return the first 4 byte boundary after the end of a str4
+// be careful about calling this version if you don't know whether
+// the string is terminated correctly.
+static inline const char* FindStr4End( const char *p )
+{
+	if( p[0] == '\0' )    // special case for SuperCollider integer address pattern
+		return p + 4;
+
+    p += 3;
+
+    while( *p )
+        p += 4;
+
+    return p + 1;
+}
+
+
+// return the first 4 byte boundary after the end of a str4
+// returns 0 if p == end or if the string is unterminated
+static inline const char* FindStr4End( const char *p, const char *end )
+{
+    if( p >= end )
+        return 0;
+
+	if( p[0] == '\0' )    // special case for SuperCollider integer address pattern
+		return p + 4;
+
+    p += 3;
+    end -= 1;
+
+    while( p < end && *p )
+        p += 4;
+
+    if( *p )
+        return 0;
+    else
+        return p + 1;
+}
+
+
+// round up to the next highest multiple of 4. unless x is already a multiple of 4
+static inline uint32 RoundUp4( uint32 x ) 
+{
+    return (x + 3) & ~((uint32)0x03);
+}
+
+
+static inline int32 ToInt32( const char *p )
+{
+#ifdef OSC_HOST_LITTLE_ENDIAN
+    union{
+        osc::int32 i;
+        char c[4];
+    } u;
+
+    u.c[0] = p[3];
+    u.c[1] = p[2];
+    u.c[2] = p[1];
+    u.c[3] = p[0];
+
+    return u.i;
+#else
+	return *(int32*)p;
+#endif
+}
+
+
+static inline uint32 ToUInt32( const char *p )
+{
+#ifdef OSC_HOST_LITTLE_ENDIAN
+    union{
+        osc::uint32 i;
+        char c[4];
+    } u;
+
+    u.c[0] = p[3];
+    u.c[1] = p[2];
+    u.c[2] = p[1];
+    u.c[3] = p[0];
+
+    return u.i;
+#else
+	return *(uint32*)p;
+#endif
+}
+
+
+static inline int64 ToInt64( const char *p )
+{
+#ifdef OSC_HOST_LITTLE_ENDIAN
+    union{
+        osc::int64 i;
+        char c[8];
+    } u;
+
+    u.c[0] = p[7];
+    u.c[1] = p[6];
+    u.c[2] = p[5];
+    u.c[3] = p[4];
+    u.c[4] = p[3];
+    u.c[5] = p[2];
+    u.c[6] = p[1];
+    u.c[7] = p[0];
+
+    return u.i;
+#else
+	return *(int64*)p;
+#endif
+}
+
+
+static inline uint64 ToUInt64( const char *p )
+{
+#ifdef OSC_HOST_LITTLE_ENDIAN
+    union{
+        osc::uint64 i;
+        char c[8];
+    } u;
+
+    u.c[0] = p[7];
+    u.c[1] = p[6];
+    u.c[2] = p[5];
+    u.c[3] = p[4];
+    u.c[4] = p[3];
+    u.c[5] = p[2];
+    u.c[6] = p[1];
+    u.c[7] = p[0];
+
+    return u.i;
+#else
+	return *(uint64*)p;
+#endif
+}
+
+//------------------------------------------------------------------------------
+
+bool ReceivedPacket::IsBundle() const
+{
+    return (Size() > 0 && Contents()[0] == '#');
+}
+
+//------------------------------------------------------------------------------
+
+bool ReceivedBundleElement::IsBundle() const
+{
+    return (Size() > 0 && Contents()[0] == '#');
+}
+
+
+osc_bundle_element_size_t ReceivedBundleElement::Size() const
+{
+    return ToInt32( sizePtr_ );
+}
+
+//------------------------------------------------------------------------------
+
+bool ReceivedMessageArgument::AsBool() const
+{
+    if( !typeTagPtr_ )
+        throw MissingArgumentException();
+	else if( *typeTagPtr_ == TRUE_TYPE_TAG )
+		return true;
+	else if( *typeTagPtr_ == FALSE_TYPE_TAG )
+		return false;
+	else
+		throw WrongArgumentTypeException();
+}
+
+
+bool ReceivedMessageArgument::AsBoolUnchecked() const
+{
+    if( !typeTagPtr_ )
+        throw MissingArgumentException();
+	else if( *typeTagPtr_ == TRUE_TYPE_TAG )
+		return true;
+    else
+	    return false;
+}
+
+
+int32 ReceivedMessageArgument::AsInt32() const
+{
+    if( !typeTagPtr_ )
+        throw MissingArgumentException();
+	else if( *typeTagPtr_ == INT32_TYPE_TAG )
+		return AsInt32Unchecked();
+	else
+		throw WrongArgumentTypeException();
+}
+
+
+int32 ReceivedMessageArgument::AsInt32Unchecked() const
+{
+#ifdef OSC_HOST_LITTLE_ENDIAN
+    union{
+        osc::int32 i;
+        char c[4];
+    } u;
+
+    u.c[0] = argumentPtr_[3];
+    u.c[1] = argumentPtr_[2];
+    u.c[2] = argumentPtr_[1];
+    u.c[3] = argumentPtr_[0];
+
+    return u.i;
+#else
+	return *(int32*)argument_;
+#endif
+}
+
+
+float ReceivedMessageArgument::AsFloat() const
+{
+    if( !typeTagPtr_ )
+        throw MissingArgumentException();
+	else if( *typeTagPtr_ == FLOAT_TYPE_TAG )
+		return AsFloatUnchecked();
+	else
+		throw WrongArgumentTypeException();
+}
+
+
+float ReceivedMessageArgument::AsFloatUnchecked() const
+{
+#ifdef OSC_HOST_LITTLE_ENDIAN
+    union{
+        float f;
+        char c[4];
+    } u;
+
+    u.c[0] = argumentPtr_[3];
+    u.c[1] = argumentPtr_[2];
+    u.c[2] = argumentPtr_[1];
+    u.c[3] = argumentPtr_[0];
+
+    return u.f;
+#else
+	return *(float*)argument_;
+#endif
+}
+
+
+char ReceivedMessageArgument::AsChar() const
+{
+    if( !typeTagPtr_ )
+        throw MissingArgumentException();
+	else if( *typeTagPtr_ == CHAR_TYPE_TAG )
+		return AsCharUnchecked();
+	else
+		throw WrongArgumentTypeException();
+}
+
+
+char ReceivedMessageArgument::AsCharUnchecked() const
+{
+    return (char)ToInt32( argumentPtr_ );
+}
+
+
+uint32 ReceivedMessageArgument::AsRgbaColor() const
+{
+    if( !typeTagPtr_ )
+        throw MissingArgumentException();
+	else if( *typeTagPtr_ == RGBA_COLOR_TYPE_TAG )
+		return AsRgbaColorUnchecked();
+	else
+		throw WrongArgumentTypeException();
+}
+
+
+uint32 ReceivedMessageArgument::AsRgbaColorUnchecked() const
+{
+	return ToUInt32( argumentPtr_ );
+}
+
+
+uint32 ReceivedMessageArgument::AsMidiMessage() const
+{
+    if( !typeTagPtr_ )
+        throw MissingArgumentException();
+	else if( *typeTagPtr_ == MIDI_MESSAGE_TYPE_TAG )
+		return AsMidiMessageUnchecked();
+	else
+		throw WrongArgumentTypeException();
+}
+
+
+uint32 ReceivedMessageArgument::AsMidiMessageUnchecked() const
+{
+	return ToUInt32( argumentPtr_ );
+}
+
+
+int64 ReceivedMessageArgument::AsInt64() const
+{
+    if( !typeTagPtr_ )
+        throw MissingArgumentException();
+	else if( *typeTagPtr_ == INT64_TYPE_TAG )
+		return AsInt64Unchecked();
+	else
+		throw WrongArgumentTypeException();
+}
+
+
+int64 ReceivedMessageArgument::AsInt64Unchecked() const
+{
+    return ToInt64( argumentPtr_ );
+}
+
+
+uint64 ReceivedMessageArgument::AsTimeTag() const
+{
+    if( !typeTagPtr_ )
+        throw MissingArgumentException();
+	else if( *typeTagPtr_ == TIME_TAG_TYPE_TAG )
+		return AsTimeTagUnchecked();
+	else
+		throw WrongArgumentTypeException();
+}
+
+
+uint64 ReceivedMessageArgument::AsTimeTagUnchecked() const
+{
+    return ToUInt64( argumentPtr_ );
+}
+
+
+double ReceivedMessageArgument::AsDouble() const
+{
+    if( !typeTagPtr_ )
+        throw MissingArgumentException();
+	else if( *typeTagPtr_ == DOUBLE_TYPE_TAG )
+		return AsDoubleUnchecked();
+	else
+		throw WrongArgumentTypeException();
+}
+
+
+double ReceivedMessageArgument::AsDoubleUnchecked() const
+{
+#ifdef OSC_HOST_LITTLE_ENDIAN
+    union{
+        double d;
+        char c[8];
+    } u;
+
+    u.c[0] = argumentPtr_[7];
+    u.c[1] = argumentPtr_[6];
+    u.c[2] = argumentPtr_[5];
+    u.c[3] = argumentPtr_[4];
+    u.c[4] = argumentPtr_[3];
+    u.c[5] = argumentPtr_[2];
+    u.c[6] = argumentPtr_[1];
+    u.c[7] = argumentPtr_[0];
+
+    return u.d;
+#else
+	return *(double*)argument_;
+#endif
+}
+
+
+const char* ReceivedMessageArgument::AsString() const
+{
+    if( !typeTagPtr_ )
+        throw MissingArgumentException();
+	else if( *typeTagPtr_ == STRING_TYPE_TAG )
+		return argumentPtr_;
+	else
+		throw WrongArgumentTypeException();
+}
+
+
+const char* ReceivedMessageArgument::AsSymbol() const
+{
+    if( !typeTagPtr_ )
+        throw MissingArgumentException();
+	else if( *typeTagPtr_ == SYMBOL_TYPE_TAG )
+		return argumentPtr_;
+	else
+		throw WrongArgumentTypeException();
+}
+
+
+void ReceivedMessageArgument::AsBlob( const void*& data, osc_bundle_element_size_t& size ) const
+{
+    if( !typeTagPtr_ )
+        throw MissingArgumentException();
+	else if( *typeTagPtr_ == BLOB_TYPE_TAG )
+		AsBlobUnchecked( data, size );
+	else
+		throw WrongArgumentTypeException();
+}
+
+
+void ReceivedMessageArgument::AsBlobUnchecked( const void*& data, osc_bundle_element_size_t& size ) const
+{
+    // read blob size as an unsigned int then validate
+    osc_bundle_element_size_t sizeResult = (osc_bundle_element_size_t)ToUInt32( argumentPtr_ );
+    if( !IsValidElementSizeValue(sizeResult) )
+        throw MalformedMessageException("invalid blob size");
+
+    size = sizeResult;
+	data = (void*)(argumentPtr_+ osc::OSC_SIZEOF_INT32);
+}
+
+std::size_t ReceivedMessageArgument::ComputeArrayItemCount() const
+{
+    // it is only valid to call ComputeArrayItemCount when the argument is the array start marker
+    if( !IsArrayBegin() )
+        throw WrongArgumentTypeException();
+
+    std::size_t result = 0;
+    unsigned int level = 0;
+    const char *typeTag = typeTagPtr_ + 1;
+
+    // iterate through all type tags. note that ReceivedMessage::Init
+    // has already checked that the message is well formed.
+    while( *typeTag ) {
+        switch( *typeTag++ ) {
+            case ARRAY_BEGIN_TYPE_TAG:
+                level += 1;
+                break;
+
+            case ARRAY_END_TYPE_TAG:
+                if(level == 0)
+                    return result;
+                level -= 1;
+                break;
+
+            default:
+                if( level == 0 ) // only count items at level 0
+                    ++result;
+        }
+    }
+
+    return result;
+}
+
+//------------------------------------------------------------------------------
+
+void ReceivedMessageArgumentIterator::Advance()
+{
+    if( !value_.typeTagPtr_ )
+        return;
+        
+    switch( *value_.typeTagPtr_++ ){
+        case '\0':
+            // don't advance past end
+            --value_.typeTagPtr_;
+            break;
+            
+        case TRUE_TYPE_TAG:
+        case FALSE_TYPE_TAG:
+        case NIL_TYPE_TAG:
+        case INFINITUM_TYPE_TAG:
+        
+            // zero length
+            break;
+
+        case INT32_TYPE_TAG:
+        case FLOAT_TYPE_TAG: 					
+        case CHAR_TYPE_TAG:
+        case RGBA_COLOR_TYPE_TAG:
+        case MIDI_MESSAGE_TYPE_TAG:
+
+            value_.argumentPtr_ += 4;
+            break;
+
+        case INT64_TYPE_TAG:
+        case TIME_TAG_TYPE_TAG:
+        case DOUBLE_TYPE_TAG:
+				
+            value_.argumentPtr_ += 8;
+            break;
+
+        case STRING_TYPE_TAG: 
+        case SYMBOL_TYPE_TAG:
+
+            // we use the unsafe function FindStr4End(char*) here because all of
+            // the arguments have already been validated in
+            // ReceivedMessage::Init() below.
+            
+            value_.argumentPtr_ = FindStr4End( value_.argumentPtr_ );
+            break;
+
+        case BLOB_TYPE_TAG:
+            {
+                // treat blob size as an unsigned int for the purposes of this calculation
+                uint32 blobSize = ToUInt32( value_.argumentPtr_ );
+                value_.argumentPtr_ = value_.argumentPtr_ + osc::OSC_SIZEOF_INT32 + RoundUp4( blobSize );
+            }
+            break;
+
+        case ARRAY_BEGIN_TYPE_TAG:
+        case ARRAY_END_TYPE_TAG: 
+
+            //    [ Indicates the beginning of an array. The tags following are for
+            //        data in the Array until a close brace tag is reached.
+            //    ] Indicates the end of an array.
+
+            // zero length, don't advance argument ptr
+            break;
+
+        default:    // unknown type tag
+            // don't advance
+            --value_.typeTagPtr_;
+            break;
+    }
+}
+
+//------------------------------------------------------------------------------
+
+ReceivedMessage::ReceivedMessage( const ReceivedPacket& packet )
+    : addressPattern_( packet.Contents() )
+{
+    Init( packet.Contents(), packet.Size() );
+}
+
+
+ReceivedMessage::ReceivedMessage( const ReceivedBundleElement& bundleElement )
+    : addressPattern_( bundleElement.Contents() )
+{
+    Init( bundleElement.Contents(), bundleElement.Size() );
+}
+
+
+bool ReceivedMessage::AddressPatternIsUInt32() const
+{
+	return (addressPattern_[0] == '\0');
+}
+
+
+uint32 ReceivedMessage::AddressPatternAsUInt32() const
+{
+    return ToUInt32( addressPattern_ );
+}
+
+
+void ReceivedMessage::Init( const char *message, osc_bundle_element_size_t size )
+{
+    if( !IsValidElementSizeValue(size) )
+        throw MalformedMessageException( "invalid message size" );
+
+    if( size == 0 )
+        throw MalformedMessageException( "zero length messages not permitted" );
+
+    if( !IsMultipleOf4(size) )
+        throw MalformedMessageException( "message size must be multiple of four" );
+
+    const char *end = message + size;
+
+    typeTagsBegin_ = FindStr4End( addressPattern_, end );
+    if( typeTagsBegin_ == 0 ){
+        // address pattern was not terminated before end
+        throw MalformedMessageException( "unterminated address pattern" );
+    }
+
+    if( typeTagsBegin_ == end ){
+        // message consists of only the address pattern - no arguments or type tags.
+        typeTagsBegin_ = 0;
+        typeTagsEnd_ = 0;
+        arguments_ = 0;
+            
+    }else{
+        if( *typeTagsBegin_ != ',' )
+            throw MalformedMessageException( "type tags not present" );
+
+        if( *(typeTagsBegin_ + 1) == '\0' ){
+            // zero length type tags
+            typeTagsBegin_ = 0;
+            typeTagsEnd_ = 0;
+            arguments_ = 0;
+
+        }else{
+            // check that all arguments are present and well formed
+                
+            arguments_ = FindStr4End( typeTagsBegin_, end );
+            if( arguments_ == 0 ){
+                throw MalformedMessageException( "type tags were not terminated before end of message" );
+            }
+
+            ++typeTagsBegin_; // advance past initial ','
+            
+            const char *typeTag = typeTagsBegin_;
+            const char *argument = arguments_;
+            unsigned int arrayLevel = 0;
+                        
+            do{
+                switch( *typeTag ){
+                    case TRUE_TYPE_TAG:
+                    case FALSE_TYPE_TAG:
+                    case NIL_TYPE_TAG:
+                    case INFINITUM_TYPE_TAG:
+                        // zero length
+                        break;
+
+                    //    [ Indicates the beginning of an array. The tags following are for
+                    //        data in the Array until a close brace tag is reached.
+                    //    ] Indicates the end of an array.
+                    case ARRAY_BEGIN_TYPE_TAG:
+                        ++arrayLevel;
+                        // (zero length argument data)
+                        break;
+
+                    case ARRAY_END_TYPE_TAG:
+                        --arrayLevel;
+                        // (zero length argument data)
+                        break;
+
+                    case INT32_TYPE_TAG:
+                    case FLOAT_TYPE_TAG:
+                    case CHAR_TYPE_TAG:
+                    case RGBA_COLOR_TYPE_TAG:
+                    case MIDI_MESSAGE_TYPE_TAG:
+
+                        if( argument == end )
+                            throw MalformedMessageException( "arguments exceed message size" );
+                        argument += 4;
+                        if( argument > end )
+                            throw MalformedMessageException( "arguments exceed message size" );
+                        break;
+
+                    case INT64_TYPE_TAG:
+                    case TIME_TAG_TYPE_TAG:
+                    case DOUBLE_TYPE_TAG:
+
+                        if( argument == end )
+                            throw MalformedMessageException( "arguments exceed message size" );
+                        argument += 8;
+                        if( argument > end )
+                            throw MalformedMessageException( "arguments exceed message size" );
+                        break;
+
+                    case STRING_TYPE_TAG: 
+                    case SYMBOL_TYPE_TAG:
+                    
+                        if( argument == end )
+                            throw MalformedMessageException( "arguments exceed message size" );
+                        argument = FindStr4End( argument, end );
+                        if( argument == 0 )
+                            throw MalformedMessageException( "unterminated string argument" );
+                        break;
+
+                    case BLOB_TYPE_TAG:
+                        {
+                            if( argument + osc::OSC_SIZEOF_INT32 > end )
+                                MalformedMessageException( "arguments exceed message size" );
+                                
+                            // treat blob size as an unsigned int for the purposes of this calculation
+                            uint32 blobSize = ToUInt32( argument );
+                            argument = argument + osc::OSC_SIZEOF_INT32 + RoundUp4( blobSize );
+                            if( argument > end )
+                                MalformedMessageException( "arguments exceed message size" );
+                        }
+                        break;
+                        
+                    default:
+                        throw MalformedMessageException( "unknown type tag" );
+                }
+
+            }while( *++typeTag != '\0' );
+            typeTagsEnd_ = typeTag;
+
+            if( arrayLevel !=  0 )
+                throw MalformedMessageException( "array was not terminated before end of message (expected ']' end of array tag)" );
+        }
+
+        // These invariants should be guaranteed by the above code.
+        // we depend on them in the implementation of ArgumentCount()
+#ifndef NDEBUG
+        std::ptrdiff_t argumentCount = typeTagsEnd_ - typeTagsBegin_;
+        assert( argumentCount >= 0 );
+        assert( argumentCount <= OSC_INT32_MAX );
+#endif
+    }
+}
+
+//------------------------------------------------------------------------------
+
+ReceivedBundle::ReceivedBundle( const ReceivedPacket& packet )
+    : elementCount_( 0 )
+{
+    Init( packet.Contents(), packet.Size() );
+}
+
+
+ReceivedBundle::ReceivedBundle( const ReceivedBundleElement& bundleElement )
+    : elementCount_( 0 )
+{
+    Init( bundleElement.Contents(), bundleElement.Size() );
+}
+
+
+void ReceivedBundle::Init( const char *bundle, osc_bundle_element_size_t size )
+{
+
+    if( !IsValidElementSizeValue(size) )
+        throw MalformedBundleException( "invalid bundle size" );
+
+    if( size < 16 )
+        throw MalformedBundleException( "packet too short for bundle" );
+
+    if( !IsMultipleOf4(size) )
+        throw MalformedBundleException( "bundle size must be multiple of four" );
+
+    if( bundle[0] != '#'
+        || bundle[1] != 'b'
+        || bundle[2] != 'u'
+        || bundle[3] != 'n'
+        || bundle[4] != 'd'
+        || bundle[5] != 'l'
+        || bundle[6] != 'e'
+        || bundle[7] != '\0' )
+            throw MalformedBundleException( "bad bundle address pattern" );    
+
+    end_ = bundle + size;
+
+    timeTag_ = bundle + 8;
+
+    const char *p = timeTag_ + 8;
+        
+    while( p < end_ ){
+        if( p + osc::OSC_SIZEOF_INT32 > end_ )
+            throw MalformedBundleException( "packet too short for elementSize" );
+
+        // treat element size as an unsigned int for the purposes of this calculation
+        uint32 elementSize = ToUInt32( p );
+        if( (elementSize & ((uint32)0x03)) != 0 )
+            throw MalformedBundleException( "bundle element size must be multiple of four" );
+
+        p += osc::OSC_SIZEOF_INT32 + elementSize;
+        if( p > end_ )
+            throw MalformedBundleException( "packet too short for bundle element" );
+
+        ++elementCount_;
+    }
+
+    if( p != end_ )
+        throw MalformedBundleException( "bundle contents " );
+}
+
+
+uint64 ReceivedBundle::TimeTag() const
+{
+    return ToUInt64( timeTag_ );
+}
+
+
+} // namespace osc
+