2002-10-23 Tim Coleman (tim@timcoleman.com)
[mono.git] / mcs / class / Mono.Data.TdsClient / Mono.Data.TdsClient.Internal / TdsComm.cs
1 //
2 // Mono.Data.TdsClient.Internal.TdsComm.cs
3 //
4 // Author:
5 //   Tim Coleman (tim@timcoleman.com)
6 //
7 // Copyright (C) 2002 Tim Coleman
8 //
9
10 using System;
11 using System.Net.Sockets;
12 using System.Text;
13 using System.Threading;
14
15 namespace Mono.Data.TdsClient.Internal {
16         internal sealed class TdsComm
17         {
18                 #region Fields
19
20                 NetworkStream stream;
21                 int packetSize;
22                 TdsPacketType packetType = TdsPacketType.None;
23                 Encoding encoder;
24
25                 byte[] outBuffer;
26                 int outBufferLength;
27                 int nextOutBufferIndex = 0;
28
29                 byte[] inBuffer;
30                 int inBufferLength;
31                 int inBufferIndex = 0;
32
33                 static int headerLength = 8;
34
35                 byte[] tmpBuf = new byte[8];
36                 byte[] resBuffer = new byte[256];
37
38                 int packetsSent = 0;
39                 int packetsReceived = 0;
40
41                 TdsVersion tdsVersion;
42                 
43                 #endregion // Fields
44                 
45                 #region Constructors
46                 
47                 public TdsComm (Socket socket, int packetSize, TdsVersion tdsVersion)
48                 {
49                         this.packetSize = packetSize;
50                         this.tdsVersion = tdsVersion;
51
52                         outBuffer = new byte[packetSize];
53                         inBuffer = new byte[packetSize];
54
55                         outBufferLength = packetSize;
56                         inBufferLength = packetSize;
57                         stream = new NetworkStream (socket);
58                 }
59                 
60                 #endregion // Constructors
61                 
62                 #region Properties
63
64                 internal Encoding Encoder {
65                         set { encoder = value; }
66                 }
67                 
68                 public int PacketSize {
69                         get { return packetSize; }
70                         set { packetSize = value; }
71                 }
72                 
73                 #endregion // Properties
74                 
75                 #region Methods
76
77                 internal void ResizeOutBuf (int newSize)
78                 {
79                         if (newSize > outBufferLength) {
80                                 byte[] newBuf = new byte[newSize];
81                                 Array.Copy (outBuffer, 0, newBuf, 0, outBufferLength);
82                                 outBufferLength = newSize;
83                                 outBuffer = newBuf;
84                         }
85                 }
86                 
87                 public void StartPacket (TdsPacketType type)
88                 {
89                         if (type != TdsPacketType.Cancel && inBufferIndex != inBufferLength)
90                         {
91                                 // SAfe It's ok to throw this exception so that we will know there
92                                 //      is a design flaw somewhere, but we should empty the buffer
93                                 //      however. Otherwise the connection will never close (e.g. if
94                                 //      SHOWPLAN_ALL is ON, a resultset will be returned by commit
95                                 //      or rollback and we will never get rid of it). It's true
96                                 //      that we should find a way to actually process these packets
97                                 //      but for now, just dump them (we have thrown an exception).
98                                 inBufferIndex = inBufferLength;
99                         }
100
101                         packetType = type;
102                         nextOutBufferIndex = headerLength;
103                 }
104
105                 public bool SomeThreadIsBuildingPacket ()
106                 {
107                         return packetType != TdsPacketType.None;
108                 }
109
110                 public void Append (byte b)
111                 {
112                         if (nextOutBufferIndex == outBufferLength) {
113                                 SendPhysicalPacket (false);
114                                 nextOutBufferIndex = headerLength;
115                         }
116                         StoreByte (nextOutBufferIndex, b);
117                         nextOutBufferIndex++;
118                 }       
119                 
120                 public void Append (byte[] b)
121                 {
122                         Append (b, b.Length, (byte) 0);
123                 }               
124
125                 public void Append (byte[] b, int len, byte pad)
126                 {
127                         int i = 0;
128                         for ( ; i < b.Length && i < len; i++)
129                             Append (b[i]);
130
131                         for ( ; i < len; i++)
132                             Append (pad);
133                 }       
134
135                 public void Append (short s)
136                 {
137                         if (tdsVersion < TdsVersion.tds70) {
138                                 Append ((byte) (((byte) (s >> 8)) & 0xff));
139                                 Append ((byte) (((byte) (s >> 0)) & 0xff));
140                         }
141                         else
142                                 Append (BitConverter.GetBytes (s));
143                 }
144
145                 public void Append (int i)
146                 {
147                         if (tdsVersion < TdsVersion.tds70) {
148                                 Append ((byte) (((byte) (i >> 24)) & 0xff));
149                                 Append ((byte) (((byte) (i >> 16)) & 0xff));
150                                 Append ((byte) (((byte) (i >> 8)) & 0xff));
151                                 Append ((byte) (((byte) (i >> 0)) & 0xff));
152                         } 
153                         else
154                                 Append (BitConverter.GetBytes (i));
155                 }
156
157                 public void Append (string s)
158                 {
159                         if (tdsVersion < TdsVersion.tds70) 
160                                 Append (encoder.GetBytes (s));
161                         else 
162                                 foreach (char c in s)
163                                         Append (BitConverter.GetBytes (c));
164                 }       
165
166                 // Appends with padding
167                 public byte[] Append (string s, int len, byte pad)
168                 {
169                         if (s == null)
170                                 return new byte[0];
171
172                         byte[] result = encoder.GetBytes (s);
173                         Append (result, len, pad);
174                         return result;
175                 }
176
177                 public void Append (double value)
178                 {
179                         Append (BitConverter.DoubleToInt64Bits (value));
180                 }
181
182                 public void Append (long l)
183                 {
184                         if (tdsVersion < TdsVersion.tds70) {
185                                 Append ((byte) (((byte) (l >> 56)) & 0xff));
186                                 Append ((byte) (((byte) (l >> 48)) & 0xff));
187                                 Append ((byte) (((byte) (l >> 40)) & 0xff));
188                                 Append ((byte) (((byte) (l >> 32)) & 0xff));
189                                 Append ((byte) (((byte) (l >> 24)) & 0xff));
190                                 Append ((byte) (((byte) (l >> 16)) & 0xff));
191                                 Append ((byte) (((byte) (l >> 8)) & 0xff));
192                                 Append ((byte) (((byte) (l >> 0)) & 0xff));
193                         }
194                         else {
195                                 Append (BitConverter.GetBytes (l));
196                         }
197                 }
198
199                 public void SendPacket ()
200                 {
201                         SendPhysicalPacket (true);
202                         nextOutBufferIndex = 0;
203                         packetType = TdsPacketType.None;
204                 }
205                 
206                 private void StoreByte (int index, byte value)
207                 {
208                         outBuffer[index] = value;
209                 }               
210
211                 private void StoreShort (int index, short s)
212                 {
213                         outBuffer[index] = (byte) (((byte) (s >> 8)) & 0xff);
214                         outBuffer[index + 1] = (byte) (((byte) (s >> 0)) & 0xff);
215                 }
216
217                 private void SendPhysicalPacket (bool isLastSegment)
218                 {
219                         if (nextOutBufferIndex > headerLength || packetType == TdsPacketType.Cancel) {
220                                 // packet type
221                                 StoreByte (0, (byte) packetType);
222                                 StoreByte (1, (byte) (isLastSegment ? 1 : 0));
223                                 StoreShort (2, (short) nextOutBufferIndex );
224                                 StoreByte (4, (byte) 0);
225                                 StoreByte (5, (byte) 0);
226                                 StoreByte (6, (byte) (tdsVersion == TdsVersion.tds70 ? 0x1 : 0x0));
227                                 StoreByte (7, (byte) 0);
228
229                                 stream.Write (outBuffer, 0, nextOutBufferIndex);
230                                 stream.Flush ();
231                                 packetsSent++;
232                         }
233                 }
234                 
235                 public byte Peek ()
236                 {
237                         // If out of data, read another physical packet.
238                         if (inBufferIndex >= inBufferLength)
239                                 GetPhysicalPacket ();
240
241                         return inBuffer[inBufferIndex];
242                 }
243
244
245                 public byte GetByte ()
246                 {
247                         byte result;
248
249                         if (inBufferIndex >= inBufferLength) {
250                                 // out of data, read another physical packet.
251                                 GetPhysicalPacket ();
252                         }
253
254                         result = inBuffer[inBufferIndex++];
255                         return result;
256                 }
257
258                 public byte[] GetBytes (int len, bool exclusiveBuffer)
259                 {
260                         byte[] result = null;
261                         int i;
262
263                         // Do not keep an internal result buffer larger than 16k.
264                         // This would unnecessarily use up memory.
265                         if (exclusiveBuffer || len > 16384)
266                                 result = new byte[len];
267                         else
268                         {
269                                 if (resBuffer.Length < len)
270                                         resBuffer = new byte[len];
271                                 result = resBuffer;
272                         }
273
274                         for (i = 0; i<len; )
275                         {
276                                 if (inBufferIndex >= inBufferLength)
277                                         GetPhysicalPacket ();
278
279                                 int avail = inBufferLength - inBufferIndex;
280                                 avail = avail>len-i ? len-i : avail;
281
282                                 System.Array.Copy (inBuffer, inBufferIndex, result, i, avail);
283                                 i += avail;
284                                 inBufferIndex += avail;
285                         }
286
287                         return result;
288                 }
289
290                 public string GetString (int len)
291                 {
292                         if (tdsVersion == TdsVersion.tds70) {
293                                 char[] chars = new char[len];
294                                 for (int i = 0; i < len; ++i) {
295                                         int lo = ((byte) GetByte ()) & 0xFF;
296                                         int hi = ((byte) GetByte ()) & 0xFF;
297                                         chars[i] = (char) (lo | ( hi << 8));
298                                 }
299                                 return new String (chars);
300                         }
301                         else {
302                                 byte[] result = new byte[len + 1];
303                                 Array.Copy (GetBytes (len, false), result, len);
304                                 result[len] = (byte) 0;
305                                 return (encoder.GetString (result));
306                         }
307                 }
308
309                 public void Skip (int i)
310                 {
311                         for ( ; i > 0; i--)
312                                 GetByte ();
313                 }
314                 // skip()
315
316
317                 public int GetNetShort ()
318                 {
319                         byte[] tmp = new byte[2];
320                         tmp[0] = GetByte ();
321                         tmp[1] = GetByte ();
322                         return Ntohs (tmp, 0);
323                 }
324
325                 public short GetTdsShort ()
326                 {
327                         byte[] input = new byte[2];
328
329                         for (int i = 0; i < 2; i += 1)
330                                 input[i] = GetByte ();
331
332                         return (BitConverter.ToInt16 (input, 0));
333                 }
334
335
336                 public int GetTdsInt ()
337                 {
338                         byte[] input = new byte[4];
339                         for (int i = 0; i < 4; i += 1)
340                                 input[i] = GetByte ();
341                         return (BitConverter.ToInt32 (input, 0));
342                 }
343
344                 public long GetTdsInt64 ()
345                 {
346                         byte[] input = new byte[8];
347                         for (int i = 0; i < 8; i += 1)
348                                 input[i] = GetByte ();
349                         return (BitConverter.ToInt64 (input, 0));
350                 }
351
352                 private void GetPhysicalPacket ()
353                 {
354                         int nread = 0;
355
356                         // read the header
357                         while (nread < 8)
358                                 nread += stream.Read (tmpBuf, nread, 8 - nread);
359
360                         TdsPacketType packetType = (TdsPacketType) tmpBuf[0];
361                         if (packetType != TdsPacketType.Logon && packetType != TdsPacketType.Query && packetType != TdsPacketType.Reply) {
362                                 throw new TdsException (String.Format ("Unknown packet type {0}", tmpBuf[0]));
363                         }
364
365                         // figure out how many bytes are remaining in this packet.
366                         int len = Ntohs (tmpBuf, 2) - 8;
367
368                         if (len >= inBuffer.Length) 
369                                 inBuffer = new byte[len];
370
371                         if (len < 0) {
372                                 throw new TdsException (String.Format ("Confused by a length of {0}", len));
373                         }
374
375                         // now get the data
376                         nread = 0;
377                         while (nread < len) {
378                                 nread += stream.Read (inBuffer, nread, len - nread);
379                         }
380
381                         packetsReceived++;
382
383                         // adjust the bookkeeping info about the incoming buffer
384                         inBufferLength = len;
385                         inBufferIndex = 0;
386                 }
387
388                 private static int Ntohs (byte[] buf, int offset)
389                 {
390                         int lo = ((int) buf[offset + 1] & 0xff);
391                         int hi = (((int) buf[offset] & 0xff ) << 8);
392
393                         return hi | lo;
394                         // return an int since we really want an _unsigned_
395                 }               
396                 #endregion // Methods
397         }
398
399 }