Jamoma API  0.6.0.a19
OscOutboundPacketStream.cpp
1 /*
2  oscpack -- Open Sound Control (OSC) packet manipulation library
3  http://www.rossbencina.com/code/oscpack
4 
5  Copyright (c) 2004-2013 Ross Bencina <rossb@audiomulch.com>
6 
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:
14 
15  The above copyright notice and this permission notice shall be
16  included in all copies or substantial portions of the Software.
17 
18  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
22  ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
23  CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 */
26 
27 /*
28  The text above constitutes the entire oscpack license; however,
29  the oscpack developer(s) also make the following non-binding requests:
30 
31  Any person wishing to distribute modifications to the Software is
32  requested to send the modifications to the original developer so that
33  they can be incorporated into the canonical version. It is also
34  requested that these non-binding requests be included whenever the
35  above license is reproduced.
36 */
37 #include "OscOutboundPacketStream.h"
38 
39 #if defined(__WIN32__) || defined(WIN32) || defined(_WIN32)
40 #include <malloc.h> // for alloca
41 #else
42 //#include <alloca.h> // alloca on Linux (also OSX)
43 #include <stdlib.h> // alloca on OSX and FreeBSD (and Linux?)
44 #endif
45 
46 #include <cassert>
47 #include <cstring> // memcpy, memmove, strcpy, strlen
48 #include <cstddef> // ptrdiff_t
49 
50 #include "OscHostEndianness.h"
51 
52 #if defined(__BORLANDC__) // workaround for BCB4 release build intrinsics bug
53 namespace std {
54 using ::__strcpy__; // avoid error: E2316 '__strcpy__' is not a member of 'std'.
55 }
56 #endif
57 
58 namespace osc{
59 
60 static void FromInt32( char *p, int32 x )
61 {
62 #ifdef OSC_HOST_LITTLE_ENDIAN
63  union{
64  osc::int32 i;
65  char c[4];
66  } u;
67 
68  u.i = x;
69 
70  p[3] = u.c[0];
71  p[2] = u.c[1];
72  p[1] = u.c[2];
73  p[0] = u.c[3];
74 #else
75  *reinterpret_cast<int32*>(p) = x;
76 #endif
77 }
78 
79 
80 static void FromUInt32( char *p, uint32 x )
81 {
82 #ifdef OSC_HOST_LITTLE_ENDIAN
83  union{
84  osc::uint32 i;
85  char c[4];
86  } u;
87 
88  u.i = x;
89 
90  p[3] = u.c[0];
91  p[2] = u.c[1];
92  p[1] = u.c[2];
93  p[0] = u.c[3];
94 #else
95  *reinterpret_cast<uint32*>(p) = x;
96 #endif
97 }
98 
99 
100 static void FromInt64( char *p, int64 x )
101 {
102 #ifdef OSC_HOST_LITTLE_ENDIAN
103  union{
104  osc::int64 i;
105  char c[8];
106  } u;
107 
108  u.i = x;
109 
110  p[7] = u.c[0];
111  p[6] = u.c[1];
112  p[5] = u.c[2];
113  p[4] = u.c[3];
114  p[3] = u.c[4];
115  p[2] = u.c[5];
116  p[1] = u.c[6];
117  p[0] = u.c[7];
118 #else
119  *reinterpret_cast<int64*>(p) = x;
120 #endif
121 }
122 
123 
124 static void FromUInt64( char *p, uint64 x )
125 {
126 #ifdef OSC_HOST_LITTLE_ENDIAN
127  union{
128  osc::uint64 i;
129  char c[8];
130  } u;
131 
132  u.i = x;
133 
134  p[7] = u.c[0];
135  p[6] = u.c[1];
136  p[5] = u.c[2];
137  p[4] = u.c[3];
138  p[3] = u.c[4];
139  p[2] = u.c[5];
140  p[1] = u.c[6];
141  p[0] = u.c[7];
142 #else
143  *reinterpret_cast<uint64*>(p) = x;
144 #endif
145 }
146 
147 
148 // round up to the next highest multiple of 4. unless x is already a multiple of 4
149 static inline std::size_t RoundUp4( std::size_t x )
150 {
151  return (x + 3) & ~((std::size_t)0x03);
152 }
153 
154 
155 OutboundPacketStream::OutboundPacketStream( char *buffer, std::size_t capacity )
156  : data_( buffer )
157  , end_( data_ + capacity )
158  , typeTagsCurrent_( end_ )
159  , messageCursor_( data_ )
160  , argumentCurrent_( data_ )
161  , elementSizePtr_( 0 )
162  , messageIsInProgress_( false )
163 {
164  // sanity check integer types declared in OscTypes.h
165  // you'll need to fix OscTypes.h if any of these asserts fail
166  assert( sizeof(osc::int32) == 4 );
167  assert( sizeof(osc::uint32) == 4 );
168  assert( sizeof(osc::int64) == 8 );
169  assert( sizeof(osc::uint64) == 8 );
170 }
171 
172 
173 OutboundPacketStream::~OutboundPacketStream()
174 {
175 
176 }
177 
178 
179 char *OutboundPacketStream::BeginElement( char *beginPtr )
180 {
181  if( elementSizePtr_ == 0 ){
182 
183  elementSizePtr_ = reinterpret_cast<uint32*>(data_);
184 
185  return beginPtr;
186 
187  }else{
188  // store an offset to the old element size ptr in the element size slot
189  // we store an offset rather than the actual pointer to be 64 bit clean.
190  *reinterpret_cast<uint32*>(beginPtr) =
191  (uint32)(reinterpret_cast<char*>(elementSizePtr_) - data_);
192 
193  elementSizePtr_ = reinterpret_cast<uint32*>(beginPtr);
194 
195  return beginPtr + 4;
196  }
197 }
198 
199 
200 void OutboundPacketStream::EndElement( char *endPtr )
201 {
202  assert( elementSizePtr_ != 0 );
203 
204  if( elementSizePtr_ == reinterpret_cast<uint32*>(data_) ){
205 
206  elementSizePtr_ = 0;
207 
208  }else{
209  // while building an element, an offset to the containing element's
210  // size slot is stored in the elements size slot (or a ptr to data_
211  // if there is no containing element). We retrieve that here
212  uint32 *previousElementSizePtr =
213  reinterpret_cast<uint32*>(data_ + *elementSizePtr_);
214 
215  // then we store the element size in the slot. note that the element
216  // size does not include the size slot, hence the - 4 below.
217 
218  std::ptrdiff_t d = endPtr - reinterpret_cast<char*>(elementSizePtr_);
219  // assert( d >= 4 && d <= 0x7FFFFFFF ); // assume packets smaller than 2Gb
220 
221  uint32 elementSize = static_cast<uint32>(d - 4);
222  FromUInt32( reinterpret_cast<char*>(elementSizePtr_), elementSize );
223 
224  // finally, we reset the element size ptr to the containing element
225  elementSizePtr_ = previousElementSizePtr;
226  }
227 }
228 
229 
230 bool OutboundPacketStream::ElementSizeSlotRequired() const
231 {
232  return (elementSizePtr_ != 0);
233 }
234 
235 
236 void OutboundPacketStream::CheckForAvailableBundleSpace()
237 {
238  std::size_t required = Size() + ((ElementSizeSlotRequired())?4:0) + 16;
239 
240  if( required > Capacity() )
241  throw OutOfBufferMemoryException();
242 }
243 
244 
245 void OutboundPacketStream::CheckForAvailableMessageSpace( const char *addressPattern )
246 {
247  // plus 4 for at least four bytes of type tag
248  std::size_t required = Size() + ((ElementSizeSlotRequired())?4:0)
249  + RoundUp4(std::strlen(addressPattern) + 1) + 4;
250 
251  if( required > Capacity() )
252  throw OutOfBufferMemoryException();
253 }
254 
255 
256 void OutboundPacketStream::CheckForAvailableArgumentSpace( std::size_t argumentLength )
257 {
258  // plus three for extra type tag, comma and null terminator
259  std::size_t required = (argumentCurrent_ - data_) + argumentLength
260  + RoundUp4( (end_ - typeTagsCurrent_) + 3 );
261 
262  if( required > Capacity() )
263  throw OutOfBufferMemoryException();
264 }
265 
266 
267 void OutboundPacketStream::Clear()
268 {
269  typeTagsCurrent_ = end_;
270  messageCursor_ = data_;
271  argumentCurrent_ = data_;
272  elementSizePtr_ = 0;
273  messageIsInProgress_ = false;
274 }
275 
276 
277 std::size_t OutboundPacketStream::Capacity() const
278 {
279  return end_ - data_;
280 }
281 
282 
283 std::size_t OutboundPacketStream::Size() const
284 {
285  std::size_t result = argumentCurrent_ - data_;
286  if( IsMessageInProgress() ){
287  // account for the length of the type tag string. the total type tag
288  // includes an initial comma, plus at least one terminating \0
289  result += RoundUp4( (end_ - typeTagsCurrent_) + 2 );
290  }
291 
292  return result;
293 }
294 
295 
296 const char *OutboundPacketStream::Data() const
297 {
298  return data_;
299 }
300 
301 
302 bool OutboundPacketStream::IsReady() const
303 {
304  return (!IsMessageInProgress() && !IsBundleInProgress());
305 }
306 
307 
308 bool OutboundPacketStream::IsMessageInProgress() const
309 {
310  return messageIsInProgress_;
311 }
312 
313 
314 bool OutboundPacketStream::IsBundleInProgress() const
315 {
316  return (elementSizePtr_ != 0);
317 }
318 
319 
320 OutboundPacketStream& OutboundPacketStream::operator<<( const BundleInitiator& rhs )
321 {
322  if( IsMessageInProgress() )
323  throw MessageInProgressException();
324 
325  CheckForAvailableBundleSpace();
326 
327  messageCursor_ = BeginElement( messageCursor_ );
328 
329  std::memcpy( messageCursor_, "#bundle\0", 8 );
330  FromUInt64( messageCursor_ + 8, rhs.timeTag );
331 
332  messageCursor_ += 16;
333  argumentCurrent_ = messageCursor_;
334 
335  return *this;
336 }
337 
338 
339 OutboundPacketStream& OutboundPacketStream::operator<<( const BundleTerminator& rhs )
340 {
341  (void) rhs;
342 
343  if( !IsBundleInProgress() )
344  throw BundleNotInProgressException();
345  if( IsMessageInProgress() )
346  throw MessageInProgressException();
347 
348  EndElement( messageCursor_ );
349 
350  return *this;
351 }
352 
353 
354 OutboundPacketStream& OutboundPacketStream::operator<<( const BeginMessage& rhs )
355 {
356  if( IsMessageInProgress() )
357  throw MessageInProgressException();
358 
359  CheckForAvailableMessageSpace( rhs.addressPattern );
360 
361  messageCursor_ = BeginElement( messageCursor_ );
362 
363  std::strcpy( messageCursor_, rhs.addressPattern );
364  std::size_t rhsLength = std::strlen(rhs.addressPattern);
365  messageCursor_ += rhsLength + 1;
366 
367  // zero pad to 4-byte boundary
368  std::size_t i = rhsLength + 1;
369  while( i & 0x3 ){
370  *messageCursor_++ = '\0';
371  ++i;
372  }
373 
374  argumentCurrent_ = messageCursor_;
375  typeTagsCurrent_ = end_;
376 
377  messageIsInProgress_ = true;
378 
379  return *this;
380 }
381 
382 
383 OutboundPacketStream& OutboundPacketStream::operator<<( const MessageTerminator& rhs )
384 {
385  (void) rhs;
386 
387  if( !IsMessageInProgress() )
388  throw MessageNotInProgressException();
389 
390  std::size_t typeTagsCount = end_ - typeTagsCurrent_;
391 
392  if( typeTagsCount ){
393 
394  char *tempTypeTags = (char*)alloca(typeTagsCount);
395  std::memcpy( tempTypeTags, typeTagsCurrent_, typeTagsCount );
396 
397  // slot size includes comma and null terminator
398  std::size_t typeTagSlotSize = RoundUp4( typeTagsCount + 2 );
399 
400  std::size_t argumentsSize = argumentCurrent_ - messageCursor_;
401 
402  std::memmove( messageCursor_ + typeTagSlotSize, messageCursor_, argumentsSize );
403 
404  messageCursor_[0] = ',';
405  // copy type tags in reverse (really forward) order
406  for( std::size_t i=0; i < typeTagsCount; ++i )
407  messageCursor_[i+1] = tempTypeTags[ (typeTagsCount-1) - i ];
408 
409  char *p = messageCursor_ + 1 + typeTagsCount;
410  for( std::size_t i=0; i < (typeTagSlotSize - (typeTagsCount + 1)); ++i )
411  *p++ = '\0';
412 
413  typeTagsCurrent_ = end_;
414 
415  // advance messageCursor_ for next message
416  messageCursor_ += typeTagSlotSize + argumentsSize;
417 
418  }else{
419  // send an empty type tags string
420  std::memcpy( messageCursor_, ",\0\0\0", 4 );
421 
422  // advance messageCursor_ for next message
423  messageCursor_ += 4;
424  }
425 
426  argumentCurrent_ = messageCursor_;
427 
428  EndElement( messageCursor_ );
429 
430  messageIsInProgress_ = false;
431 
432  return *this;
433 }
434 
435 
436 OutboundPacketStream& OutboundPacketStream::operator<<( bool rhs )
437 {
438  CheckForAvailableArgumentSpace(0);
439 
440  *(--typeTagsCurrent_) = (char)((rhs) ? TRUE_TYPE_TAG : FALSE_TYPE_TAG);
441 
442  return *this;
443 }
444 
445 
446 OutboundPacketStream& OutboundPacketStream::operator<<( const NilType& rhs )
447 {
448  (void) rhs;
449  CheckForAvailableArgumentSpace(0);
450 
451  *(--typeTagsCurrent_) = NIL_TYPE_TAG;
452 
453  return *this;
454 }
455 
456 
457 OutboundPacketStream& OutboundPacketStream::operator<<( const InfinitumType& rhs )
458 {
459  (void) rhs;
460  CheckForAvailableArgumentSpace(0);
461 
462  *(--typeTagsCurrent_) = INFINITUM_TYPE_TAG;
463 
464  return *this;
465 }
466 
467 
468 OutboundPacketStream& OutboundPacketStream::operator<<( int32 rhs )
469 {
470  CheckForAvailableArgumentSpace(4);
471 
472  *(--typeTagsCurrent_) = INT32_TYPE_TAG;
473  FromInt32( argumentCurrent_, rhs );
474  argumentCurrent_ += 4;
475 
476  return *this;
477 }
478 
479 
480 OutboundPacketStream& OutboundPacketStream::operator<<( float rhs )
481 {
482  CheckForAvailableArgumentSpace(4);
483 
484  *(--typeTagsCurrent_) = FLOAT_TYPE_TAG;
485 
486 #ifdef OSC_HOST_LITTLE_ENDIAN
487  union{
488  float f;
489  char c[4];
490  } u;
491 
492  u.f = rhs;
493 
494  argumentCurrent_[3] = u.c[0];
495  argumentCurrent_[2] = u.c[1];
496  argumentCurrent_[1] = u.c[2];
497  argumentCurrent_[0] = u.c[3];
498 #else
499  *reinterpret_cast<float*>(argumentCurrent_) = rhs;
500 #endif
501 
502  argumentCurrent_ += 4;
503 
504  return *this;
505 }
506 
507 
508 OutboundPacketStream& OutboundPacketStream::operator<<( char rhs )
509 {
510  CheckForAvailableArgumentSpace(4);
511 
512  *(--typeTagsCurrent_) = CHAR_TYPE_TAG;
513  FromInt32( argumentCurrent_, rhs );
514  argumentCurrent_ += 4;
515 
516  return *this;
517 }
518 
519 
520 OutboundPacketStream& OutboundPacketStream::operator<<( const RgbaColor& rhs )
521 {
522  CheckForAvailableArgumentSpace(4);
523 
524  *(--typeTagsCurrent_) = RGBA_COLOR_TYPE_TAG;
525  FromUInt32( argumentCurrent_, rhs );
526  argumentCurrent_ += 4;
527 
528  return *this;
529 }
530 
531 
532 OutboundPacketStream& OutboundPacketStream::operator<<( const MidiMessage& rhs )
533 {
534  CheckForAvailableArgumentSpace(4);
535 
536  *(--typeTagsCurrent_) = MIDI_MESSAGE_TYPE_TAG;
537  FromUInt32( argumentCurrent_, rhs );
538  argumentCurrent_ += 4;
539 
540  return *this;
541 }
542 
543 
544 OutboundPacketStream& OutboundPacketStream::operator<<( int64 rhs )
545 {
546  CheckForAvailableArgumentSpace(8);
547 
548  *(--typeTagsCurrent_) = INT64_TYPE_TAG;
549  FromInt64( argumentCurrent_, rhs );
550  argumentCurrent_ += 8;
551 
552  return *this;
553 }
554 
555 
556 OutboundPacketStream& OutboundPacketStream::operator<<( const TimeTag& rhs )
557 {
558  CheckForAvailableArgumentSpace(8);
559 
560  *(--typeTagsCurrent_) = TIME_TAG_TYPE_TAG;
561  FromUInt64( argumentCurrent_, rhs );
562  argumentCurrent_ += 8;
563 
564  return *this;
565 }
566 
567 
568 OutboundPacketStream& OutboundPacketStream::operator<<( double rhs )
569 {
570  CheckForAvailableArgumentSpace(8);
571 
572  *(--typeTagsCurrent_) = DOUBLE_TYPE_TAG;
573 
574 #ifdef OSC_HOST_LITTLE_ENDIAN
575  union{
576  double f;
577  char c[8];
578  } u;
579 
580  u.f = rhs;
581 
582  argumentCurrent_[7] = u.c[0];
583  argumentCurrent_[6] = u.c[1];
584  argumentCurrent_[5] = u.c[2];
585  argumentCurrent_[4] = u.c[3];
586  argumentCurrent_[3] = u.c[4];
587  argumentCurrent_[2] = u.c[5];
588  argumentCurrent_[1] = u.c[6];
589  argumentCurrent_[0] = u.c[7];
590 #else
591  *reinterpret_cast<double*>(argumentCurrent_) = rhs;
592 #endif
593 
594  argumentCurrent_ += 8;
595 
596  return *this;
597 }
598 
599 
600 OutboundPacketStream& OutboundPacketStream::operator<<( const char *rhs )
601 {
602  CheckForAvailableArgumentSpace( RoundUp4(std::strlen(rhs) + 1) );
603 
604  *(--typeTagsCurrent_) = STRING_TYPE_TAG;
605  std::strcpy( argumentCurrent_, rhs );
606  std::size_t rhsLength = std::strlen(rhs);
607  argumentCurrent_ += rhsLength + 1;
608 
609  // zero pad to 4-byte boundary
610  std::size_t i = rhsLength + 1;
611  while( i & 0x3 ){
612  *argumentCurrent_++ = '\0';
613  ++i;
614  }
615 
616  return *this;
617 }
618 
619 
620 OutboundPacketStream& OutboundPacketStream::operator<<( const Symbol& rhs )
621 {
622  CheckForAvailableArgumentSpace( RoundUp4(std::strlen(rhs) + 1) );
623 
624  *(--typeTagsCurrent_) = SYMBOL_TYPE_TAG;
625  std::strcpy( argumentCurrent_, rhs );
626  std::size_t rhsLength = std::strlen(rhs);
627  argumentCurrent_ += rhsLength + 1;
628 
629  // zero pad to 4-byte boundary
630  std::size_t i = rhsLength + 1;
631  while( i & 0x3 ){
632  *argumentCurrent_++ = '\0';
633  ++i;
634  }
635 
636  return *this;
637 }
638 
639 
640 OutboundPacketStream& OutboundPacketStream::operator<<( const Blob& rhs )
641 {
642  CheckForAvailableArgumentSpace( 4 + RoundUp4(rhs.size) );
643 
644  *(--typeTagsCurrent_) = BLOB_TYPE_TAG;
645  FromUInt32( argumentCurrent_, rhs.size );
646  argumentCurrent_ += 4;
647 
648  std::memcpy( argumentCurrent_, rhs.data, rhs.size );
649  argumentCurrent_ += rhs.size;
650 
651  // zero pad to 4-byte boundary
652  unsigned long i = rhs.size;
653  while( i & 0x3 ){
654  *argumentCurrent_++ = '\0';
655  ++i;
656  }
657 
658  return *this;
659 }
660 
661 OutboundPacketStream& OutboundPacketStream::operator<<( const ArrayInitiator& rhs )
662 {
663  (void) rhs;
664  CheckForAvailableArgumentSpace(0);
665 
666  *(--typeTagsCurrent_) = ARRAY_BEGIN_TYPE_TAG;
667 
668  return *this;
669 }
670 
671 OutboundPacketStream& OutboundPacketStream::operator<<( const ArrayTerminator& rhs )
672 {
673  (void) rhs;
674  CheckForAvailableArgumentSpace(0);
675 
676  *(--typeTagsCurrent_) = ARRAY_END_TYPE_TAG;
677 
678  return *this;
679 }
680 
681 } // namespace osc
682 
683 
STL namespace.