shithub: rtmp

ref: 94d64d663d67e8553bb95a138574737147b38426
dir: /amf0.c/

View raw version
#include <u.h>
#include <libc.h>
#include "amf0.h"
#include "util.h"

enum {
	Anum,
	Abool,
	Astr,
	Aobj,
	Anull = 5,
	Aarr = 8,
	Aend,
	Alstr = 12,
};

#define atleast(what, x) do{ \
	if(p == nil) \
		return nil; \
	if(e-p < x){ \
		werrstr(what ": buffer short: expected %d, have %d", x, (int)(e-p)); \
		return nil; \
	} \
}while(0)

u8int *
a₀null(u8int *p, u8int *e)
{
	return a₀byte(p, e, Anull);
}

u8int *
a₀bool(u8int *p, u8int *e, int v)
{
	atleast("bool", 2);
	*p++ = Abool;
	*p++ = !!v;
	return p;
}

u8int *
a₀byte(u8int *p, u8int *e, u8int byte)
{
	atleast("byte", 1);
	*p++ = byte;
	return p;
}

u8int *
a₀i16(u8int *p, u8int *e, s16int i)
{
	atleast("i16", 2);
	*p++ = i >> 8;
	*p++ = i;
	return p;
}

u8int *
a₀i24(u8int *p, u8int *e, s32int i)
{
	atleast("i24", 3);
	*p++ = i >> 16;
	*p++ = i >> 8;
	*p++ = i;
	return p;
}

u8int *
a₀i32(u8int *p, u8int *e, s32int i)
{
	atleast("i32", 4);
	*p++ = i >> 24;
	*p++ = i >> 16;
	*p++ = i >> 8;
	*p++ = i;
	return p;
}

u8int *
a₀i32le(u8int *p, u8int *e, s32int i)
{
	atleast("i32le", 4);
	*p++ = i;
	*p++ = i >> 8;
	*p++ = i >> 16;
	*p++ = i >> 24;
	return p;
}

u8int *
a₀num(u8int *p, u8int *e, double v)
{
	union {
		double v;
		u64int u;
	}x;

	atleast("num", 1+8);
	*p++ = Anum;
	x.v = v;
	*p++ = x.u >> 56;
	*p++ = x.u >> 48;
	*p++ = x.u >> 40;
	*p++ = x.u >> 32;
	*p++ = x.u >> 24;
	*p++ = x.u >> 16;
	*p++ = x.u >> 8;
	*p++ = x.u;

	return p;
}

u8int *
a₀str(u8int *p, u8int *e, char *s)
{
	int n;

	n = strlen(s);
	if(n <= 0xffff){
		atleast("str", 1+2+n);
		*p++ = Astr;
		return (u8int*)memmove(a₀i16(p, e, n), s, n) + n;
	}

	atleast("lstr", 1+4+n);
	*p++ = Alstr;
	return (u8int*)memmove(a₀i32(p, e, n), s, n) + n;
}

u8int *
a₀arr(u8int *p, u8int *e)
{
	return a₀byte(p, e, Aarr);
}

u8int *
a₀obj(u8int *p, u8int *e)
{
	return a₀byte(p, e, Aobj);
}

u8int *
a₀end(u8int *p, u8int *e)
{
	return a₀i24(p, e, Aend);
}

static u8int *
a₀kv(u8int *p, u8int *e, char *name)
{
	int n;

	if((n = strlen(name)) > 0xffff){
		werrstr("string too long");
		return nil;
	}
	atleast("kv", 2+n);
	p = a₀i16(p, e, n);

	return (u8int*)memmove(p, name, n) + n;
}

u8int *
a₀kvbool(u8int *p, u8int *e, char *name, int v)
{
	return a₀bool(a₀kv(p, e, name), e, v);
}

u8int *
a₀kvnum(u8int *p, u8int *e, char *name, double v)
{
	return a₀num(a₀kv(p, e, name), e, v);
}

u8int *
a₀kvstr(u8int *p, u8int *e, char *name, char *v)
{
	return a₀str(a₀kv(p, e, name), e, v);
}

u8int *
a₀data(u8int *p, u8int *e, u8int *d, int sz)
{
	atleast("data", sz);

	return (u8int*)memmove(p, d, sz) + sz;
}

u8int *
a₀byteget(u8int *p, u8int *e, u8int *byte)
{
	atleast("byte", 1);
	*byte = *p;
	return p+1;
}

u8int *
a₀i16get(u8int *p, u8int *e, s16int *i)
{
	atleast("i16", 2);
	*i = p[0]<<8 | p[1];
	return p+2;
}

u8int *
a₀i24get(u8int *p, u8int *e, s32int *i)
{
	atleast("i24", 3);
	*i = p[0]<<16 | p[1]<<8 | p[2];
	return p+3;
}

u8int *
a₀i32get(u8int *p, u8int *e, s32int *i)
{
	atleast("i32", 4);
	*i = p[0]<<24 | p[1]<<16 | p[2]<<8 | p[3];
	return p+4;
}

