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 #include "OscOutboundPacketStream.h"
36 #if defined(__WIN32__) || defined(WIN32)
37 #include <malloc.h> // for alloca
40 #include "OscHostEndianness.h"
45 static void FromInt32( char *p, int32 x )
47 #ifdef OSC_HOST_LITTLE_ENDIAN
60 *reinterpret_cast<int32*>(p) = x;
65 static void FromUInt32( char *p, uint32 x )
67 #ifdef OSC_HOST_LITTLE_ENDIAN
80 *reinterpret_cast<uint32*>(p) = x;
85 static void FromInt64( char *p, int64 x )
87 #ifdef OSC_HOST_LITTLE_ENDIAN
104 *reinterpret_cast<int64*>(p) = x;
109 static void FromUInt64( char *p, uint64 x )
111 #ifdef OSC_HOST_LITTLE_ENDIAN
128 *reinterpret_cast<uint64*>(p) = x;
133 static inline long RoundUp4( long x )
135 return ((x-1) & (~0x03L)) + 4;
139 OutboundPacketStream::OutboundPacketStream( char *buffer, unsigned long capacity )
141 , end_( data_ + capacity )
142 , typeTagsCurrent_( end_ )
143 , messageCursor_( data_ )
144 , argumentCurrent_( data_ )
145 , elementSizePtr_( 0 )
146 , messageIsInProgress_( false )
152 OutboundPacketStream::~OutboundPacketStream()
158 char *OutboundPacketStream::BeginElement( char *beginPtr )
160 if( elementSizePtr_ == 0 ){
162 elementSizePtr_ = reinterpret_cast<uint32*>(data_);
167 // store an offset to the old element size ptr in the element size slot
168 // we store an offset rather than the actual pointer to be 64 bit clean.
169 *reinterpret_cast<uint32*>(beginPtr) =
170 (uint32)(reinterpret_cast<char*>(elementSizePtr_) - data_);
172 elementSizePtr_ = reinterpret_cast<uint32*>(beginPtr);
179 void OutboundPacketStream::EndElement( char *endPtr )
181 assert( elementSizePtr_ != 0 );
183 if( elementSizePtr_ == reinterpret_cast<uint32*>(data_) ){
188 // while building an element, an offset to the containing element's
189 // size slot is stored in the elements size slot (or a ptr to data_
190 // if there is no containing element). We retrieve that here
191 uint32 *previousElementSizePtr =
192 (uint32*)(data_ + *reinterpret_cast<uint32*>(elementSizePtr_));
194 // then we store the element size in the slot, note that the element
195 // size does not include the size slot, hence the - 4 below.
197 (endPtr - reinterpret_cast<char*>(elementSizePtr_)) - 4;
198 FromUInt32( reinterpret_cast<char*>(elementSizePtr_), elementSize );
200 // finally, we reset the element size ptr to the containing element
201 elementSizePtr_ = previousElementSizePtr;
206 bool OutboundPacketStream::ElementSizeSlotRequired() const
208 return (elementSizePtr_ != 0);
212 void OutboundPacketStream::CheckForAvailableBundleSpace()
214 unsigned long required = Size() + ((ElementSizeSlotRequired())?4:0) + 16;
216 if( required > Capacity() )
217 throw OutOfBufferMemoryException();
221 void OutboundPacketStream::CheckForAvailableMessageSpace( const char *addressPattern )
223 // plus 4 for at least four bytes of type tag
224 unsigned long required = Size() + ((ElementSizeSlotRequired())?4:0)
225 + RoundUp4(strlen(addressPattern) + 1) + 4;
227 if( required > Capacity() )
228 throw OutOfBufferMemoryException();
232 void OutboundPacketStream::CheckForAvailableArgumentSpace( long argumentLength )
234 // plus three for extra type tag, comma and null terminator
235 unsigned long required = (argumentCurrent_ - data_) + argumentLength
236 + RoundUp4( (end_ - typeTagsCurrent_) + 3 );
238 if( required > Capacity() )
239 throw OutOfBufferMemoryException();
243 void OutboundPacketStream::Clear()
245 typeTagsCurrent_ = end_;
246 messageCursor_ = data_;
247 argumentCurrent_ = data_;
249 messageIsInProgress_ = false;
253 unsigned int OutboundPacketStream::Capacity() const
259 unsigned int OutboundPacketStream::Size() const
261 unsigned int result = argumentCurrent_ - data_;
262 if( IsMessageInProgress() ){
263 // account for the length of the type tag string. the total type tag
264 // includes an initial comma, plus at least one terminating \0
265 result += RoundUp4( (end_ - typeTagsCurrent_) + 2 );
272 const char *OutboundPacketStream::Data() const
278 bool OutboundPacketStream::IsReady() const
280 return (!IsMessageInProgress() && !IsBundleInProgress());
284 bool OutboundPacketStream::IsMessageInProgress() const
286 return messageIsInProgress_;
290 bool OutboundPacketStream::IsBundleInProgress() const
292 return (elementSizePtr_ != 0);
296 OutboundPacketStream& OutboundPacketStream::operator<<( const BundleInitiator& rhs )
298 if( IsMessageInProgress() )
299 throw MessageInProgressException();
301 CheckForAvailableBundleSpace();
303 messageCursor_ = BeginElement( messageCursor_ );
305 memcpy( messageCursor_, "#bundle\0", 8 );
306 FromUInt64( messageCursor_ + 8, rhs.timeTag );
308 messageCursor_ += 16;
309 argumentCurrent_ = messageCursor_;
315 OutboundPacketStream& OutboundPacketStream::operator<<( const BundleTerminator& rhs )
319 if( !IsBundleInProgress() )
320 throw BundleNotInProgressException();
321 if( IsMessageInProgress() )
322 throw MessageInProgressException();
324 EndElement( messageCursor_ );
330 OutboundPacketStream& OutboundPacketStream::operator<<( const BeginMessage& rhs )
332 if( IsMessageInProgress() )
333 throw MessageInProgressException();
335 CheckForAvailableMessageSpace( rhs.addressPattern );
337 messageCursor_ = BeginElement( messageCursor_ );
339 strcpy( messageCursor_, rhs.addressPattern );
340 unsigned long rhsLength = strlen(rhs.addressPattern);
341 messageCursor_ += rhsLength + 1;
343 // zero pad to 4-byte boundary
344 unsigned long i = rhsLength + 1;
346 *messageCursor_++ = '\0';
350 argumentCurrent_ = messageCursor_;
351 typeTagsCurrent_ = end_;
353 messageIsInProgress_ = true;
359 OutboundPacketStream& OutboundPacketStream::operator<<( const MessageTerminator& rhs )
363 if( !IsMessageInProgress() )
364 throw MessageNotInProgressException();
366 int typeTagsCount = end_ - typeTagsCurrent_;
370 char *tempTypeTags = (char*)alloca(typeTagsCount);
371 memcpy( tempTypeTags, typeTagsCurrent_, typeTagsCount );
373 // slot size includes comma and null terminator
374 int typeTagSlotSize = RoundUp4( typeTagsCount + 2 );
376 uint32 argumentsSize = argumentCurrent_ - messageCursor_;
378 memmove( messageCursor_ + typeTagSlotSize, messageCursor_, argumentsSize );
380 messageCursor_[0] = ',';
381 // copy type tags in reverse (really forward) order
382 for( int i=0; i < typeTagsCount; ++i )
383 messageCursor_[i+1] = tempTypeTags[ (typeTagsCount-1) - i ];
385 char *p = messageCursor_ + 1 + typeTagsCount;
386 for( int i=0; i < (typeTagSlotSize - (typeTagsCount + 1)); ++i )
389 typeTagsCurrent_ = end_;
391 // advance messageCursor_ for next message
392 messageCursor_ += typeTagSlotSize + argumentsSize;
395 // send an empty type tags string
396 memcpy( messageCursor_, ",\0\0\0", 4 );
398 // advance messageCursor_ for next message
402 argumentCurrent_ = messageCursor_;
404 EndElement( messageCursor_ );
406 messageIsInProgress_ = false;
412 OutboundPacketStream& OutboundPacketStream::operator<<( bool rhs )
414 CheckForAvailableArgumentSpace(0);
416 *(--typeTagsCurrent_) = (char)((rhs) ? TRUE_TYPE_TAG : FALSE_TYPE_TAG);
422 OutboundPacketStream& OutboundPacketStream::operator<<( const NilType& rhs )
425 CheckForAvailableArgumentSpace(0);
427 *(--typeTagsCurrent_) = NIL_TYPE_TAG;
433 OutboundPacketStream& OutboundPacketStream::operator<<( const InfinitumType& rhs )
436 CheckForAvailableArgumentSpace(0);
438 *(--typeTagsCurrent_) = INFINITUM_TYPE_TAG;
444 OutboundPacketStream& OutboundPacketStream::operator<<( int32 rhs )
446 CheckForAvailableArgumentSpace(4);
448 *(--typeTagsCurrent_) = INT32_TYPE_TAG;
449 FromInt32( argumentCurrent_, rhs );
450 argumentCurrent_ += 4;
456 OutboundPacketStream& OutboundPacketStream::operator<<( float rhs )
458 CheckForAvailableArgumentSpace(4);
460 *(--typeTagsCurrent_) = FLOAT_TYPE_TAG;
462 #ifdef OSC_HOST_LITTLE_ENDIAN
470 argumentCurrent_[3] = u.c[0];
471 argumentCurrent_[2] = u.c[1];
472 argumentCurrent_[1] = u.c[2];
473 argumentCurrent_[0] = u.c[3];
475 *reinterpret_cast<float*>(argumentCurrent_) = rhs;
478 argumentCurrent_ += 4;
484 OutboundPacketStream& OutboundPacketStream::operator<<( char rhs )
486 CheckForAvailableArgumentSpace(4);
488 *(--typeTagsCurrent_) = CHAR_TYPE_TAG;
489 FromInt32( argumentCurrent_, rhs );
490 argumentCurrent_ += 4;
496 OutboundPacketStream& OutboundPacketStream::operator<<( const RgbaColor& rhs )
498 CheckForAvailableArgumentSpace(4);
500 *(--typeTagsCurrent_) = RGBA_COLOR_TYPE_TAG;
501 FromUInt32( argumentCurrent_, rhs );
502 argumentCurrent_ += 4;
508 OutboundPacketStream& OutboundPacketStream::operator<<( const MidiMessage& rhs )
510 CheckForAvailableArgumentSpace(4);
512 *(--typeTagsCurrent_) = MIDI_MESSAGE_TYPE_TAG;
513 FromUInt32( argumentCurrent_, rhs );
514 argumentCurrent_ += 4;
520 OutboundPacketStream& OutboundPacketStream::operator<<( int64 rhs )
522 CheckForAvailableArgumentSpace(8);
524 *(--typeTagsCurrent_) = INT64_TYPE_TAG;
525 FromInt64( argumentCurrent_, rhs );
526 argumentCurrent_ += 8;
532 OutboundPacketStream& OutboundPacketStream::operator<<( const TimeTag& rhs )
534 CheckForAvailableArgumentSpace(8);
536 *(--typeTagsCurrent_) = TIME_TAG_TYPE_TAG;
537 FromUInt64( argumentCurrent_, rhs );
538 argumentCurrent_ += 8;
544 OutboundPacketStream& OutboundPacketStream::operator<<( double rhs )
546 CheckForAvailableArgumentSpace(8);
548 *(--typeTagsCurrent_) = DOUBLE_TYPE_TAG;
550 #ifdef OSC_HOST_LITTLE_ENDIAN
558 argumentCurrent_[7] = u.c[0];
559 argumentCurrent_[6] = u.c[1];
560 argumentCurrent_[5] = u.c[2];
561 argumentCurrent_[4] = u.c[3];
562 argumentCurrent_[3] = u.c[4];
563 argumentCurrent_[2] = u.c[5];
564 argumentCurrent_[1] = u.c[6];
565 argumentCurrent_[0] = u.c[7];
567 *reinterpret_cast<double*>(argumentCurrent_) = rhs;
570 argumentCurrent_ += 8;
576 OutboundPacketStream& OutboundPacketStream::operator<<( const char *rhs )
578 CheckForAvailableArgumentSpace( RoundUp4(strlen(rhs) + 1) );
580 *(--typeTagsCurrent_) = STRING_TYPE_TAG;
581 strcpy( argumentCurrent_, rhs );
582 unsigned long rhsLength = strlen(rhs);
583 argumentCurrent_ += rhsLength + 1;
585 // zero pad to 4-byte boundary
586 unsigned long i = rhsLength + 1;
588 *argumentCurrent_++ = '\0';
596 OutboundPacketStream& OutboundPacketStream::operator<<( const Symbol& rhs )
598 CheckForAvailableArgumentSpace( RoundUp4(strlen(rhs) + 1) );
600 *(--typeTagsCurrent_) = SYMBOL_TYPE_TAG;
601 strcpy( argumentCurrent_, rhs );
602 unsigned long rhsLength = strlen(rhs);
603 argumentCurrent_ += rhsLength + 1;
605 // zero pad to 4-byte boundary
606 unsigned long i = rhsLength + 1;
608 *argumentCurrent_++ = '\0';
616 OutboundPacketStream& OutboundPacketStream::operator<<( const Blob& rhs )
618 CheckForAvailableArgumentSpace( 4 + RoundUp4(rhs.size) );
620 *(--typeTagsCurrent_) = BLOB_TYPE_TAG;
621 FromUInt32( argumentCurrent_, rhs.size );
622 argumentCurrent_ += 4;
624 memcpy( argumentCurrent_, rhs.data, rhs.size );
625 argumentCurrent_ += rhs.size;
627 // zero pad to 4-byte boundary
628 unsigned long i = rhs.size;
630 *argumentCurrent_++ = '\0';