shithub: rtmp

ref: 8fb3df30a16cc34dcb10c3c068ddca913fb71047
dir: /amf.c/

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

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

#define atleast(x) do{ \
	if(p == nil) \
		return nil; \
	if(e-p < x){ \
		werrstr("buffer short"); \
		return nil; \
	} \
}while(0)

u8int *
amfbool(u8int *p, u8int *e, int v)
{
	atleast(2);
	*p++ = Abool;
	*p++ = !!v;
	return p;
}

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

u8int *
amfi16(u8int *p, u8int *e, s16int i)
{
	atleast(2);
	*p++ = i >> 8;
	*p++ = i;
	return p;
}

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

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

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

	atleast(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 *
amfstr(u8int *p, u8int *e, char *s)
{
	int n;

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

	atleast(1+4+n);
	*p++ = Alstr;
	return (u8int*)memmove(amfi32(p, e, n), s, n) + n;
}

u8int *
amfarr(u8int *p, u8int *e)
{
	return amfbyte(p, e, Aarr);
}

u8int *
amfobj(u8int *p, u8int *e)
{
	return amfbyte(p, e, Aobj);
}

u8int *
amfend(u8int *p, u8int *e)
{
	return amfi24(p, e, Aend);
}

static u8int *
amfkv(u8int *p, u8int *e, char *name)
{
	int n;

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

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

u8int *
amfkvbool(u8int *p, u8int *e, char *name, int v)
{
	return amfbool(amfkv(p, e, name), e, v);
}

u8int *
amfkvnum(u8int *p, u8int *e, char *name, double v)
{
	return amfnum(amfkv(p, e, name), e, v);
}

u8int *
amfkvstr(u8int *p, u8int *e, char *name, char *v)
{
	return amfstr(amfkv(p, e, name), e, v);
}

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

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

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

u8int *
amfi32get(u8int *p, u8int *e, s32int *i)
{
	atleast(4);
	*i = p[0]<<16 | p[1]<<16 | p[2]<<8 | p[3];
	return p+4;
}

u8int *
amffmt(Fmt *f, u8int *p, u8int *e, int one)
{
	union {
		double v;
		u64int u;
	}x;
	s16int s16;
	int n;

	for(; p != e;){
		atleast(1);

		switch(*p++){
		case Anum:
			atleast(8);
			x.u = (uvlong)p[0]<<56 | (uvlong)p[1]<<48 | (uvlong)p[2]<<40 | (uvlong)p[3]<<32;
			x.u |= p[4]<<24 | p[5]<<16 | p[6]<<8 | p[7];
			fmtprint(f, "%g", x.v);
			p += 8;
			break;
		case Abool:
			atleast(1);
			fmtprint(f, *p ? "true" : "false");
			p++;
			break;
		case Astr:
			if((p = amfi16get(p, e, &s16)) == nil)
				return nil;
			n = s16;
String:
			atleast(n);
			/* FIXME this isn't correct - UTF-8 */
			fmtprint(f, "%.*#q", n, (char*)p);
			p += n;
			break;
		case Aobj:
			fmtprint(f, "O{");
			for(;;){
				if((p = amfi16get(p, e, &s16)) == nil)
					return nil;
				if(s16 == 0){
					atleast(1);
					if(*p != Aend){
						werrstr("object doesn't end well");
						return nil;
					}
					p++;
					fmtprint(f, "}");
					break;
				}
				n = s16;
				atleast(n);
				fmtprint(f, "%.*s=", n, (char*)p);
				p += n;
				p = amffmt(f, p, e, 1);
				fmtprint(f, " ");
			}
			break;
		case Aarr:
			fmtprint(f, "A{");
			atleast(4);
			p += 4;
			break;
		case Aend:
			fmtprint(f, "}");
			break;
		case Alstr:
			if((p = amfi32get(p, e, &n)) == nil)
				return nil;
			goto String;
		default:
			fmtprint(f, "?%d?", p[-1]);
			break;
		}

		if(one)
			break;
		if(p != e)
			fmtprint(f, " ");
	}

	return p;
}