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 "OscOutboundPacketStream.h" rob@76: rob@76: #if defined(__WIN32__) || defined(WIN32) || defined(_WIN32) rob@76: #include // for alloca rob@76: #else rob@76: //#include // alloca on Linux (also OSX) rob@76: #include // alloca on OSX and FreeBSD (and Linux?) rob@76: #endif rob@76: rob@76: #include rob@76: #include // memcpy, memmove, strcpy, strlen rob@76: #include // ptrdiff_t rob@76: rob@76: #include "OscHostEndianness.h" rob@76: rob@76: #if defined(__BORLANDC__) // workaround for BCB4 release build intrinsics bug rob@76: namespace std { rob@76: using ::__strcpy__; // avoid error: E2316 '__strcpy__' is not a member of 'std'. rob@76: } rob@76: #endif rob@76: rob@76: namespace osc{ rob@76: rob@76: static void FromInt32( char *p, int32 x ) 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.i = x; rob@76: rob@76: p[3] = u.c[0]; rob@76: p[2] = u.c[1]; rob@76: p[1] = u.c[2]; rob@76: p[0] = u.c[3]; rob@76: #else rob@76: *reinterpret_cast(p) = x; rob@76: #endif rob@76: } rob@76: rob@76: rob@76: static void FromUInt32( char *p, uint32 x ) 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.i = x; rob@76: rob@76: p[3] = u.c[0]; rob@76: p[2] = u.c[1]; rob@76: p[1] = u.c[2]; rob@76: p[0] = u.c[3]; rob@76: #else rob@76: *reinterpret_cast(p) = x; rob@76: #endif rob@76: } rob@76: rob@76: rob@76: static void FromInt64( char *p, int64 x ) 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.i = x; rob@76: rob@76: p[7] = u.c[0]; rob@76: p[6] = u.c[1]; rob@76: p[5] = u.c[2]; rob@76: p[4] = u.c[3]; rob@76: p[3] = u.c[4]; rob@76: p[2] = u.c[5]; rob@76: p[1] = u.c[6]; rob@76: p[0] = u.c[7]; rob@76: #else rob@76: *reinterpret_cast(p) = x; rob@76: #endif rob@76: } rob@76: rob@76: rob@76: static void FromUInt64( char *p, uint64 x ) 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.i = x; rob@76: rob@76: p[7] = u.c[0]; rob@76: p[6] = u.c[1]; rob@76: p[5] = u.c[2]; rob@76: p[4] = u.c[3]; rob@76: p[3] = u.c[4]; rob@76: p[2] = u.c[5]; rob@76: p[1] = u.c[6]; rob@76: p[0] = u.c[7]; rob@76: #else rob@76: *reinterpret_cast(p) = x; rob@76: #endif 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 std::size_t RoundUp4( std::size_t x ) rob@76: { rob@76: return (x + 3) & ~((std::size_t)0x03); rob@76: } rob@76: rob@76: rob@76: OutboundPacketStream::OutboundPacketStream( char *buffer, std::size_t capacity ) rob@76: : data_( buffer ) rob@76: , end_( data_ + capacity ) rob@76: , typeTagsCurrent_( end_ ) rob@76: , messageCursor_( data_ ) rob@76: , argumentCurrent_( data_ ) rob@76: , elementSizePtr_( 0 ) rob@76: , messageIsInProgress_( false ) 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: rob@76: rob@76: OutboundPacketStream::~OutboundPacketStream() rob@76: { rob@76: rob@76: } rob@76: rob@76: rob@76: char *OutboundPacketStream::BeginElement( char *beginPtr ) rob@76: { rob@76: if( elementSizePtr_ == 0 ){ rob@76: rob@76: elementSizePtr_ = reinterpret_cast(data_); rob@76: rob@76: return beginPtr; rob@76: rob@76: }else{ rob@76: // store an offset to the old element size ptr in the element size slot rob@76: // we store an offset rather than the actual pointer to be 64 bit clean. rob@76: *reinterpret_cast(beginPtr) = rob@76: (uint32)(reinterpret_cast(elementSizePtr_) - data_); rob@76: rob@76: elementSizePtr_ = reinterpret_cast(beginPtr); rob@76: rob@76: return beginPtr + 4; rob@76: } rob@76: } rob@76: rob@76: rob@76: void OutboundPacketStream::EndElement( char *endPtr ) rob@76: { rob@76: assert( elementSizePtr_ != 0 ); rob@76: rob@76: if( elementSizePtr_ == reinterpret_cast(data_) ){ rob@76: rob@76: elementSizePtr_ = 0; rob@76: rob@76: }else{ rob@76: // while building an element, an offset to the containing element's rob@76: // size slot is stored in the elements size slot (or a ptr to data_ rob@76: // if there is no containing element). We retrieve that here rob@76: uint32 *previousElementSizePtr = rob@76: reinterpret_cast(data_ + *elementSizePtr_); rob@76: rob@76: // then we store the element size in the slot. note that the element rob@76: // size does not include the size slot, hence the - 4 below. rob@76: rob@76: std::ptrdiff_t d = endPtr - reinterpret_cast(elementSizePtr_); rob@76: // assert( d >= 4 && d <= 0x7FFFFFFF ); // assume packets smaller than 2Gb rob@76: rob@76: uint32 elementSize = static_cast(d - 4); rob@76: FromUInt32( reinterpret_cast(elementSizePtr_), elementSize ); rob@76: rob@76: // finally, we reset the element size ptr to the containing element rob@76: elementSizePtr_ = previousElementSizePtr; rob@76: } rob@76: } rob@76: rob@76: rob@76: bool OutboundPacketStream::ElementSizeSlotRequired() const rob@76: { rob@76: return (elementSizePtr_ != 0); rob@76: } rob@76: rob@76: rob@76: void OutboundPacketStream::CheckForAvailableBundleSpace() rob@76: { rob@76: std::size_t required = Size() + ((ElementSizeSlotRequired())?4:0) + 16; rob@76: rob@76: if( required > Capacity() ) rob@76: throw OutOfBufferMemoryException(); rob@76: } rob@76: rob@76: rob@76: void OutboundPacketStream::CheckForAvailableMessageSpace( const char *addressPattern ) rob@76: { rob@76: // plus 4 for at least four bytes of type tag rob@76: std::size_t required = Size() + ((ElementSizeSlotRequired())?4:0) rob@76: + RoundUp4(std::strlen(addressPattern) + 1) + 4; rob@76: rob@76: if( required > Capacity() ) rob@76: throw OutOfBufferMemoryException(); rob@76: } rob@76: rob@76: rob@76: void OutboundPacketStream::CheckForAvailableArgumentSpace( std::size_t argumentLength ) rob@76: { rob@76: // plus three for extra type tag, comma and null terminator rob@76: std::size_t required = (argumentCurrent_ - data_) + argumentLength rob@76: + RoundUp4( (end_ - typeTagsCurrent_) + 3 ); rob@76: rob@76: if( required > Capacity() ) rob@76: throw OutOfBufferMemoryException(); rob@76: } rob@76: rob@76: rob@76: void OutboundPacketStream::Clear() rob@76: { rob@76: typeTagsCurrent_ = end_; rob@76: messageCursor_ = data_; rob@76: argumentCurrent_ = data_; rob@76: elementSizePtr_ = 0; rob@76: messageIsInProgress_ = false; rob@76: } rob@76: rob@76: rob@76: std::size_t OutboundPacketStream::Capacity() const rob@76: { rob@76: return end_ - data_; rob@76: } rob@76: rob@76: rob@76: std::size_t OutboundPacketStream::Size() const rob@76: { rob@76: std::size_t result = argumentCurrent_ - data_; rob@76: if( IsMessageInProgress() ){ rob@76: // account for the length of the type tag string. the total type tag rob@76: // includes an initial comma, plus at least one terminating \0 rob@76: result += RoundUp4( (end_ - typeTagsCurrent_) + 2 ); rob@76: } rob@76: rob@76: return result; rob@76: } rob@76: rob@76: rob@76: const char *OutboundPacketStream::Data() const rob@76: { rob@76: return data_; rob@76: } rob@76: rob@76: rob@76: bool OutboundPacketStream::IsReady() const rob@76: { rob@76: return (!IsMessageInProgress() && !IsBundleInProgress()); rob@76: } rob@76: rob@76: rob@76: bool OutboundPacketStream::IsMessageInProgress() const rob@76: { rob@76: return messageIsInProgress_; rob@76: } rob@76: rob@76: rob@76: bool OutboundPacketStream::IsBundleInProgress() const rob@76: { rob@76: return (elementSizePtr_ != 0); rob@76: } rob@76: rob@76: rob@76: OutboundPacketStream& OutboundPacketStream::operator<<( const BundleInitiator& rhs ) rob@76: { rob@76: if( IsMessageInProgress() ) rob@76: throw MessageInProgressException(); rob@76: rob@76: CheckForAvailableBundleSpace(); rob@76: rob@76: messageCursor_ = BeginElement( messageCursor_ ); rob@76: rob@76: std::memcpy( messageCursor_, "#bundle\0", 8 ); rob@76: FromUInt64( messageCursor_ + 8, rhs.timeTag ); rob@76: rob@76: messageCursor_ += 16; rob@76: argumentCurrent_ = messageCursor_; rob@76: rob@76: return *this; rob@76: } rob@76: rob@76: rob@76: OutboundPacketStream& OutboundPacketStream::operator<<( const BundleTerminator& rhs ) rob@76: { rob@76: (void) rhs; rob@76: rob@76: if( !IsBundleInProgress() ) rob@76: throw BundleNotInProgressException(); rob@76: if( IsMessageInProgress() ) rob@76: throw MessageInProgressException(); rob@76: rob@76: EndElement( messageCursor_ ); rob@76: rob@76: return *this; rob@76: } rob@76: rob@76: rob@76: OutboundPacketStream& OutboundPacketStream::operator<<( const BeginMessage& rhs ) rob@76: { rob@76: if( IsMessageInProgress() ) rob@76: throw MessageInProgressException(); rob@76: rob@76: CheckForAvailableMessageSpace( rhs.addressPattern ); rob@76: rob@76: messageCursor_ = BeginElement( messageCursor_ ); rob@76: rob@76: std::strcpy( messageCursor_, rhs.addressPattern ); rob@76: std::size_t rhsLength = std::strlen(rhs.addressPattern); rob@76: messageCursor_ += rhsLength + 1; rob@76: rob@76: // zero pad to 4-byte boundary rob@76: std::size_t i = rhsLength + 1; rob@76: while( i & 0x3 ){ rob@76: *messageCursor_++ = '\0'; rob@76: ++i; rob@76: } rob@76: rob@76: argumentCurrent_ = messageCursor_; rob@76: typeTagsCurrent_ = end_; rob@76: rob@76: messageIsInProgress_ = true; rob@76: rob@76: return *this; rob@76: } rob@76: rob@76: rob@76: OutboundPacketStream& OutboundPacketStream::operator<<( const MessageTerminator& rhs ) rob@76: { rob@76: (void) rhs; rob@76: rob@76: if( !IsMessageInProgress() ) rob@76: throw MessageNotInProgressException(); rob@76: rob@76: std::size_t typeTagsCount = end_ - typeTagsCurrent_; rob@76: rob@76: if( typeTagsCount ){ rob@76: rob@76: char *tempTypeTags = (char*)alloca(typeTagsCount); rob@76: std::memcpy( tempTypeTags, typeTagsCurrent_, typeTagsCount ); rob@76: rob@76: // slot size includes comma and null terminator rob@76: std::size_t typeTagSlotSize = RoundUp4( typeTagsCount + 2 ); rob@76: rob@76: std::size_t argumentsSize = argumentCurrent_ - messageCursor_; rob@76: rob@76: std::memmove( messageCursor_ + typeTagSlotSize, messageCursor_, argumentsSize ); rob@76: rob@76: messageCursor_[0] = ','; rob@76: // copy type tags in reverse (really forward) order rob@76: for( std::size_t i=0; i < typeTagsCount; ++i ) rob@76: messageCursor_[i+1] = tempTypeTags[ (typeTagsCount-1) - i ]; rob@76: rob@76: char *p = messageCursor_ + 1 + typeTagsCount; rob@76: for( std::size_t i=0; i < (typeTagSlotSize - (typeTagsCount + 1)); ++i ) rob@76: *p++ = '\0'; rob@76: rob@76: typeTagsCurrent_ = end_; rob@76: rob@76: // advance messageCursor_ for next message rob@76: messageCursor_ += typeTagSlotSize + argumentsSize; rob@76: rob@76: }else{ rob@76: // send an empty type tags string rob@76: std::memcpy( messageCursor_, ",\0\0\0", 4 ); rob@76: rob@76: // advance messageCursor_ for next message rob@76: messageCursor_ += 4; rob@76: } rob@76: rob@76: argumentCurrent_ = messageCursor_; rob@76: rob@76: EndElement( messageCursor_ ); rob@76: rob@76: messageIsInProgress_ = false; rob@76: rob@76: return *this; rob@76: } rob@76: rob@76: rob@76: OutboundPacketStream& OutboundPacketStream::operator<<( bool rhs ) rob@76: { rob@76: CheckForAvailableArgumentSpace(0); rob@76: rob@76: *(--typeTagsCurrent_) = (char)((rhs) ? TRUE_TYPE_TAG : FALSE_TYPE_TAG); rob@76: rob@76: return *this; rob@76: } rob@76: rob@76: rob@76: OutboundPacketStream& OutboundPacketStream::operator<<( const NilType& rhs ) rob@76: { rob@76: (void) rhs; rob@76: CheckForAvailableArgumentSpace(0); rob@76: rob@76: *(--typeTagsCurrent_) = NIL_TYPE_TAG; rob@76: rob@76: return *this; rob@76: } rob@76: rob@76: rob@76: OutboundPacketStream& OutboundPacketStream::operator<<( const InfinitumType& rhs ) rob@76: { rob@76: (void) rhs; rob@76: CheckForAvailableArgumentSpace(0); rob@76: rob@76: *(--typeTagsCurrent_) = INFINITUM_TYPE_TAG; rob@76: rob@76: return *this; rob@76: } rob@76: rob@76: rob@76: OutboundPacketStream& OutboundPacketStream::operator<<( int32 rhs ) rob@76: { rob@76: CheckForAvailableArgumentSpace(4); rob@76: rob@76: *(--typeTagsCurrent_) = INT32_TYPE_TAG; rob@76: FromInt32( argumentCurrent_, rhs ); rob@76: argumentCurrent_ += 4; rob@76: rob@76: return *this; rob@76: } rob@76: rob@76: rob@76: OutboundPacketStream& OutboundPacketStream::operator<<( float rhs ) rob@76: { rob@76: CheckForAvailableArgumentSpace(4); rob@76: rob@76: *(--typeTagsCurrent_) = FLOAT_TYPE_TAG; 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.f = rhs; rob@76: rob@76: argumentCurrent_[3] = u.c[0]; rob@76: argumentCurrent_[2] = u.c[1]; rob@76: argumentCurrent_[1] = u.c[2]; rob@76: argumentCurrent_[0] = u.c[3]; rob@76: #else rob@76: *reinterpret_cast(argumentCurrent_) = rhs; rob@76: #endif rob@76: rob@76: argumentCurrent_ += 4; rob@76: rob@76: return *this; rob@76: } rob@76: rob@76: rob@76: OutboundPacketStream& OutboundPacketStream::operator<<( char rhs ) rob@76: { rob@76: CheckForAvailableArgumentSpace(4); rob@76: rob@76: *(--typeTagsCurrent_) = CHAR_TYPE_TAG; rob@76: FromInt32( argumentCurrent_, rhs ); rob@76: argumentCurrent_ += 4; rob@76: rob@76: return *this; rob@76: } rob@76: rob@76: rob@76: OutboundPacketStream& OutboundPacketStream::operator<<( const RgbaColor& rhs ) rob@76: { rob@76: CheckForAvailableArgumentSpace(4); rob@76: rob@76: *(--typeTagsCurrent_) = RGBA_COLOR_TYPE_TAG; rob@76: FromUInt32( argumentCurrent_, rhs ); rob@76: argumentCurrent_ += 4; rob@76: rob@76: return *this; rob@76: } rob@76: rob@76: rob@76: OutboundPacketStream& OutboundPacketStream::operator<<( const MidiMessage& rhs ) rob@76: { rob@76: CheckForAvailableArgumentSpace(4); rob@76: rob@76: *(--typeTagsCurrent_) = MIDI_MESSAGE_TYPE_TAG; rob@76: FromUInt32( argumentCurrent_, rhs ); rob@76: argumentCurrent_ += 4; rob@76: rob@76: return *this; rob@76: } rob@76: rob@76: rob@76: OutboundPacketStream& OutboundPacketStream::operator<<( int64 rhs ) rob@76: { rob@76: CheckForAvailableArgumentSpace(8); rob@76: rob@76: *(--typeTagsCurrent_) = INT64_TYPE_TAG; rob@76: FromInt64( argumentCurrent_, rhs ); rob@76: argumentCurrent_ += 8; rob@76: rob@76: return *this; rob@76: } rob@76: rob@76: rob@76: OutboundPacketStream& OutboundPacketStream::operator<<( const TimeTag& rhs ) rob@76: { rob@76: CheckForAvailableArgumentSpace(8); rob@76: rob@76: *(--typeTagsCurrent_) = TIME_TAG_TYPE_TAG; rob@76: FromUInt64( argumentCurrent_, rhs ); rob@76: argumentCurrent_ += 8; rob@76: rob@76: return *this; rob@76: } rob@76: rob@76: rob@76: OutboundPacketStream& OutboundPacketStream::operator<<( double rhs ) rob@76: { rob@76: CheckForAvailableArgumentSpace(8); rob@76: rob@76: *(--typeTagsCurrent_) = DOUBLE_TYPE_TAG; rob@76: rob@76: #ifdef OSC_HOST_LITTLE_ENDIAN rob@76: union{ rob@76: double f; rob@76: char c[8]; rob@76: } u; rob@76: rob@76: u.f = rhs; rob@76: rob@76: argumentCurrent_[7] = u.c[0]; rob@76: argumentCurrent_[6] = u.c[1]; rob@76: argumentCurrent_[5] = u.c[2]; rob@76: argumentCurrent_[4] = u.c[3]; rob@76: argumentCurrent_[3] = u.c[4]; rob@76: argumentCurrent_[2] = u.c[5]; rob@76: argumentCurrent_[1] = u.c[6]; rob@76: argumentCurrent_[0] = u.c[7]; rob@76: #else rob@76: *reinterpret_cast(argumentCurrent_) = rhs; rob@76: #endif rob@76: rob@76: argumentCurrent_ += 8; rob@76: rob@76: return *this; rob@76: } rob@76: rob@76: rob@76: OutboundPacketStream& OutboundPacketStream::operator<<( const char *rhs ) rob@76: { rob@76: CheckForAvailableArgumentSpace( RoundUp4(std::strlen(rhs) + 1) ); rob@76: rob@76: *(--typeTagsCurrent_) = STRING_TYPE_TAG; rob@76: std::strcpy( argumentCurrent_, rhs ); rob@76: std::size_t rhsLength = std::strlen(rhs); rob@76: argumentCurrent_ += rhsLength + 1; rob@76: rob@76: // zero pad to 4-byte boundary rob@76: std::size_t i = rhsLength + 1; rob@76: while( i & 0x3 ){ rob@76: *argumentCurrent_++ = '\0'; rob@76: ++i; rob@76: } rob@76: rob@76: return *this; rob@76: } rob@76: rob@76: rob@76: OutboundPacketStream& OutboundPacketStream::operator<<( const Symbol& rhs ) rob@76: { rob@76: CheckForAvailableArgumentSpace( RoundUp4(std::strlen(rhs) + 1) ); rob@76: rob@76: *(--typeTagsCurrent_) = SYMBOL_TYPE_TAG; rob@76: std::strcpy( argumentCurrent_, rhs ); rob@76: std::size_t rhsLength = std::strlen(rhs); rob@76: argumentCurrent_ += rhsLength + 1; rob@76: rob@76: // zero pad to 4-byte boundary rob@76: std::size_t i = rhsLength + 1; rob@76: while( i & 0x3 ){ rob@76: *argumentCurrent_++ = '\0'; rob@76: ++i; rob@76: } rob@76: rob@76: return *this; rob@76: } rob@76: rob@76: rob@76: OutboundPacketStream& OutboundPacketStream::operator<<( const Blob& rhs ) rob@76: { rob@76: CheckForAvailableArgumentSpace( 4 + RoundUp4(rhs.size) ); rob@76: rob@76: *(--typeTagsCurrent_) = BLOB_TYPE_TAG; rob@76: FromUInt32( argumentCurrent_, rhs.size ); rob@76: argumentCurrent_ += 4; rob@76: rob@76: std::memcpy( argumentCurrent_, rhs.data, rhs.size ); rob@76: argumentCurrent_ += rhs.size; rob@76: rob@76: // zero pad to 4-byte boundary rob@76: unsigned long i = rhs.size; rob@76: while( i & 0x3 ){ rob@76: *argumentCurrent_++ = '\0'; rob@76: ++i; rob@76: } rob@76: rob@76: return *this; rob@76: } rob@76: rob@76: OutboundPacketStream& OutboundPacketStream::operator<<( const ArrayInitiator& rhs ) rob@76: { rob@76: (void) rhs; rob@76: CheckForAvailableArgumentSpace(0); rob@76: rob@76: *(--typeTagsCurrent_) = ARRAY_BEGIN_TYPE_TAG; rob@76: rob@76: return *this; rob@76: } rob@76: rob@76: OutboundPacketStream& OutboundPacketStream::operator<<( const ArrayTerminator& rhs ) rob@76: { rob@76: (void) rhs; rob@76: CheckForAvailableArgumentSpace(0); rob@76: rob@76: *(--typeTagsCurrent_) = ARRAY_END_TYPE_TAG; rob@76: rob@76: return *this; rob@76: } rob@76: rob@76: } // namespace osc rob@76: rob@76: