~hp/osc.lv2

ddc1f317fe0d3cb8687b62751cc3b01c74551473 — Hanspeter Portner 5 months ago 33b6c8a
Bracket all if/else/for constructs
6 files changed, 313 insertions(+), 2 deletions(-)

M meson_options.txt
M src/forge.c
M src/reader.c
M src/stream.c
M src/util.c
M src/writer.c
M meson_options.txt => meson_options.txt +1 -1
@@ 6,4 6,4 @@ option('build-tests',
	value : true,
  yield : true)

option('version', type : 'string', value : '0.1.187')
option('version', type : 'string', value : '0.1.189')

M src/forge.c => src/forge.c +66 -1
@@ 135,9 135,13 @@ lv2_osc_forge_message_varlist(LV2_Atom_Forge *forge, LV2_OSC_URID *osc_urid,
	LV2_Atom_Forge_Ref ref;

	if(!lv2_osc_check_path(path) || !lv2_osc_check_fmt(fmt, 0))
	{
		return 0;
	}
	if(!(ref = lv2_osc_forge_message_head(forge, osc_urid, frame, path)))
	{
		return 0;
	}

	for(const char *type = fmt; *type; type++)
	{


@@ 146,20 150,26 @@ lv2_osc_forge_message_varlist(LV2_Atom_Forge *forge, LV2_OSC_URID *osc_urid,
			case LV2_OSC_INT32:
			{
				if(!(ref = lv2_osc_forge_int(forge, osc_urid, va_arg(args, int32_t))))
				{
					return 0;
				}
				break;
			}
			case LV2_OSC_FLOAT:
			{
				if(!(ref = lv2_osc_forge_float(forge, osc_urid, (float)va_arg(args, double))))
				{
					return 0;
				}
				break;
			}
			case LV2_OSC_STRING:
			{
				const char *s = va_arg(args, const char *);
				if(!s || !(ref = lv2_osc_forge_string(forge, osc_urid, s, strlen(s))))
				{
					return 0;
				}
				break;
			}
			case LV2_OSC_BLOB:


@@ 167,20 177,26 @@ lv2_osc_forge_message_varlist(LV2_Atom_Forge *forge, LV2_OSC_URID *osc_urid,
				const int32_t size = va_arg(args, int32_t);
				const uint8_t *b = va_arg(args, const uint8_t *);
				if(!b || !(ref = lv2_osc_forge_blob(forge, osc_urid, b, size)))
				{
					return 0;
				}
				break;
			}
			
			case LV2_OSC_INT64:
			{
				if(!(ref = lv2_osc_forge_long(forge, osc_urid, va_arg(args, int64_t))))
				{
					return 0;
				}
				break;
			}
			case LV2_OSC_DOUBLE:
			{
				if(!(ref = lv2_osc_forge_double(forge, osc_urid, va_arg(args, double))))
				{
					return 0;
				}
				break;
			}
			case LV2_OSC_TIMETAG:


@@ 190,39 206,50 @@ lv2_osc_forge_message_varlist(LV2_Atom_Forge *forge, LV2_OSC_URID *osc_urid,
					.fraction = va_arg(args, uint32_t)
				};
				if(!(ref = lv2_osc_forge_timetag(forge, osc_urid, &timetag)))
				{
					return 0;
				}
				break;
			}
			
			case LV2_OSC_TRUE:
			{
				if(!(ref = lv2_osc_forge_true(forge, osc_urid)))
				{
					return 0;
				}
				break;
			}
			case LV2_OSC_FALSE:
			{
				if(!(ref = lv2_osc_forge_false(forge, osc_urid)))
				{
					return 0;
				}
				break;
			}
			case LV2_OSC_NIL:
			{
				if(!(ref = lv2_osc_forge_nil(forge, osc_urid)))
				{
					return 0;
				}
				break;
			}
			case LV2_OSC_IMPULSE:
			{
				if(!(ref = lv2_osc_forge_impulse(forge, osc_urid)))
				{
					return 0;
				}
				break;
			}

			case LV2_OSC_SYMBOL:
			{
				if(!(ref = lv2_osc_forge_symbol(forge, osc_urid, va_arg(args, uint32_t))))
				{
					return 0;
				}
				break;
			}
			case LV2_OSC_MIDI:


@@ 230,13 257,17 @@ lv2_osc_forge_message_varlist(LV2_Atom_Forge *forge, LV2_OSC_URID *osc_urid,
				const int32_t size = va_arg(args, int32_t);
				const uint8_t *m = va_arg(args, const uint8_t *);
				if(!m || !(ref = lv2_osc_forge_midi(forge, osc_urid, m, size)))
				{
					return 0;
				}
				break;
			}
			case LV2_OSC_CHAR:
			{
				if(!(ref = lv2_osc_forge_char(forge, osc_urid, (char)va_arg(args, int))))
				{
					return 0;
				}
				break;
			}
			case LV2_OSC_RGBA:


@@ 246,7 277,9 @@ lv2_osc_forge_message_varlist(LV2_Atom_Forge *forge, LV2_OSC_URID *osc_urid,
						(uint8_t)va_arg(args, unsigned),
						(uint8_t)va_arg(args, unsigned),
						(uint8_t)va_arg(args, unsigned))))
				{
					return 0;
				}
				break;
			}
		}


