ref: d36842808af48e19a4335fb513801cd9c7ea6464
dir: /amf.c/
#include <u.h> #include <libc.h> #include "amf.h" #include "util.h" enum { Anum, Abool, Astr, Aobj, 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 * amfbool(u8int *p, u8int *e, int v) { atleast("bool", 2); *p++ = Abool; *p++ = !!v; return p; } u8int * amfbyte(u8int *p, u8int *e, u8int byte) { atleast("byte", 1); *p++ = byte; return p; } u8int * amfi16(u8int *p, u8int *e, s16int i) { atleast("i16", 2); *p++ = i >> 8; *p++ = i; return p; } u8int * amfi24(u8int *p, u8int *e, s32int i) { atleast("i24", 3); *p++ = i >> 16; *p++ = i >> 8; *p++ = i; return p; } u8int * amfi32(u8int *p, u8int *e, s32int i) { atleast("i32", 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("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 * amfstr(u8int *p, u8int *e, char *s) { int n; n = strlen(s); if(n <= 0xffff){ atleast("str", 1+2+n); *p++ = Astr; return (u8int*)memmove(amfi16(p, e, n), s, n) + n; } atleast("lstr", 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("kv", 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("byte", 1); *byte = *p; return p+1; } u8int * amfi16get(u8int *p, u8int *e, s16int *i) { atleast("i16", 2); *i = p[0]<<8 | p[1]; return p+2; } u8int * amfi24get(u8int *p, u8int *e, s32int *i) { atleast("i24", 3); *i = p[0]<<16 | p[1]<<8 | p[2]; return p+3; } u8int * amfi32get(u8int *p, u8int *e, s32int *i) { atleast("i32", 4); *i = p[0]<<16 | p[1]<<16 | p[2]<<8 | p[3]; return p+4; } void amffree(Amf *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]); amffree(a->obj.v[i]); } break; case Tarr: for(i = 0; i < a->arr.n; i++) amffree(a->arr.v[i]); case Tnum: case Tbool: break; default: sysfatal("unknown amf type %d", a->type); } free(a); } u8int * amfparse(Amf **amf, u8int *p, u8int *e) { s16int s16; union { double v; u64int u; }x; int n; Amf *a; atleast("type", 1); a = ecalloc(1, sizeof(Amf)); *amf = nil; switch(*p++){ case Anum: atleast("num", 8); for(n = 0, x.u = 0; n < 8; n++) x.u = x.u<<8 | *p++; a->type = Tnum; a->num = x.v; break; case Abool: atleast("bool", 1); a->type = Tbool; a->bool = *p++; break; case Alstr: if((p = amfi32get(p, e, &n)) == nil) return nil; if(0){ case Astr: if((p = amfi16get(p, e, &s16)) == nil) return nil; n = s16; } atleast("str", n); a->str = estrndup(p, n); p += n; break; case Aobj: atleast("obj", 3); /* Aend should be there anyway */ a->type = Tobj; for(a->obj.n = 0;;){ if((p = amfi16get(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 = amfparse(&a->obj.v[a->obj.n-1], p, e)) == nil){ werrstr("obj val: %r"); break; } } break; case Aarr: a->type = Tarr; if((p = amfi32get(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 = amfparse(&a->arr.v[n], p, e)) == nil){ werrstr("arr el: %r"); break; } } if((p = amfi24get(p, e, &n)) == nil || n != Aend){ p = nil; werrstr("array doesn't end with Aend"); } break; case Aend: p = nil; werrstr("unexpected Aend"); break; default: werrstr("unexpected type %d", p[-1]); p = nil; break; } if(p == nil) amffree(a); else *amf = a; return p; } int amffmt(Fmt *f) { Amf *a; int i; a = va_arg(f->args, Amf*); 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; default: sysfatal("unknown amf type %d", a->type); } return 0; }