163 lines
5.8 KiB
C
163 lines
5.8 KiB
C
|
// OSC buffer packing,
|
||
|
// - rlyeh, public domain.
|
||
|
//
|
||
|
// pack format: (i)nt, (h)int64, (t)ime, (f)loat, (s)tring, (S)ymbol, (c)har, (r)gba, (m)idi,
|
||
|
// (T)true, (N|F)nil+false, (I)nfinity, (b)lob, (d)ouble, @todo: ([)array.
|
||
|
//
|
||
|
// warning: osc_pack() generates OSC compliant messages, however,
|
||
|
// every osc_pack_va() call generates a 32-bit length prefix (in machine dependant order)
|
||
|
|
||
|
API int osc_bundle( char *buf, uint64_t ts );
|
||
|
API int osc_pack( char *buf, const char *addr, const char *fmt, ... );
|
||
|
API char* osc_pack_va( const char *addr, const char *fmt, ... );
|
||
|
|
||
|
|
||
|
#pragma once
|
||
|
/*
|
||
|
#ifdef _WIN32
|
||
|
#include <winsock2.h>
|
||
|
#pragma comment(lib, "ws2_32")
|
||
|
#else
|
||
|
#include <arpa/inet.h>
|
||
|
#endif
|
||
|
*/
|
||
|
|
||
|
int osc__buffer_vl( char *buf, const char *fmt, va_list vl ) {
|
||
|
// if `buf` is NULL, just calc needed space
|
||
|
if( !buf ) {
|
||
|
int bytes = 0;
|
||
|
while( *fmt++ ) {
|
||
|
switch( fmt[-1] ) {
|
||
|
default: // bypass
|
||
|
break; case 'T': case 'F': case 'N': case 'I': bytes += 4;
|
||
|
break; case 'i': case 'c': case 'r': case 'm': bytes += 4; (void)va_arg(vl, uint32_t);
|
||
|
break; case 't': case 'h': bytes += 8; (void)va_arg(vl, uint64_t);
|
||
|
break; case 'd': bytes += 8; (void)va_arg(vl, double);
|
||
|
break; case 'f': bytes += 4; (void)va_arg(vl, double);
|
||
|
break; case 'b': bytes += va_arg(vl, uint32_t); (void)va_arg(vl, const char *);
|
||
|
break; case 's': case 'S': bytes += strlen( va_arg(vl, const char *) ) + 1;
|
||
|
}
|
||
|
}
|
||
|
return bytes;
|
||
|
}
|
||
|
|
||
|
char *src = buf;
|
||
|
while( *fmt++ ) {
|
||
|
switch( fmt[-1] ) {
|
||
|
default: *buf++ = fmt[-1]; // bypass
|
||
|
break; case 'T': case 'F': case 'N': case 'I':
|
||
|
{}
|
||
|
break; case 'i': case 'c': case 'r': case 'm':
|
||
|
{ uint32_t i = va_arg(vl, uint32_t); i = ntohl(i); memcpy(buf, &i, 4); buf += 4; }
|
||
|
break; case 't': case 'h':
|
||
|
{ uint64_t l = va_arg(vl, uint64_t); l = ntohll(l); memcpy(buf, &l, 8); buf += 8; }
|
||
|
break; case 'd':
|
||
|
{ union { double f; uint64_t i; } u; u.f = va_arg(vl, double); u.i = ntohll(u.i); memcpy(buf, &u.i, 8); buf += 8; }
|
||
|
break; case 'f':
|
||
|
{ union { float f; uint32_t i; } u; u.f = (float)va_arg(vl, double); u.i = ntohl(u.i); memcpy(buf, &u.i, 4); buf += 4; }
|
||
|
break; case 'b':
|
||
|
{ uint32_t l = va_arg(vl, uint32_t), ll = ntohl(l); memcpy(buf, &ll, 4); buf += 4; /*}*/
|
||
|
/*{*/ const char *s = va_arg(vl, const char *); int32_t i = 0;
|
||
|
memcpy(buf, s, l); memcpy(buf + l, &i, 4); buf += l; buf += 3 - (((intptr_t)buf+3) & 3); }
|
||
|
break; case 's': case 'S':
|
||
|
{ const char *s = va_arg(vl, const char *); int32_t i = 0, l = (int32_t)strlen(s) + 1;
|
||
|
memcpy(buf, s, l); memcpy(buf + l, &i, 4); buf += l; buf += 3 - (((intptr_t)buf+3) & 3); }
|
||
|
}
|
||
|
}
|
||
|
return buf - src;
|
||
|
}
|
||
|
|
||
|
int osc__buffer( char *buf, const char *fmt, ... ) {
|
||
|
va_list vl;
|
||
|
va_start(vl, fmt);
|
||
|
int l = osc__buffer_vl(buf, fmt, vl);
|
||
|
va_end(vl);
|
||
|
return l;
|
||
|
}
|
||
|
|
||
|
int osc_pack( char *buf, const char *addr, const char *fmt, ... ) {
|
||
|
char tmp[8192];
|
||
|
va_list vl;
|
||
|
va_start(vl, fmt);
|
||
|
int l2 = osc__buffer_vl(tmp, fmt, vl);
|
||
|
va_end(vl);
|
||
|
int l1 = osc__buffer(buf, "s,s", addr, fmt);
|
||
|
memcpy(buf+l1, tmp, l2);
|
||
|
return l1+l2;
|
||
|
}
|
||
|
|
||
|
char* osc_pack_va( const char *addr, const char *fmt, ... ) { // @todo: optimize me
|
||
|
char buf[1024];
|
||
|
|
||
|
char tmp[8192];
|
||
|
va_list vl;
|
||
|
va_start(vl, fmt);
|
||
|
int l2 = osc__buffer_vl(tmp, fmt, vl);
|
||
|
va_end(vl);
|
||
|
int l1 = osc__buffer(buf, "s,s", addr, fmt);
|
||
|
memcpy(buf+l1, tmp, l2);
|
||
|
int total = l1+l2;
|
||
|
|
||
|
char *out = va("%*.s", 4+total, "");
|
||
|
memcpy(out, &total, 4);
|
||
|
memcpy(out+4, buf, total);
|
||
|
return out;
|
||
|
}
|
||
|
|
||
|
int osc_bundle( char *buf, uint64_t ts ) {
|
||
|
return osc__buffer( buf, "sh", "#bundle", ts );
|
||
|
}
|
||
|
|
||
|
#ifdef OSCPACK_DEMO
|
||
|
|
||
|
int main() {
|
||
|
// OSC message
|
||
|
{
|
||
|
char buf[4096];
|
||
|
int l = osc_pack(buf, "/foo", "iisff", 1000, -1, "hello", 1.234f, 5.678f);
|
||
|
//use as: udp_send(socket, buf+4, l-4);
|
||
|
hexdump(buf, l);
|
||
|
|
||
|
assert( 0 == memcmp( buf,
|
||
|
"\x2f\x66\x6f\x6f\x00\x00\x00\x00\x2c\x69\x69\x73\x66\x66\x00\x00"
|
||
|
"\x00\x00\x03\xe8\xff\xff\xff\xff\x68\x65\x6c\x6c\x6f\x00\x00\x00"
|
||
|
"\x3f\x9d\xf3\xb6\x40\xb5\xb2\x2d", l-4));
|
||
|
}
|
||
|
|
||
|
// OSC message (w/ initial 4-bytes payload)
|
||
|
{
|
||
|
char *buf = osc_pack_va("/foo", "iisff", 1000, -1, "hello", 1.234f, 5.678f);
|
||
|
int l = *(int*)buf;
|
||
|
printf("---------------%x\n", l);
|
||
|
hexdump(buf, l);
|
||
|
|
||
|
assert( 0 == memcmp( buf+4, //"\x28\x00\x00\x00"
|
||
|
"\x2f\x66\x6f\x6f\x00\x00\x00\x00\x2c\x69\x69\x73\x66\x66\x00\x00"
|
||
|
"\x00\x00\x03\xe8\xff\xff\xff\xff\x68\x65\x6c\x6c\x6f\x00\x00\x00"
|
||
|
"\x3f\x9d\xf3\xb6\x40\xb5\xb2\x2d", l-4));
|
||
|
}
|
||
|
|
||
|
// OSC bundle
|
||
|
{
|
||
|
// OSC bundle test taken from Julien Pommier's oscpkt.hh
|
||
|
// wr.startBundle();
|
||
|
// wr.addMessage("/foo").pushInt32(1000,-1).pushStr("hello").pushFloat(1.234f,5.678f);
|
||
|
// wr.endBundle();
|
||
|
|
||
|
char buf[4096];
|
||
|
int h = osc_bundle(buf, 1ULL);
|
||
|
int m = osc_pack(buf+h, "/foo", "iisff", 1000, -1, "hello", 1.234f, 5.678f);
|
||
|
|
||
|
int l = h+m;
|
||
|
hexdump(buf, h+m);
|
||
|
|
||
|
assert( l == 0x3c-4 );
|
||
|
assert( 0 == memcmp( buf,
|
||
|
"\x23\x62\x75\x6e\x64\x6c\x65\x00\x00\x00\x00\x00\x00\x00\x00\x01"
|
||
|
"\x2f\x66\x6f\x6f\x00\x00\x00\x00\x2c\x69\x69\x73"
|
||
|
"\x66\x66\x00\x00\x00\x00\x03\xe8\xff\xff\xff\xff\x68\x65\x6c\x6c"
|
||
|
"\x6f\x00\x00\x00\x3f\x9d\xf3\xb6\x40\xb5\xb2\x2d", l) );
|
||
|
}
|
||
|
}
|
||
|
#endif
|