1 // Written in the D programming language. 2 3 /** 4 Module for encoding objects of the 9P / Styx protocol into raw bytes. 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.encoder; 12 13 private { 14 import styx2000.protomsg.typeconv; 15 import styx2000.protomsg.verificators; 16 } 17 18 public { 19 import styx2000.protoconst.messages; 20 import styx2000.protoconst.sizes; 21 22 import styx2000.protobj; 23 } 24 25 26 /** 27 Encodes a set of 9P / Styx protocol objects into byte representation (array of unsigned bytes) 28 Params: 29 msg = Array of 9P / Styx protocol objects that represents message. 30 31 Typical usage: 32 ---- 33 import std.stdio; 34 // Example of server message (R_STAT) 35 36 // initially, zero size of message 37 Size size = new Size; 38 // tag (mark foir message) 39 Tag tag = new Tag(1); 40 // R-message, type: stat 41 Type type = new Type(STYX_MESSAGE_TYPE.R_STAT); 42 // unique server object 43 Qid qid = new Qid(STYX_QID_TYPE.QTDIR, 0, 0); 44 45 // chmod 775 (drwxrwxr-x) 46 Perm perm = new Perm; 47 perm.setPerm( 48 STYX_FILE_PERMISSION.DMDIR | STYX_FILE_PERMISSION.OWNER_EXEC | STYX_FILE_PERMISSION.OWNER_READ | STYX_FILE_PERMISSION.OWNER_WRITE | 49 STYX_FILE_PERMISSION.GROUP_READ | STYX_FILE_PERMISSION.GROUP_WRITE | STYX_FILE_PERMISSION.GROUP_EXEC | 50 STYX_FILE_PERMISSION.OTHER_READ | STYX_FILE_PERMISSION.OTHER_EXEC 51 ); 52 53 // construct stat 54 Stat stat = new Stat( 55 // type and dev for kernel use 56 77, 4, 57 qid, 58 // permissions 59 perm, 60 // access time (as unix timestamp) 61 0, 62 // modification time (as unix timestamp) 63 0, 64 // conventional length for all directories is 0 65 0, 66 // file name (this, directory name) 67 "test", 68 // user name (owner of file) 69 "duser", 70 // user group name 71 "d_user", 72 // others group name 73 "" 74 ); 75 76 // all 9P object in one array with general type 77 StyxObject[] msg = cast(StyxObject[]) [size, type, tag, stat]; 78 msg.encode.writeln; 79 ---- 80 */ 81 auto encode(StyxObject[] msg) 82 { 83 ubyte[] _representation; 84 85 if (msg.length == 0) 86 { 87 throw new Exception(`Bad Styx message length: empty message`); 88 } 89 else 90 { 91 if (msg.length < 3) 92 { 93 throw new Exception(`Bad Styx message length: message contains no Styx header`); 94 } 95 else 96 { 97 auto size = fromStyxObject!Size(msg[0]); 98 if (size is null) 99 { 100 throw new Exception(`Bad message content: wrong Size object`); 101 } 102 103 auto type = fromStyxObject!Type(msg[1]); 104 if (type is null) 105 { 106 throw new Exception(`Bad message content: wrong Type object`); 107 } 108 109 auto tag = fromStyxObject!Tag(msg[2]); 110 if (tag is null) 111 { 112 throw new Exception(`Bad message content: wrong Tag object`); 113 } 114 115 if (!hasValidFieldsCount(type, msg)) 116 { 117 throw new Exception(`Bad message content: wrong fields count`); 118 } 119 120 if (!hasValidFieldsTypes(type, msg)) 121 { 122 throw new Exception(`Bad message content: wrong Styx type in message`); 123 } 124 125 uint length = 0; 126 127 foreach (f; msg[1..$]) 128 { 129 if (f is null) 130 { 131 throw new Exception("Bad message content: null Styx object"); 132 } 133 134 auto packed = f.pack; 135 length += cast(uint) packed.length; 136 _representation ~= packed; 137 } 138 139 size.setSize(length + 4); 140 _representation = size ~ _representation; 141 } 142 } 143 144 return _representation; 145 }