u8int *
a₀i32leget(u8int *p, u8int *e, s32int *i)
{
	atleast("i32le", 4);
	*i = p[0] | p[1]<<8 | p[2]<<16 | p[3]<<24;
	return p+4;
}

void
a₀free(A₀ *a)
{
	int i;

	if(a == nil)
		return;

	switch(a->type){
	case Tstr:
		free(a->str);
		break;
	case Tobj:
		for(i = 0; i < a->obj.n; i++){
			free(a->obj.k[i]);
			a₀free(a->obj.v[i]);
		}
		free(a->obj.k);
		free(a->obj.v);
		break;
	case Tarr:
		for(i = 0; i < a->arr.n; i++)
			a₀free(a->arr.v[i]);
		free(a->arr.v);
	case Tnull:
	case Tnum:
	case Tbool:
		break;
	default:
		sysfatal("unknown a₀ type %d", a->type);
	}

	free(a);
}

u8int *
a₀parse(A₀ **a₀, u8int *p, u8int *e)
{
	s16int s16;
	union {
		double v;
		u64int u;
	}x;
	int n;
	A₀ *a;

	*a₀ = nil;
	atleast("type", 1);
	a = ecalloc(1, sizeof(*a));
	a->type = -1;

	switch(*p++){
	case Anull:
		a->type = Tnull;
		break;
	case Anum:
		atleast("num", 8);
		for(n = 0, x.u = 0; n < 8; n++)
			x.u = x.u<<8 | *p++;
		a->num = x.v;
		a->type = Tnum;
		break;
	case Abool:
		atleast("bool", 1);
		a->bool = *p++;
		a->type = Tbool;
		break;
	case Alstr:
		if((p = a₀i32get(p, e, &n)) == nil)
			return nil;
		if(0){
	case Astr:
			if((p = a₀i16get(p, e, &s16)) == nil)
				return nil;
			n = s16;
		}
		atleast("str", n);
		a->str = estrndup(p, n);
		p += n;
		a->type = Tstr;
		break;
	case Aobj:
		atleast("obj", 3); /* Aend should be there anyway */
		for(a->obj.n = 0;;){
			if((p = a₀i16get(p, e, &s16)) == nil)
				break;
			if(s16 == 0){
				atleast("obj end?", 1);
				if(*p != Aend){
					werrstr("object doesn't end well");
					p = nil;
					break;
				}
				p++;
				break;
			}
			n = s16;
			atleast("obj key", n);
			a->obj.n++;
			a->obj.k = erealloc(a->obj.k, sizeof(*a->obj.k)*a->obj.n);
			a->obj.v = erealloc(a->obj.v, sizeof(*a->obj.v)*a->obj.n);
			a->obj.k[a->obj.n-1] = estrndup(p, n);
			p += n;
			if((p = a₀parse(&a->obj.v[a->obj.n-1], p, e)) == nil){
				werrstr("obj val: %r");
				break;
			}
		}
		a->type = Tobj;
		break;
	case Aarr:
		if((p = a₀i32get(p, e, &a->arr.n)) == nil)
			break;
		a->arr.v = emalloc(sizeof(*a->arr.v)*a->arr.n);
		for(n = 0; n < a->arr.n; n++){
			if((p = a₀parse(&a->arr.v[n], p, e)) == nil){
				werrstr("arr el: %r");
				break;
			}
		}
		if((p = a₀i24get(p, e, &n)) == nil || n != Aend){
			p = nil;
			werrstr("array doesn't end with Aend");
		}
		a->type = Tarr;
		break;
	case Aend:
		werrstr("unexpected Aend");
		p = nil;
		break;
	default:
		werrstr("unexpected type %d", p[-1]);
		p = nil;
		break;
	}

	if(p == nil)
		a₀free(a);
	else
		*a₀ = a;

	return p;
}

int
a₀fmt(Fmt *f)
{
	A₀ *a;
	int i;

	a = va_arg(f->args, A₀*);

	switch(a->type){
	case Tstr:
		fmtprint(f, "%#q", a->str);
		break;
	case Tobj:
		fmtprint(f, "{");
		for(i = 0; i < a->obj.n; i++)
			fmtprint(f, "%s%q:%A", i > 0 ? ", " : "", a->obj.k[i], a->obj.v[i]);
		fmtprint(f, "}");
		break;
	case Tarr:
		fmtprint(f, "[");
		for(i = 0; i < a->arr.n; i++)
			fmtprint(f, "%s%A", i > 0 ? ", " : "", a->arr.v[i]);
		fmtprint(f, "]");
		break;
	case Tnum:
		fmtprint(f, "%g", a->num);
		break;
	case Tbool:
		fmtprint(f, a->bool ? "true" : "false");
		break;
	case Tnull:
		fmtprint(f, "null");
		break;
	default:
		sysfatal("unknown a₀ type %d", a->type);
	}

	return 0;
}