ref: dad1582627ca77f86f0a6c64d9503a36cab8f00c
dir: /amf0.c/
#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 *
amf0null(u8int *p, u8int *e)
{
return amf0byte(p, e, Anull);
}
u8int *
amf0bool(u8int *p, u8int *e, int v)
{
atleast("bool", 2);
*p++ = Abool;
*p++ = !!v;
return p;
}
u8int *
amf0byte(u8int *p, u8int *e, u8int byte)
{
atleast("byte", 1);
*p++ = byte;
return p;
}
u8int *
amf0i16(u8int *p, u8int *e, s16int i)
{
atleast("i16", 2);
*p++ = i >> 8;
*p++ = i;
return p;
}
u8int *
amf0i24(u8int *p, u8int *e, s32int i)
{
atleast("i24", 3);
*p++ = i >> 16;
*p++ = i >> 8;
*p++ = i;
return p;
}
u8int *
amf0i32(u8int *p, u8int *e, s32int i)
{
atleast("i32", 4);
*p++ = i >> 24;
*p++ = i >> 16;
*p++ = i >> 8;
*p++ = i;
return p;
}
u8int *
amf0num(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 *
amf0str(u8int *p, u8int *e, char *s)
{
int n;
n = strlen(s);
if(n <= 0xffff){
atleast("str", 1+2+n);
*p++ = Astr;
return (u8int*)memmove(amf0i16(p, e, n), s, n) + n;
}
atleast("lstr", 1+4+n);
*p++ = Alstr;
return (u8int*)memmove(amf0i32(p, e, n), s, n) + n;
}
u8int *
amf0arr(u8int *p, u8int *e)
{
return amf0byte(p, e, Aarr);
}
u8int *
amf0obj(u8int *p, u8int *e)
{
return amf0byte(p, e, Aobj);
}
u8int *
amf0end(u8int *p, u8int *e)
{
return amf0i24(p, e, Aend);
}
static u8int *
amf0kv(u8int *p, u8int *e, char *name)
{
int n;
if((n = strlen(name)) > 0xffff){
werrstr("string too long");
return nil;
}
atleast("kv", 2+n);
p = amf0i16(p, e, n);
return (u8int*)memmove(p, name, n) + n;
}
u8int *
amf0kvbool(u8int *p, u8int *e, char *name, int v)
{
return amf0bool(amf0kv(p, e, name), e, v);
}
u8int *
amf0kvnum(u8int *p, u8int *e, char *name, double v)
{
return amf0num(amf0kv(p, e, name), e, v);
}
u8int *
amf0kvstr(u8int *p, u8int *e, char *name, char *v)
{
return amf0str(amf0kv(p, e, name), e, v);
}
u8int *
amf0byteget(u8int *p, u8int *e, u8int *byte)
{
atleast("byte", 1);
*byte = *p;
return p+1;
}
u8int *
amf0i16get(u8int *p, u8int *e, s16int *i)
{
atleast("i16", 2);
*i = p[0]<<8 | p[1];
return p+2;
}
u8int *
amf0i24get(u8int *p, u8int *e, s32int *i)
{
atleast("i24", 3);
*i = p[0]<<16 | p[1]<<8 | p[2];
return p+3;
}
u8int *
amf0i32get(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 *
amf0i32leget(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
amf0free(Amf0 *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]);
amf0free(a->obj.v[i]);
}
break;
case Tarr:
for(i = 0; i < a->arr.n; i++)
amf0free(a->arr.v[i]);
case Tnull:
case Tnum:
case Tbool:
break;
default:
sysfatal("unknown amf0 type %d", a->type);
}
free(a);
}
u8int *
amf0parse(Amf0 **amf0, u8int *p, u8int *e)
{
s16int s16;
union {
double v;
u64int u;
}x;
int n;
Amf0 *a;
*amf0 = 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 = amf0i32get(p, e, &n)) == nil)
return nil;
if(0){
case Astr:
if((p = amf0i16get(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 = amf0i16get(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 = amf0parse(&a->obj.v[a->obj.n-1], p, e)) == nil){
werrstr("obj val: %r");
break;
}
}
a->type = Tobj;
break;
case Aarr:
if((p = amf0i32get(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 = amf0parse(&a->arr.v[n], p, e)) == nil){
werrstr("arr el: %r");
break;
}
}
if((p = amf0i24get(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)
amf0free(a);
else
*amf0 = a;
return p;
}
int
amf0fmt(Fmt *f)
{
Amf0 *a;
int i;
a = va_arg(f->args, Amf0*);
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 amf0 type %d", a->type);
}
return 0;
}