v4k-git-backup/tools/labs/oscpack.h

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