annotate src/liblo-0.26/src/message.c @ 33:879bdc878826

Ah, we've already done this. Merge, taking everything from the branch with the older commits.
author Chris Cannam
date Fri, 04 Jul 2014 10:37:37 +0100
parents e13257ea84a4
children
rev   line source
Chris@4 1 /*
Chris@4 2 * Copyright (C) 2004 Steve Harris
Chris@4 3 *
Chris@4 4 * This program is free software; you can redistribute it and/or modify
Chris@4 5 * it under the terms of the GNU Lesser General Public License as
Chris@4 6 * published by the Free Software Foundation; either version 2.1 of the
Chris@4 7 * License, or (at your option) any later version.
Chris@4 8 *
Chris@4 9 * This program is distributed in the hope that it will be useful,
Chris@4 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Chris@4 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Chris@4 12 * GNU Lesser General Public License for more details.
Chris@4 13 *
Chris@4 14 * $Id$
Chris@4 15 */
Chris@4 16
Chris@4 17 #include <stdarg.h>
Chris@4 18 #include <stdlib.h>
Chris@4 19 #include <stdio.h>
Chris@4 20 #include <string.h>
Chris@4 21 #include <math.h>
Chris@4 22
Chris@4 23 #ifdef WIN32
Chris@4 24 #include <winsock2.h>
Chris@4 25 #include <ws2tcpip.h>
Chris@4 26 #else
Chris@4 27 #include <netinet/in.h>
Chris@4 28 #endif
Chris@4 29
Chris@4 30 #include "lo_types_internal.h"
Chris@4 31 #include "lo_internal.h"
Chris@4 32 #include "lo/lo.h"
Chris@4 33
Chris@4 34 #define LO_DEF_TYPE_SIZE 8
Chris@4 35 #define LO_DEF_DATA_SIZE 8
Chris@4 36
Chris@4 37 static char lo_numerical_types[] = {
Chris@4 38 LO_INT32,
Chris@4 39 LO_FLOAT,
Chris@4 40 LO_INT64,
Chris@4 41 LO_DOUBLE,
Chris@4 42 '\0'
Chris@4 43 };
Chris@4 44
Chris@4 45 static char lo_string_types[] = {
Chris@4 46 LO_STRING,
Chris@4 47 LO_SYMBOL,
Chris@4 48 '\0'
Chris@4 49 };
Chris@4 50
Chris@4 51 static int lo_message_add_typechar(lo_message m, char t);
Chris@4 52 static void *lo_message_add_data(lo_message m, size_t s);
Chris@4 53 void lo_arg_pp_internal(lo_type type, void *data, int bigendian);
Chris@4 54
Chris@4 55 // Used for calculating new sizes when expanding message data buffers.
Chris@4 56 // Note that log(x)/0.69315 = log2(x): this simply finds the next
Chris@4 57 // highest power of 2.
Chris@4 58 #if 1
Chris@4 59 #define lo_pow2_over(a,b) \
Chris@4 60 a = ((b > a) ? (a << ((int)((log(((double)b/(double)a))/0.69315)+1))) : a);
Chris@4 61 #else
Chris@4 62 #define lo_pow2_over(a,b) \
Chris@4 63 while (b > a) {a *= 2;}
Chris@4 64 #endif
Chris@4 65
Chris@4 66 lo_message lo_message_new()
Chris@4 67 {
Chris@4 68 lo_message m = malloc(sizeof(struct _lo_message));
Chris@4 69 if (!m) {
Chris@4 70 return m;
Chris@4 71 }
Chris@4 72
Chris@4 73 m->types = calloc(LO_DEF_TYPE_SIZE, sizeof(char));
Chris@4 74 m->types[0] = ',';
Chris@4 75 m->types[1] = '\0';
Chris@4 76 m->typelen = 1;
Chris@4 77 m->typesize = LO_DEF_TYPE_SIZE;
Chris@4 78 m->data = NULL;
Chris@4 79 m->datalen = 0;
Chris@4 80 m->datasize = 0;
Chris@4 81 m->source = NULL;
Chris@4 82 m->argv = NULL;
Chris@4 83 m->ts = LO_TT_IMMEDIATE;
Chris@4 84
Chris@4 85 return m;
Chris@4 86 }
Chris@4 87
Chris@4 88 void lo_message_free(lo_message m)
Chris@4 89 {
Chris@4 90 if (m) {
Chris@4 91 free(m->types);
Chris@4 92 free(m->data);
Chris@4 93 free(m->argv);
Chris@4 94 }
Chris@4 95 free(m);
Chris@4 96 }
Chris@4 97
Chris@4 98 /* Don't call lo_message_add_varargs_internal directly, use
Chris@4 99 * lo_message_add_varargs, a macro wrapping this function with
Chris@4 100 * appropriate values for file and line */
Chris@4 101
Chris@4 102 int lo_message_add_varargs_internal(lo_message msg, const char *types,
Chris@4 103 va_list ap, const char *file, int line)
Chris@4 104 {
Chris@4 105 int count = 0;
Chris@4 106 int i;
Chris@4 107 int64_t i64;
Chris@4 108 float f;
Chris@4 109 char *s;
Chris@4 110 lo_blob b;
Chris@4 111 uint8_t *m;
Chris@4 112 lo_timetag tt;
Chris@4 113 double d;
Chris@4 114 int ret = 0;
Chris@4 115
Chris@4 116 while (types && *types) {
Chris@4 117 count++;
Chris@4 118 i=0;
Chris@4 119 i64=0;
Chris@4 120 f=0;
Chris@4 121 s=0;
Chris@4 122 b=0;
Chris@4 123 m=0;
Chris@4 124 d=0;
Chris@4 125 switch (*types++) {
Chris@4 126
Chris@4 127 case LO_INT32:
Chris@4 128 i = va_arg(ap, int32_t);
Chris@4 129 lo_message_add_int32(msg, i);
Chris@4 130 break;
Chris@4 131
Chris@4 132 case LO_FLOAT:
Chris@4 133 f = (float)va_arg(ap, double);
Chris@4 134 lo_message_add_float(msg, f);
Chris@4 135 break;
Chris@4 136
Chris@4 137 case LO_STRING:
Chris@4 138 s = va_arg(ap, char *);
Chris@4 139 #ifdef __GNUC__
Chris@4 140 if (s == (char *)LO_MARKER_A) {
Chris@4 141 fprintf(stderr, "liblo error: lo_send or lo_message_add called with "
Chris@4 142 "invalid string pointer for arg %d, probably arg mismatch\n"
Chris@4 143 "at %s:%d, exiting.\n", count, file, line);
Chris@4 144 }
Chris@4 145 #endif
Chris@4 146 lo_message_add_string(msg, s);
Chris@4 147 break;
Chris@4 148
Chris@4 149 case LO_BLOB:
Chris@4 150 b = va_arg(ap, lo_blob);
Chris@4 151 lo_message_add_blob(msg, b);
Chris@4 152 break;
Chris@4 153
Chris@4 154 case LO_INT64:
Chris@4 155 i64 = va_arg(ap, int64_t);
Chris@4 156 lo_message_add_int64(msg, i64);
Chris@4 157 break;
Chris@4 158
Chris@4 159 case LO_TIMETAG:
Chris@4 160 tt = va_arg(ap, lo_timetag);
Chris@4 161 lo_message_add_timetag(msg, tt);
Chris@4 162 break;
Chris@4 163
Chris@4 164 case LO_DOUBLE:
Chris@4 165 d = va_arg(ap, double);
Chris@4 166 lo_message_add_double(msg, d);
Chris@4 167 break;
Chris@4 168
Chris@4 169 case LO_SYMBOL:
Chris@4 170 s = va_arg(ap, char *);
Chris@4 171 #ifdef __GNUC__
Chris@4 172 if (s == (char *)LO_MARKER_A) {
Chris@4 173 fprintf(stderr, "liblo error: lo_send or lo_message_add called with "
Chris@4 174 "invalid symbol pointer for arg %d, probably arg mismatch\n"
Chris@4 175 "at %s:%d, exiting.\n", count, file, line);
Chris@4 176 va_end(ap);
Chris@4 177 return -2;
Chris@4 178 }
Chris@4 179 #endif
Chris@4 180 lo_message_add_symbol(msg, s);
Chris@4 181 break;
Chris@4 182
Chris@4 183 case LO_CHAR:
Chris@4 184 i = va_arg(ap, int);
Chris@4 185 lo_message_add_char(msg, i);
Chris@4 186 break;
Chris@4 187
Chris@4 188 case LO_MIDI:
Chris@4 189 m = va_arg(ap, uint8_t *);
Chris@4 190 lo_message_add_midi(msg, m);
Chris@4 191 break;
Chris@4 192
Chris@4 193 case LO_TRUE:
Chris@4 194 lo_message_add_true(msg);
Chris@4 195 break;
Chris@4 196
Chris@4 197 case LO_FALSE:
Chris@4 198 lo_message_add_false(msg);
Chris@4 199 break;
Chris@4 200
Chris@4 201 case LO_NIL:
Chris@4 202 lo_message_add_nil(msg);
Chris@4 203 break;
Chris@4 204
Chris@4 205 case LO_INFINITUM:
Chris@4 206 lo_message_add_infinitum(msg);
Chris@4 207 break;
Chris@4 208
Chris@4 209 default:
Chris@4 210 ret = -1; // unknown type
Chris@4 211 fprintf(stderr, "liblo warning: unknown type '%c' at %s:%d\n",
Chris@4 212 *(types-1), file, line);
Chris@4 213 break;
Chris@4 214 }
Chris@4 215 }
Chris@4 216 #ifdef __GNUC__
Chris@4 217 i = va_arg(ap, uint32_t);
Chris@4 218 if (i != LO_MARKER_A) {
Chris@4 219 ret = -2; // bad format/args
Chris@4 220 fprintf(stderr, "liblo error: lo_send, lo_message_add, or lo_message_add_varargs called with "
Chris@4 221 "mismatching types and data at\n%s:%d, exiting.\n", file, line);
Chris@4 222 va_end(ap);
Chris@4 223 return ret;
Chris@4 224 }
Chris@4 225 i = va_arg(ap, uint32_t);
Chris@4 226 if (i != LO_MARKER_B) {
Chris@4 227 ret = -2; // bad format/args
Chris@4 228 fprintf(stderr, "liblo error: lo_send, lo_message_add, or lo_message_add_varargs called with "
Chris@4 229 "mismatching types and data at\n%s:%d, exiting.\n", file, line);
Chris@4 230 }
Chris@4 231 #endif
Chris@4 232 va_end(ap);
Chris@4 233
Chris@4 234 return ret;
Chris@4 235 }
Chris@4 236
Chris@4 237 /* Don't call lo_message_add_internal directly, use lo_message_add,
Chris@4 238 * a macro wrapping this function with appropriate values for file and line */
Chris@4 239
Chris@4 240 #ifdef __GNUC__
Chris@4 241 int lo_message_add_internal(lo_message msg, const char *file, const int line,
Chris@4 242 const char *types, ...)
Chris@4 243 #else
Chris@4 244 int lo_message_add(lo_message msg, const char *types, ...)
Chris@4 245 #endif
Chris@4 246 {
Chris@4 247 va_list ap;
Chris@4 248 int ret = 0;
Chris@4 249
Chris@4 250 #ifndef __GNUC__
Chris@4 251 const char *file = "";
Chris@4 252 const int line = 0;
Chris@4 253 #endif
Chris@4 254
Chris@4 255 va_start(ap, types);
Chris@4 256 ret = lo_message_add_varargs_internal(msg, types, ap, file, line);
Chris@4 257
Chris@4 258 return ret;
Chris@4 259 }
Chris@4 260
Chris@4 261 int lo_message_add_int32(lo_message m, int32_t a)
Chris@4 262 {
Chris@4 263 lo_pcast32 b;
Chris@4 264 int32_t *nptr = lo_message_add_data(m, sizeof(a));
Chris@4 265 if (!nptr) return -1;
Chris@4 266 b.i = a;
Chris@4 267
Chris@4 268 if (lo_message_add_typechar(m, LO_INT32))
Chris@4 269 return -1;
Chris@4 270 *nptr = b.nl;
Chris@4 271 return 0;
Chris@4 272 }
Chris@4 273
Chris@4 274 int lo_message_add_float(lo_message m, float a)
Chris@4 275 {
Chris@4 276 lo_pcast32 b;
Chris@4 277 int32_t *nptr = lo_message_add_data(m, sizeof(a));
Chris@4 278 if (!nptr) return -1;
Chris@4 279 b.f = a;
Chris@4 280
Chris@4 281 if (lo_message_add_typechar(m, LO_FLOAT))
Chris@4 282 return -1;
Chris@4 283 *nptr = b.nl;
Chris@4 284 return 0;
Chris@4 285 }
Chris@4 286
Chris@4 287 int lo_message_add_string(lo_message m, const char *a)
Chris@4 288 {
Chris@4 289 const int size = lo_strsize(a);
Chris@4 290 char *nptr = lo_message_add_data(m, size);
Chris@4 291 if (!nptr) return -1;
Chris@4 292
Chris@4 293 if (lo_message_add_typechar(m, LO_STRING))
Chris@4 294 return -1;
Chris@4 295 strncpy(nptr, a, size);
Chris@4 296 return 0;
Chris@4 297 }
Chris@4 298
Chris@4 299 int lo_message_add_blob(lo_message m, lo_blob a)
Chris@4 300 {
Chris@4 301 const uint32_t size = lo_blobsize(a);
Chris@4 302 const uint32_t dsize = lo_blob_datasize(a);
Chris@4 303 char *nptr = lo_message_add_data(m, size);
Chris@4 304 if (!nptr) return -1;
Chris@4 305
Chris@4 306 if (lo_message_add_typechar(m, LO_BLOB))
Chris@4 307 return -1;
Chris@4 308 memset(nptr + size - 4, 0, 4);
Chris@4 309
Chris@4 310 memcpy(nptr, &dsize, sizeof(dsize));
Chris@4 311 memcpy(nptr + sizeof(int32_t), lo_blob_dataptr(a), lo_blob_datasize(a));
Chris@4 312 return 0;
Chris@4 313 }
Chris@4 314
Chris@4 315 int lo_message_add_int64(lo_message m, int64_t a)
Chris@4 316 {
Chris@4 317 lo_pcast64 b;
Chris@4 318 uint64_t *nptr = lo_message_add_data(m, sizeof(a));
Chris@4 319 if (!nptr) return -1;
Chris@4 320 b.i = a;
Chris@4 321
Chris@4 322 if (lo_message_add_typechar(m, LO_INT64))
Chris@4 323 return -1;
Chris@4 324 *nptr = b.nl;
Chris@4 325 return 0;
Chris@4 326 }
Chris@4 327
Chris@4 328 int lo_message_add_timetag(lo_message m, lo_timetag a)
Chris@4 329 {
Chris@4 330 lo_pcast64 b;
Chris@4 331 uint64_t *nptr = lo_message_add_data(m, sizeof(a));
Chris@4 332 if (!nptr) return -1;
Chris@4 333 b.tt = a;
Chris@4 334
Chris@4 335 if (lo_message_add_typechar(m, LO_TIMETAG))
Chris@4 336 return -1;
Chris@4 337 *nptr = b.nl;
Chris@4 338 return 0;
Chris@4 339 }
Chris@4 340
Chris@4 341 int lo_message_add_double(lo_message m, double a)
Chris@4 342 {
Chris@4 343 lo_pcast64 b;
Chris@4 344 uint64_t *nptr = lo_message_add_data(m, sizeof(a));
Chris@4 345 if (!nptr) return -1;
Chris@4 346 b.f = a;
Chris@4 347
Chris@4 348 if (lo_message_add_typechar(m, LO_DOUBLE))
Chris@4 349 return -1;
Chris@4 350 *nptr = b.nl;
Chris@4 351 return 0;
Chris@4 352 }
Chris@4 353
Chris@4 354 int lo_message_add_symbol(lo_message m, const char *a)
Chris@4 355 {
Chris@4 356 const int size = lo_strsize(a);
Chris@4 357 char *nptr = lo_message_add_data(m, size);
Chris@4 358 if (!nptr) return -1;
Chris@4 359
Chris@4 360 if (lo_message_add_typechar(m, LO_SYMBOL))
Chris@4 361 return -1;
Chris@4 362 strncpy(nptr, a, size);
Chris@4 363 return 0;
Chris@4 364 }
Chris@4 365
Chris@4 366 int lo_message_add_char(lo_message m, char a)
Chris@4 367 {
Chris@4 368 lo_pcast32 b;
Chris@4 369 int32_t *nptr = lo_message_add_data(m, sizeof(int32_t));
Chris@4 370 if (!nptr) return -1;
Chris@4 371
Chris@4 372 b.c = a;
Chris@4 373
Chris@4 374 if (lo_message_add_typechar(m, LO_CHAR))
Chris@4 375 return -1;
Chris@4 376 *nptr = b.nl;
Chris@4 377 return 0;
Chris@4 378 }
Chris@4 379
Chris@4 380 int lo_message_add_midi(lo_message m, uint8_t a[4])
Chris@4 381 {
Chris@4 382 char *nptr = lo_message_add_data(m, 4);
Chris@4 383 if (!nptr) return -1;
Chris@4 384
Chris@4 385 if (lo_message_add_typechar(m, LO_MIDI))
Chris@4 386 return -1;
Chris@4 387
Chris@4 388 memcpy(nptr, a, sizeof(a));
Chris@4 389 return 0;
Chris@4 390 }
Chris@4 391
Chris@4 392 int lo_message_add_true(lo_message m)
Chris@4 393 {
Chris@4 394 return lo_message_add_typechar(m, LO_TRUE);
Chris@4 395 }
Chris@4 396
Chris@4 397 int lo_message_add_false(lo_message m)
Chris@4 398 {
Chris@4 399 return lo_message_add_typechar(m, LO_FALSE);
Chris@4 400 }
Chris@4 401
Chris@4 402 int lo_message_add_nil(lo_message m)
Chris@4 403 {
Chris@4 404 return lo_message_add_typechar(m, LO_NIL);
Chris@4 405 }
Chris@4 406
Chris@4 407 int lo_message_add_infinitum(lo_message m)
Chris@4 408 {
Chris@4 409 return lo_message_add_typechar(m, LO_INFINITUM);
Chris@4 410 }
Chris@4 411
Chris@4 412 static int lo_message_add_typechar(lo_message m, char t)
Chris@4 413 {
Chris@4 414 if (m->typelen + 1 >= m->typesize) {
Chris@4 415 int new_typesize = m->typesize * 2;
Chris@4 416 char *new_types = 0;
Chris@4 417 if (!new_typesize)
Chris@4 418 new_typesize = LO_DEF_TYPE_SIZE;
Chris@4 419 new_types = realloc(m->types, new_typesize);
Chris@4 420 if (!new_types) return -1;
Chris@4 421 m->types = new_types;
Chris@4 422 m->typesize = new_typesize;
Chris@4 423 }
Chris@4 424 m->types[m->typelen] = t;
Chris@4 425 m->typelen++;
Chris@4 426 m->types[m->typelen] = '\0';
Chris@4 427 if (m->argv) {
Chris@4 428 free(m->argv);
Chris@4 429 m->argv = NULL;
Chris@4 430 }
Chris@4 431 return 0;
Chris@4 432 }
Chris@4 433
Chris@4 434 static void *lo_message_add_data(lo_message m, size_t s)
Chris@4 435 {
Chris@4 436 uint32_t old_dlen = m->datalen;
Chris@4 437 int new_datasize = m->datasize;
Chris@4 438 int new_datalen = m->datalen + s;
Chris@4 439 void *new_data = 0;
Chris@4 440
Chris@4 441 if (!new_datasize)
Chris@4 442 new_datasize = LO_DEF_DATA_SIZE;
Chris@4 443
Chris@4 444 lo_pow2_over(new_datasize, new_datalen);
Chris@4 445 new_data = realloc(m->data, new_datasize);
Chris@4 446 if (!new_data)
Chris@4 447 return 0;
Chris@4 448
Chris@4 449 m->datalen = new_datalen;
Chris@4 450 m->datasize = new_datasize;
Chris@4 451 m->data = new_data;
Chris@4 452
Chris@4 453 if (m->argv) {
Chris@4 454 free(m->argv);
Chris@4 455 m->argv = NULL;
Chris@4 456 }
Chris@4 457
Chris@4 458 return (void*)((char*)m->data + old_dlen);
Chris@4 459 }
Chris@4 460
Chris@4 461 int lo_strsize(const char *s)
Chris@4 462 {
Chris@4 463 return 4 * (strlen(s) / 4 + 1);
Chris@4 464 }
Chris@4 465
Chris@4 466 size_t lo_arg_size(lo_type type, void *data)
Chris@4 467 {
Chris@4 468 switch (type) {
Chris@4 469 case LO_TRUE:
Chris@4 470 case LO_FALSE:
Chris@4 471 case LO_NIL:
Chris@4 472 case LO_INFINITUM:
Chris@4 473 return 0;
Chris@4 474
Chris@4 475 case LO_INT32:
Chris@4 476 case LO_FLOAT:
Chris@4 477 case LO_MIDI:
Chris@4 478 case LO_CHAR:
Chris@4 479 return 4;
Chris@4 480
Chris@4 481 case LO_INT64:
Chris@4 482 case LO_TIMETAG:
Chris@4 483 case LO_DOUBLE:
Chris@4 484 return 8;
Chris@4 485
Chris@4 486 case LO_STRING:
Chris@4 487 case LO_SYMBOL:
Chris@4 488 return lo_strsize((char *)data);
Chris@4 489
Chris@4 490 case LO_BLOB:
Chris@4 491 return lo_blobsize((lo_blob)data);
Chris@4 492
Chris@4 493 default:
Chris@4 494 fprintf(stderr, "liblo warning: unhandled OSC type '%c' at %s:%d\n", type, __FILE__, __LINE__);
Chris@4 495 return 0;
Chris@4 496 }
Chris@4 497
Chris@4 498 return 0;
Chris@4 499 }
Chris@4 500
Chris@4 501 char *lo_get_path(void *data, ssize_t size)
Chris@4 502 {
Chris@4 503 ssize_t result = lo_validate_string(data, size);
Chris@4 504 return (result >= 4) ? (char *)data : NULL;
Chris@4 505 }
Chris@4 506
Chris@4 507 ssize_t lo_validate_string(void *data, ssize_t size)
Chris@4 508 {
Chris@4 509 ssize_t i = 0, len = 0;
Chris@4 510 char *pos = data;
Chris@4 511
Chris@4 512 if (size < 0) {
Chris@4 513 return -LO_ESIZE; // invalid size
Chris@4 514 }
Chris@4 515 for (i = 0; i < size; ++i) {
Chris@4 516 if (pos[i] == '\0') {
Chris@4 517 len = 4 * (i / 4 + 1);
Chris@4 518 break;
Chris@4 519 }
Chris@4 520 }
Chris@4 521 if (0 == len) {
Chris@4 522 return -LO_ETERM; // string not terminated
Chris@4 523 }
Chris@4 524 if (len > size) {
Chris@4 525 return -LO_ESIZE; // would overflow buffer
Chris@4 526 }
Chris@4 527 for (; i < len; ++i) {
Chris@4 528 if (pos[i] != '\0') {
Chris@4 529 return -LO_EPAD; // non-zero char found in pad area
Chris@4 530 }
Chris@4 531 }
Chris@4 532 return len;
Chris@4 533 }
Chris@4 534
Chris@4 535
Chris@4 536 ssize_t lo_validate_blob(void *data, ssize_t size)
Chris@4 537 {
Chris@4 538 ssize_t i, end, len;
Chris@4 539 uint32_t dsize;
Chris@4 540 char *pos = (char *)data;
Chris@4 541
Chris@4 542 if (size < 0) {
Chris@4 543 return -LO_ESIZE; // invalid size
Chris@4 544 }
Chris@4 545 dsize = lo_otoh32(*(uint32_t*)data);
Chris@4 546 if (dsize > LO_MAX_MSG_SIZE) { // avoid int overflow in next step
Chris@4 547 return -LO_ESIZE;
Chris@4 548 }
Chris@4 549 end = sizeof(uint32_t) + dsize; // end of data
Chris@4 550 len = 4 * (end / 4 + 1); // full padded size
Chris@4 551 if (len > size) {
Chris@4 552 return -LO_ESIZE; // would overflow buffer
Chris@4 553 }
Chris@4 554 for (i = end; i < len; ++i) {
Chris@4 555 if (pos[i] != '\0') {
Chris@4 556 return -LO_EPAD; // non-zero char found in pad area
Chris@4 557 }
Chris@4 558 }
Chris@4 559 return len;
Chris@4 560 }
Chris@4 561
Chris@4 562
Chris@4 563 ssize_t lo_validate_bundle(void *data, ssize_t size)
Chris@4 564 {
Chris@4 565 ssize_t len = 0, remain = size;
Chris@4 566 char *pos = data;
Chris@4 567 uint32_t elem_len;
Chris@4 568
Chris@4 569 len = lo_validate_string(data, size);
Chris@4 570 if (len < 0) {
Chris@4 571 return -LO_ESIZE; // invalid size
Chris@4 572 }
Chris@4 573 if (0 != strcmp(data, "#bundle")) {
Chris@4 574 return -LO_EINVALIDBUND; // not a bundle
Chris@4 575 }
Chris@4 576 pos += len;
Chris@4 577 remain -= len;
Chris@4 578
Chris@4 579 // time tag
Chris@4 580 if (remain < 8) {
Chris@4 581 return -LO_ESIZE;
Chris@4 582 }
Chris@4 583 pos += 8;
Chris@4 584 remain -= 8;
Chris@4 585
Chris@4 586 while (remain >= 4) {
Chris@4 587 elem_len = lo_otoh32(*((uint32_t *)pos));
Chris@4 588 pos += 4;
Chris@4 589 remain -= 4;
Chris@4 590 if (elem_len > remain) {
Chris@4 591 return -LO_ESIZE;
Chris@4 592 }
Chris@4 593 pos += elem_len;
Chris@4 594 remain -= elem_len;
Chris@4 595 }
Chris@4 596 if (0 != remain) {
Chris@4 597 return -LO_ESIZE;
Chris@4 598 }
Chris@4 599 return size;
Chris@4 600 }
Chris@4 601
Chris@4 602
Chris@4 603 ssize_t lo_validate_arg(lo_type type, void *data, ssize_t size)
Chris@4 604 {
Chris@4 605 if (size < 0) {
Chris@4 606 return -1;
Chris@4 607 }
Chris@4 608 switch (type) {
Chris@4 609 case LO_TRUE:
Chris@4 610 case LO_FALSE:
Chris@4 611 case LO_NIL:
Chris@4 612 case LO_INFINITUM:
Chris@4 613 return 0;
Chris@4 614
Chris@4 615 case LO_INT32:
Chris@4 616 case LO_FLOAT:
Chris@4 617 case LO_MIDI:
Chris@4 618 case LO_CHAR:
Chris@4 619 return size >= 4 ? 4 : -LO_ESIZE;
Chris@4 620
Chris@4 621 case LO_INT64:
Chris@4 622 case LO_TIMETAG:
Chris@4 623 case LO_DOUBLE:
Chris@4 624 return size >= 8 ? 8 : -LO_ESIZE;
Chris@4 625
Chris@4 626 case LO_STRING:
Chris@4 627 case LO_SYMBOL:
Chris@4 628 return lo_validate_string((char *)data, size);
Chris@4 629
Chris@4 630 case LO_BLOB:
Chris@4 631 return lo_validate_blob((lo_blob)data, size);
Chris@4 632
Chris@4 633 default:
Chris@4 634 return -LO_EINVALIDTYPE;
Chris@4 635 }
Chris@4 636 return -LO_INT_ERR;
Chris@4 637 }
Chris@4 638
Chris@4 639 /* convert endianness of arg pointed to by data from network to host */
Chris@4 640 void lo_arg_host_endian(lo_type type, void *data)
Chris@4 641 {
Chris@4 642 switch (type) {
Chris@4 643 case LO_INT32:
Chris@4 644 case LO_FLOAT:
Chris@4 645 case LO_BLOB:
Chris@4 646 case LO_CHAR:
Chris@4 647 *(int32_t *)data = lo_otoh32(*(int32_t *)data);
Chris@4 648 break;
Chris@4 649
Chris@4 650 case LO_INT64:
Chris@4 651 case LO_TIMETAG:
Chris@4 652 case LO_DOUBLE:
Chris@4 653 *(int64_t *)data = lo_otoh64(*(int64_t *)data);
Chris@4 654 break;
Chris@4 655
Chris@4 656 case LO_STRING:
Chris@4 657 case LO_SYMBOL:
Chris@4 658 case LO_MIDI:
Chris@4 659 case LO_TRUE:
Chris@4 660 case LO_FALSE:
Chris@4 661 case LO_NIL:
Chris@4 662 case LO_INFINITUM:
Chris@4 663 /* these are fine */
Chris@4 664 break;
Chris@4 665
Chris@4 666 default:
Chris@4 667 fprintf(stderr, "liblo warning: unhandled OSC type '%c' at %s:%d\n",
Chris@4 668 type, __FILE__, __LINE__);
Chris@4 669 break;
Chris@4 670 }
Chris@4 671 }
Chris@4 672
Chris@4 673 /* convert endianness of arg pointed to by data from host to network */
Chris@4 674 void lo_arg_network_endian(lo_type type, void *data)
Chris@4 675 {
Chris@4 676 switch (type) {
Chris@4 677 case LO_INT32:
Chris@4 678 case LO_FLOAT:
Chris@4 679 case LO_BLOB:
Chris@4 680 case LO_CHAR:
Chris@4 681 *(int32_t *)data = lo_htoo32(*(int32_t *)data);
Chris@4 682 break;
Chris@4 683
Chris@4 684 case LO_INT64:
Chris@4 685 case LO_TIMETAG:
Chris@4 686 case LO_DOUBLE:
Chris@4 687 *(int64_t *)data = lo_htoo64(*(int64_t *)data);
Chris@4 688 break;
Chris@4 689
Chris@4 690 case LO_STRING:
Chris@4 691 case LO_SYMBOL:
Chris@4 692 case LO_MIDI:
Chris@4 693 case LO_TRUE:
Chris@4 694 case LO_FALSE:
Chris@4 695 case LO_NIL:
Chris@4 696 case LO_INFINITUM:
Chris@4 697 /* these are fine */
Chris@4 698 break;
Chris@4 699
Chris@4 700 default:
Chris@4 701 fprintf(stderr, "liblo warning: unhandled OSC type '%c' at %s:%d\n",
Chris@4 702 type, __FILE__, __LINE__);
Chris@4 703 break;
Chris@4 704 }
Chris@4 705 }
Chris@4 706
Chris@4 707 lo_address lo_message_get_source(lo_message m)
Chris@4 708 {
Chris@4 709 return m->source;
Chris@4 710 }
Chris@4 711
Chris@4 712 lo_timetag lo_message_get_timestamp(lo_message m)
Chris@4 713 {
Chris@4 714 return m->ts;
Chris@4 715 }
Chris@4 716
Chris@4 717 size_t lo_message_length(lo_message m, const char *path)
Chris@4 718 {
Chris@4 719 return lo_strsize(path) + lo_strsize(m->types) + m->datalen;
Chris@4 720 }
Chris@4 721
Chris@4 722 int lo_message_get_argc(lo_message m)
Chris@4 723 {
Chris@4 724 return m->typelen - 1;
Chris@4 725 }
Chris@4 726
Chris@4 727 lo_arg **lo_message_get_argv(lo_message m)
Chris@4 728 {
Chris@4 729 int i, argc;
Chris@4 730 char *types, *ptr;
Chris@4 731 lo_arg **argv;
Chris@4 732
Chris@4 733 if (NULL != m->argv) { return m->argv; }
Chris@4 734
Chris@4 735 i = 0;
Chris@4 736 argc = m->typelen - 1;
Chris@4 737 types = m->types + 1;
Chris@4 738 ptr = m->data;
Chris@4 739
Chris@4 740 argv = calloc(argc, sizeof(lo_arg *));
Chris@4 741 for (i = 0; i < argc; ++i) {
Chris@4 742 size_t len = lo_arg_size(types[i], ptr);
Chris@4 743 argv[i] = len ? (lo_arg*)ptr : NULL;
Chris@4 744 ptr += len;
Chris@4 745 }
Chris@4 746 m->argv = argv;
Chris@4 747 return argv;
Chris@4 748 }
Chris@4 749
Chris@4 750 char *lo_message_get_types(lo_message m)
Chris@4 751 {
Chris@4 752 return m->types + 1;
Chris@4 753 }
Chris@4 754
Chris@4 755 void *lo_message_serialise(lo_message m, const char *path, void *to,
Chris@4 756 size_t *size)
Chris@4 757 {
Chris@4 758 int i, argc;
Chris@4 759 char *types, *ptr;
Chris@4 760 size_t s = lo_message_length(m, path);
Chris@4 761
Chris@4 762 if (size) {
Chris@4 763 *size = s;
Chris@4 764 }
Chris@4 765
Chris@4 766 if (!to) {
Chris@4 767 to = calloc(1, s);
Chris@4 768 }
Chris@4 769 memset((char*)to + lo_strsize(path) - 4, 0, 4); // ensure zero-padding
Chris@4 770 strcpy(to, path);
Chris@4 771 memset((char*)to + lo_strsize(path) + lo_strsize(m->types) - 4, 0, 4);
Chris@4 772 strcpy((char*)to + lo_strsize(path), m->types);
Chris@4 773
Chris@4 774 types = m->types + 1;
Chris@4 775 ptr = (char*)to + lo_strsize(path) + lo_strsize(m->types);
Chris@4 776 memcpy(ptr, m->data, m->datalen);
Chris@4 777
Chris@4 778 i = 0;
Chris@4 779 argc = m->typelen - 1;
Chris@4 780 for (i = 0; i < argc; ++i) {
Chris@4 781 size_t len = lo_arg_size(types[i], ptr);
Chris@4 782 lo_arg_network_endian(types[i], ptr);
Chris@4 783 ptr += len;
Chris@4 784 }
Chris@4 785 return to;
Chris@4 786 }
Chris@4 787
Chris@4 788
Chris@4 789 lo_message lo_message_deserialise(void *data, size_t size, int *result)
Chris@4 790 {
Chris@4 791 lo_message msg = NULL;
Chris@4 792 char *types = NULL, *ptr = NULL;
Chris@4 793 int i = 0, argc = 0, remain = size, res = 0, len;
Chris@4 794
Chris@4 795 if (remain <= 0) { res = LO_ESIZE; goto fail; }
Chris@4 796
Chris@4 797 msg = malloc(sizeof(struct _lo_message));
Chris@4 798 if (!msg) { res = LO_EALLOC; goto fail; }
Chris@4 799
Chris@4 800 msg->types = NULL;
Chris@4 801 msg->typelen = 0;
Chris@4 802 msg->typesize = 0;
Chris@4 803 msg->data = NULL;
Chris@4 804 msg->datalen = 0;
Chris@4 805 msg->datasize = 0;
Chris@4 806 msg->source = NULL;
Chris@4 807 msg->argv = NULL;
Chris@4 808 msg->ts = LO_TT_IMMEDIATE;
Chris@4 809
Chris@4 810 // path
Chris@4 811 len = lo_validate_string(data, remain);
Chris@4 812 if (len < 0) {
Chris@4 813 res = LO_EINVALIDPATH; // invalid path string
Chris@4 814 goto fail;
Chris@4 815 }
Chris@4 816 remain -= len;
Chris@4 817
Chris@4 818 // types
Chris@4 819 if (remain <= 0) {
Chris@4 820 res = LO_ENOTYPE; // no type tag string
Chris@4 821 goto fail;
Chris@4 822 }
Chris@4 823 types = (char*)data + len;
Chris@4 824 len = lo_validate_string(types, remain);
Chris@4 825 if (len < 0) {
Chris@4 826 res = LO_EINVALIDTYPE; // invalid type tag string
Chris@4 827 goto fail;
Chris@4 828 }
Chris@4 829 if (types[0] != ',') {
Chris@4 830 res = LO_EBADTYPE; // type tag string missing initial comma
Chris@4 831 goto fail;
Chris@4 832 }
Chris@4 833 remain -= len;
Chris@4 834
Chris@4 835 msg->typelen = strlen(types);
Chris@4 836 msg->typesize = len;
Chris@4 837 msg->types = malloc(msg->typesize);
Chris@4 838 if (NULL == msg->types) { res = LO_EALLOC; goto fail; }
Chris@4 839 memcpy(msg->types, types, msg->typesize);
Chris@4 840
Chris@4 841 // args
Chris@4 842 msg->data = malloc(remain);
Chris@4 843 if (NULL == msg->data) { res = LO_EALLOC; goto fail; }
Chris@4 844 memcpy(msg->data, types + len, remain);
Chris@4 845 msg->datalen = msg->datasize = remain;
Chris@4 846 ptr = msg->data;
Chris@4 847
Chris@4 848 ++types;
Chris@4 849 argc = msg->typelen - 1;
Chris@4 850 if (argc) {
Chris@4 851 msg->argv = calloc(argc, sizeof(lo_arg *));
Chris@4 852 if (NULL == msg->argv) { res = LO_EALLOC; goto fail; }
Chris@4 853 }
Chris@4 854
Chris@4 855 for (i = 0; remain >= 0 && i < argc; ++i) {
Chris@4 856 len = lo_validate_arg((lo_type)types[i], ptr, remain);
Chris@4 857 if (len < 0) {
Chris@4 858 res = LO_EINVALIDARG; // invalid argument
Chris@4 859 goto fail;
Chris@4 860 }
Chris@4 861 lo_arg_host_endian((lo_type)types[i], ptr);
Chris@4 862 msg->argv[i] = len ? (lo_arg*)ptr : NULL;
Chris@4 863 remain -= len;
Chris@4 864 ptr += len;
Chris@4 865 }
Chris@4 866 if (0 != remain || i != argc) {
Chris@4 867 res = LO_ESIZE; // size/argument mismatch
Chris@4 868 goto fail;
Chris@4 869 }
Chris@4 870
Chris@4 871 if (result) { *result = res; }
Chris@4 872 return msg;
Chris@4 873
Chris@4 874 fail:
Chris@4 875 if (msg) { lo_message_free(msg); }
Chris@4 876 if (result) { *result = res; }
Chris@4 877 return NULL;
Chris@4 878 }
Chris@4 879
Chris@4 880 void lo_message_pp(lo_message m)
Chris@4 881 {
Chris@4 882 void *d = m->data;
Chris@4 883 void *end = (char*)m->data + m->datalen;
Chris@4 884 int i;
Chris@4 885
Chris@4 886 printf("%s ", m->types);
Chris@4 887 for (i = 1; m->types[i]; i++) {
Chris@4 888 if (i > 1) {
Chris@4 889 printf(" ");
Chris@4 890 }
Chris@4 891
Chris@4 892 lo_arg_pp_internal(m->types[i], d, 1);
Chris@4 893 d = (char*)d + lo_arg_size(m->types[i], d);
Chris@4 894 }
Chris@4 895 putchar('\n');
Chris@4 896 if (d != end) {
Chris@4 897 fprintf(stderr, "liblo warning: type and data do not match (off by %d) in message %p\n",
Chris@4 898 abs((char*)d - (char*)end), m);
Chris@4 899 }
Chris@4 900 }
Chris@4 901
Chris@4 902 void lo_arg_pp(lo_type type, void *data)
Chris@4 903 {
Chris@4 904 lo_arg_pp_internal(type, data, 0);
Chris@4 905 }
Chris@4 906
Chris@4 907 void lo_arg_pp_internal(lo_type type, void *data, int bigendian)
Chris@4 908 {
Chris@4 909 lo_pcast32 val32;
Chris@4 910 lo_pcast64 val64;
Chris@4 911 int size;
Chris@4 912 int i;
Chris@4 913
Chris@4 914 size = lo_arg_size(type, data);
Chris@4 915 if (size == 4 || type == LO_BLOB) {
Chris@4 916 if (bigendian) {
Chris@4 917 val32.nl = lo_otoh32(*(int32_t *)data);
Chris@4 918 } else {
Chris@4 919 val32.nl = *(int32_t *)data;
Chris@4 920 }
Chris@4 921 } else if (size == 8) {
Chris@4 922 if (bigendian) {
Chris@4 923 val64.nl = lo_otoh64(*(int64_t *)data);
Chris@4 924 } else {
Chris@4 925 val64.nl = *(int64_t *)data;
Chris@4 926 }
Chris@4 927 }
Chris@4 928
Chris@4 929 switch (type) {
Chris@4 930 case LO_INT32:
Chris@4 931 printf("%d", val32.i);
Chris@4 932 break;
Chris@4 933
Chris@4 934 case LO_FLOAT:
Chris@4 935 printf("%f", val32.f);
Chris@4 936 break;
Chris@4 937
Chris@4 938 case LO_STRING:
Chris@4 939 printf("\"%s\"", (char *)data);
Chris@4 940 break;
Chris@4 941
Chris@4 942 case LO_BLOB:
Chris@4 943 printf("[");
Chris@4 944 if (val32.i > 12) {
Chris@4 945 printf("%d byte blob", val32.i);
Chris@4 946 } else {
Chris@4 947 printf("%db ", val32.i);
Chris@4 948 for (i=0; i<val32.i; i++) {
Chris@4 949 printf("0x%02x", *((char *)(data) + 4 + i));
Chris@4 950 if (i+1 < val32.i) printf(" ");
Chris@4 951 }
Chris@4 952 }
Chris@4 953 printf("]");
Chris@4 954 break;
Chris@4 955
Chris@4 956 case LO_INT64:
Chris@4 957 printf("%lld", (long long int)val64.i);
Chris@4 958 break;
Chris@4 959
Chris@4 960 case LO_TIMETAG:
Chris@4 961 printf("%08x.%08x", val64.tt.sec, val64.tt.frac);
Chris@4 962 break;
Chris@4 963
Chris@4 964 case LO_DOUBLE:
Chris@4 965 printf("%f", val64.f);
Chris@4 966 break;
Chris@4 967
Chris@4 968 case LO_SYMBOL:
Chris@4 969 printf("'%s", (char *)data);
Chris@4 970 break;
Chris@4 971
Chris@4 972 case LO_CHAR:
Chris@4 973 printf("'%c'", (char)val32.c);
Chris@4 974 break;
Chris@4 975
Chris@4 976 case LO_MIDI:
Chris@4 977 printf("MIDI [");
Chris@4 978 for (i=0; i<4; i++) {
Chris@4 979 printf("0x%02x", *((uint8_t *)(data) + i));
Chris@4 980 if (i+1 < 4) printf(" ");
Chris@4 981 }
Chris@4 982 printf("]");
Chris@4 983 break;
Chris@4 984
Chris@4 985 case LO_TRUE:
Chris@4 986 printf("#T");
Chris@4 987 break;
Chris@4 988
Chris@4 989 case LO_FALSE:
Chris@4 990 printf("#F");
Chris@4 991 break;
Chris@4 992
Chris@4 993 case LO_NIL:
Chris@4 994 printf("Nil");
Chris@4 995 break;
Chris@4 996
Chris@4 997 case LO_INFINITUM:
Chris@4 998 printf("Infinitum");
Chris@4 999 break;
Chris@4 1000
Chris@4 1001 default:
Chris@4 1002 fprintf(stderr, "liblo warning: unhandled type: %c\n", type);
Chris@4 1003 break;
Chris@4 1004 }
Chris@4 1005 }
Chris@4 1006
Chris@4 1007 int lo_is_numerical_type(lo_type a)
Chris@4 1008 {
Chris@4 1009 return strchr(lo_numerical_types, a) != 0;
Chris@4 1010 }
Chris@4 1011
Chris@4 1012 int lo_is_string_type(lo_type a)
Chris@4 1013 {
Chris@4 1014 return strchr(lo_string_types, a) != 0;
Chris@4 1015 }
Chris@4 1016
Chris@4 1017 int lo_coerce(lo_type type_to, lo_arg *to, lo_type type_from, lo_arg *from)
Chris@4 1018 {
Chris@4 1019 if (type_to == type_from) {
Chris@4 1020 memcpy(to, from, lo_arg_size(type_from, from));
Chris@4 1021
Chris@4 1022 return 1;
Chris@4 1023 }
Chris@4 1024
Chris@4 1025 if (lo_is_string_type(type_to) && lo_is_string_type(type_from)) {
Chris@4 1026 strcpy((char *)to, (char *)from);
Chris@4 1027
Chris@4 1028 return 1;
Chris@4 1029 }
Chris@4 1030
Chris@4 1031 if (lo_is_numerical_type(type_to) && lo_is_numerical_type(type_from)) {
Chris@4 1032 switch (type_to) {
Chris@4 1033 case LO_INT32:
Chris@4 1034 to->i = (uint32_t)lo_hires_val(type_from, from);
Chris@4 1035 break;
Chris@4 1036
Chris@4 1037 case LO_INT64:
Chris@4 1038 to->i64 = (uint64_t)lo_hires_val(type_from, from);
Chris@4 1039 break;
Chris@4 1040
Chris@4 1041 case LO_FLOAT:
Chris@4 1042 to->f = (float)lo_hires_val(type_from, from);
Chris@4 1043 break;
Chris@4 1044
Chris@4 1045 case LO_DOUBLE:
Chris@4 1046 to->d = (double)lo_hires_val(type_from, from);
Chris@4 1047 break;
Chris@4 1048
Chris@4 1049 default:
Chris@4 1050 fprintf(stderr, "liblo: bad coercion: %c -> %c\n", type_from,
Chris@4 1051 type_to);
Chris@4 1052 return 0;
Chris@4 1053 }
Chris@4 1054 return 1;
Chris@4 1055 }
Chris@4 1056
Chris@4 1057 return 0;
Chris@4 1058 }
Chris@4 1059
Chris@4 1060 lo_hires lo_hires_val(lo_type type, lo_arg *p)
Chris@4 1061 {
Chris@4 1062 switch (type) {
Chris@4 1063 case LO_INT32:
Chris@4 1064 return p->i;
Chris@4 1065 case LO_INT64:
Chris@4 1066 return p->h;
Chris@4 1067 case LO_FLOAT:
Chris@4 1068 return p->f;
Chris@4 1069 case LO_DOUBLE:
Chris@4 1070 return p->d;
Chris@4 1071 default:
Chris@4 1072 fprintf(stderr, "liblo: hires val requested of non numerical type '%c' at %s:%d\n", type, __FILE__, __LINE__);
Chris@4 1073 break;
Chris@4 1074 }
Chris@4 1075
Chris@4 1076 return 0.0l;
Chris@4 1077 }
Chris@4 1078
Chris@4 1079
Chris@4 1080
Chris@4 1081 /* vi:set ts=8 sts=4 sw=4: */