2 oscpack -- Open Sound Control packet manipulation library
3 http://www.audiomulch.com/~rossb/oscpack
5 Copyright (c) 2004-2005 Ross Bencina <rossb@audiomulch.com>
7 Permission is hereby granted, free of charge, to any person obtaining
8 a copy of this software and associated documentation files
9 (the "Software"), to deal in the Software without restriction,
10 including without limitation the rights to use, copy, modify, merge,
11 publish, distribute, sublicense, and/or sell copies of the Software,
12 and to permit persons to whom the Software is furnished to do so,
13 subject to the following conditions:
15 The above copyright notice and this permission notice shall be
16 included in all copies or substantial portions of the Software.
18 Any person wishing to distribute modifications to the Software is
19 requested to send the modifications to the original developer so that
20 they can be incorporated into the canonical version.
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
25 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
26 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
27 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 #undef _CRT_SECURE_NO_WARNINGS
31 #define _CRT_SECURE_NO_WARNINGS
33 #include "OscOutboundPacketStream.h"
39 #include <common/endian.h>
41 #if defined(__WIN32__) || defined(WIN32)
42 #include <malloc.h> // for alloca
45 #include "OscHostEndianness.h"
50 static void FromInt32( char *p, int32 x )
52 #ifdef OSC_HOST_LITTLE_ENDIAN
64 *reinterpret_cast<int32*>(p) = caspar::swap_byte_order(x);
66 *reinterpret_cast<int32*>(p) = x;
71 static void FromUInt32( char *p, uint32 x )
73 #ifdef OSC_HOST_LITTLE_ENDIAN
85 *reinterpret_cast<uint32*>(p) = caspar::swap_byte_order(x);
87 *reinterpret_cast<uint32*>(p) = x;
92 static void FromInt64( char *p, int64 x )
94 #ifdef OSC_HOST_LITTLE_ENDIAN
110 *reinterpret_cast<int64*>(p) = caspar::swap_byte_order(x);
112 *reinterpret_cast<int64*>(p) = x;
117 static void FromUInt64( char *p, uint64 x )
119 #ifdef OSC_HOST_LITTLE_ENDIAN
135 *reinterpret_cast<uint64*>(p) = caspar::swap_byte_order(x);
137 *reinterpret_cast<uint64*>(p) = x;
142 static inline long RoundUp4( long x )
144 return ((x-1) & (~0x03L)) + 4;
148 OutboundPacketStream::OutboundPacketStream( char *buffer, unsigned long capacity )
150 , end_( data_ + capacity )
151 , typeTagsCurrent_( end_ )
152 , messageCursor_( data_ )
153 , argumentCurrent_( data_ )
154 , elementSizePtr_( 0 )
155 , messageIsInProgress_( false )
161 OutboundPacketStream::~OutboundPacketStream()
167 char *OutboundPacketStream::BeginElement( char *beginPtr )
169 if( elementSizePtr_ == 0 ){
171 elementSizePtr_ = reinterpret_cast<uint32*>(data_);
176 // store an offset to the old element size ptr in the element size slot
177 // we store an offset rather than the actual pointer to be 64 bit clean.
178 *reinterpret_cast<uint32*>(beginPtr) =
179 (uint32)(reinterpret_cast<char*>(elementSizePtr_) - data_);
181 elementSizePtr_ = reinterpret_cast<uint32*>(beginPtr);
188 void OutboundPacketStream::EndElement( char *endPtr )
190 assert( elementSizePtr_ != 0 );
192 if( elementSizePtr_ == reinterpret_cast<uint32*>(data_) ){
197 // while building an element, an offset to the containing element's
198 // size slot is stored in the elements size slot (or a ptr to data_
199 // if there is no containing element). We retrieve that here
200 uint32 *previousElementSizePtr =
201 (uint32*)(data_ + *reinterpret_cast<uint32*>(elementSizePtr_));
203 // then we store the element size in the slot, note that the element
204 // size does not include the size slot, hence the - 4 below.
206 static_cast<uint32>(endPtr - reinterpret_cast<char*>(elementSizePtr_)) - 4;
207 FromUInt32( reinterpret_cast<char*>(elementSizePtr_), elementSize );
209 // finally, we reset the element size ptr to the containing element
210 elementSizePtr_ = previousElementSizePtr;
215 bool OutboundPacketStream::ElementSizeSlotRequired() const
217 return (elementSizePtr_ != 0);
221 void OutboundPacketStream::CheckForAvailableBundleSpace()
223 unsigned long required = Size() + ((ElementSizeSlotRequired())?4:0) + 16;
225 if( required > Capacity() )
226 throw OutOfBufferMemoryException();
230 void OutboundPacketStream::CheckForAvailableMessageSpace( const char *addressPattern )
232 // plus 4 for at least four bytes of type tag
233 unsigned long required = Size() + ((ElementSizeSlotRequired())?4:0)
234 + RoundUp4(static_cast<unsigned long>(strlen(addressPattern)) + 1) + 4;
236 if( required > Capacity() )
237 throw OutOfBufferMemoryException();
241 void OutboundPacketStream::CheckForAvailableArgumentSpace( long argumentLength )
243 // plus three for extra type tag, comma and null terminator
244 unsigned long required = static_cast<unsigned long>(argumentCurrent_ - data_) + argumentLength
245 + RoundUp4( static_cast<unsigned long>(end_ - typeTagsCurrent_) + 3 );
247 if( required > Capacity() )
248 throw OutOfBufferMemoryException();
252 void OutboundPacketStream::Clear()
254 typeTagsCurrent_ = end_;
255 messageCursor_ = data_;
256 argumentCurrent_ = data_;
258 messageIsInProgress_ = false;
262 unsigned int OutboundPacketStream::Capacity() const
264 return static_cast<int>(end_ - data_);
268 unsigned int OutboundPacketStream::Size() const
270 unsigned int result = static_cast<unsigned long>(argumentCurrent_ - data_);
271 if( IsMessageInProgress() ){
272 // account for the length of the type tag string. the total type tag
273 // includes an initial comma, plus at least one terminating \0
274 result += RoundUp4( static_cast<unsigned long>(end_ - typeTagsCurrent_) + 2 );
281 const char *OutboundPacketStream::Data() const
287 bool OutboundPacketStream::IsReady() const
289 return (!IsMessageInProgress() && !IsBundleInProgress());
293 bool OutboundPacketStream::IsMessageInProgress() const
295 return messageIsInProgress_;
299 bool OutboundPacketStream::IsBundleInProgress() const
301 return (elementSizePtr_ != 0);
305 OutboundPacketStream& OutboundPacketStream::operator<<( const BundleInitiator& rhs )
307 if( IsMessageInProgress() )
308 throw MessageInProgressException();
310 CheckForAvailableBundleSpace();
312 messageCursor_ = BeginElement( messageCursor_ );
314 memcpy( messageCursor_, "#bundle\0", 8 );
315 FromUInt64( messageCursor_ + 8, rhs.timeTag );
317 messageCursor_ += 16;
318 argumentCurrent_ = messageCursor_;
324 OutboundPacketStream& OutboundPacketStream::operator<<( const BundleTerminator& rhs )
328 if( !IsBundleInProgress() )
329 throw BundleNotInProgressException();
330 if( IsMessageInProgress() )
331 throw MessageInProgressException();
333 EndElement( messageCursor_ );
339 OutboundPacketStream& OutboundPacketStream::operator<<( const BeginMessage& rhs )
341 if( IsMessageInProgress() )
342 throw MessageInProgressException();
344 CheckForAvailableMessageSpace( rhs.addressPattern );
346 messageCursor_ = BeginElement( messageCursor_ );
348 strcpy( messageCursor_, rhs.addressPattern );
349 unsigned long rhsLength = static_cast<unsigned long>(strlen(rhs.addressPattern));
350 messageCursor_ += rhsLength + 1;
352 // zero pad to 4-byte boundary
353 unsigned long i = rhsLength + 1;
355 *messageCursor_++ = '\0';
359 argumentCurrent_ = messageCursor_;
360 typeTagsCurrent_ = end_;
362 messageIsInProgress_ = true;
368 OutboundPacketStream& OutboundPacketStream::operator<<( const MessageTerminator& rhs )
372 if( !IsMessageInProgress() )
373 throw MessageNotInProgressException();
375 int typeTagsCount = static_cast<int>(end_ - typeTagsCurrent_);
379 char *tempTypeTags = (char*)alloca(typeTagsCount);
380 memcpy( tempTypeTags, typeTagsCurrent_, typeTagsCount );
382 // slot size includes comma and null terminator
383 int typeTagSlotSize = RoundUp4( typeTagsCount + 2 );
385 uint32 argumentsSize = static_cast<uint32>(argumentCurrent_ - messageCursor_);
387 memmove( messageCursor_ + typeTagSlotSize, messageCursor_, argumentsSize );
389 messageCursor_[0] = ',';
390 // copy type tags in reverse (really forward) order
391 for( int i=0; i < typeTagsCount; ++i )
392 messageCursor_[i+1] = tempTypeTags[ (typeTagsCount-1) - i ];
394 char *p = messageCursor_ + 1 + typeTagsCount;
395 for( int i=0; i < (typeTagSlotSize - (typeTagsCount + 1)); ++i )
398 typeTagsCurrent_ = end_;
400 // advance messageCursor_ for next message
401 messageCursor_ += typeTagSlotSize + argumentsSize;
404 // send an empty type tags string
405 memcpy( messageCursor_, ",\0\0\0", 4 );
407 // advance messageCursor_ for next message
411 argumentCurrent_ = messageCursor_;
413 EndElement( messageCursor_ );
415 messageIsInProgress_ = false;
421 OutboundPacketStream& OutboundPacketStream::operator<<( bool rhs )
423 CheckForAvailableArgumentSpace(0);
425 *(--typeTagsCurrent_) = (char)((rhs) ? TRUE_TYPE_TAG : FALSE_TYPE_TAG);
431 OutboundPacketStream& OutboundPacketStream::operator<<( const NilType& rhs )
434 CheckForAvailableArgumentSpace(0);
436 *(--typeTagsCurrent_) = NIL_TYPE_TAG;
442 OutboundPacketStream& OutboundPacketStream::operator<<( const InfinitumType& rhs )
445 CheckForAvailableArgumentSpace(0);
447 *(--typeTagsCurrent_) = INFINITUM_TYPE_TAG;
453 OutboundPacketStream& OutboundPacketStream::operator<<( int32 rhs )
455 CheckForAvailableArgumentSpace(4);
457 *(--typeTagsCurrent_) = INT32_TYPE_TAG;
458 FromInt32( argumentCurrent_, rhs );
459 argumentCurrent_ += 4;
465 OutboundPacketStream& OutboundPacketStream::operator<<( float rhs )
467 CheckForAvailableArgumentSpace(4);
469 *(--typeTagsCurrent_) = FLOAT_TYPE_TAG;
471 #ifdef OSC_HOST_LITTLE_ENDIAN
479 argumentCurrent_[3] = u.c[0];
480 argumentCurrent_[2] = u.c[1];
481 argumentCurrent_[1] = u.c[2];
482 argumentCurrent_[0] = u.c[3];
484 *reinterpret_cast<float*>(argumentCurrent_) = rhs;
487 argumentCurrent_ += 4;
493 OutboundPacketStream& OutboundPacketStream::operator<<( char rhs )
495 CheckForAvailableArgumentSpace(4);
497 *(--typeTagsCurrent_) = CHAR_TYPE_TAG;
498 FromInt32( argumentCurrent_, rhs );
499 argumentCurrent_ += 4;
505 OutboundPacketStream& OutboundPacketStream::operator<<( const RgbaColor& rhs )
507 CheckForAvailableArgumentSpace(4);
509 *(--typeTagsCurrent_) = RGBA_COLOR_TYPE_TAG;
510 FromUInt32( argumentCurrent_, rhs );
511 argumentCurrent_ += 4;
517 OutboundPacketStream& OutboundPacketStream::operator<<( const MidiMessage& rhs )
519 CheckForAvailableArgumentSpace(4);
521 *(--typeTagsCurrent_) = MIDI_MESSAGE_TYPE_TAG;
522 FromUInt32( argumentCurrent_, rhs );
523 argumentCurrent_ += 4;
529 OutboundPacketStream& OutboundPacketStream::operator<<( int64 rhs )
531 CheckForAvailableArgumentSpace(8);
533 *(--typeTagsCurrent_) = INT64_TYPE_TAG;
534 FromInt64( argumentCurrent_, rhs );
535 argumentCurrent_ += 8;
541 OutboundPacketStream& OutboundPacketStream::operator<<( const TimeTag& rhs )
543 CheckForAvailableArgumentSpace(8);
545 *(--typeTagsCurrent_) = TIME_TAG_TYPE_TAG;
546 FromUInt64( argumentCurrent_, rhs );
547 argumentCurrent_ += 8;
553 OutboundPacketStream& OutboundPacketStream::operator<<( double rhs )
555 CheckForAvailableArgumentSpace(8);
557 *(--typeTagsCurrent_) = DOUBLE_TYPE_TAG;
559 #ifdef OSC_HOST_LITTLE_ENDIAN
567 argumentCurrent_[7] = u.c[0];
568 argumentCurrent_[6] = u.c[1];
569 argumentCurrent_[5] = u.c[2];
570 argumentCurrent_[4] = u.c[3];
571 argumentCurrent_[3] = u.c[4];
572 argumentCurrent_[2] = u.c[5];
573 argumentCurrent_[1] = u.c[6];
574 argumentCurrent_[0] = u.c[7];
576 *reinterpret_cast<double*>(argumentCurrent_) = rhs;
579 argumentCurrent_ += 8;
585 OutboundPacketStream& OutboundPacketStream::operator<<( const char *rhs )
587 CheckForAvailableArgumentSpace( RoundUp4(static_cast<long>(strlen(rhs)) + 1) );
589 *(--typeTagsCurrent_) = STRING_TYPE_TAG;
590 strcpy( argumentCurrent_, rhs );
591 unsigned long rhsLength = static_cast<unsigned long>(strlen(rhs));
592 argumentCurrent_ += rhsLength + 1;
594 // zero pad to 4-byte boundary
595 unsigned long i = rhsLength + 1;
597 *argumentCurrent_++ = '\0';
605 OutboundPacketStream& OutboundPacketStream::operator<<( const Symbol& rhs )
607 CheckForAvailableArgumentSpace( RoundUp4(static_cast<long>(strlen(rhs)) + 1) );
609 *(--typeTagsCurrent_) = SYMBOL_TYPE_TAG;
610 strcpy( argumentCurrent_, rhs );
611 unsigned long rhsLength = static_cast<unsigned long>(strlen(rhs));
612 argumentCurrent_ += rhsLength + 1;
614 // zero pad to 4-byte boundary
615 unsigned long i = rhsLength + 1;
617 *argumentCurrent_++ = '\0';
625 OutboundPacketStream& OutboundPacketStream::operator<<( const Blob& rhs )
627 CheckForAvailableArgumentSpace( 4 + RoundUp4(rhs.size) );
629 *(--typeTagsCurrent_) = BLOB_TYPE_TAG;
630 FromUInt32( argumentCurrent_, rhs.size );
631 argumentCurrent_ += 4;
633 memcpy( argumentCurrent_, rhs.data, rhs.size );
634 argumentCurrent_ += rhs.size;
636 // zero pad to 4-byte boundary
637 unsigned long i = rhs.size;
639 *argumentCurrent_++ = '\0';