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 }