* Makefile: Build the make-map.exe in Mono.Unix.Native; add /nowarn:0618 to
[mono.git] / mcs / class / FirebirdSql.Data.Firebird / FirebirdSql.Data.Gds / XdrStream.cs
1 /*
2  *      Firebird ADO.NET Data provider for .NET and Mono 
3  * 
4  *         The contents of this file are subject to the Initial 
5  *         Developer's Public License Version 1.0 (the "License"); 
6  *         you may not use this file except in compliance with the 
7  *         License. You may obtain a copy of the License at 
8  *         http://www.firebirdsql.org/index.php?op=doc&id=idpl
9  *
10  *         Software distributed under the License is distributed on 
11  *         an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either 
12  *         express or implied. See the License for the specific 
13  *         language governing rights and limitations under the License.
14  * 
15  *      Copyright (c) 2002, 2005 Carlos Guzman Alvarez
16  *      All Rights Reserved.
17  */
18
19 using System;
20 using System.Collections;
21 using System.IO;
22 using System.Net;
23 using System.Text;
24 using System.Globalization;
25
26 using FirebirdSql.Data.Common;
27
28 namespace FirebirdSql.Data.Gds
29 {
30         internal class XdrStream : Stream
31         {
32                 #region Static Fields
33
34                 private static byte[] fill;
35                 private static byte[] pad;
36
37                 #endregion
38
39                 #region Static Properties
40
41                 internal static byte[] Fill
42                 {
43                         get
44                         {
45                                 if (fill == null)
46                                 {
47                                         fill = new byte[32767];
48                                         for (int i = 0; i < fill.Length; i++)
49                                         {
50                                                 fill[i] = 32;
51                                         }
52                                 }
53
54                                 return fill;
55                         }
56                 }
57
58                 private static byte[] Pad
59                 {
60                         get
61                         {
62                                 if (pad == null)
63                                 {
64                                         pad = new byte[] { 0, 0, 0, 0 };
65                                 }
66
67                                 return pad;
68                         }
69                 }
70
71                 #endregion
72
73                 #region Fields
74
75                 private byte[]  buffer;
76                 private Charset charset;
77                 private Stream  innerStream;
78
79                 #endregion
80
81                 #region Stream Properties
82
83                 public override bool CanWrite
84                 {
85                         get { return this.innerStream.CanWrite; }
86                 }
87
88                 public override bool CanRead
89                 {
90                         get { return this.innerStream.CanRead; }
91                 }
92
93                 public override bool CanSeek
94                 {
95                         get { return this.innerStream.CanSeek; }
96                 }
97
98                 public override long Position
99                 {
100                         get { return this.innerStream.Position; }
101                         set { this.innerStream.Position = value; }
102                 }
103
104                 public override long Length
105                 {
106                         get { return this.innerStream.Length; }
107                 }
108
109                 #endregion
110
111                 #region Constructors
112
113                 public XdrStream() : this(Charset.DefaultCharset)
114                 {
115                 }
116
117                 public XdrStream(Charset charset) : this(new MemoryStream(), charset)
118                 {
119                 }
120
121                 public XdrStream(byte[] buffer, Charset charset) : this(new MemoryStream(buffer), charset)
122                 {
123                 }
124
125                 public XdrStream(Stream innerStream, Charset charset) : base()
126                 {
127                         this.buffer                     = new byte[8] { 0, 0, 0, 0, 0, 0, 0, 0 };
128                         this.innerStream        = innerStream;
129                         this.charset            = charset;
130                 }
131
132                 #endregion
133
134                 #region Stream methods
135
136                 public override void Close()
137                 {
138                         if (this.innerStream != null)
139                         {
140                                 this.innerStream.Close();
141                         }
142
143                         this.buffer                     = null;
144                         this.charset            = null;
145                         this.innerStream        = null;
146                 }
147
148                 public override void Flush()
149                 {
150                         this.CheckDisposed();
151
152                         this.innerStream.Flush();
153                 }
154
155                 public override void SetLength(long length)
156                 {
157                         this.CheckDisposed();
158
159                         this.innerStream.SetLength(length);
160                 }
161
162                 public override long Seek(long offset, System.IO.SeekOrigin loc)
163                 {
164                         this.CheckDisposed();
165
166                         return this.innerStream.Seek(offset, loc);
167                 }
168
169                 public override int Read(byte[] buffer, int offset, int count)
170                 {
171                         this.CheckDisposed();
172
173                         if (this.CanRead)
174                         {
175                                 return this.innerStream.Read(buffer, offset, count);
176                         }
177
178                         throw new InvalidOperationException("Read operations are not allowed by this stream");
179                 }
180
181                 public override void WriteByte(byte value)
182                 {
183                         this.CheckDisposed();
184
185                         this.innerStream.WriteByte(value);
186                 }
187
188                 public override void Write(byte[] buffer, int offset, int count)
189                 {
190                         this.CheckDisposed();
191
192                         if (this.CanWrite)
193                         {
194                                 this.innerStream.Write(buffer, offset, count);
195                         }
196                         else
197                         {
198                                 throw new InvalidOperationException("Write operations are not allowed by this stream");
199                         }
200                 }
201
202                 public byte[] ToArray()
203                 {
204                         this.CheckDisposed();
205
206                         if (this.innerStream is MemoryStream)
207                         {
208                                 return ((MemoryStream)this.innerStream).ToArray();
209                         }
210
211                         throw new InvalidOperationException();
212                 }
213
214                 #endregion
215
216                 #region Xdr     Read Methods
217
218                 public byte[] ReadBytes(int count)
219                 {
220                         byte[] buffer = new byte[count];
221                         this.Read(buffer, 0, buffer.Length);
222
223                         return buffer;
224                 }
225
226                 public byte[] ReadOpaque(int length)
227                 {
228                         byte[] buffer = new byte[length];
229                         int readed = 0;
230
231                         if (length > 0)
232                         {
233                                 while (readed < length)
234                                 {
235                                         readed += this.Read(buffer, readed, length - readed);
236                                 }
237
238                                 int padLength = ((4 - length) & 3);
239                                 if (padLength > 0)
240                                 {
241                                         this.Read(Pad, 0, padLength);
242                                 }
243                         }
244
245                         return buffer;
246                 }
247
248                 public byte[] ReadBuffer()
249                 {
250                         return this.ReadOpaque(this.ReadInt32());
251                 }
252
253                 public string ReadString()
254                 {
255                         return this.ReadString(this.charset);
256                 }
257
258                 public string ReadString(int length)
259                 {
260                         return this.ReadString(this.charset, length);
261                 }
262
263                 public string ReadString(Charset charset)
264                 {
265                         return this.ReadString(charset, this.ReadInt32());
266                 }
267
268                 public string ReadString(Charset charset, int length)
269                 {
270                         byte[] buffer = this.ReadOpaque(length);
271
272                         return charset.GetString(buffer, 0, buffer.Length);
273                 }
274
275                 public short ReadInt16()
276                 {
277                         return Convert.ToInt16(this.ReadInt32());
278                 }
279
280                 public int ReadInt32()
281                 {
282                         this.Read(buffer, 0, 4);
283
284                         return IPAddress.HostToNetworkOrder(BitConverter.ToInt32(buffer, 0));
285                 }
286
287                 public long ReadInt64()
288                 {
289                         this.Read(buffer, 0, 8);
290
291                         return IPAddress.HostToNetworkOrder(BitConverter.ToInt64(buffer, 0));
292                 }
293
294                 public Guid ReadGuid(int length)
295                 {
296                         return new Guid(this.ReadOpaque(length));
297                 }
298
299                 public float ReadSingle()
300                 {
301                         return BitConverter.ToSingle(BitConverter.GetBytes(this.ReadInt32()), 0);
302                 }
303
304                 public double ReadDouble()
305                 {
306                         return BitConverter.ToDouble(BitConverter.GetBytes(this.ReadInt64()), 0);
307                 }
308
309                 public DateTime ReadDateTime()
310                 {
311                         DateTime date = this.ReadDate();
312                         DateTime time = this.ReadTime();
313
314                         return new System.DateTime(
315                                 date.Year, date.Month, date.Day,
316                                 time.Hour, time.Minute, time.Second, time.Millisecond);
317                 }
318
319                 public DateTime ReadDate()
320                 {
321                         return TypeDecoder.DecodeDate(this.ReadInt32());
322                 }
323
324                 public DateTime ReadTime()
325                 {
326                         return TypeDecoder.DecodeTime(this.ReadInt32());
327                 }
328
329                 public decimal ReadDecimal(int type, int scale)
330                 {
331                         decimal value = 0;
332
333                         switch (type & ~1)
334                         {
335                                 case IscCodes.SQL_SHORT:
336                                         value = TypeDecoder.DecodeDecimal(this.ReadInt16(), scale, type);
337                                         break;
338
339                                 case IscCodes.SQL_LONG:
340                                         value = TypeDecoder.DecodeDecimal(this.ReadInt32(), scale, type);
341                                         break;
342
343                                 case IscCodes.SQL_QUAD:
344                                 case IscCodes.SQL_INT64:
345                                         value = TypeDecoder.DecodeDecimal(this.ReadInt64(), scale, type);
346                                         break;
347
348                                 case IscCodes.SQL_DOUBLE:
349                                 case IscCodes.SQL_D_FLOAT:
350                                         value = Convert.ToDecimal(this.ReadDouble());
351                                         break;
352                         }
353
354                         return value;
355                 }
356
357                 public object ReadValue(DbField field)
358                 {
359                         object fieldValue = null;
360                         Charset innerCharset = (this.charset.Name != "NONE") ? this.charset : field.Charset;
361
362                         switch (field.DbDataType)
363                         {
364                                 case DbDataType.Char:
365                                         {
366                                                 string s = this.ReadString(innerCharset, field.Length);
367
368                                                 if ((field.Length % field.Charset.BytesPerCharacter) == 0 &&
369                                                         s.Length > field.CharCount)
370                                                 {
371                                                         fieldValue = s.Substring(0, field.CharCount);
372                                                 }
373                                                 else
374                                                 {
375                                                         fieldValue = s;
376                                                 }
377                                         }
378                                         break;
379
380                                 case DbDataType.VarChar:
381                                         fieldValue = this.ReadString(innerCharset).TrimEnd();
382                                         break;
383
384                                 case DbDataType.SmallInt:
385                                         fieldValue = this.ReadInt16();
386                                         break;
387
388                                 case DbDataType.Integer:
389                                         fieldValue = this.ReadInt32();
390                                         break;
391
392                                 case DbDataType.Array:
393                                 case DbDataType.Binary:
394                                 case DbDataType.Text:
395                                 case DbDataType.BigInt:
396                                         fieldValue = this.ReadInt64();
397                                         break;
398
399                                 case DbDataType.Decimal:
400                                 case DbDataType.Numeric:
401                                         fieldValue = this.ReadDecimal(
402                                                 field.DataType,
403                                                 field.NumericScale);
404                                         break;
405
406                                 case DbDataType.Float:
407                                         fieldValue = this.ReadSingle();
408                                         break;
409
410                                 case DbDataType.Guid:
411                                         fieldValue = this.ReadGuid(field.Length);
412                                         break;
413
414                                 case DbDataType.Double:
415                                         fieldValue = this.ReadDouble();
416                                         break;
417
418                                 case DbDataType.Date:
419                                         fieldValue = this.ReadDate();
420                                         break;
421
422                                 case DbDataType.Time:
423                                         fieldValue = this.ReadTime();
424                                         break;
425
426                                 case DbDataType.TimeStamp:
427                                         fieldValue = this.ReadDateTime();
428                                         break;
429                         }
430
431                         int sqlInd = this.ReadInt32();
432
433                         if (sqlInd == 0)
434                         {
435                                 return fieldValue;
436                         }
437                         else if (sqlInd == -1)
438                         {
439                                 return null;
440                         }
441                         else
442                         {
443                                 throw new IscException("invalid sqlind value: " + sqlInd);
444                         }
445                 }
446
447                 #endregion
448
449                 #region Xdr     Write Methods
450
451                 public void WriteOpaque(byte[] buffer)
452                 {
453                         this.WriteOpaque(buffer, buffer.Length);
454                 }
455
456                 public void WriteOpaque(byte[] buffer, int length)
457                 {
458                         if (buffer != null && length > 0)
459                         {
460                                 this.Write(buffer, 0, buffer.Length);
461                                 this.Write(Fill, 0, length - buffer.Length);
462                                 this.Write(Pad, 0, ((4 - length) & 3));
463                         }
464                 }
465
466                 public void WriteBuffer(byte[] buffer)
467                 {
468                         this.WriteBuffer(buffer, buffer == null ? 0 : buffer.Length);
469                 }
470
471                 public void WriteBuffer(byte[] buffer, int length)
472                 {
473                         this.Write(length);
474                         if (buffer != null && length > 0)
475                         {
476                                 this.Write(buffer, 0, length);
477                                 this.Write(Pad, 0, ((4 - length) & 3));
478                         }
479                 }
480
481                 public void WriteBlobBuffer(byte[] buffer)
482                 {
483                         int length = buffer.Length;     // 2 for short for buffer length
484
485                         if (length > short.MaxValue)
486                         {
487                                 throw (new IOException()); //Need a     value???
488                         }
489
490                         this.Write(length + 2);
491                         this.Write(length + 2); //bizarre but true!     three copies of the     length
492                         this.WriteByte((byte)((length >> 0) & 0xff));
493                         this.WriteByte((byte)((length >> 8) & 0xff));
494                         this.Write(buffer, 0, length);
495
496                         this.Write(Pad, 0, ((4 - length + 2) & 3));
497                 }
498
499                 public void WriteTyped(int type, byte[] buffer)
500                 {
501                         int length;
502
503                         if (buffer == null)
504                         {
505                                 this.Write(1);
506                                 this.WriteByte((byte)type);
507                                 length = 1;
508                         }
509                         else
510                         {
511                                 length = buffer.Length + 1;
512                                 this.Write(length);
513                                 this.WriteByte((byte)type);
514                                 this.Write(buffer, 0, buffer.Length);
515                         }
516                         this.Write(Pad, 0, ((4 - length) & 3));
517                 }
518
519                 public void Write(string value)
520                 {
521                         byte[] buffer = this.charset.GetBytes(value);
522
523                         this.WriteBuffer(buffer, buffer.Length);
524                 }
525
526                 public void Write(short value)
527                 {
528                         this.Write((int)value);
529                 }
530
531                 public void Write(int value)
532                 {
533                         this.Write(BitConverter.GetBytes(IPAddress.NetworkToHostOrder(value)), 0, 4);
534                 }
535
536                 public void Write(long value)
537                 {
538                         this.Write(BitConverter.GetBytes(IPAddress.NetworkToHostOrder(value)), 0, 8);
539                 }
540
541                 public void Write(float value)
542                 {
543                         byte[] buffer = BitConverter.GetBytes(value);
544
545                         this.Write(BitConverter.ToInt32(buffer, 0));
546                 }
547
548                 public void Write(double value)
549                 {
550                         byte[] buffer = BitConverter.GetBytes(value);
551
552                         this.Write(BitConverter.ToInt64(buffer, 0));
553                 }
554
555                 public void Write(decimal value, int type, int scale)
556                 {
557                         object numeric = TypeEncoder.EncodeDecimal(value, scale, type);
558
559                         switch (type & ~1)
560                         {
561                                 case IscCodes.SQL_SHORT:
562                                         this.Write((short)numeric);
563                                         break;
564
565                                 case IscCodes.SQL_LONG:
566                                         this.Write((int)numeric);
567                                         break;
568
569                                 case IscCodes.SQL_QUAD:
570                                 case IscCodes.SQL_INT64:
571                                         this.Write((long)numeric);
572                                         break;
573
574                                 case IscCodes.SQL_DOUBLE:
575                                 case IscCodes.SQL_D_FLOAT:
576                                         this.Write((double)value);
577                                         break;
578                         }
579                 }
580
581                 public void Write(DateTime value)
582                 {
583                         this.WriteDate(value);
584                         this.WriteTime(value);
585                 }
586
587                 public void WriteDate(DateTime value)
588                 {
589                         this.Write(TypeEncoder.EncodeDate(Convert.ToDateTime(value)));
590                 }
591
592                 public void WriteTime(DateTime value)
593                 {
594                         this.Write(TypeEncoder.EncodeTime(Convert.ToDateTime(value)));
595                 }
596
597                 public void Write(Descriptor descriptor)
598                 {
599                         for (int i = 0; i < descriptor.Count; i++)
600                         {
601                                 this.Write(descriptor[i]);
602                         }
603                 }
604
605                 public void Write(DbField param)
606                 {
607                         Charset innerCharset = (this.charset.Name != "NONE") ? this.charset : param.Charset;
608
609                         param.FixNull();
610
611                         try
612                         {
613                                 switch (param.DbDataType)
614                                 {
615                                         case DbDataType.Char:
616                                                 {
617                                                         string svalue = param.DbValue.GetString();
618
619                                                         if ((param.Length % param.Charset.BytesPerCharacter) == 0 &&
620                                                                 svalue.Length > param.CharCount)
621                                                         {
622                                                                 throw new IscException(335544321);
623                                                         }
624
625                                                         this.WriteOpaque(innerCharset.GetBytes(svalue), param.Length);
626                                                 }
627                                                 break;
628
629                                         case DbDataType.VarChar:
630                                                 {
631                                                         string svalue = param.DbValue.GetString().TrimEnd();
632
633                                                         if ((param.Length % param.Charset.BytesPerCharacter) == 0 &&
634                                                                 svalue.Length > param.CharCount)
635                                                         {
636                                                                 throw new IscException(335544321);
637                                                         }
638
639                                                         byte[] data = innerCharset.GetBytes(svalue);
640
641                                                         this.WriteBuffer(data, data.Length);
642                                                 }
643                                                 break;
644
645                                         case DbDataType.SmallInt:
646                                                 this.Write(param.DbValue.GetInt16());
647                                                 break;
648
649                                         case DbDataType.Integer:
650                                                 this.Write(param.DbValue.GetInt32());
651                                                 break;
652
653                                         case DbDataType.BigInt:
654                                         case DbDataType.Array:
655                                         case DbDataType.Binary:
656                                         case DbDataType.Text:
657                                                 this.Write(param.DbValue.GetInt64());
658                                                 break;
659
660                                         case DbDataType.Decimal:
661                                         case DbDataType.Numeric:
662                                                 this.Write(
663                                                         param.DbValue.GetDecimal(),
664                                                         param.DataType,
665                                                         param.NumericScale);
666                                                 break;
667
668                                         case DbDataType.Float:
669                                                 this.Write(param.DbValue.GetFloat());
670                                                 break;
671
672                                         case DbDataType.Guid:
673                                                 this.WriteOpaque(param.DbValue.GetGuid().ToByteArray());
674                                                 break;
675
676                                         case DbDataType.Double:
677                                                 this.Write(param.DbValue.GetDouble());
678                                                 break;
679
680                                         case DbDataType.Date:
681                                                 this.Write(param.DbValue.EncodeDate());
682                                                 break;
683
684                                         case DbDataType.Time:
685                                                 this.Write(param.DbValue.EncodeTime());
686                                                 break;
687
688                                         case DbDataType.TimeStamp:
689                                                 this.Write(param.DbValue.EncodeDate());
690                                                 this.Write(param.DbValue.EncodeTime());
691                                                 break;
692
693                                         default:
694                                                 throw new IscException("Unknown sql data type: " + param.DataType);
695                                 }
696
697                                 this.Write(param.NullFlag);
698                         }
699                         catch (IOException)
700                         {
701                                 throw new IscException(IscCodes.isc_net_write_err);
702                         }
703                 }
704
705                 #endregion
706
707                 #region Private Methods
708
709                 private void CheckDisposed()
710                 {
711                         if (this.innerStream == null)
712                         {
713                                 throw new ObjectDisposedException("The XdrStream is closed.");
714                         }
715                 }
716
717                 #endregion
718         }
719 }