comparison oscpack/osc/OscOutboundPacketStream.cpp @ 76:0ae87af84e2f

added oscgroups
author Rob Canning <rob@foo.net>
date Sun, 13 Jul 2014 10:07:41 +0100
parents
children
comparison
equal deleted inserted replaced
75:3a2845e3156e 76:0ae87af84e2f
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