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 #if defined(__WIN32__) || defined(WIN32)
40 #include <malloc.h> // for alloca
43 #include "OscHostEndianness.h"
48 static void FromInt32( char *p, int32 x )
50 #ifdef OSC_HOST_LITTLE_ENDIAN
63 *reinterpret_cast<int32*>(p) = x;
68 static void FromUInt32( char *p, uint32 x )
70 #ifdef OSC_HOST_LITTLE_ENDIAN
83 *reinterpret_cast<uint32*>(p) = x;
88 static void FromInt64( char *p, int64 x )
90 #ifdef OSC_HOST_LITTLE_ENDIAN
107 *reinterpret_cast<int64*>(p) = x;
112 static void FromUInt64( char *p, uint64 x )
114 #ifdef OSC_HOST_LITTLE_ENDIAN
131 *reinterpret_cast<uint64*>(p) = x;
136 static inline long RoundUp4( long x )
138 return ((x-1) & (~0x03L)) + 4;
142 OutboundPacketStream::OutboundPacketStream( char *buffer, unsigned long capacity )
144 , end_( data_ + capacity )
145 , typeTagsCurrent_( end_ )
146 , messageCursor_( data_ )
147 , argumentCurrent_( data_ )
148 , elementSizePtr_( 0 )
149 , messageIsInProgress_( false )
155 OutboundPacketStream::~OutboundPacketStream()
161 char *OutboundPacketStream::BeginElement( char *beginPtr )
163 if( elementSizePtr_ == 0 ){
165 elementSizePtr_ = reinterpret_cast<uint32*>(data_);
170 // store an offset to the old element size ptr in the element size slot
171 // we store an offset rather than the actual pointer to be 64 bit clean.
172 *reinterpret_cast<uint32*>(beginPtr) =
173 (uint32)(reinterpret_cast<char*>(elementSizePtr_) - data_);
175 elementSizePtr_ = reinterpret_cast<uint32*>(beginPtr);
182 void OutboundPacketStream::EndElement( char *endPtr )
184 assert( elementSizePtr_ != 0 );
186 if( elementSizePtr_ == reinterpret_cast<uint32*>(data_) ){
191 // while building an element, an offset to the containing element's
192 // size slot is stored in the elements size slot (or a ptr to data_
193 // if there is no containing element). We retrieve that here
194 uint32 *previousElementSizePtr =
195 (uint32*)(data_ + *reinterpret_cast<uint32*>(elementSizePtr_));
197 // then we store the element size in the slot, note that the element
198 // size does not include the size slot, hence the - 4 below.
200 static_cast<uint32>(endPtr - reinterpret_cast<char*>(elementSizePtr_)) - 4;
201 FromUInt32( reinterpret_cast<char*>(elementSizePtr_), elementSize );
203 // finally, we reset the element size ptr to the containing element
204 elementSizePtr_ = previousElementSizePtr;
209 bool OutboundPacketStream::ElementSizeSlotRequired() const
211 return (elementSizePtr_ != 0);
215 void OutboundPacketStream::CheckForAvailableBundleSpace()
217 unsigned long required = Size() + ((ElementSizeSlotRequired())?4:0) + 16;
219 if( required > Capacity() )
220 throw OutOfBufferMemoryException();
224 void OutboundPacketStream::CheckForAvailableMessageSpace( const char *addressPattern )
226 // plus 4 for at least four bytes of type tag
227 unsigned long required = Size() + ((ElementSizeSlotRequired())?4:0)
228 + RoundUp4(static_cast<unsigned long>(strlen(addressPattern)) + 1) + 4;
230 if( required > Capacity() )
231 throw OutOfBufferMemoryException();
235 void OutboundPacketStream::CheckForAvailableArgumentSpace( long argumentLength )
237 // plus three for extra type tag, comma and null terminator
238 unsigned long required = static_cast<unsigned long>(argumentCurrent_ - data_) + argumentLength
239 + RoundUp4( static_cast<unsigned long>(end_ - typeTagsCurrent_) + 3 );
241 if( required > Capacity() )
242 throw OutOfBufferMemoryException();
246 void OutboundPacketStream::Clear()
248 typeTagsCurrent_ = end_;
249 messageCursor_ = data_;
250 argumentCurrent_ = data_;
252 messageIsInProgress_ = false;
256 unsigned int OutboundPacketStream::Capacity() const
258 return static_cast<int>(end_ - data_);
262 unsigned int OutboundPacketStream::Size() const
264 unsigned int result = static_cast<unsigned long>(argumentCurrent_ - data_);
265 if( IsMessageInProgress() ){
266 // account for the length of the type tag string. the total type tag
267 // includes an initial comma, plus at least one terminating \0
268 result += RoundUp4( static_cast<unsigned long>(end_ - typeTagsCurrent_) + 2 );
275 const char *OutboundPacketStream::Data() const
281 bool OutboundPacketStream::IsReady() const
283 return (!IsMessageInProgress() && !IsBundleInProgress());
287 bool OutboundPacketStream::IsMessageInProgress() const
289 return messageIsInProgress_;
293 bool OutboundPacketStream::IsBundleInProgress() const
295 return (elementSizePtr_ != 0);
299 OutboundPacketStream& OutboundPacketStream::operator<<( const BundleInitiator& rhs )
301 if( IsMessageInProgress() )
302 throw MessageInProgressException();
304 CheckForAvailableBundleSpace();
306 messageCursor_ = BeginElement( messageCursor_ );
308 memcpy( messageCursor_, "#bundle\0", 8 );
309 FromUInt64( messageCursor_ + 8, rhs.timeTag );
311 messageCursor_ += 16;
312 argumentCurrent_ = messageCursor_;
318 OutboundPacketStream& OutboundPacketStream::operator<<( const BundleTerminator& rhs )
322 if( !IsBundleInProgress() )
323 throw BundleNotInProgressException();
324 if( IsMessageInProgress() )
325 throw MessageInProgressException();
327 EndElement( messageCursor_ );
333 OutboundPacketStream& OutboundPacketStream::operator<<( const BeginMessage& rhs )
335 if( IsMessageInProgress() )
336 throw MessageInProgressException();
338 CheckForAvailableMessageSpace( rhs.addressPattern );
340 messageCursor_ = BeginElement( messageCursor_ );
342 strcpy( messageCursor_, rhs.addressPattern );
343 unsigned long rhsLength = static_cast<unsigned long>(strlen(rhs.addressPattern));
344 messageCursor_ += rhsLength + 1;
346 // zero pad to 4-byte boundary
347 unsigned long i = rhsLength + 1;
349 *messageCursor_++ = '\0';
353 argumentCurrent_ = messageCursor_;
354 typeTagsCurrent_ = end_;
356 messageIsInProgress_ = true;
362 OutboundPacketStream& OutboundPacketStream::operator<<( const MessageTerminator& rhs )
366 if( !IsMessageInProgress() )
367 throw MessageNotInProgressException();
369 int typeTagsCount = static_cast<int>(end_ - typeTagsCurrent_);
373 char *tempTypeTags = (char*)alloca(typeTagsCount);
374 memcpy( tempTypeTags, typeTagsCurrent_, typeTagsCount );
376 // slot size includes comma and null terminator
377 int typeTagSlotSize = RoundUp4( typeTagsCount + 2 );
379 uint32 argumentsSize = static_cast<uint32>(argumentCurrent_ - messageCursor_);
381 memmove( messageCursor_ + typeTagSlotSize, messageCursor_, argumentsSize );
383 messageCursor_[0] = ',';
384 // copy type tags in reverse (really forward) order
385 for( int i=0; i < typeTagsCount; ++i )
386 messageCursor_[i+1] = tempTypeTags[ (typeTagsCount-1) - i ];
388 char *p = messageCursor_ + 1 + typeTagsCount;
389 for( int i=0; i < (typeTagSlotSize - (typeTagsCount + 1)); ++i )
392 typeTagsCurrent_ = end_;
394 // advance messageCursor_ for next message
395 messageCursor_ += typeTagSlotSize + argumentsSize;
398 // send an empty type tags string
399 memcpy( messageCursor_, ",\0\0\0", 4 );
401 // advance messageCursor_ for next message
405 argumentCurrent_ = messageCursor_;
407 EndElement( messageCursor_ );
409 messageIsInProgress_ = false;
415 OutboundPacketStream& OutboundPacketStream::operator<<( bool rhs )
417 CheckForAvailableArgumentSpace(0);
419 *(--typeTagsCurrent_) = (char)((rhs) ? TRUE_TYPE_TAG : FALSE_TYPE_TAG);
425 OutboundPacketStream& OutboundPacketStream::operator<<( const NilType& rhs )
428 CheckForAvailableArgumentSpace(0);
430 *(--typeTagsCurrent_) = NIL_TYPE_TAG;
436 OutboundPacketStream& OutboundPacketStream::operator<<( const InfinitumType& rhs )
439 CheckForAvailableArgumentSpace(0);
441 *(--typeTagsCurrent_) = INFINITUM_TYPE_TAG;
447 OutboundPacketStream& OutboundPacketStream::operator<<( int32 rhs )
449 CheckForAvailableArgumentSpace(4);
451 *(--typeTagsCurrent_) = INT32_TYPE_TAG;
452 FromInt32( argumentCurrent_, rhs );
453 argumentCurrent_ += 4;
459 OutboundPacketStream& OutboundPacketStream::operator<<( float rhs )
461 CheckForAvailableArgumentSpace(4);
463 *(--typeTagsCurrent_) = FLOAT_TYPE_TAG;
465 #ifdef OSC_HOST_LITTLE_ENDIAN
473 argumentCurrent_[3] = u.c[0];
474 argumentCurrent_[2] = u.c[1];
475 argumentCurrent_[1] = u.c[2];
476 argumentCurrent_[0] = u.c[3];
478 *reinterpret_cast<float*>(argumentCurrent_) = rhs;
481 argumentCurrent_ += 4;
487 OutboundPacketStream& OutboundPacketStream::operator<<( char rhs )
489 CheckForAvailableArgumentSpace(4);
491 *(--typeTagsCurrent_) = CHAR_TYPE_TAG;
492 FromInt32( argumentCurrent_, rhs );
493 argumentCurrent_ += 4;
499 OutboundPacketStream& OutboundPacketStream::operator<<( const RgbaColor& rhs )
501 CheckForAvailableArgumentSpace(4);
503 *(--typeTagsCurrent_) = RGBA_COLOR_TYPE_TAG;
504 FromUInt32( argumentCurrent_, rhs );
505 argumentCurrent_ += 4;
511 OutboundPacketStream& OutboundPacketStream::operator<<( const MidiMessage& rhs )
513 CheckForAvailableArgumentSpace(4);
515 *(--typeTagsCurrent_) = MIDI_MESSAGE_TYPE_TAG;
516 FromUInt32( argumentCurrent_, rhs );
517 argumentCurrent_ += 4;
523 OutboundPacketStream& OutboundPacketStream::operator<<( int64 rhs )
525 CheckForAvailableArgumentSpace(8);
527 *(--typeTagsCurrent_) = INT64_TYPE_TAG;
528 FromInt64( argumentCurrent_, rhs );
529 argumentCurrent_ += 8;
535 OutboundPacketStream& OutboundPacketStream::operator<<( const TimeTag& rhs )
537 CheckForAvailableArgumentSpace(8);
539 *(--typeTagsCurrent_) = TIME_TAG_TYPE_TAG;
540 FromUInt64( argumentCurrent_, rhs );
541 argumentCurrent_ += 8;
547 OutboundPacketStream& OutboundPacketStream::operator<<( double rhs )
549 CheckForAvailableArgumentSpace(8);
551 *(--typeTagsCurrent_) = DOUBLE_TYPE_TAG;
553 #ifdef OSC_HOST_LITTLE_ENDIAN
561 argumentCurrent_[7] = u.c[0];
562 argumentCurrent_[6] = u.c[1];
563 argumentCurrent_[5] = u.c[2];
564 argumentCurrent_[4] = u.c[3];
565 argumentCurrent_[3] = u.c[4];
566 argumentCurrent_[2] = u.c[5];
567 argumentCurrent_[1] = u.c[6];
568 argumentCurrent_[0] = u.c[7];
570 *reinterpret_cast<double*>(argumentCurrent_) = rhs;
573 argumentCurrent_ += 8;
579 OutboundPacketStream& OutboundPacketStream::operator<<( const char *rhs )
581 CheckForAvailableArgumentSpace( RoundUp4(static_cast<long>(strlen(rhs)) + 1) );
583 *(--typeTagsCurrent_) = STRING_TYPE_TAG;
584 strcpy( argumentCurrent_, rhs );
585 unsigned long rhsLength = static_cast<unsigned long>(strlen(rhs));
586 argumentCurrent_ += rhsLength + 1;
588 // zero pad to 4-byte boundary
589 unsigned long i = rhsLength + 1;
591 *argumentCurrent_++ = '\0';
599 OutboundPacketStream& OutboundPacketStream::operator<<( const Symbol& rhs )
601 CheckForAvailableArgumentSpace( RoundUp4(static_cast<long>(strlen(rhs)) + 1) );
603 *(--typeTagsCurrent_) = SYMBOL_TYPE_TAG;
604 strcpy( argumentCurrent_, rhs );
605 unsigned long rhsLength = static_cast<unsigned long>(strlen(rhs));
606 argumentCurrent_ += rhsLength + 1;
608 // zero pad to 4-byte boundary
609 unsigned long i = rhsLength + 1;
611 *argumentCurrent_++ = '\0';
619 OutboundPacketStream& OutboundPacketStream::operator<<( const Blob& rhs )
621 CheckForAvailableArgumentSpace( 4 + RoundUp4(rhs.size) );
623 *(--typeTagsCurrent_) = BLOB_TYPE_TAG;
624 FromUInt32( argumentCurrent_, rhs.size );
625 argumentCurrent_ += 4;
627 memcpy( argumentCurrent_, rhs.data, rhs.size );
628 argumentCurrent_ += rhs.size;
630 // zero pad to 4-byte boundary
631 unsigned long i = rhs.size;
633 *argumentCurrent_++ = '\0';