2 * Firebird ADO.NET Data provider for .NET and Mono
\r
4 * The contents of this file are subject to the Initial
\r
5 * Developer's Public License Version 1.0 (the "License");
\r
6 * you may not use this file except in compliance with the
\r
7 * License. You may obtain a copy of the License at
\r
8 * http://www.firebirdsql.org/index.php?op=doc&id=idpl
\r
10 * Software distributed under the License is distributed on
\r
11 * an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
\r
12 * express or implied. See the License for the specific
\r
13 * language governing rights and limitations under the License.
\r
15 * Copyright (c) 2002, 2005 Carlos Guzman Alvarez
\r
16 * All Rights Reserved.
\r
20 using System.Collections;
\r
24 using System.Globalization;
\r
26 using FirebirdSql.Data.Common;
\r
28 namespace FirebirdSql.Data.Gds
\r
30 internal class XdrStream : Stream
\r
32 #region Static Fields
\r
34 private static byte[] fill;
\r
35 private static byte[] pad;
\r
39 #region Static Properties
\r
41 internal static byte[] Fill
\r
47 fill = new byte[32767];
\r
48 for (int i = 0; i < fill.Length; i++)
\r
58 private static byte[] Pad
\r
64 pad = new byte[] { 0, 0, 0, 0 };
\r
75 private byte[] buffer;
\r
76 private Charset charset;
\r
77 private Stream innerStream;
\r
81 #region Stream Properties
\r
83 public override bool CanWrite
\r
85 get { return this.innerStream.CanWrite; }
\r
88 public override bool CanRead
\r
90 get { return this.innerStream.CanRead; }
\r
93 public override bool CanSeek
\r
95 get { return this.innerStream.CanSeek; }
\r
98 public override long Position
\r
100 get { return this.innerStream.Position; }
\r
101 set { this.innerStream.Position = value; }
\r
104 public override long Length
\r
106 get { return this.innerStream.Length; }
\r
111 #region Constructors
\r
113 public XdrStream() : this(Charset.DefaultCharset)
\r
117 public XdrStream(Charset charset) : this(new MemoryStream(), charset)
\r
121 public XdrStream(byte[] buffer, Charset charset) : this(new MemoryStream(buffer), charset)
\r
125 public XdrStream(Stream innerStream, Charset charset) : base()
\r
127 this.buffer = new byte[8] { 0, 0, 0, 0, 0, 0, 0, 0 };
\r
128 this.innerStream = innerStream;
\r
129 this.charset = charset;
\r
134 #region Stream methods
\r
136 public override void Close()
\r
138 if (this.innerStream != null)
\r
140 this.innerStream.Close();
\r
143 this.buffer = null;
\r
144 this.charset = null;
\r
145 this.innerStream = null;
\r
148 public override void Flush()
\r
150 this.CheckDisposed();
\r
152 this.innerStream.Flush();
\r
155 public override void SetLength(long length)
\r
157 this.CheckDisposed();
\r
159 this.innerStream.SetLength(length);
\r
162 public override long Seek(long offset, System.IO.SeekOrigin loc)
\r
164 this.CheckDisposed();
\r
166 return this.innerStream.Seek(offset, loc);
\r
169 public override int Read(byte[] buffer, int offset, int count)
\r
171 this.CheckDisposed();
\r
175 return this.innerStream.Read(buffer, offset, count);
\r
178 throw new InvalidOperationException("Read operations are not allowed by this stream");
\r
181 public override void WriteByte(byte value)
\r
183 this.CheckDisposed();
\r
185 this.innerStream.WriteByte(value);
\r
188 public override void Write(byte[] buffer, int offset, int count)
\r
190 this.CheckDisposed();
\r
194 this.innerStream.Write(buffer, offset, count);
\r
198 throw new InvalidOperationException("Write operations are not allowed by this stream");
\r
202 public byte[] ToArray()
\r
204 this.CheckDisposed();
\r
206 if (this.innerStream is MemoryStream)
\r
208 return ((MemoryStream)this.innerStream).ToArray();
\r
211 throw new InvalidOperationException();
\r
216 #region Xdr Read Methods
\r
218 public byte[] ReadBytes(int count)
\r
220 byte[] buffer = new byte[count];
\r
221 this.Read(buffer, 0, buffer.Length);
\r
226 public byte[] ReadOpaque(int length)
\r
228 byte[] buffer = new byte[length];
\r
233 while (readed < length)
\r
235 readed += this.Read(buffer, readed, length - readed);
\r
238 int padLength = ((4 - length) & 3);
\r
241 this.Read(Pad, 0, padLength);
\r
248 public byte[] ReadBuffer()
\r
250 return this.ReadOpaque(this.ReadInt32());
\r
253 public string ReadString()
\r
255 return this.ReadString(this.charset);
\r
258 public string ReadString(int length)
\r
260 return this.ReadString(this.charset, length);
\r
263 public string ReadString(Charset charset)
\r
265 return this.ReadString(charset, this.ReadInt32());
\r
268 public string ReadString(Charset charset, int length)
\r
270 byte[] buffer = this.ReadOpaque(length);
\r
272 return charset.GetString(buffer, 0, buffer.Length);
\r
275 public short ReadInt16()
\r
277 return Convert.ToInt16(this.ReadInt32());
\r
280 public int ReadInt32()
\r
282 this.Read(buffer, 0, 4);
\r
284 return IPAddress.HostToNetworkOrder(BitConverter.ToInt32(buffer, 0));
\r
287 public long ReadInt64()
\r
289 this.Read(buffer, 0, 8);
\r
291 return IPAddress.HostToNetworkOrder(BitConverter.ToInt64(buffer, 0));
\r
294 public Guid ReadGuid(int length)
\r
296 return new Guid(this.ReadOpaque(length));
\r
299 public float ReadSingle()
\r
301 return BitConverter.ToSingle(BitConverter.GetBytes(this.ReadInt32()), 0);
\r
304 public double ReadDouble()
\r
306 return BitConverter.ToDouble(BitConverter.GetBytes(this.ReadInt64()), 0);
\r
309 public DateTime ReadDateTime()
\r
311 DateTime date = this.ReadDate();
\r
312 DateTime time = this.ReadTime();
\r
314 return new System.DateTime(
\r
315 date.Year, date.Month, date.Day,
\r
316 time.Hour, time.Minute, time.Second, time.Millisecond);
\r
319 public DateTime ReadDate()
\r
321 return TypeDecoder.DecodeDate(this.ReadInt32());
\r
324 public DateTime ReadTime()
\r
326 return TypeDecoder.DecodeTime(this.ReadInt32());
\r
329 public decimal ReadDecimal(int type, int scale)
\r
335 case IscCodes.SQL_SHORT:
\r
336 value = TypeDecoder.DecodeDecimal(this.ReadInt16(), scale, type);
\r
339 case IscCodes.SQL_LONG:
\r
340 value = TypeDecoder.DecodeDecimal(this.ReadInt32(), scale, type);
\r
343 case IscCodes.SQL_QUAD:
\r
344 case IscCodes.SQL_INT64:
\r
345 value = TypeDecoder.DecodeDecimal(this.ReadInt64(), scale, type);
\r
348 case IscCodes.SQL_DOUBLE:
\r
349 case IscCodes.SQL_D_FLOAT:
\r
350 value = Convert.ToDecimal(this.ReadDouble());
\r
357 public object ReadValue(DbField field)
\r
359 object fieldValue = null;
\r
360 Charset innerCharset = (this.charset.Name != "NONE") ? this.charset : field.Charset;
\r
362 switch (field.DbDataType)
\r
364 case DbDataType.Char:
\r
366 string s = this.ReadString(innerCharset, field.Length);
\r
368 if ((field.Length % field.Charset.BytesPerCharacter) == 0 &&
\r
369 s.Length > field.CharCount)
\r
371 fieldValue = s.Substring(0, field.CharCount);
\r
380 case DbDataType.VarChar:
\r
381 fieldValue = this.ReadString(innerCharset).TrimEnd();
\r
384 case DbDataType.SmallInt:
\r
385 fieldValue = this.ReadInt16();
\r
388 case DbDataType.Integer:
\r
389 fieldValue = this.ReadInt32();
\r
392 case DbDataType.Array:
\r
393 case DbDataType.Binary:
\r
394 case DbDataType.Text:
\r
395 case DbDataType.BigInt:
\r
396 fieldValue = this.ReadInt64();
\r
399 case DbDataType.Decimal:
\r
400 case DbDataType.Numeric:
\r
401 fieldValue = this.ReadDecimal(
\r
403 field.NumericScale);
\r
406 case DbDataType.Float:
\r
407 fieldValue = this.ReadSingle();
\r
410 case DbDataType.Guid:
\r
411 fieldValue = this.ReadGuid(field.Length);
\r
414 case DbDataType.Double:
\r
415 fieldValue = this.ReadDouble();
\r
418 case DbDataType.Date:
\r
419 fieldValue = this.ReadDate();
\r
422 case DbDataType.Time:
\r
423 fieldValue = this.ReadTime();
\r
426 case DbDataType.TimeStamp:
\r
427 fieldValue = this.ReadDateTime();
\r
431 int sqlInd = this.ReadInt32();
\r
437 else if (sqlInd == -1)
\r
443 throw new IscException("invalid sqlind value: " + sqlInd);
\r
449 #region Xdr Write Methods
\r
451 public void WriteOpaque(byte[] buffer)
\r
453 this.WriteOpaque(buffer, buffer.Length);
\r
456 public void WriteOpaque(byte[] buffer, int length)
\r
458 if (buffer != null && length > 0)
\r
460 this.Write(buffer, 0, buffer.Length);
\r
461 this.Write(Fill, 0, length - buffer.Length);
\r
462 this.Write(Pad, 0, ((4 - length) & 3));
\r
466 public void WriteBuffer(byte[] buffer)
\r
468 this.WriteBuffer(buffer, buffer == null ? 0 : buffer.Length);
\r
471 public void WriteBuffer(byte[] buffer, int length)
\r
473 this.Write(length);
\r
474 if (buffer != null && length > 0)
\r
476 this.Write(buffer, 0, length);
\r
477 this.Write(Pad, 0, ((4 - length) & 3));
\r
481 public void WriteBlobBuffer(byte[] buffer)
\r
483 int length = buffer.Length; // 2 for short for buffer length
\r
485 if (length > short.MaxValue)
\r
487 throw (new IOException()); //Need a value???
\r
490 this.Write(length + 2);
\r
491 this.Write(length + 2); //bizarre but true! three copies of the length
\r
492 this.WriteByte((byte)((length >> 0) & 0xff));
\r
493 this.WriteByte((byte)((length >> 8) & 0xff));
\r
494 this.Write(buffer, 0, length);
\r
496 this.Write(Pad, 0, ((4 - length + 2) & 3));
\r
499 public void WriteTyped(int type, byte[] buffer)
\r
503 if (buffer == null)
\r
506 this.WriteByte((byte)type);
\r
511 length = buffer.Length + 1;
\r
512 this.Write(length);
\r
513 this.WriteByte((byte)type);
\r
514 this.Write(buffer, 0, buffer.Length);
\r
516 this.Write(Pad, 0, ((4 - length) & 3));
\r
519 public void Write(string value)
\r
521 byte[] buffer = this.charset.GetBytes(value);
\r
523 this.WriteBuffer(buffer, buffer.Length);
\r
526 public void Write(short value)
\r
528 this.Write((int)value);
\r
531 public void Write(int value)
\r
533 this.Write(BitConverter.GetBytes(IPAddress.NetworkToHostOrder(value)), 0, 4);
\r
536 public void Write(long value)
\r
538 this.Write(BitConverter.GetBytes(IPAddress.NetworkToHostOrder(value)), 0, 8);
\r
541 public void Write(float value)
\r
543 byte[] buffer = BitConverter.GetBytes(value);
\r
545 this.Write(BitConverter.ToInt32(buffer, 0));
\r
548 public void Write(double value)
\r
550 byte[] buffer = BitConverter.GetBytes(value);
\r
552 this.Write(BitConverter.ToInt64(buffer, 0));
\r
555 public void Write(decimal value, int type, int scale)
\r
557 object numeric = TypeEncoder.EncodeDecimal(value, scale, type);
\r
561 case IscCodes.SQL_SHORT:
\r
562 this.Write((short)numeric);
\r
565 case IscCodes.SQL_LONG:
\r
566 this.Write((int)numeric);
\r
569 case IscCodes.SQL_QUAD:
\r
570 case IscCodes.SQL_INT64:
\r
571 this.Write((long)numeric);
\r
574 case IscCodes.SQL_DOUBLE:
\r
575 case IscCodes.SQL_D_FLOAT:
\r
576 this.Write((double)value);
\r
581 public void Write(DateTime value)
\r
583 this.WriteDate(value);
\r
584 this.WriteTime(value);
\r
587 public void WriteDate(DateTime value)
\r
589 this.Write(TypeEncoder.EncodeDate(Convert.ToDateTime(value)));
\r
592 public void WriteTime(DateTime value)
\r
594 this.Write(TypeEncoder.EncodeTime(Convert.ToDateTime(value)));
\r
597 public void Write(Descriptor descriptor)
\r
599 for (int i = 0; i < descriptor.Count; i++)
\r
601 this.Write(descriptor[i]);
\r
605 public void Write(DbField param)
\r
607 Charset innerCharset = (this.charset.Name != "NONE") ? this.charset : param.Charset;
\r
613 switch (param.DbDataType)
\r
615 case DbDataType.Char:
\r
617 string svalue = param.DbValue.GetString();
\r
619 if ((param.Length % param.Charset.BytesPerCharacter) == 0 &&
\r
620 svalue.Length > param.CharCount)
\r
622 throw new IscException(335544321);
\r
625 this.WriteOpaque(innerCharset.GetBytes(svalue), param.Length);
\r
629 case DbDataType.VarChar:
\r
631 string svalue = param.DbValue.GetString().TrimEnd();
\r
633 if ((param.Length % param.Charset.BytesPerCharacter) == 0 &&
\r
634 svalue.Length > param.CharCount)
\r
636 throw new IscException(335544321);
\r
639 byte[] data = innerCharset.GetBytes(svalue);
\r
641 this.WriteBuffer(data, data.Length);
\r
645 case DbDataType.SmallInt:
\r
646 this.Write(param.DbValue.GetInt16());
\r
649 case DbDataType.Integer:
\r
650 this.Write(param.DbValue.GetInt32());
\r
653 case DbDataType.BigInt:
\r
654 case DbDataType.Array:
\r
655 case DbDataType.Binary:
\r
656 case DbDataType.Text:
\r
657 this.Write(param.DbValue.GetInt64());
\r
660 case DbDataType.Decimal:
\r
661 case DbDataType.Numeric:
\r
663 param.DbValue.GetDecimal(),
\r
665 param.NumericScale);
\r
668 case DbDataType.Float:
\r
669 this.Write(param.DbValue.GetFloat());
\r
672 case DbDataType.Guid:
\r
673 this.WriteOpaque(param.DbValue.GetGuid().ToByteArray());
\r
676 case DbDataType.Double:
\r
677 this.Write(param.DbValue.GetDouble());
\r
680 case DbDataType.Date:
\r
681 this.Write(param.DbValue.EncodeDate());
\r
684 case DbDataType.Time:
\r
685 this.Write(param.DbValue.EncodeTime());
\r
688 case DbDataType.TimeStamp:
\r
689 this.Write(param.DbValue.EncodeDate());
\r
690 this.Write(param.DbValue.EncodeTime());
\r
694 throw new IscException("Unknown sql data type: " + param.DataType);
\r
697 this.Write(param.NullFlag);
\r
699 catch (IOException)
\r
701 throw new IscException(IscCodes.isc_net_write_err);
\r
707 #region Private Methods
\r
709 private void CheckDisposed()
\r
711 if (this.innerStream == null)
\r
713 throw new ObjectDisposedException("The XdrStream is closed.");
\r