]> git.sesse.net Git - casparcg/blob - protocol/osc/oscpack/OscOutboundPacketStream.cpp
- Implemented real-time state notification using OSC-UDP.
[casparcg] / protocol / osc / oscpack / OscOutboundPacketStream.cpp
1 /*
2         oscpack -- Open Sound Control packet manipulation library
3         http://www.audiomulch.com/~rossb/oscpack
4
5         Copyright (c) 2004-2005 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         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.
21
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.
29 */
30 #undef _CRT_SECURE_NO_WARNINGS
31 #define _CRT_SECURE_NO_WARNINGS
32
33 #include "OscOutboundPacketStream.h"
34
35 #include <string.h>
36 #include <stdlib.h>
37 #include <assert.h>
38
39 #if defined(__WIN32__) || defined(WIN32)
40 #include <malloc.h> // for alloca
41 #endif
42
43 #include "OscHostEndianness.h"
44
45
46 namespace osc{
47
48 static void FromInt32( char *p, int32 x )
49 {
50 #ifdef OSC_HOST_LITTLE_ENDIAN
51     union{
52         osc::int32 i;
53         char c[4];
54     } u;
55
56     u.i = x;
57
58     p[3] = u.c[0];
59     p[2] = u.c[1];
60     p[1] = u.c[2];
61     p[0] = u.c[3];
62 #else
63     *reinterpret_cast<int32*>(p) = x;
64 #endif
65 }
66
67
68 static void FromUInt32( char *p, uint32 x )
69 {
70 #ifdef OSC_HOST_LITTLE_ENDIAN
71     union{
72         osc::uint32 i;
73         char c[4];
74     } u;
75
76     u.i = x;
77
78     p[3] = u.c[0];
79     p[2] = u.c[1];
80     p[1] = u.c[2];
81     p[0] = u.c[3];
82 #else
83     *reinterpret_cast<uint32*>(p) = x;
84 #endif
85 }
86
87
88 static void FromInt64( char *p, int64 x )
89 {
90 #ifdef OSC_HOST_LITTLE_ENDIAN
91     union{
92         osc::int64 i;
93         char c[8];
94     } u;
95
96     u.i = x;
97
98     p[7] = u.c[0];
99     p[6] = u.c[1];
100     p[5] = u.c[2];
101     p[4] = u.c[3];
102     p[3] = u.c[4];
103     p[2] = u.c[5];
104     p[1] = u.c[6];
105     p[0] = u.c[7];
106 #else
107     *reinterpret_cast<int64*>(p) = x;
108 #endif
109 }
110
111
112 static void FromUInt64( char *p, uint64 x )
113 {
114 #ifdef OSC_HOST_LITTLE_ENDIAN
115     union{
116         osc::uint64 i;
117         char c[8];
118     } u;
119
120     u.i = x;
121
122     p[7] = u.c[0];
123     p[6] = u.c[1];
124     p[5] = u.c[2];
125     p[4] = u.c[3];
126     p[3] = u.c[4];
127     p[2] = u.c[5];
128     p[1] = u.c[6];
129     p[0] = u.c[7];
130 #else
131     *reinterpret_cast<uint64*>(p) = x;
132 #endif
133 }
134
135
136 static inline long RoundUp4( long x )
137 {
138     return ((x-1) & (~0x03L)) + 4;
139 }
140
141
142 OutboundPacketStream::OutboundPacketStream( char *buffer, unsigned long capacity )
143     : data_( buffer )
144     , end_( data_ + capacity )
145     , typeTagsCurrent_( end_ )
146     , messageCursor_( data_ )
147     , argumentCurrent_( data_ )
148     , elementSizePtr_( 0 )
149     , messageIsInProgress_( false )
150 {
151
152 }
153
154
155 OutboundPacketStream::~OutboundPacketStream()
156 {
157
158 }
159
160
161 char *OutboundPacketStream::BeginElement( char *beginPtr )
162 {
163     if( elementSizePtr_ == 0 ){
164
165         elementSizePtr_ = reinterpret_cast<uint32*>(data_);
166
167         return beginPtr;
168
169     }else{
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_);
174
175         elementSizePtr_ = reinterpret_cast<uint32*>(beginPtr);
176
177         return beginPtr + 4;
178     }
179 }
180
181
182 void OutboundPacketStream::EndElement( char *endPtr )
183 {
184     assert( elementSizePtr_ != 0 );
185
186     if( elementSizePtr_ == reinterpret_cast<uint32*>(data_) ){
187
188         elementSizePtr_ = 0;
189
190     }else{
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_));
196
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.
199         uint32 elementSize =
200                 static_cast<uint32>(endPtr - reinterpret_cast<char*>(elementSizePtr_)) - 4;
201         FromUInt32( reinterpret_cast<char*>(elementSizePtr_), elementSize );
202
203         // finally, we reset the element size ptr to the containing element
204         elementSizePtr_ = previousElementSizePtr;
205     }
206 }
207
208
209 bool OutboundPacketStream::ElementSizeSlotRequired() const
210 {
211     return (elementSizePtr_ != 0);
212 }
213
214
215 void OutboundPacketStream::CheckForAvailableBundleSpace()
216 {
217     unsigned long required = Size() + ((ElementSizeSlotRequired())?4:0) + 16;
218
219     if( required > Capacity() )
220         throw OutOfBufferMemoryException();
221 }
222
223
224 void OutboundPacketStream::CheckForAvailableMessageSpace( const char *addressPattern )
225 {
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;
229
230     if( required > Capacity() )
231         throw OutOfBufferMemoryException();
232 }
233
234
235 void OutboundPacketStream::CheckForAvailableArgumentSpace( long argumentLength )
236 {
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 );
240
241     if( required > Capacity() )
242         throw OutOfBufferMemoryException();
243 }
244
245
246 void OutboundPacketStream::Clear()
247 {
248     typeTagsCurrent_ = end_;
249     messageCursor_ = data_;
250     argumentCurrent_ = data_;
251     elementSizePtr_ = 0;
252     messageIsInProgress_ = false;
253 }
254
255
256 unsigned int OutboundPacketStream::Capacity() const
257 {
258     return static_cast<int>(end_ - data_);
259 }
260
261
262 unsigned int OutboundPacketStream::Size() const
263 {
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 );
269     }
270
271     return result;
272 }
273
274
275 const char *OutboundPacketStream::Data() const
276 {
277     return data_;
278 }
279
280
281 bool OutboundPacketStream::IsReady() const
282 {
283     return (!IsMessageInProgress() && !IsBundleInProgress());
284 }
285
286
287 bool OutboundPacketStream::IsMessageInProgress() const
288 {
289     return messageIsInProgress_;
290 }
291
292
293 bool OutboundPacketStream::IsBundleInProgress() const
294 {
295     return (elementSizePtr_ != 0);
296 }
297
298
299 OutboundPacketStream& OutboundPacketStream::operator<<( const BundleInitiator& rhs )
300 {
301     if( IsMessageInProgress() )
302         throw MessageInProgressException();
303
304     CheckForAvailableBundleSpace();
305
306     messageCursor_ = BeginElement( messageCursor_ );
307
308     memcpy( messageCursor_, "#bundle\0", 8 );
309     FromUInt64( messageCursor_ + 8, rhs.timeTag );
310
311     messageCursor_ += 16;
312     argumentCurrent_ = messageCursor_;
313
314     return *this;
315 }
316
317
318 OutboundPacketStream& OutboundPacketStream::operator<<( const BundleTerminator& rhs )
319 {
320     (void) rhs;
321
322     if( !IsBundleInProgress() )
323         throw BundleNotInProgressException();
324     if( IsMessageInProgress() )
325         throw MessageInProgressException();
326
327     EndElement( messageCursor_ );
328
329     return *this;
330 }
331
332
333 OutboundPacketStream& OutboundPacketStream::operator<<( const BeginMessage& rhs )
334 {
335     if( IsMessageInProgress() )
336         throw MessageInProgressException();
337
338     CheckForAvailableMessageSpace( rhs.addressPattern );
339
340     messageCursor_ = BeginElement( messageCursor_ );
341
342     strcpy( messageCursor_, rhs.addressPattern );
343     unsigned long rhsLength = static_cast<unsigned long>(strlen(rhs.addressPattern));
344     messageCursor_ += rhsLength + 1;
345
346     // zero pad to 4-byte boundary
347     unsigned long i = rhsLength + 1;
348     while( i & 0x3 ){
349         *messageCursor_++ = '\0';
350         ++i;
351     }
352
353     argumentCurrent_ = messageCursor_;
354     typeTagsCurrent_ = end_;
355
356     messageIsInProgress_ = true;
357
358     return *this;
359 }
360
361
362 OutboundPacketStream& OutboundPacketStream::operator<<( const MessageTerminator& rhs )
363 {
364     (void) rhs;
365
366     if( !IsMessageInProgress() )
367         throw MessageNotInProgressException();
368
369     int typeTagsCount = static_cast<int>(end_ - typeTagsCurrent_);
370
371     if( typeTagsCount ){
372
373         char *tempTypeTags = (char*)alloca(typeTagsCount);
374         memcpy( tempTypeTags, typeTagsCurrent_, typeTagsCount );
375
376         // slot size includes comma and null terminator
377         int typeTagSlotSize = RoundUp4( typeTagsCount + 2 );
378
379         uint32 argumentsSize = static_cast<uint32>(argumentCurrent_ - messageCursor_);
380
381         memmove( messageCursor_ + typeTagSlotSize, messageCursor_, argumentsSize );
382
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 ];
387
388         char *p = messageCursor_ + 1 + typeTagsCount;
389         for( int i=0; i < (typeTagSlotSize - (typeTagsCount + 1)); ++i )
390             *p++ = '\0';
391
392         typeTagsCurrent_ = end_;
393
394         // advance messageCursor_ for next message
395         messageCursor_ += typeTagSlotSize + argumentsSize;
396
397     }else{
398         // send an empty type tags string
399         memcpy( messageCursor_, ",\0\0\0", 4 );
400
401         // advance messageCursor_ for next message
402         messageCursor_ += 4;
403     }
404
405     argumentCurrent_ = messageCursor_;
406
407     EndElement( messageCursor_ );
408
409     messageIsInProgress_ = false;
410
411     return *this;
412 }
413
414
415 OutboundPacketStream& OutboundPacketStream::operator<<( bool rhs )
416 {
417     CheckForAvailableArgumentSpace(0);
418
419     *(--typeTagsCurrent_) = (char)((rhs) ? TRUE_TYPE_TAG : FALSE_TYPE_TAG);
420
421     return *this;
422 }
423
424
425 OutboundPacketStream& OutboundPacketStream::operator<<( const NilType& rhs )
426 {
427     (void) rhs;
428     CheckForAvailableArgumentSpace(0);
429
430     *(--typeTagsCurrent_) = NIL_TYPE_TAG;
431
432     return *this;
433 }
434
435
436 OutboundPacketStream& OutboundPacketStream::operator<<( const InfinitumType& rhs )
437 {
438     (void) rhs;
439     CheckForAvailableArgumentSpace(0);
440
441     *(--typeTagsCurrent_) = INFINITUM_TYPE_TAG;
442
443     return *this;
444 }
445
446
447 OutboundPacketStream& OutboundPacketStream::operator<<( int32 rhs )
448 {
449     CheckForAvailableArgumentSpace(4);
450
451     *(--typeTagsCurrent_) = INT32_TYPE_TAG;
452     FromInt32( argumentCurrent_, rhs );
453     argumentCurrent_ += 4;
454
455     return *this;
456 }
457
458
459 OutboundPacketStream& OutboundPacketStream::operator<<( float rhs )
460 {
461     CheckForAvailableArgumentSpace(4);
462
463     *(--typeTagsCurrent_) = FLOAT_TYPE_TAG;
464
465 #ifdef OSC_HOST_LITTLE_ENDIAN
466     union{
467         float f;
468         char c[4];
469     } u;
470
471     u.f = rhs;
472
473     argumentCurrent_[3] = u.c[0];
474     argumentCurrent_[2] = u.c[1];
475     argumentCurrent_[1] = u.c[2];
476     argumentCurrent_[0] = u.c[3];
477 #else
478     *reinterpret_cast<float*>(argumentCurrent_) = rhs;
479 #endif
480
481     argumentCurrent_ += 4;
482
483     return *this;
484 }
485
486
487 OutboundPacketStream& OutboundPacketStream::operator<<( char rhs )
488 {
489     CheckForAvailableArgumentSpace(4);
490
491     *(--typeTagsCurrent_) = CHAR_TYPE_TAG;
492     FromInt32( argumentCurrent_, rhs );
493     argumentCurrent_ += 4;
494
495     return *this;
496 }
497
498
499 OutboundPacketStream& OutboundPacketStream::operator<<( const RgbaColor& rhs )
500 {
501     CheckForAvailableArgumentSpace(4);
502
503     *(--typeTagsCurrent_) = RGBA_COLOR_TYPE_TAG;
504     FromUInt32( argumentCurrent_, rhs );
505     argumentCurrent_ += 4;
506
507     return *this;
508 }
509
510
511 OutboundPacketStream& OutboundPacketStream::operator<<( const MidiMessage& rhs )
512 {
513     CheckForAvailableArgumentSpace(4);
514
515     *(--typeTagsCurrent_) = MIDI_MESSAGE_TYPE_TAG;
516     FromUInt32( argumentCurrent_, rhs );
517     argumentCurrent_ += 4;
518
519     return *this;
520 }
521
522
523 OutboundPacketStream& OutboundPacketStream::operator<<( int64 rhs )
524 {
525     CheckForAvailableArgumentSpace(8);
526
527     *(--typeTagsCurrent_) = INT64_TYPE_TAG;
528     FromInt64( argumentCurrent_, rhs );
529     argumentCurrent_ += 8;
530
531     return *this;
532 }
533
534
535 OutboundPacketStream& OutboundPacketStream::operator<<( const TimeTag& rhs )
536 {
537     CheckForAvailableArgumentSpace(8);
538
539     *(--typeTagsCurrent_) = TIME_TAG_TYPE_TAG;
540     FromUInt64( argumentCurrent_, rhs );
541     argumentCurrent_ += 8;
542
543     return *this;
544 }
545
546
547 OutboundPacketStream& OutboundPacketStream::operator<<( double rhs )
548 {
549     CheckForAvailableArgumentSpace(8);
550
551     *(--typeTagsCurrent_) = DOUBLE_TYPE_TAG;
552
553 #ifdef OSC_HOST_LITTLE_ENDIAN
554     union{
555         double f;
556         char c[8];
557     } u;
558
559     u.f = rhs;
560
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];
569 #else
570     *reinterpret_cast<double*>(argumentCurrent_) = rhs;
571 #endif
572
573     argumentCurrent_ += 8;
574
575     return *this;
576 }
577
578
579 OutboundPacketStream& OutboundPacketStream::operator<<( const char *rhs )
580 {
581     CheckForAvailableArgumentSpace( RoundUp4(static_cast<long>(strlen(rhs)) + 1) );
582
583     *(--typeTagsCurrent_) = STRING_TYPE_TAG;
584     strcpy( argumentCurrent_, rhs );
585     unsigned long rhsLength = static_cast<unsigned long>(strlen(rhs));
586     argumentCurrent_ += rhsLength + 1;
587
588     // zero pad to 4-byte boundary
589     unsigned long i = rhsLength + 1;
590     while( i & 0x3 ){
591         *argumentCurrent_++ = '\0';
592         ++i;
593     }
594
595     return *this;
596 }
597
598
599 OutboundPacketStream& OutboundPacketStream::operator<<( const Symbol& rhs )
600 {
601     CheckForAvailableArgumentSpace( RoundUp4(static_cast<long>(strlen(rhs)) + 1) );
602
603     *(--typeTagsCurrent_) = SYMBOL_TYPE_TAG;
604     strcpy( argumentCurrent_, rhs );
605     unsigned long rhsLength = static_cast<unsigned long>(strlen(rhs));
606     argumentCurrent_ += rhsLength + 1;
607
608     // zero pad to 4-byte boundary
609     unsigned long i = rhsLength + 1;
610     while( i & 0x3 ){
611         *argumentCurrent_++ = '\0';
612         ++i;
613     }
614
615     return *this;
616 }
617
618
619 OutboundPacketStream& OutboundPacketStream::operator<<( const Blob& rhs )
620 {
621     CheckForAvailableArgumentSpace( 4 + RoundUp4(rhs.size) );
622
623     *(--typeTagsCurrent_) = BLOB_TYPE_TAG;
624     FromUInt32( argumentCurrent_, rhs.size );
625     argumentCurrent_ += 4;
626     
627     memcpy( argumentCurrent_, rhs.data, rhs.size );
628     argumentCurrent_ += rhs.size;
629
630     // zero pad to 4-byte boundary
631     unsigned long i = rhs.size;
632     while( i & 0x3 ){
633         *argumentCurrent_++ = '\0';
634         ++i;
635     }
636
637     return *this;
638 }
639
640 } // namespace osc
641
642