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