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