implement Ninep; include "sys.m"; sys: Sys; include "9p.m"; STR: con BIT16SZ; # string lengthTAG: con BIT16SZ;FID: con BIT32SZ;QID: con BIT8SZ+BIT32SZ+BIT64SZ;LEN: con BIT16SZ; # stat and qid array lengthsCOUNT: con BIT32SZ;OFFSET: con BIT64SZ; H: con BIT32SZ+BIT8SZ+BIT16SZ; # minimum header length: size[4] type tag[2] ## the following array could be shorter if it were indexed by (type-Tversion)#hdrlen := array[Tmax] of{Tversion => H+COUNT+STR, # size[4] Tversion tag[2] msize[4] version[s]Rversion => H+COUNT+STR, # size[4] Rversion tag[2] msize[4] version[s] Tauth => H+FID+STR+STR, # size[4] Tauth tag[2] afid[4] uname[s] aname[s]Rauth => H+QID, # size[4] Rauth tag[2] aqid[13] Rerror => H+STR, # size[4] Rerror tag[2] ename[s] Tflush => H+TAG, # size[4] Tflush tag[2] oldtag[2]Rflush => H, # size[4] Rflush tag[2] Tattach => H+FID+FID+STR+STR, # size[4] Tattach tag[2] fid[4] afid[4] uname[s] aname[s]Rattach => H+QID, # size[4] Rattach tag[2] qid[13] Twalk => H+FID+FID+LEN, # size[4] Twalk tag[2] fid[4] newfid[4] nwname[2] nwname*(wname[s])Rwalk => H+LEN, # size[4] Rwalk tag[2] nwqid[2] nwqid*(wqid[13]) Topen => H+FID+BIT8SZ, # size[4] Topen tag[2] fid[4] mode[1]Ropen => H+QID+COUNT, # size[4] Ropen tag[2] qid[13] iounit[4] Tcreate => H+FID+STR+BIT32SZ+BIT8SZ, # size[4] Tcreate tag[2] fid[4] name[s] perm[4] mode[1]Rcreate => H+QID+COUNT, # size[4] Rcreate tag[2] qid[13] iounit[4] Tread => H+FID+OFFSET+COUNT, # size[4] Tread tag[2] fid[4] offset[8] count[4]Rread => H+COUNT, # size[4] Rread tag[2] count[4] data[count] Twrite => H+FID+OFFSET+COUNT, # size[4] Twrite tag[2] fid[4] offset[8] count[4] data[count]Rwrite => H+COUNT, # size[4] Rwrite tag[2] count[4] Tclunk => H+FID, # size[4] Tclunk tag[2] fid[4]Rclunk => H, # size[4] Rclunk tag[2] Tremove => H+FID, # size[4] Tremove tag[2] fid[4]Rremove => H, # size[4] Rremove tag[2] Tstat => H+FID, # size[4] Tstat tag[2] fid[4]Rstat => H+LEN, # size[4] Rstat tag[2] stat[n] Twstat => H+FID+LEN, # size[4] Twstat tag[2] fid[4] stat[n]Rwstat => H, # size[4] Rwstat tag[2]}; init(){ sys = load Sys Sys->PATH;} utflen(s: string): int{ # the domain is 16-bit unicode only, which is all that Inferno now implements n := l := len s; for(i:=0; i<l; i++) if((c := s[i]) > 16r7F){ n++; if(c > 16r7FF) n++; } return n;} packdirsize(d: Sys->Dir): int{ return STATFIXLEN+utflen(d.name)+utflen(d.uid)+utflen(d.gid)+utflen(d.muid);} packdir(f: Sys->Dir): array of byte{ ds := packdirsize(f); a := array[ds] of byte; # size[2] a[0] = byte (ds-LEN); a[1] = byte ((ds-LEN)>>8); # type[2] a[2] = byte f.dtype; a[3] = byte (f.dtype>>8); # dev[4] a[4] = byte f.dev; a[5] = byte (f.dev>>8); a[6] = byte (f.dev>>16); a[7] = byte (f.dev>>24); # qid.type[1] # qid.vers[4] # qid.path[8] pqid(a, 8, f.qid); # mode[4] a[21] = byte f.mode; a[22] = byte (f.mode>>8); a[23] = byte (f.mode>>16); a[24] = byte (f.mode>>24); # atime[4] a[25] = byte f.atime; a[26] = byte (f.atime>>8); a[27] = byte (f.atime>>16); a[28] = byte (f.atime>>24); # mtime[4] a[29] = byte f.mtime; a[30] = byte (f.mtime>>8); a[31] = byte (f.mtime>>16); a[32] = byte (f.mtime>>24); # length[8] p64(a, 33, big f.length); # name[s] i := pstring(a, 33+BIT64SZ, f.name); i = pstring(a, i, f.uid); i = pstring(a, i, f.gid); i = pstring(a, i, f.muid); if(i != len a) raise "assertion: Ninep->packdir: bad count"; # can't happen unless packedsize is wrong return a;} pqid(a: array of byte, o: int, q: Sys->Qid): int{ a[o] = byte q.qtype; v := q.vers; a[o+1] = byte v; a[o+2] = byte (v>>8); a[o+3] = byte (v>>16); a[o+4] = byte (v>>24); v = int q.path; a[o+5] = byte v; a[o+6] = byte (v>>8); a[o+7] = byte (v>>16); a[o+8] = byte (v>>24); v = int (q.path >> 32); a[o+9] = byte v; a[o+10] = byte (v>>8); a[o+11] = byte (v>>16); a[o+12] = byte (v>>24); return o+QID;} pstring(a: array of byte, o: int, s: string): int{ sa := array of byte s; # could do conversion ourselves n := len sa; a[o] = byte n; a[o+1] = byte (n>>8); a[o+2:] = sa; return o+LEN+n;} p32(a: array of byte, o: int, v: int): int{ a[o] = byte v; a[o+1] = byte (v>>8); a[o+2] = byte (v>>16); a[o+3] = byte (v>>24); return o+BIT32SZ;} p64(a: array of byte, o: int, b: big): int{ i := int b; a[o] = byte i; a[o+1] = byte (i>>8); a[o+2] = byte (i>>16); a[o+3] = byte (i>>24); i = int (b>>32); a[o+4] = byte i; a[o+5] = byte (i>>8); a[o+6] = byte (i>>16); a[o+7] = byte (i>>24); return o+BIT64SZ;} unpackdir(a: array of byte): (int, Sys->Dir){ dir: Sys->Dir; if(len a < STATFIXLEN) return (0, dir); # size[2] sz := ((int a[1] << 8) | int a[0])+LEN; # bytes this packed dir should occupy if(len a < sz) return (0, dir); # type[2] dir.dtype = (int a[3]<<8) | int a[2]; # dev[4] dir.dev = (((((int a[7] << 8) | int a[6]) << 8) | int a[5]) << 8) | int a[4]; # qid.type[1] # qid.vers[4] # qid.path[8] dir.qid = gqid(a, 8); # mode[4] dir.mode = (((((int a[24] << 8) | int a[23]) << 8) | int a[22]) << 8) | int a[21]; # atime[4] dir.atime = (((((int a[28] << 8) | int a[27]) << 8) | int a[26]) << 8) | int a[25]; # mtime[4] dir.mtime = (((((int a[32] << 8) | int a[31]) << 8) | int a[30]) << 8) | int a[29]; # length[8] v0 := (((((int a[36] << 8) | int a[35]) << 8) | int a[34]) << 8) | int a[33]; v1 := (((((int a[40] << 8) | int a[39]) << 8) | int a[38]) << 8) | int a[37]; dir.length = (big v1 << 32) | (big v0 & 16rFFFFFFFF); # name[s], uid[s], gid[s], muid[s] i: int; (dir.name, i) = gstring(a, 41); (dir.uid, i) = gstring(a, i); (dir.gid, i) = gstring(a, i); (dir.muid, i) = gstring(a, i); if(i != sz) return (0, dir); return (i, dir);} gqid(f: array of byte, i: int): Sys->Qid{ qtype := int f[i]; vers := (((((int f[i+4] << 8) | int f[i+3]) << 8) | int f[i+2]) << 8) | int f[i+1]; i += BIT8SZ+BIT32SZ; path0 := (((((int f[i+3] << 8) | int f[i+2]) << 8) | int f[i+1]) << 8) | int f[i]; i += BIT32SZ; path1 := (((((int f[i+3] << 8) | int f[i+2]) << 8) | int f[i+1]) << 8) | int f[i]; path := (big path1 << 32) | (big path0 & 16rFFFFFFFF); return (path, vers, qtype);} g32(f: array of byte, i: int): int{ return (((((int f[i+3] << 8) | int f[i+2]) << 8) | int f[i+1]) << 8) | int f[i];} g64(f: array of byte, i: int): big{ b0 := (((((int f[i+3] << 8) | int f[i+2]) << 8) | int f[i+1]) << 8) | int f[i]; b1 := (((((int f[i+7] << 8) | int f[i+6]) << 8) | int f[i+5]) << 8) | int f[i+4]; return (big b1 << 32) | (big b0 & 16rFFFFFFFF);} gstring(a: array of byte, o: int): (string, int){ if(o < 0 || o+STR > len a) return (nil, -1); l := (int a[o+1] << 8) | int a[o]; o += STR; e := o+l; if(e > len a) return (nil, -1); return (string a[o:e], e);} ttag2type := array[] of {tagof Tmsg.Readerror => 0,tagof Tmsg.Version => Tversion,tagof Tmsg.Auth => Tauth,tagof Tmsg.Attach => Tattach,tagof Tmsg.Flush => Tflush,tagof Tmsg.Walk => Twalk,tagof Tmsg.Open => Topen,tagof Tmsg.Create => Tcreate,tagof Tmsg.Read => Tread,tagof Tmsg.Write => Twrite,tagof Tmsg.Clunk => Tclunk,tagof Tmsg.Stat => Tstat,tagof Tmsg.Remove => Tremove,tagof Tmsg.Wstat => Twstat,}; Tmsg.mtype(t: self ref Tmsg): int{ return ttag2type[tagof t];} Tmsg.packedsize(t: self ref Tmsg): int{ mtype := ttag2type[tagof t]; if(mtype <= 0) return 0; ml := hdrlen[mtype]; pick m := t { Version => ml += utflen(m.version); Auth => ml += utflen(m.uname)+utflen(m.aname); Attach => ml += utflen(m.uname)+utflen(m.aname); Walk => for(i:=0; i<len m.names; i++) ml += STR+utflen(m.names[i]); Create => ml += utflen(m.name); Write => ml += len m.data; Wstat => ml += packdirsize(m.stat); } return ml;} Tmsg.pack(t: self ref Tmsg): array of byte{ if(t == nil) return nil; ds := t.packedsize(); if(ds <= 0) return nil; d := array[ds] of byte; d[0] = byte ds; d[1] = byte (ds>>8); d[2] = byte (ds>>16); d[3] = byte (ds>>24); d[4] = byte ttag2type[tagof t]; d[5] = byte t.tag; d[6] = byte (t.tag >> 8); pick m := t { Version => p32(d, H, m.msize); pstring(d, H+COUNT, m.version); Auth => p32(d, H, m.afid); o := pstring(d, H+FID, m.uname); pstring(d, o, m.aname); Flush => v := m.oldtag; d[H] = byte v; d[H+1] = byte (v>>8); Attach => p32(d, H, m.fid); p32(d, H+FID, m.afid); o := pstring(d, H+2*FID, m.uname); pstring(d, o, m.aname); Walk => d[H] = byte m.fid; d[H+1] = byte (m.fid>>8); d[H+2] = byte (m.fid>>16); d[H+3] = byte (m.fid>>24); d[H+FID] = byte m.newfid; d[H+FID+1] = byte (m.newfid>>8); d[H+FID+2] = byte (m.newfid>>16); d[H+FID+3] = byte (m.newfid>>24); n := len m.names; d[H+2*FID] = byte n; d[H+2*FID+1] = byte (n>>8); o := H+2*FID+LEN; for(i := 0; i < n; i++) o = pstring(d, o, m.names[i]); Open => p32(d, H, m.fid); d[H+FID] = byte m.mode; Create => p32(d, H, m.fid); o := pstring(d, H+FID, m.name); p32(d, o, m.perm); d[o+BIT32SZ] = byte m.mode; Read => p32(d, H, m.fid); p64(d, H+FID, m.offset); p32(d, H+FID+OFFSET, m.count); Write => p32(d, H, m.fid); p64(d, H+FID, m.offset); n := len m.data; p32(d, H+FID+OFFSET, n); d[H+FID+OFFSET+COUNT:] = m.data; Clunk or Remove or Stat => p32(d, H, m.fid); Wstat => p32(d, H, m.fid); stat := packdir(m.stat); n := len stat; d[H+FID] = byte n; d[H+FID+1] = byte (n>>8); d[H+FID+LEN:] = stat; * => raise sys->sprint("assertion: Ninep->Tmsg.pack: bad tag: %d", tagof t); } return d;} Tmsg.unpack(f: array of byte): (int, ref Tmsg){ if(len f < H) return (0, nil); size := (int f[1] << 8) | int f[0]; size |= ((int f[3] << 8) | int f[2]) << 16; if(len f != size){ if(len f < size) return (0, nil); # need more data f = f[0:size]; # trim to exact length } mtype := int f[4]; if(mtype >= len hdrlen || (mtype&1) != 0 || size < hdrlen[mtype]) return (-1, nil); tag := (int f[6] << 8) | int f[5]; fid := 0; if(hdrlen[mtype] >= H+FID) fid = g32(f, H); # fid is always in same place: extract it once for all if there # return out of each case body for a legal message; # break out of the case for an illegal one Decode: case mtype { * => sys->print("styx: Tmsg.unpack: bad type %d\n", mtype); Tversion => msize := fid; (version, o) := gstring(f, H+COUNT); if(o <= 0) break; return (o, ref Tmsg.Version(tag, msize, version)); Tauth => (uname, o1) := gstring(f, H+FID); (aname, o2) := gstring(f, o1); if(o2 <= 0) break; return (o2, ref Tmsg.Auth(tag, fid, uname, aname)); Tflush => oldtag := (int f[H+1] << 8) | int f[H]; return (H+TAG, ref Tmsg.Flush(tag, oldtag)); Tattach => afid := g32(f, H+FID); (uname, o1) := gstring(f, H+2*FID); (aname, o2) := gstring(f, o1); if(o2 <= 0) break; return (o2, ref Tmsg.Attach(tag, fid, afid, uname, aname)); Twalk => newfid := g32(f, H+FID); n := (int f[H+2*FID+1] << 8) | int f[H+2*FID]; if(n > MAXWELEM) break; o := H+2*FID+LEN; names: array of string = nil; if(n > 0){ names = array[n] of string; for(i:=0; i<n; i++){ (names[i], o) = gstring(f, o); if(o <= 0) break Decode; } } return (o, ref Tmsg.Walk(tag, fid, newfid, names)); Topen => return (H+FID+BIT8SZ, ref Tmsg.Open(tag, fid, int f[H+FID])); Tcreate => (name, o) := gstring(f, H+FID); if(o <= 0 || o+BIT32SZ+BIT8SZ > len f) break; perm := g32(f, o); o += BIT32SZ; mode := int f[o++]; return (o, ref Tmsg.Create(tag, fid, name, perm, mode)); Tread => offset := g64(f, H+FID); count := g32(f, H+FID+OFFSET); return (H+FID+OFFSET+COUNT, ref Tmsg.Read(tag, fid, offset, count)); Twrite => offset := g64(f, H+FID); count := g32(f, H+FID+OFFSET); O: con H+FID+OFFSET+COUNT; if(count > len f-O) break; data := f[O:O+count]; return (O+count, ref Tmsg.Write(tag, fid, offset, data)); Tclunk => return (H+FID, ref Tmsg.Clunk(tag, fid)); Tremove => return (H+FID, ref Tmsg.Remove(tag, fid)); Tstat => return (H+FID, ref Tmsg.Stat(tag, fid)); Twstat => n := int (f[H+FID+1]<<8) | int f[H+FID]; if(len f < H+FID+LEN+n) break; (ds, stat) := unpackdir(f[H+FID+LEN:]); if(ds != n){ sys->print("Ninep->Tmsg.unpack: wstat count: %d/%d\n", ds, n); # temporary break; } return (H+FID+LEN+n, ref Tmsg.Wstat(tag, fid, stat)); } return (-1, nil); # illegal} tmsgname := array[] of {tagof Tmsg.Readerror => "Readerror",Lines 1–500 of 914. Load more
implement Ninep; include "sys.m"; sys: Sys; include "9p.m"; STR: con BIT16SZ; # string length TAG: con BIT16SZ; FID: con BIT32SZ; QID: con BIT8SZ+BIT32SZ+BIT64SZ; LEN: con BIT16SZ; # stat and qid array lengths COUNT: con BIT32SZ; OFFSET: con BIT64SZ; H: con BIT32SZ+BIT8SZ+BIT16SZ; # minimum header length: size[4] type tag[2] # # the following array could be shorter if it were indexed by (type-Tversion) # hdrlen := array[Tmax] of { Tversion => H+COUNT+STR, # size[4] Tversion tag[2] msize[4] version[s] Rversion => H+COUNT+STR, # size[4] Rversion tag[2] msize[4] version[s] Tauth => H+FID+STR+STR, # size[4] Tauth tag[2] afid[4] uname[s] aname[s] Rauth => H+QID, # size[4] Rauth tag[2] aqid[13] Rerror => H+STR, # size[4] Rerror tag[2] ename[s] Tflush => H+TAG, # size[4] Tflush tag[2] oldtag[2] Rflush => H, # size[4] Rflush tag[2] Tattach => H+FID+FID+STR+STR, # size[4] Tattach tag[2] fid[4] afid[4] uname[s] aname[s] Rattach => H+QID, # size[4] Rattach tag[2] qid[13] Twalk => H+FID+FID+LEN, # size[4] Twalk tag[2] fid[4] newfid[4] nwname[2] nwname*(wname[s]) Rwalk => H+LEN, # size[4] Rwalk tag[2] nwqid[2] nwqid*(wqid[13]) Topen => H+FID+BIT8SZ, # size[4] Topen tag[2] fid[4] mode[1] Ropen => H+QID+COUNT, # size[4] Ropen tag[2] qid[13] iounit[4] Tcreate => H+FID+STR+BIT32SZ+BIT8SZ, # size[4] Tcreate tag[2] fid[4] name[s] perm[4] mode[1] Rcreate => H+QID+COUNT, # size[4] Rcreate tag[2] qid[13] iounit[4] Tread => H+FID+OFFSET+COUNT, # size[4] Tread tag[2] fid[4] offset[8] count[4] Rread => H+COUNT, # size[4] Rread tag[2] count[4] data[count] Twrite => H+FID+OFFSET+COUNT, # size[4] Twrite tag[2] fid[4] offset[8] count[4] data[count] Rwrite => H+COUNT, # size[4] Rwrite tag[2] count[4] Tclunk => H+FID, # size[4] Tclunk tag[2] fid[4] Rclunk => H, # size[4] Rclunk tag[2] Tremove => H+FID, # size[4] Tremove tag[2] fid[4] Rremove => H, # size[4] Rremove tag[2] Tstat => H+FID, # size[4] Tstat tag[2] fid[4] Rstat => H+LEN, # size[4] Rstat tag[2] stat[n] Twstat => H+FID+LEN, # size[4] Twstat tag[2] fid[4] stat[n] Rwstat => H, # size[4] Rwstat tag[2] }; init() { sys = load Sys Sys->PATH; } utflen(s: string): int { # the domain is 16-bit unicode only, which is all that Inferno now implements n := l := len s; for(i:=0; i<l; i++) if((c := s[i]) > 16r7F){ n++; if(c > 16r7FF) n++; } return n; } packdirsize(d: Sys->Dir): int { return STATFIXLEN+utflen(d.name)+utflen(d.uid)+utflen(d.gid)+utflen(d.muid); } packdir(f: Sys->Dir): array of byte { ds := packdirsize(f); a := array[ds] of byte; # size[2] a[0] = byte (ds-LEN); a[1] = byte ((ds-LEN)>>8); # type[2] a[2] = byte f.dtype; a[3] = byte (f.dtype>>8); # dev[4] a[4] = byte f.dev; a[5] = byte (f.dev>>8); a[6] = byte (f.dev>>16); a[7] = byte (f.dev>>24); # qid.type[1] # qid.vers[4] # qid.path[8] pqid(a, 8, f.qid); # mode[4] a[21] = byte f.mode; a[22] = byte (f.mode>>8); a[23] = byte (f.mode>>16); a[24] = byte (f.mode>>24); # atime[4] a[25] = byte f.atime; a[26] = byte (f.atime>>8); a[27] = byte (f.atime>>16); a[28] = byte (f.atime>>24); # mtime[4] a[29] = byte f.mtime; a[30] = byte (f.mtime>>8); a[31] = byte (f.mtime>>16); a[32] = byte (f.mtime>>24); # length[8] p64(a, 33, big f.length); # name[s] i := pstring(a, 33+BIT64SZ, f.name); i = pstring(a, i, f.uid); i = pstring(a, i, f.gid); i = pstring(a, i, f.muid); if(i != len a) raise "assertion: Ninep->packdir: bad count"; # can't happen unless packedsize is wrong return a; } pqid(a: array of byte, o: int, q: Sys->Qid): int { a[o] = byte q.qtype; v := q.vers; a[o+1] = byte v; a[o+2] = byte (v>>8); a[o+3] = byte (v>>16); a[o+4] = byte (v>>24); v = int q.path; a[o+5] = byte v; a[o+6] = byte (v>>8); a[o+7] = byte (v>>16); a[o+8] = byte (v>>24); v = int (q.path >> 32); a[o+9] = byte v; a[o+10] = byte (v>>8); a[o+11] = byte (v>>16); a[o+12] = byte (v>>24); return o+QID; } pstring(a: array of byte, o: int, s: string): int { sa := array of byte s; # could do conversion ourselves n := len sa; a[o] = byte n; a[o+1] = byte (n>>8); a[o+2:] = sa; return o+LEN+n; } p32(a: array of byte, o: int, v: int): int { a[o] = byte v; a[o+1] = byte (v>>8); a[o+2] = byte (v>>16); a[o+3] = byte (v>>24); return o+BIT32SZ; } p64(a: array of byte, o: int, b: big): int { i := int b; a[o] = byte i; a[o+1] = byte (i>>8); a[o+2] = byte (i>>16); a[o+3] = byte (i>>24); i = int (b>>32); a[o+4] = byte i; a[o+5] = byte (i>>8); a[o+6] = byte (i>>16); a[o+7] = byte (i>>24); return o+BIT64SZ; } unpackdir(a: array of byte): (int, Sys->Dir) { dir: Sys->Dir; if(len a < STATFIXLEN) return (0, dir); # size[2] sz := ((int a[1] << 8) | int a[0])+LEN; # bytes this packed dir should occupy if(len a < sz) return (0, dir); # type[2] dir.dtype = (int a[3]<<8) | int a[2]; # dev[4] dir.dev = (((((int a[7] << 8) | int a[6]) << 8) | int a[5]) << 8) | int a[4]; # qid.type[1] # qid.vers[4] # qid.path[8] dir.qid = gqid(a, 8); # mode[4] dir.mode = (((((int a[24] << 8) | int a[23]) << 8) | int a[22]) << 8) | int a[21]; # atime[4] dir.atime = (((((int a[28] << 8) | int a[27]) << 8) | int a[26]) << 8) | int a[25]; # mtime[4] dir.mtime = (((((int a[32] << 8) | int a[31]) << 8) | int a[30]) << 8) | int a[29]; # length[8] v0 := (((((int a[36] << 8) | int a[35]) << 8) | int a[34]) << 8) | int a[33]; v1 := (((((int a[40] << 8) | int a[39]) << 8) | int a[38]) << 8) | int a[37]; dir.length = (big v1 << 32) | (big v0 & 16rFFFFFFFF); # name[s], uid[s], gid[s], muid[s] i: int; (dir.name, i) = gstring(a, 41); (dir.uid, i) = gstring(a, i); (dir.gid, i) = gstring(a, i); (dir.muid, i) = gstring(a, i); if(i != sz) return (0, dir); return (i, dir); } gqid(f: array of byte, i: int): Sys->Qid { qtype := int f[i]; vers := (((((int f[i+4] << 8) | int f[i+3]) << 8) | int f[i+2]) << 8) | int f[i+1]; i += BIT8SZ+BIT32SZ; path0 := (((((int f[i+3] << 8) | int f[i+2]) << 8) | int f[i+1]) << 8) | int f[i]; i += BIT32SZ; path1 := (((((int f[i+3] << 8) | int f[i+2]) << 8) | int f[i+1]) << 8) | int f[i]; path := (big path1 << 32) | (big path0 & 16rFFFFFFFF); return (path, vers, qtype); } g32(f: array of byte, i: int): int { return (((((int f[i+3] << 8) | int f[i+2]) << 8) | int f[i+1]) << 8) | int f[i]; } g64(f: array of byte, i: int): big { b0 := (((((int f[i+3] << 8) | int f[i+2]) << 8) | int f[i+1]) << 8) | int f[i]; b1 := (((((int f[i+7] << 8) | int f[i+6]) << 8) | int f[i+5]) << 8) | int f[i+4]; return (big b1 << 32) | (big b0 & 16rFFFFFFFF); } gstring(a: array of byte, o: int): (string, int) { if(o < 0 || o+STR > len a) return (nil, -1); l := (int a[o+1] << 8) | int a[o]; o += STR; e := o+l; if(e > len a) return (nil, -1); return (string a[o:e], e); } ttag2type := array[] of { tagof Tmsg.Readerror => 0, tagof Tmsg.Version => Tversion, tagof Tmsg.Auth => Tauth, tagof Tmsg.Attach => Tattach, tagof Tmsg.Flush => Tflush, tagof Tmsg.Walk => Twalk, tagof Tmsg.Open => Topen, tagof Tmsg.Create => Tcreate, tagof Tmsg.Read => Tread, tagof Tmsg.Write => Twrite, tagof Tmsg.Clunk => Tclunk, tagof Tmsg.Stat => Tstat, tagof Tmsg.Remove => Tremove, tagof Tmsg.Wstat => Twstat, }; Tmsg.mtype(t: self ref Tmsg): int { return ttag2type[tagof t]; } Tmsg.packedsize(t: self ref Tmsg): int { mtype := ttag2type[tagof t]; if(mtype <= 0) return 0; ml := hdrlen[mtype]; pick m := t { Version => ml += utflen(m.version); Auth => ml += utflen(m.uname)+utflen(m.aname); Attach => ml += utflen(m.uname)+utflen(m.aname); Walk => for(i:=0; i<len m.names; i++) ml += STR+utflen(m.names[i]); Create => ml += utflen(m.name); Write => ml += len m.data; Wstat => ml += packdirsize(m.stat); } return ml; } Tmsg.pack(t: self ref Tmsg): array of byte { if(t == nil) return nil; ds := t.packedsize(); if(ds <= 0) return nil; d := array[ds] of byte; d[0] = byte ds; d[1] = byte (ds>>8); d[2] = byte (ds>>16); d[3] = byte (ds>>24); d[4] = byte ttag2type[tagof t]; d[5] = byte t.tag; d[6] = byte (t.tag >> 8); pick m := t { Version => p32(d, H, m.msize); pstring(d, H+COUNT, m.version); Auth => p32(d, H, m.afid); o := pstring(d, H+FID, m.uname); pstring(d, o, m.aname); Flush => v := m.oldtag; d[H] = byte v; d[H+1] = byte (v>>8); Attach => p32(d, H, m.fid); p32(d, H+FID, m.afid); o := pstring(d, H+2*FID, m.uname); pstring(d, o, m.aname); Walk => d[H] = byte m.fid; d[H+1] = byte (m.fid>>8); d[H+2] = byte (m.fid>>16); d[H+3] = byte (m.fid>>24); d[H+FID] = byte m.newfid; d[H+FID+1] = byte (m.newfid>>8); d[H+FID+2] = byte (m.newfid>>16); d[H+FID+3] = byte (m.newfid>>24); n := len m.names; d[H+2*FID] = byte n; d[H+2*FID+1] = byte (n>>8); o := H+2*FID+LEN; for(i := 0; i < n; i++) o = pstring(d, o, m.names[i]); Open => p32(d, H, m.fid); d[H+FID] = byte m.mode; Create => p32(d, H, m.fid); o := pstring(d, H+FID, m.name); p32(d, o, m.perm); d[o+BIT32SZ] = byte m.mode; Read => p32(d, H, m.fid); p64(d, H+FID, m.offset); p32(d, H+FID+OFFSET, m.count); Write => p32(d, H, m.fid); p64(d, H+FID, m.offset); n := len m.data; p32(d, H+FID+OFFSET, n); d[H+FID+OFFSET+COUNT:] = m.data; Clunk or Remove or Stat => p32(d, H, m.fid); Wstat => p32(d, H, m.fid); stat := packdir(m.stat); n := len stat; d[H+FID] = byte n; d[H+FID+1] = byte (n>>8); d[H+FID+LEN:] = stat; * => raise sys->sprint("assertion: Ninep->Tmsg.pack: bad tag: %d", tagof t); } return d; } Tmsg.unpack(f: array of byte): (int, ref Tmsg) { if(len f < H) return (0, nil); size := (int f[1] << 8) | int f[0]; size |= ((int f[3] << 8) | int f[2]) << 16; if(len f != size){ if(len f < size) return (0, nil); # need more data f = f[0:size]; # trim to exact length } mtype := int f[4]; if(mtype >= len hdrlen || (mtype&1) != 0 || size < hdrlen[mtype]) return (-1, nil); tag := (int f[6] << 8) | int f[5]; fid := 0; if(hdrlen[mtype] >= H+FID) fid = g32(f, H); # fid is always in same place: extract it once for all if there # return out of each case body for a legal message; # break out of the case for an illegal one Decode: case mtype { * => sys->print("styx: Tmsg.unpack: bad type %d\n", mtype); Tversion => msize := fid; (version, o) := gstring(f, H+COUNT); if(o <= 0) break; return (o, ref Tmsg.Version(tag, msize, version)); Tauth => (uname, o1) := gstring(f, H+FID); (aname, o2) := gstring(f, o1); if(o2 <= 0) break; return (o2, ref Tmsg.Auth(tag, fid, uname, aname)); Tflush => oldtag := (int f[H+1] << 8) | int f[H]; return (H+TAG, ref Tmsg.Flush(tag, oldtag)); Tattach => afid := g32(f, H+FID); (uname, o1) := gstring(f, H+2*FID); (aname, o2) := gstring(f, o1); if(o2 <= 0) break; return (o2, ref Tmsg.Attach(tag, fid, afid, uname, aname)); Twalk => newfid := g32(f, H+FID); n := (int f[H+2*FID+1] << 8) | int f[H+2*FID]; if(n > MAXWELEM) break; o := H+2*FID+LEN; names: array of string = nil; if(n > 0){ names = array[n] of string; for(i:=0; i<n; i++){ (names[i], o) = gstring(f, o); if(o <= 0) break Decode; } } return (o, ref Tmsg.Walk(tag, fid, newfid, names)); Topen => return (H+FID+BIT8SZ, ref Tmsg.Open(tag, fid, int f[H+FID])); Tcreate => (name, o) := gstring(f, H+FID); if(o <= 0 || o+BIT32SZ+BIT8SZ > len f) break; perm := g32(f, o); o += BIT32SZ; mode := int f[o++]; return (o, ref Tmsg.Create(tag, fid, name, perm, mode)); Tread => offset := g64(f, H+FID); count := g32(f, H+FID+OFFSET); return (H+FID+OFFSET+COUNT, ref Tmsg.Read(tag, fid, offset, count)); Twrite => offset := g64(f, H+FID); count := g32(f, H+FID+OFFSET); O: con H+FID+OFFSET+COUNT; if(count > len f-O) break; data := f[O:O+count]; return (O+count, ref Tmsg.Write(tag, fid, offset, data)); Tclunk => return (H+FID, ref Tmsg.Clunk(tag, fid)); Tremove => return (H+FID, ref Tmsg.Remove(tag, fid)); Tstat => return (H+FID, ref Tmsg.Stat(tag, fid)); Twstat => n := int (f[H+FID+1]<<8) | int f[H+FID]; if(len f < H+FID+LEN+n) break; (ds, stat) := unpackdir(f[H+FID+LEN:]); if(ds != n){ sys->print("Ninep->Tmsg.unpack: wstat count: %d/%d\n", ds, n); # temporary break; } return (H+FID+LEN+n, ref Tmsg.Wstat(tag, fid, stat)); } return (-1, nil); # illegal } tmsgname := array[] of { tagof Tmsg.Readerror => "Readerror",