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 		uint vlsPosition;
33 	
34 		foreach (e; listOfTypes)
35 		{
36 			auto messageField = new typeof(e);
37 			messageField.unpack(bytes[vlsPosition..$]);
38 			vlsPosition += cast(uint) (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 			{
54 				// version
55 				case R_VERSION:
56 					msg = decodeMessageFields!Rversion(bytes);
57 					break;
58 				case T_VERSION:
59 					msg = decodeMessageFields!Tversion(bytes);
60 					break;
61 				// auth
62 				case R_AUTH:
63 					msg = decodeMessageFields!Rauth(bytes);
64 					break;
65 				case T_AUTH:
66 					msg = decodeMessageFields!Tauth(bytes);
67 					break;
68 				// error
69 				case R_ERROR:
70 					msg = decodeMessageFields!Rerror(bytes);
71 					break;
72 				// flush
73 				case R_FLUSH:
74 					msg = decodeMessageFields!Rflush(bytes);
75 					break;
76 				case T_FLUSH:
77 					msg = decodeMessageFields!Tflush(bytes);
78 					break;
79 				// attach
80 				case R_ATTACH:
81 					msg = decodeMessageFields!Rattach(bytes);
82 					break;
83 				case T_ATTACH:
84 					msg = decodeMessageFields!Tattach(bytes);
85 					break;
86 				// walk
87 				case R_WALK:
88 					msg = decodeMessageFields!Rwalk(bytes);
89 					break;
90 				case T_WALK:
91 					msg = decodeMessageFields!Twalk(bytes);
92 					break;
93 				// open
94 				case R_OPEN:
95 					msg = decodeMessageFields!Ropen(bytes);
96 					break;
97 				case T_OPEN:
98 					msg = decodeMessageFields!Topen(bytes);
99 					break;
100 				// create
101 				case R_CREATE:
102 					msg = decodeMessageFields!Rcreate(bytes);
103 					break;
104 				case T_CREATE:
105 					msg = decodeMessageFields!Tcreate(bytes);
106 					break;
107 				// read
108 				case R_READ:
109 					msg = decodeMessageFields!Rread(bytes);
110 					break;
111 				case T_READ:
112 					msg = decodeMessageFields!Tread(bytes);
113 					break;
114 				// write
115 				case R_WRITE:
116 					msg = decodeMessageFields!Rwrite(bytes);
117 					break;
118 				case T_WRITE:
119 					msg = decodeMessageFields!Twrite(bytes);
120 					break;
121 				// clunk
122 				case R_CLUNK:
123 					msg = decodeMessageFields!Rclunk(bytes);
124 					break;
125 				case T_CLUNK:
126 					msg = decodeMessageFields!Tclunk(bytes);
127 					break;
128 				// remove
129 				case R_REMOVE:
130 					msg = decodeMessageFields!Rremove(bytes);
131 					break;
132 				case T_REMOVE:
133 					msg = decodeMessageFields!Tremove(bytes);
134 					break;
135 				// stat
136 				case R_STAT:
137 					msg = decodeMessageFields!Rstat(bytes);
138 					break;
139 				case T_STAT:
140 					msg = decodeMessageFields!Tstat(bytes);
141 					break;
142 				// wstat
143 				case R_WSTAT:
144 					msg = decodeMessageFields!Rwstat(bytes);
145 					break;
146 				case T_WSTAT:
147 					msg = decodeMessageFields!Twstat(bytes);
148 					break;
149 				default:
150 					//throw new Exception("Bad message type");
151 					break;
152 			}
153 		}
154 		return msg;
155 	}
156 }
157 
158 /**
159 Decodes the received set of bytes into 9P / Styx protocol objects.
160 Params:
161 bytes = Array of unsigned bytes that represents message.
162 
163 Typical usage:
164 ----
165 import std.stdio;
166 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]
167 );
168 // 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="")
169 msg.writeln;
170 ----
171 */
172 auto decode(ubyte[] bytes...)
173 {
174 	StyxObject[] msg; 
175 	
176 	if (bytes.length == 0)
177 	{
178 		throw new Exception(`Bad Styx message length: empty message`);
179 	}
180 	else
181 	{
182 		if (bytes.length < 7)
183 		{
184 			throw new Exception(`Bad Styx message length: message contains no Styx header`);
185 		}
186 		else
187 		{
188 			size_t messageLength = fromLEBytes!size_t(bytes[0..4]);
189 			
190 			if (bytes.length < messageLength)
191 			{
192 				throw new Exception(`Bad Styx message length: size in header exceeded real message length`);
193 			}
194 			else
195 			{
196 				ubyte type = bytes[4];
197 				
198 				if ((type < 100) || (type > 127))
199 				{
200 					throw new Exception(`Bad message content: wrong message type`);
201 				}
202 				else
203 				{
204 					auto msgType = new Type(cast(STYX_MESSAGE_TYPE) type);
205 					msg = decodeMessageFields(msgType, bytes);
206 				}
207 			}
208 		}
209 	}
210 	
211 	return msg;
212 }