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 }