55using System . IO ;
66using System . Runtime . Serialization ;
77using System . Runtime . Serialization . Formatters . Binary ;
8+ using System . Text ;
89using NUnit . Engine . Communication . Messages ;
910
1011namespace NUnit . Engine . Communication . Protocols
1112{
1213 /// <summary>
13- /// BinarySerializationProtocol serializes messages in the following format:
14+ /// BinarySerializationProtocol serializes messages as an array of bytes in the following format:
1415 ///
15- /// [Message Length (4 bytes)][Serialized Message Content]
16- ///
17- /// The message content is in binary form as produced by the .NET BinaryFormatter.
18- /// Each message of length n is serialized as n + 4 bytes.
16+ /// <Message Length> <Message Code> [<Additional Message Data>]
17+ ///
18+ /// The length of the message is encoded as four bytes, from lowest to highest order.
19+ ///
20+ /// The message code is always four bytes and indicates the type of message.
21+ ///
22+ /// Messages taking an additional data argument encode it in the remaining bytes. The
23+ /// argument length may therefore be calculated as overall length - 8 bytes. Messages
24+ /// without an argument are serialized as 8 bytes.
1925 /// </summary>
2026 public class BinarySerializationProtocol : ISerializationProtocol
2127 {
28+ private const int MSG_LENGTH_SIZE = 4 ;
29+ private const int MSG_CODE_SIZE = 4 ;
30+
2231 /// <summary>
2332 /// Maximum length of a message.
2433 /// </summary>
@@ -34,24 +43,30 @@ public class BinarySerializationProtocol : ISerializationProtocol
3443 /// </summary>
3544 /// <param name="message">Message to be serialized</param>
3645 /// <returns>A byte[] containing the message, serialized as per the protocol.</returns>
37- public byte [ ] Encode ( object message )
46+ public byte [ ] Encode ( TestEngineMessage message )
3847 {
39- //Serialize the message to a byte array
40- var serializedMessage = SerializeMessage ( message ) ;
48+ string code = message . Code ;
49+ string data = message . Data ;
50+ Encoding utf8 = Encoding . UTF8 ;
51+
52+ // TODO: Compile time check
53+ if ( utf8 . GetByteCount ( code ) != MSG_CODE_SIZE )
54+ throw new ArgumentException ( $ "Invalid message code { code } ") ;
55+
56+ int dataLength = message . Data != null ? utf8 . GetByteCount ( message . Data ) : 0 ;
57+ int messageLength = dataLength + MSG_CODE_SIZE ;
4158
42- //Check for message length
43- var messageLength = serializedMessage . Length ;
59+ //Check message length
4460 if ( messageLength > MAX_MESSAGE_LENGTH )
45- {
4661 throw new Exception ( "Message is too big (" + messageLength + " bytes). Max allowed length is " + MAX_MESSAGE_LENGTH + " bytes." ) ;
47- }
4862
4963 //Create a byte array including the length of the message (4 bytes) and serialized message content
50- var bytes = new byte [ messageLength + 4 ] ;
64+ var bytes = new byte [ messageLength + MSG_LENGTH_SIZE ] ;
5165 WriteInt32 ( bytes , 0 , messageLength ) ;
52- Array . Copy ( serializedMessage , 0 , bytes , 4 , messageLength ) ;
66+ utf8 . GetBytes ( code , 0 , code . Length , bytes , MSG_LENGTH_SIZE ) ;
67+ if ( dataLength > 0 )
68+ utf8 . GetBytes ( data , 0 , data . Length , bytes , MSG_LENGTH_SIZE + MSG_CODE_SIZE ) ;
5369
54- //Return serialized message by this protocol
5570 return bytes ;
5671 }
5772
@@ -139,23 +154,21 @@ private bool ReadSingleMessage(ICollection<TestEngineMessage> messages)
139154
140155 //If stream has less than 4 bytes, that means we can not even read length of the message
141156 //So, return false to wait more for bytes from remorte application.
142- if ( _receiveMemoryStream . Length < 4 )
157+ if ( _receiveMemoryStream . Length < MSG_LENGTH_SIZE )
143158 {
144159 return false ;
145160 }
146161
147162 //Read length of the message
148163 var messageLength = ReadInt32 ( _receiveMemoryStream ) ;
149164 if ( messageLength > MAX_MESSAGE_LENGTH )
150- {
151165 throw new Exception ( "Message is too big (" + messageLength + " bytes). Max allowed length is " + MAX_MESSAGE_LENGTH + " bytes." ) ;
152- }
153166
154167 //If message is zero-length (It must not be but good approach to check it)
155168 if ( messageLength == 0 )
156169 {
157170 //if no more bytes, return immediately
158- if ( _receiveMemoryStream . Length == 4 )
171+ if ( _receiveMemoryStream . Length == MSG_LENGTH_SIZE )
159172 {
160173 _receiveMemoryStream = new MemoryStream ( ) ; //Clear the stream
161174 return false ;
@@ -164,12 +177,12 @@ private bool ReadSingleMessage(ICollection<TestEngineMessage> messages)
164177 //Create a new memory stream from current except first 4-bytes.
165178 var bytes = _receiveMemoryStream . ToArray ( ) ;
166179 _receiveMemoryStream = new MemoryStream ( ) ;
167- _receiveMemoryStream . Write ( bytes , 4 , bytes . Length - 4 ) ;
180+ _receiveMemoryStream . Write ( bytes , MSG_LENGTH_SIZE , bytes . Length - MSG_LENGTH_SIZE ) ;
168181 return true ;
169182 }
170183
171184 //If all bytes of the message is not received yet, return to wait more bytes
172- if ( _receiveMemoryStream . Length < ( 4 + messageLength ) )
185+ if ( _receiveMemoryStream . Length < ( MSG_LENGTH_SIZE + messageLength ) )
173186 {
174187 _receiveMemoryStream . Position = _receiveMemoryStream . Length ;
175188 return false ;
@@ -178,7 +191,15 @@ private bool ReadSingleMessage(ICollection<TestEngineMessage> messages)
178191 //Read bytes of serialized message and deserialize it
179192 var serializedMessageBytes = ReadByteArray ( _receiveMemoryStream , messageLength ) ;
180193
181- messages . Add ( ( TestEngineMessage ) DeserializeMessage ( serializedMessageBytes ) ) ;
194+ Encoding utf8 = Encoding . UTF8 ;
195+
196+ string code = utf8 . GetString ( serializedMessageBytes , 0 , MSG_CODE_SIZE ) ;
197+
198+ string data = messageLength > MSG_CODE_SIZE
199+ ? utf8 . GetString ( serializedMessageBytes , MSG_CODE_SIZE , messageLength - MSG_CODE_SIZE )
200+ : null ;
201+
202+ messages . Add ( new TestEngineMessage ( code , data ) ) ;
182203
183204 //Read remaining bytes to an array
184205 var remainingBytes = ReadByteArray ( _receiveMemoryStream , ( int ) ( _receiveMemoryStream . Length - ( 4 + messageLength ) ) ) ;
@@ -188,7 +209,7 @@ private bool ReadSingleMessage(ICollection<TestEngineMessage> messages)
188209 _receiveMemoryStream . Write ( remainingBytes , 0 , remainingBytes . Length ) ;
189210
190211 //Return true to re-call this method to try to read next message
191- return ( remainingBytes . Length > 4 ) ;
212+ return ( remainingBytes . Length > MSG_LENGTH_SIZE ) ;
192213 }
193214
194215 /// <summary>
@@ -212,10 +233,15 @@ private static void WriteInt32(byte[] buffer, int startIndex, int number)
212233 private static int ReadInt32 ( Stream stream )
213234 {
214235 var buffer = ReadByteArray ( stream , 4 ) ;
215- return ( ( buffer [ 0 ] << 24 ) |
216- ( buffer [ 1 ] << 16 ) |
217- ( buffer [ 2 ] << 8 ) |
218- ( buffer [ 3 ] )
236+ return ReadInt32 ( buffer , 0 ) ;
237+ }
238+
239+ private static int ReadInt32 ( byte [ ] buffer , int index )
240+ {
241+ return ( ( buffer [ index ] << 24 ) |
242+ ( buffer [ index + 1 ] << 16 ) |
243+ ( buffer [ index + 2 ] << 8 ) |
244+ ( buffer [ index + 3 ] )
219245 ) ;
220246 }
221247
0 commit comments