1 // Written in the D programming language. 2 3 /** 4 Module for decoding raw bytes to messages of the 9P / Styx protocol. 5 6 Copyright: LightHouse Software, 2021 7 License: $(HTTP https://github.com/aquaratixc/ESL-License, Experimental Software License 1.0). 8 Authors: Oleg Bakharev, 9 Ilya Pertsev 10 */ 11 module styx2000.protomsg.decoder; 12 13 private { 14 import styx2000.lowlevel.endianness; 15 16 import styx2000.protomsg.typeconv; 17 } 18 19 public { 20 import styx2000.protoconst.messages; 21 22 import styx2000.protobj; 23 } 24 25 private { 26 /// Decode fields with using only types list 27 auto decodeMessageFields(E...)(ubyte[] bytes...) 28 { 29 E listOfTypes; 30 StyxObject[] msg; 31 32 ulong vlsPosition = 0; 33 34 foreach (e; listOfTypes) 35 { 36 auto messageField = new typeof(e); 37 messageField.unpack(bytes[vlsPosition..$]); 38 vlsPosition += (messageField.length); 39 msg ~= cast(StyxObject) messageField; 40 } 41 42 return msg; 43 } 44 45 /// Decode message based on message type 46 auto decodeMessageFields(Type type, ubyte[] bytes...) 47 { 48 StyxObject[] msg; 49 50 with (STYX_MESSAGE_TYPE) 51 { 52 switch(type.getType) { 53 // version 54 case R_VERSION: 55 msg = decodeMessageFields!Rversion(bytes); 56 break; 57 case T_VERSION: 58 msg = decodeMessageFields!Tversion(bytes); 59 break; 60 // auth 61 case R_AUTH: 62 msg = decodeMessageFields!Rauth(bytes); 63 break; 64 case T_AUTH: 65 msg = decodeMessageFields!Tauth(bytes); 66 break; 67 // error 68 case R_ERROR: 69 msg = decodeMessageFields!Rerror(bytes); 70 break; 71 // flush 72 case R_FLUSH: 73 msg = decodeMessageFields!Rflush(bytes); 74 break; 75 case T_FLUSH: 76 msg = decodeMessageFields!Tflush(bytes); 77 break; 78 // attach 79 case R_ATTACH: 80 msg = decodeMessageFields!Rattach(bytes); 81 break; 82 case T_ATTACH: 83 msg = decodeMessageFields!Tattach(bytes); 84 break; 85 // walk 86 case R_WALK: 87 msg = decodeMessageFields!Rwalk(bytes); 88 break; 89 case T_WALK: 90 msg = decodeMessageFields!Twalk(bytes); 91 break; 92 // open 93 case R_OPEN: 94 msg = decodeMessageFields!Ropen(bytes); 95 break; 96 case T_OPEN: 97 msg = decodeMessageFields!Topen(bytes); 98 break; 99 // create 100 case R_CREATE: 101 msg = decodeMessageFields!Rcreate(bytes); 102 break; 103 case T_CREATE: 104 msg = decodeMessageFields!Tcreate(bytes); 105 break; 106 // read 107 case R_READ: 108 msg = decodeMessageFields!Rread(bytes); 109 break; 110 case T_READ: 111 msg = decodeMessageFields!Tread(bytes); 112 break; 113 // write 114 case R_WRITE: 115 msg = decodeMessageFields!Rwrite(bytes); 116 break; 117 case T_WRITE: 118 msg = decodeMessageFields!Twrite(bytes); 119 break; 120 // clunk 121 case R_CLUNK: 122 msg = decodeMessageFields!Rclunk(bytes); 123 break; 124 case T_CLUNK: 125 msg = decodeMessageFields!Tclunk(bytes); 126 break; 127 // remove 128 case R_REMOVE: 129 msg = decodeMessageFields!Rremove(bytes); 130 break; 131 case T_REMOVE: 132 msg = decodeMessageFields!Tremove(bytes); 133 break; 134 // stat 135 case R_STAT: 136 msg = decodeMessageFields!Rstat(bytes); 137 break; 138 case T_STAT: 139 msg = decodeMessageFields!Tstat(bytes); 140 break; 141 // wstat 142 case R_WSTAT: 143 msg = decodeMessageFields!Rwstat(bytes); 144 break; 145 case T_WSTAT: 146 msg = decodeMessageFields!Twstat(bytes); 147 break; 148 default: 149 //throw new Exception("Bad message type"); 150 break; 151 } 152 } 153 return msg; 154 } 155 } 156 157 /** 158 Decodes the received set of bytes into 9P / Styx protocol objects. 159 Params: 160 bytes = Array of unsigned bytes that represents message. 161 162 Typical usage: 163 ---- 164 import std.stdio; 165 auto msg = decode([73, 0, 0, 0, 125, 1, 0, 64, 0, 62, 0, 77, 0, 4, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 253, 1, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 116, 101, 115, 116, 5, 0, 100, 117, 115, 101, 114, 6, 0, 100, 95, 117, 115, 101, 114, 0, 0] 166 ); 167 // prints [Size(size=73), R_STAT, Tag(tag=0x0001), Stat(type=77, dev=4, qid=Qid(type=QTDIR, vers=0, path=0), mode=Permissions(perm=0x800001fd), atime=0, mtime=0, length=0, name="test", uid="duser", gid="d_user", muid="") 168 msg.writeln; 169 ---- 170 */ 171 auto decode(ubyte[] bytes...) 172 { 173 StyxObject[] msg; 174 175 if (bytes.length == 0) 176 { 177 throw new Exception(`Bad Styx message length: empty message`); 178 } 179 else 180 { 181 if (bytes.length < 7) 182 { 183 throw new Exception(`Bad Styx message length: message contains no Styx header`); 184 } 185 else 186 { 187 size_t messageLength = fromLEBytes!size_t(bytes[0..4]); 188 189 if (bytes.length < messageLength) 190 { 191 throw new Exception(`Bad Styx message length: size in header exceeded real message length`); 192 } 193 else 194 { 195 ubyte type = bytes[4]; 196 197 if ((type < 100) || (type > 127)) 198 { 199 throw new Exception(`Bad message content: wrong message type`); 200 } 201 else 202 { 203 auto msgType = new Type(cast(STYX_MESSAGE_TYPE) type); 204 msg = decodeMessageFields(msgType, bytes); 205 } 206 } 207 } 208 } 209 210 return msg; 211 }