@@ 293,7 326,9 @@ lv2_osc_forge_packet(LV2_Atom_Forge *forge, LV2_OSC_URID *osc_urid,
			OSC_READER_BUNDLE_ITERATE(&reader, itm)
			{
				if(!(ref = lv2_osc_forge_packet(forge, osc_urid, map, itm->body, itm->size)))
				{
					return 0;
				}
			}

			lv2_osc_forge_pop(forge, frame);


@@ 314,69 349,91 @@ lv2_osc_forge_packet(LV2_Atom_Forge *forge, LV2_OSC_URID *osc_urid,
					case LV2_OSC_INT32:
					{
						if(!(ref = lv2_osc_forge_int(forge, osc_urid, arg->i)))
						{
							return 0;
						}
						break;
					}
					case LV2_OSC_FLOAT:
					{
						if(!(ref = lv2_osc_forge_float(forge, osc_urid, arg->f)))
						{
							return 0;
						}
						break;
					}
					case LV2_OSC_STRING:
					{
						if(!(ref = lv2_osc_forge_string(forge, osc_urid, arg->s, arg->size - 1)))
						{
							return 0;
						}
						break;
					}
					case LV2_OSC_BLOB:
					{
						if(!(ref = lv2_osc_forge_blob(forge, osc_urid, arg->b, arg->size)))
						{
							return 0;
						}
						break;
					}

					case LV2_OSC_INT64:
					{
						if(!(ref = lv2_osc_forge_long(forge, osc_urid, arg->h)))
						{
							return 0;
						}
						break;
					}
					case LV2_OSC_DOUBLE:
					{
						if(!(ref = lv2_osc_forge_double(forge, osc_urid, arg->d)))
						{
							return 0;
						}
						break;
					}
					case LV2_OSC_TIMETAG:
					{
						if(!(ref = lv2_osc_forge_timetag(forge, osc_urid, LV2_OSC_TIMETAG_CREATE(arg->t))))
						{
							return 0;
						}
						break;
					}

					case LV2_OSC_TRUE:
					{
						if(!(ref = lv2_osc_forge_true(forge, osc_urid)))
						{
							return 0;
						}
						break;
					}
					case LV2_OSC_FALSE:
					{
						if(!(ref = lv2_osc_forge_false(forge, osc_urid)))
						{
							return 0;
						}
						break;
					}
					case LV2_OSC_NIL:
					{
						if(!(ref = lv2_osc_forge_nil(forge, osc_urid)))
						{
							return 0;
						}
						break;
					}
					case LV2_OSC_IMPULSE:
					{
						if(!(ref = lv2_osc_forge_impulse(forge, osc_urid)))
						{
							return 0;
						}
						break;
					}



@@ 384,25 441,33 @@ lv2_osc_forge_packet(LV2_Atom_Forge *forge, LV2_OSC_URID *osc_urid,
					{
						if(!(ref = lv2_osc_forge_symbol(forge, osc_urid,
								map->map(map->handle, arg->S))))
						{
							return 0;
						}
						break;
					}
					case LV2_OSC_MIDI:
					{
						if(!(ref = lv2_osc_forge_midi(forge, osc_urid, &arg->b[1], arg->size - 1)))
						{
							return 0;
						}
						break;
					}
					case LV2_OSC_CHAR:
					{
						if(!(ref = lv2_osc_forge_char(forge, osc_urid, arg->c)))
						{
							return 0;
						}
						break;
					}
					case LV2_OSC_RGBA:
					{
						if(!(ref = lv2_osc_forge_rgba(forge, osc_urid, arg->R, arg->G, arg->B, arg->A)))
						{
							return 0;
						}
						break;
					}
				}

M src/reader.c => src/reader.c +90 -0
@@ 29,7 29,9 @@ bool
lv2_osc_reader_be32toh(LV2_OSC_Reader *reader, union swap32_t *s32)
{
	if(lv2_osc_reader_overflow(reader, 4))
	{
		return false;
	}

	s32->u = *(const uint32_t *)reader->ptr;
	s32->u = be32toh(s32->u);


@@ 42,7 44,9 @@ bool
lv2_osc_reader_be64toh(LV2_OSC_Reader *reader, union swap64_t *s64)
{
	if(lv2_osc_reader_overflow(reader, 8))
	{
		return false;
	}

	s64->u = *(const uint64_t *)reader->ptr;
	s64->u = be64toh(s64->u);


@@ 56,7 60,9 @@ lv2_osc_reader_get_int32(LV2_OSC_Reader *reader, int32_t *i)
{
	union swap32_t s32;
	if(!lv2_osc_reader_be32toh(reader, &s32))
	{
		return false;
	}

	*i = s32.i;



@@ 68,7 74,9 @@ lv2_osc_reader_get_float(LV2_OSC_Reader *reader, float *f)
{
	union swap32_t s32;
	if(!lv2_osc_reader_be32toh(reader, &s32))
	{
		return false;
	}

	*f = s32.f;



@@ 80,7 88,9 @@ lv2_osc_reader_get_int64(LV2_OSC_Reader *reader, int64_t *h)
{
	union swap64_t s64;
	if(!lv2_osc_reader_be64toh(reader, &s64))
	{
		return false;
	}

	*h = s64.h;



@@ 92,7 102,9 @@ lv2_osc_reader_get_timetag(LV2_OSC_Reader *reader, uint64_t *t)
{
	union swap64_t s64;
	if(!lv2_osc_reader_be64toh(reader, &s64))
	{
		return false;
	}

	*t = s64.u;



@@ 104,7 116,9 @@ lv2_osc_reader_get_double(LV2_OSC_Reader *reader, double *d)
{
	union swap64_t s64;
	if(!lv2_osc_reader_be64toh(reader, &s64))
	{
		return false;
	}

	*d = s64.d;



@@ 117,7 131,9 @@ lv2_osc_reader_get_string(LV2_OSC_Reader *reader, const char **s)
	const char *str = (const char *)reader->ptr;
	const size_t padded = LV2_OSC_PADDED_SIZE(strlen(str) + 1);
	if(lv2_osc_reader_overflow(reader, padded ))
	{
		return false;
	}

	*s = str;
	reader->ptr += padded;


@@ 135,7 151,9 @@ bool
lv2_osc_reader_get_midi(LV2_OSC_Reader *reader, const uint8_t **m)
{
	if(lv2_osc_reader_overflow(reader, 4))
	{
		return false;
	}

	*m = reader->ptr;
	reader->ptr += 4;


@@ 147,11 165,15 @@ bool
lv2_osc_reader_get_blob(LV2_OSC_Reader *reader, int32_t *len, const uint8_t **body)
{
	if(!lv2_osc_reader_get_int32(reader, len))
	{
		return false;
	}

	const size_t padded = LV2_OSC_PADDED_SIZE(*len);
	if(lv2_osc_reader_overflow(reader, padded))
	{
		return false;
	}

	*body = reader->ptr;
	reader->ptr += padded;


@@ 163,7 185,9 @@ bool
lv2_osc_reader_get_rgba(LV2_OSC_Reader *reader, uint8_t *r, uint8_t *g, uint8_t *b, uint8_t *a)
{
	if(lv2_osc_reader_overflow(reader, 4))
	{
		return false;
	}

	*r = reader->ptr[0];
	*g = reader->ptr[1];


@@ 179,7 203,9 @@ lv2_osc_reader_get_char(LV2_OSC_Reader *reader, char *c)
{
	int32_t i;
	if(!lv2_osc_reader_get_int32(reader, &i))
	{
		return false;
	}

	*c = i;



@@ 190,10 216,14 @@ LV2_OSC_Item *
lv2_osc_reader_item_raw(LV2_OSC_Reader *reader, LV2_OSC_Item *itm)
{
	if(!lv2_osc_reader_get_int32(reader, &itm->size))
	{
		return NULL;
	}

	if(lv2_osc_reader_overflow(reader, itm->size))
	{
		return NULL;
	}

	itm->body = reader->ptr;



@@ 204,19 234,27 @@ LV2_OSC_Item *
lv2_osc_reader_item_begin(LV2_OSC_Reader *reader, LV2_OSC_Item *itm, size_t len)
{
	if(lv2_osc_reader_overflow(reader, len))
	{
		return NULL;
	}

	itm->end = reader->ptr + len;

	if(lv2_osc_reader_overflow(reader, 16))
	{
		return NULL;
	}

	if(strncmp((const char *)reader->ptr, "#bundle", 8))
	{
		return NULL;
	}
	reader->ptr += 8;

	if(!lv2_osc_reader_get_timetag(reader, &itm->timetag))
	{
		return NULL;
	}

	return lv2_osc_reader_item_raw(reader, itm);
}


@@ 243,7 281,9 @@ lv2_osc_reader_arg_raw(LV2_OSC_Reader *reader, LV2_OSC_Arg *arg)
		case LV2_OSC_INT32:
		{
			if(!lv2_osc_reader_get_int32(reader, &arg->i))
			{
				return NULL;
			}
			arg->size = 4;

			break;


@@ 251,7 291,9 @@ lv2_osc_reader_arg_raw(LV2_OSC_Reader *reader, LV2_OSC_Arg *arg)
		case LV2_OSC_FLOAT:
		{
			if(!lv2_osc_reader_get_float(reader, &arg->f))
			{
				return NULL;
			}
			arg->size = 4;

			break;


@@ 259,7 301,9 @@ lv2_osc_reader_arg_raw(LV2_OSC_Reader *reader, LV2_OSC_Arg *arg)
		case LV2_OSC_STRING:
		{
			if(!lv2_osc_reader_get_string(reader, &arg->s))
			{
				return NULL;
			}
			arg->size = strlen(arg->s) + 1;

			break;


@@ 267,7 311,9 @@ lv2_osc_reader_arg_raw(LV2_OSC_Reader *reader, LV2_OSC_Arg *arg)
		case LV2_OSC_BLOB:
		{
			if(!lv2_osc_reader_get_blob(reader, &arg->size, &arg->b))
			{
				return NULL;
			}
			//arg->size = arg->size;

			break;


@@ 282,7 328,9 @@ lv2_osc_reader_arg_raw(LV2_OSC_Reader *reader, LV2_OSC_Arg *arg)
		case LV2_OSC_INT64:
		{
			if(!lv2_osc_reader_get_int64(reader, &arg->h))
			{
				return NULL;
			}
			arg->size = 8;

			break;


@@ 290,7 338,9 @@ lv2_osc_reader_arg_raw(LV2_OSC_Reader *reader, LV2_OSC_Arg *arg)
		case LV2_OSC_DOUBLE:
		{
			if(!lv2_osc_reader_get_double(reader, &arg->d))
			{
				return NULL;
			}
			arg->size = 8;

			break;


@@ 298,7 348,9 @@ lv2_osc_reader_arg_raw(LV2_OSC_Reader *reader, LV2_OSC_Arg *arg)
		case LV2_OSC_TIMETAG:
		{
			if(!lv2_osc_reader_get_timetag(reader, &arg->t))
			{
				return NULL;
			}
			arg->size = 8;

			break;


@@ 307,7 359,9 @@ lv2_osc_reader_arg_raw(LV2_OSC_Reader *reader, LV2_OSC_Arg *arg)
		case LV2_OSC_MIDI:
		{
			if(!lv2_osc_reader_get_midi(reader, &arg->m))
			{
				return NULL;
			}
			arg->size = 4;

			break;


@@ 315,7 369,9 @@ lv2_osc_reader_arg_raw(LV2_OSC_Reader *reader, LV2_OSC_Arg *arg)
		case LV2_OSC_SYMBOL:
		{
			if(!lv2_osc_reader_get_symbol(reader, &arg->S))
			{
				return NULL;
			}
			arg->size = strlen(arg->S) + 1;

			break;


@@ 323,7 379,9 @@ lv2_osc_reader_arg_raw(LV2_OSC_Reader *reader, LV2_OSC_Arg *arg)
		case LV2_OSC_CHAR:
		{
			if(!lv2_osc_reader_get_char(reader, &arg->c))
			{
				return NULL;
			}
			arg->size = 4;

			break;


@@ 331,7 389,9 @@ lv2_osc_reader_arg_raw(LV2_OSC_Reader *reader, LV2_OSC_Arg *arg)
		case LV2_OSC_RGBA:
		{
			if(!lv2_osc_reader_get_rgba(reader, &arg->R, &arg->G, &arg->B, &arg->A))
			{
				return NULL;
			}
			arg->size = 4;

			break;


@@ 345,18 405,26 @@ LV2_OSC_Arg *
lv2_osc_reader_arg_begin(LV2_OSC_Reader *reader, LV2_OSC_Arg *arg, size_t len)
{
	if(lv2_osc_reader_overflow(reader, len))
	{
		return NULL;
	}

	arg->end = reader->ptr + len;

	if(!lv2_osc_reader_get_string(reader, &arg->path)) //TODO check for validity
	{
		return NULL;
	}

	if(!lv2_osc_reader_get_string(reader, &arg->type)) //TODO check for validity
	{
		return NULL;
	}

	if(*arg->type != ',')
	{
		return NULL;
	}

	arg->type++; // skip ','



@@ 386,19 454,27 @@ lv2_osc_reader_arg_varlist(LV2_OSC_Reader *reader, const char *fmt, va_list args
		{
			case LV2_OSC_INT32:
				if(!lv2_osc_reader_get_int32(reader, va_arg(args, int32_t *)))
				{
					return false;
				}
				break;
			case LV2_OSC_FLOAT:
				if(!lv2_osc_reader_get_float(reader, va_arg(args, float *)))
				{
					return false;
				}
				break;
			case LV2_OSC_STRING:
				if(!lv2_osc_reader_get_string(reader, va_arg(args, const char **)))
				{
					return false;
				}
				break;
			case LV2_OSC_BLOB:
				if(!lv2_osc_reader_get_blob(reader, va_arg(args, int32_t *), va_arg(args, const uint8_t **)))
				{
					return false;
				}
				break;

			case LV2_OSC_TRUE:


@@ 409,33 485,47 @@ lv2_osc_reader_arg_varlist(LV2_OSC_Reader *reader, const char *fmt, va_list args

			case LV2_OSC_INT64:
				if(!lv2_osc_reader_get_int64(reader, va_arg(args, int64_t *)))
				{
					return false;
				}
				break;
			case LV2_OSC_DOUBLE:
				if(!lv2_osc_reader_get_double(reader, va_arg(args, double *)))
				{
					return false;
				}
				break;
			case LV2_OSC_TIMETAG:
				if(!lv2_osc_reader_get_timetag(reader, va_arg(args, uint64_t *)))
				{
					return false;
				}
				break;

			case LV2_OSC_MIDI:
				if(!lv2_osc_reader_get_midi(reader, va_arg(args, const uint8_t **)))
				{
					return false;
				}
				break;
			case LV2_OSC_SYMBOL:
				if(!lv2_osc_reader_get_symbol(reader, va_arg(args, const char **)))
				{
					return false;
				}
				break;
			case LV2_OSC_CHAR:
				if(!lv2_osc_reader_get_char(reader, va_arg(args, char *)))
				{
					return false;
				}
				break;
			case LV2_OSC_RGBA:
				if(!lv2_osc_reader_get_rgba(reader, va_arg(args, uint8_t *), va_arg(args, uint8_t *),
						va_arg(args, uint8_t *), va_arg(args, uint8_t *)))
				{
					return false;
				}
				break;
		}
	}

M src/stream.c => src/stream.c +12 -0
@@ 595,7 595,9 @@ size_t
lv2_osc_slip_encode_inline(uint8_t *dst, size_t len)
{
	if(len == 0)
	{
		return 0;
	}

	const uint8_t *end = dst + len;



@@ 604,7 606,9 @@ lv2_osc_slip_encode_inline(uint8_t *dst, size_t len)
	for(const uint8_t *from=dst; from<end; from++, size++)
	{
		if( (*from == SLIP_END) || (*from == SLIP_ESC))
		{
			size ++;
		}
	}

	// fast track if no escaping needed


@@ 633,7 637,9 @@ lv2_osc_slip_encode_inline(uint8_t *dst, size_t len)
			*to-- = SLIP_ESC;
		}
		else
		{
			*to-- = *from;
		}
	}
	*to-- = SLIP_END;



@@ 661,13 667,19 @@ lv2_osc_slip_decode_inline(uint8_t *dst, size_t len, size_t *size)
		if(*src == SLIP_ESC)
		{
			if(src == end-1)
			{
				break;
			}

			src++;
			if(*src == SLIP_END_REPLACE)
			{
				*ptr++ = SLIP_END;
			}
			else if(*src == SLIP_ESC_REPLACE)
			{
				*ptr++ = SLIP_ESC;
			}
			src++;
		}
		else if(*src == SLIP_END)

M src/util.c => src/util.c +58 -0
@@ 147,11 147,17 @@ lv2_osc_check_path(const char *path)
	assert(path);

	if(path[0] != '/')
	{
		return false;
	}

	for(const char *ptr=path+1; *ptr!='\0'; ptr++)
	{
		if( (isprint(*ptr) == 0) || (strchr(invalid_path_chars, *ptr) != NULL) )
		{
			return false;
		}
	}

	return true;
}


@@ 162,11 168,17 @@ lv2_osc_check_fmt(const char *format, int offset)
	assert(format);

	if(offset && (format[0] != ',') )
	{
		return false;
	}

	for(const char *ptr=format+offset; *ptr!='\0'; ptr++)
	{
		if(strchr(valid_format_chars, *ptr) == NULL)
		{
			return false;
		}
	}

	return true;
}


@@ 217,45 229,75 @@ lv2_osc_argument_type(LV2_OSC_URID *osc_urid, const LV2_Atom *atom)
	const LV2_Atom_Object *obj = (const LV2_Atom_Object *)atom;

	if(atom->type == osc_urid->ATOM_Int)
	{
		return LV2_OSC_INT32;
	}
	else if(atom->type == osc_urid->ATOM_Float)
	{
		return LV2_OSC_FLOAT;
	}
	else if(atom->type == osc_urid->ATOM_String)
	{
		return LV2_OSC_STRING;
	}
	else if(atom->type == osc_urid->ATOM_Chunk)
	{
		return LV2_OSC_BLOB;
	}

	else if(atom->type == osc_urid->ATOM_Long)
	{
		return LV2_OSC_INT64;
	}
	else if(atom->type == osc_urid->ATOM_Double)
	{
		return LV2_OSC_DOUBLE;
	}
	else if( (atom->type == osc_urid->ATOM_Object) && (obj->body.otype == osc_urid->OSC_Timetag) )
	{
		return LV2_OSC_TIMETAG;
	}

	else if(atom->type == osc_urid->ATOM_Bool)
	{
		if(((const LV2_Atom_Bool *)atom)->body)
		{
			return LV2_OSC_TRUE;
		}
		else
		{
			return LV2_OSC_FALSE;
		}
	}
	else if(atom->type == osc_urid->ATOM_Literal)
	{
		const LV2_Atom_Literal *lit = (const LV2_Atom_Literal *)atom;
		if(lit->body.datatype == osc_urid->OSC_Nil)
		{
			return LV2_OSC_NIL;
		}
		else if(lit->body.datatype == osc_urid->OSC_Impulse)
		{
			return LV2_OSC_IMPULSE;
		}
		else if(lit->body.datatype == osc_urid->OSC_Char)
		{
			return LV2_OSC_CHAR;
		}
		else if(lit->body.datatype == osc_urid->OSC_RGBA)
		{
			return LV2_OSC_RGBA;
		}
	}

	else if(atom->type == osc_urid->ATOM_URID)
	{
		return LV2_OSC_SYMBOL;
	}
	else if(atom->type == osc_urid->MIDI_MidiEvent)
	{
		return LV2_OSC_MIDI;
	}

	return '\0';
}


@@ 445,9 487,13 @@ lv2_osc_bundle_body_get(LV2_OSC_URID *osc_urid, uint32_t size, const LV2_Atom_Ob
		0);

	if(!*timetag || ((*timetag)->atom.type != osc_urid->ATOM_Object) || ((*timetag)->body.otype != osc_urid->OSC_Timetag))
	{
		return false;
	}
	if(!*items || ((*items)->atom.type != osc_urid->ATOM_Tuple))
	{
		return false;
	}

	return true;
}


@@ 475,10 521,14 @@ lv2_osc_message_body_get(LV2_OSC_URID *osc_urid, uint32_t size, const LV2_Atom_O
		0);

	if(!*path || ((*path)->atom.type != osc_urid->ATOM_String))
	{
		return false;
	}
	// message without arguments is valid
	if( *arguments && ((*arguments)->atom.type != osc_urid->ATOM_Tuple))
	{
		return false;
	}

	return true;
}


@@ 501,7 551,9 @@ lv2_osc_body_unroll(LV2_OSC_URID *osc_urid, uint32_t size, const LV2_Atom_Object
		const LV2_Atom_Tuple *items = NULL;

		if(!lv2_osc_bundle_body_get(osc_urid, size, body, &timetag, &items))
		{
			return false;
		}

		LV2_OSC_Timetag tt;
		lv2_osc_timetag_get(osc_urid, &timetag->atom, &tt);


@@ 511,7 563,9 @@ lv2_osc_body_unroll(LV2_OSC_URID *osc_urid, uint32_t size, const LV2_Atom_Object
			const LV2_Atom_Object *obj= (const LV2_Atom_Object *)atom;

			if(!lv2_osc_body_unroll(osc_urid, obj->atom.size, &obj->body, method, data))
			{
				return false;
			}
		}

		return true;


@@ 522,10 576,14 @@ lv2_osc_body_unroll(LV2_OSC_URID *osc_urid, uint32_t size, const LV2_Atom_Object
		const LV2_Atom_Tuple *arguments = NULL;

		if(!lv2_osc_message_body_get(osc_urid, size, body, &path, &arguments))
		{
			return false;
		}

		if(method)
		{
			method(LV2_ATOM_BODY_CONST(path), arguments, data);
		}

		return true;
	}

M src/writer.c => src/writer.c +86 -0
@@ 25,7 25,9 @@ size_t
lv2_osc_writer_get_size(LV2_OSC_Writer *writer)
{
	if(writer->ptr > writer->buf)
	{
		return writer->ptr - writer->buf;
	}

	return 0;
}


@@ 36,7 38,9 @@ lv2_osc_writer_finalize(LV2_OSC_Writer *writer, size_t *size)
	*size = lv2_osc_writer_get_size(writer);

	if(*size)
	{
		return writer->buf;
	}

	return NULL;
}


@@ 51,7 55,9 @@ bool
lv2_osc_writer_htobe32(LV2_OSC_Writer *writer, union swap32_t *s32)
{
	if(lv2_osc_writer_overflow(writer, 4))
	{
		return false;
	}

	s32->u = htobe32(s32->u);
	*(uint32_t *)writer->ptr = s32->u;


@@ 64,7 70,9 @@ bool
lv2_osc_writer_htobe64(LV2_OSC_Writer *writer, union swap64_t *s64)
{
	if(lv2_osc_writer_overflow(writer, 8))
	{
		return false;
	}

	s64->u = htobe64(s64->u);
	*(uint64_t *)writer->ptr = s64->u;


@@ 91,7 99,9 @@ lv2_osc_writer_add_string(LV2_OSC_Writer *writer, const char *s)
	const size_t rawlen = strlen(s) + 1;
	const size_t padded = LV2_OSC_PADDED_SIZE(rawlen);
	if(lv2_osc_writer_overflow(writer, padded))
	{
		return false;
	}

	const uint32_t blank = 0;
	memcpy(writer->ptr + padded - sizeof(uint32_t), &blank, sizeof(uint32_t));


@@ 131,10 141,14 @@ lv2_osc_writer_add_blob_inline(LV2_OSC_Writer *writer, int32_t len, uint8_t **bo
	const size_t len_padded = LV2_OSC_PADDED_SIZE(len);
	const size_t size = 4 + len_padded;
	if(lv2_osc_writer_overflow(writer, size))
	{
		return false;
	}

	if(!lv2_osc_writer_add_int32(writer, len))
	{
		return false;
	}

	*body = writer->ptr;
	//memset(&writer->ptr[len], 0x0, len_padded - len);


@@ 148,7 162,9 @@ lv2_osc_writer_add_blob(LV2_OSC_Writer *writer, int32_t len, const uint8_t *body
{
	uint8_t *dst;
	if(!lv2_osc_writer_add_blob_inline(writer, len, &dst))
	{
		return false;
	}

	memcpy(dst, body, len);



@@ 159,7 175,9 @@ bool
lv2_osc_writer_add_midi_inline(LV2_OSC_Writer *writer, int32_t len, uint8_t **m)
{
	if( (len > 4) || lv2_osc_writer_overflow(writer, 4))
	{
		return false;
	}

	*m = writer->ptr;
	//memset(&writer->ptr[len], 0x0, 4 - len);


@@ 173,7 191,9 @@ lv2_osc_writer_add_midi(LV2_OSC_Writer *writer, int32_t len, const uint8_t *m)
{
	uint8_t *dst;
	if(!lv2_osc_writer_add_midi_inline(writer, len, &dst))
	{
		return false;
	}

	memcpy(dst, m, len);



@@ 184,7 204,9 @@ bool
lv2_osc_writer_add_rgba(LV2_OSC_Writer *writer, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
	if(lv2_osc_writer_overflow(writer, 4))
	{
		return false;
	}

	writer->ptr[0] = r;
	writer->ptr[1] = g;


@@ 205,7 227,9 @@ bool
lv2_osc_writer_push_bundle(LV2_OSC_Writer *writer, LV2_OSC_Writer_Frame *frame, uint64_t t)
{
	if(lv2_osc_writer_overflow(writer, 16))
	{
		return false;
	}

	frame->ref = writer->ptr;



@@ 233,7 257,9 @@ lv2_osc_writer_pop_bundle(LV2_OSC_Writer *writer, LV2_OSC_Writer_Frame *frame)
lv2_osc_writer_push_item(LV2_OSC_Writer *writer, LV2_OSC_Writer_Frame *frame)
{
	if(lv2_osc_writer_overflow(writer, 4))
	{
		return false;
	}

	frame->ref = writer->ptr;
	writer->ptr += 4;


@@ 270,7 296,9 @@ lv2_osc_writer_add_format(LV2_OSC_Writer *writer, const char *fmt)
	const size_t rawlen = strlen(fmt) + 1;
	const size_t padded = LV2_OSC_PADDED_SIZE(rawlen + 1);
	if(lv2_osc_writer_overflow(writer, padded))
	{
		return false;
	}

	const uint32_t blank = 0;
	memcpy(writer->ptr + padded - sizeof(uint32_t), &blank, sizeof(uint32_t));


@@ 290,21 318,29 @@ lv2_osc_writer_arg_varlist(LV2_OSC_Writer *writer, const char *fmt, va_list args
		{
			case LV2_OSC_INT32:
				if(!lv2_osc_writer_add_int32(writer, va_arg(args, int32_t)))
				{
					return false;
				}
				break;
			case LV2_OSC_FLOAT:
				if(!lv2_osc_writer_add_float(writer, (float)va_arg(args, double)))
				{
					return false;
				}
				break;
			case LV2_OSC_STRING:
				if(!lv2_osc_writer_add_string(writer, va_arg(args, const char *)))
				{
					return false;
				}
				break;
			case LV2_OSC_BLOB:
			{
				const int32_t len = va_arg(args, int32_t);
				if(!lv2_osc_writer_add_blob(writer, len, va_arg(args, const uint8_t *)))
				{
					return false;
				}
			}	break;

			case LV2_OSC_TRUE:


@@ 315,30 351,42 @@ lv2_osc_writer_arg_varlist(LV2_OSC_Writer *writer, const char *fmt, va_list args

			case LV2_OSC_INT64:
				if(!lv2_osc_writer_add_int64(writer, va_arg(args, int64_t)))
				{
					return false;
				}
				break;
			case LV2_OSC_DOUBLE:
				if(!lv2_osc_writer_add_double(writer, va_arg(args, double)))
				{
					return false;
				}
				break;
			case LV2_OSC_TIMETAG:
				if(!lv2_osc_writer_add_timetag(writer, va_arg(args, uint64_t)))
				{
					return false;
				}
				break;

			case LV2_OSC_MIDI:
			{
				const int32_t len = va_arg(args, int32_t);
				if(!lv2_osc_writer_add_midi(writer, len, va_arg(args, const uint8_t *)))
				{
					return false;
				}
			}	break;
			case LV2_OSC_SYMBOL:
				if(!lv2_osc_writer_add_symbol(writer, va_arg(args, const char *)))
				{
					return false;
				}
				break;
			case LV2_OSC_CHAR:
				if(!lv2_osc_writer_add_char(writer, va_arg(args, int)))
				{
					return false;
				}
				break;
			case LV2_OSC_RGBA:
			{


@@ 347,7 395,9 @@ lv2_osc_writer_arg_varlist(LV2_OSC_Writer *writer, const char *fmt, va_list args
				const uint8_t b = va_arg(args, unsigned);
				const uint8_t a = va_arg(args, unsigned);
				if(!lv2_osc_writer_add_rgba(writer, r, g, b, a))
				{
					return false;
				}
			}	break;
		}
	}


@@ 372,10 422,14 @@ bool
lv2_osc_writer_message_varlist(LV2_OSC_Writer *writer, const char *path, const char *fmt, va_list args)
{
	if(!lv2_osc_writer_add_path(writer, path))
	{
		return false;
	}

	if(!lv2_osc_writer_add_format(writer, fmt))
	{
		return false;
	}

	return lv2_osc_writer_arg_varlist(writer, fmt, args);
}


@@ 403,14 457,18 @@ lv2_osc_writer_packet(LV2_OSC_Writer *writer, LV2_OSC_URID *osc_urid,
		const LV2_Atom_Tuple *items = NULL;

		if(!lv2_osc_bundle_body_get(osc_urid, size, body, &timetag, &items))
		{
			return false;
		}

		LV2_OSC_Timetag tt;
		LV2_OSC_Writer_Frame bndl = { .ref = 0 };

		lv2_osc_timetag_get(osc_urid, &timetag->atom, &tt);
		if(!lv2_osc_writer_push_bundle(writer, &bndl, lv2_osc_timetag_parse(&tt)))
		{
			return false;
		}

		LV2_ATOM_TUPLE_FOREACH(items, atom)
		{


@@ 435,7 493,9 @@ lv2_osc_writer_packet(LV2_OSC_Writer *writer, LV2_OSC_URID *osc_urid,
		if(lv2_osc_message_body_get(osc_urid, size, body, &path, &arguments))
		{
			if(!lv2_osc_writer_add_path(writer, LV2_ATOM_BODY_CONST(path)))
			{
				return false;
			}

			char fmt [128]; //TODO how big?
			char *ptr = fmt;


@@ 445,7 505,9 @@ lv2_osc_writer_packet(LV2_OSC_Writer *writer, LV2_OSC_URID *osc_urid,
			}
			*ptr = '\0';
			if(!lv2_osc_writer_add_format(writer, fmt))
			{
				return false;
			}

			LV2_ATOM_TUPLE_FOREACH(arguments, atom)
			{


@@ 454,40 516,54 @@ lv2_osc_writer_packet(LV2_OSC_Writer *writer, LV2_OSC_URID *osc_urid,
				if(atom->type == osc_urid->ATOM_Int)
				{
					if(!lv2_osc_writer_add_int32(writer, ((const LV2_Atom_Int *)atom)->body))
					{
						return false;
					}
				}
				else if(atom->type == osc_urid->ATOM_Float)
				{
					if(!lv2_osc_writer_add_float(writer, ((const LV2_Atom_Float *)atom)->body))
					{
						return false;
					}
				}
				else if(atom->type == osc_urid->ATOM_String)
				{
					if(!lv2_osc_writer_add_string(writer, LV2_ATOM_BODY_CONST(atom)))
					{
						return false;
					}
				}
				else if(atom->type == osc_urid->ATOM_Chunk)
				{
					if(!lv2_osc_writer_add_blob(writer, atom->size, LV2_ATOM_BODY_CONST(atom)))
					{
						return false;
					}
				}

				else if(atom->type == osc_urid->ATOM_Long)
				{
					if(!lv2_osc_writer_add_int64(writer, ((const LV2_Atom_Long *)atom)->body))
					{
						return false;
					}
				}
				else if(atom->type == osc_urid->ATOM_Double)
				{
					if(!lv2_osc_writer_add_double(writer, ((const LV2_Atom_Double *)atom)->body))
					{
						return false;
					}
				}
				else if( (atom->type == osc_urid->ATOM_Object) && (obj->body.otype == osc_urid->OSC_Timetag) )
				{
					LV2_OSC_Timetag tt;
					lv2_osc_timetag_get(osc_urid, &obj->atom, &tt);
					if(!lv2_osc_writer_add_timetag(writer, lv2_osc_timetag_parse(&tt)))
					{
						return false;
					}
				}

				// there is nothing to do for: true, false, nil, impulse


@@ 496,13 572,17 @@ lv2_osc_writer_packet(LV2_OSC_Writer *writer, LV2_OSC_URID *osc_urid,
				{
					const char *symbol = unmap->unmap(unmap->handle, ((const LV2_Atom_URID *)atom)->body);
					if(!symbol || !lv2_osc_writer_add_symbol(writer, symbol))
					{
						return false;
					}
				}
				else if(atom->type == osc_urid->MIDI_MidiEvent)
				{
					uint8_t *m = NULL;
					if(!lv2_osc_writer_add_midi_inline(writer, atom->size + 1, &m))
					{
						return false;
					}
					m[0] = 0x0; // port
					memcpy(&m[1], LV2_ATOM_BODY_CONST(atom), atom->size);
				}


@@ 514,16 594,22 @@ lv2_osc_writer_packet(LV2_OSC_Writer *writer, LV2_OSC_URID *osc_urid,
					{
						const char c = *(const char *)LV2_ATOM_CONTENTS_CONST(LV2_Atom_Literal, lit);
						if(!lv2_osc_writer_add_char(writer, c))
						{
							return false;
						}
					}
					else if(lit->body.datatype == osc_urid->OSC_RGBA)
					{
						const char *rgba = LV2_ATOM_CONTENTS_CONST(LV2_Atom_Literal, atom);
						uint8_t r, g, b, a;
						if(sscanf(rgba, "%02"SCNx8"%02"SCNx8"%02"SCNx8"%02"SCNx8, &r, &g, &b, &a) != 4)
						{
							return false;
						}
						if(!lv2_osc_writer_add_rgba(writer, r, g, b, a))
						{
							return false;
						}
					}
				}
			}