Update Reference Sources to .NET Framework 4.6.1
[mono.git] / mcs / class / referencesource / System.Data / System / Data / SQLTypes / SQLChars.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="SqlChars.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 //  </copyright>                                                                                                                                
5 // <owner current="true" primary="true">junfang</owner>
6 // <owner current="true" primary="false">[....]</owner>
7 // <owner current="true" primary="false">[....]</owner>
8 //------------------------------------------------------------------------------
9
10 //**************************************************************************
11 // @File: SqlChars.cs
12 // @Owner: junfang
13 //
14 // Created by:  JunFang
15 //
16 // Description: Class SqlChars is used to represent a char/varchar/nchar/nvarchar
17 //              data from SQL Server. It contains a char array buffer, which can
18 //              be refilled. For example, in data access, user could use one instance
19 //              of SqlChars to bind to a binary column, and we will just keep copying
20 //              the data into the same instance, and avoid allocation per row.
21 //
22 // Notes: 
23 //      
24 // History:
25 //
26 //     @Version: Yukon
27 //     120214 JXF  09/23/02 SqlBytes/SqlChars class indexer
28 //     112296 AZA  07/06/02 Seal SqlAccess classes.
29 //     107151 AZA  04/18/02 Track byte array buffer as well as SqlBytes in 
30 //                          sqlaccess.
31 //     107216 JXF  04/17/02 Bug 514927
32 //     106854 JXF  04/15/02 Fix http suites due to SqlChars
33 //     106448 JXF  04/12/02 Bugs on sqlchars
34 //     105715 JXF  04/05/02 Handle NULL properly in SqlBytes.SetLength
35 //     91128 JXF  10/17/01 Make SqlBytes not unsafe
36 //
37 //   04/20/01  JunFang  Created.
38 //
39 // @EndHeader@
40 //**************************************************************************
41
42
43 namespace System.Data.SqlTypes {
44         using System;
45         using System.IO;
46         using System.Runtime.InteropServices;
47         using System.Diagnostics;
48         using System.Data.Common;
49         using System.Data.Sql;
50     using System.Data.SqlClient;
51         using System.Data.SqlTypes;
52         using System.Xml;
53         using System.Xml.Schema;
54         using System.Xml.Serialization;
55         using System.Runtime.Serialization;
56         using System.Security.Permissions;
57
58         [Serializable,XmlSchemaProvider("GetXsdType")]
59     public sealed class SqlChars : System.Data.SqlTypes.INullable, IXmlSerializable, ISerializable {
60                 // --------------------------------------------------------------
61                 //        Data members
62                 // --------------------------------------------------------------
63
64                 // SqlChars has five possible states
65                 // 1) SqlChars is Null
66                 //              - m_stream must be null, m_lCuLen must be x_lNull
67                 // 2) SqlChars contains a valid buffer, 
68                 //              - m_rgchBuf must not be null, and m_stream must be null
69                 // 3) SqlChars contains a valid pointer
70                 //              - m_rgchBuf could be null or not,
71                 //                      if not null, content is garbage, should never look into it.
72                 //      - m_stream must be null.
73                 // 4) SqlChars contains a SqlStreamChars
74                 //      - m_stream must not be null
75                 //      - m_rgchBuf could be null or not. if not null, content is garbage, should never look into it.
76                 //              - m_lCurLen must be x_lNull.
77                 // 5) SqlChars contains a Lazy Materialized Blob (ie, StorageState.Delayed)
78                 //
79                 internal char[]             m_rgchBuf;  // Data buffer
80                 private  long               m_lCurLen;  // Current data length
81                 internal SqlStreamChars     m_stream;
82                 private  SqlBytesCharsState m_state;
83
84                 private  char[]             m_rgchWorkBuf;      // A 1-char work buffer.
85
86                 // The max data length that we support at this time.
87                 private const long x_lMaxLen = (long)System.Int32.MaxValue;
88
89                 private const long x_lNull = -1L;
90
91                 // --------------------------------------------------------------
92                 //        Constructor(s)
93                 // --------------------------------------------------------------
94
95                 // Public default constructor used for XML serialization
96                 public SqlChars() {
97                         SetNull();
98                 }
99
100                 // Create a SqlChars with an in-memory buffer
101                 public SqlChars(char[] buffer) {
102                         m_rgchBuf = buffer;
103                         m_stream = null;
104                         if (m_rgchBuf == null) {
105                                 m_state = SqlBytesCharsState.Null;
106                                 m_lCurLen = x_lNull;
107             }
108                         else {
109                                 m_state = SqlBytesCharsState.Buffer;
110                                 m_lCurLen = (long)m_rgchBuf.Length;
111             }
112
113                         m_rgchWorkBuf = null;
114
115                         AssertValid();
116         }
117
118         // Create a SqlChars from a SqlString
119         public SqlChars(SqlString value) : this (value.IsNull ? (char[])null : value.Value.ToCharArray()) {
120         }
121
122                 // Create a SqlChars from a SqlStreamChars
123                 internal SqlChars(SqlStreamChars s) {
124                         m_rgchBuf = null;
125                         m_lCurLen = x_lNull;
126                         m_stream = s;
127                         m_state = (s == null) ? SqlBytesCharsState.Null : SqlBytesCharsState.Stream;
128
129                         m_rgchWorkBuf = null;
130
131                         AssertValid();
132         }
133
134                 // Constructor required for serialization. Deserializes as a Buffer. If the bits have been tampered with
135                 // then this will throw a SerializationException or a InvalidCastException.
136                 private SqlChars(SerializationInfo info, StreamingContext context)
137                         {
138                         m_stream = null;
139                         m_rgchWorkBuf = null;
140
141                         if (info.GetBoolean("IsNull"))
142                                 {
143                                 m_state = SqlBytesCharsState.Null;
144                                 m_rgchBuf = null;
145                                 }
146                         else
147                                 {
148                                 m_state = SqlBytesCharsState.Buffer;
149                                 m_rgchBuf = (char[]) info.GetValue("data", typeof(char[]));
150                                 m_lCurLen = m_rgchBuf.Length;
151                                 }
152
153                         AssertValid();
154                         }
155
156                 // --------------------------------------------------------------
157                 //        Public properties
158                 // --------------------------------------------------------------
159
160                 // INullable
161                 public bool IsNull {
162                         get {
163                             return m_state == SqlBytesCharsState.Null;
164                         }
165                 }
166
167                 // Property: the in-memory buffer of SqlChars
168                 //              Return Buffer even if SqlChars is Null.
169
170                 public char[] Buffer {
171                         get {
172                                 if (FStream())  {
173                                         CopyStreamToBuffer();
174                 }
175                                 return m_rgchBuf;
176             }
177         }
178
179                 // Property: the actual length of the data
180                 public long Length {
181                         get {
182                                 switch (m_state) {
183                                         case SqlBytesCharsState.Null: 
184                         throw new SqlNullValueException();
185
186                                         case SqlBytesCharsState.Stream:
187                         return m_stream.Length;
188
189                                         default:
190                                                 return m_lCurLen;
191                 }
192             }
193         }
194
195                 // Property: the max length of the data
196                 //              Return MaxLength even if SqlChars is Null.
197                 //              When the buffer is also null, return -1.
198                 //              If containing a Stream, return -1.
199
200                 public long MaxLength {
201                         get {
202                                 switch (m_state) {
203                                         case SqlBytesCharsState.Stream:
204                                                 return -1L;
205
206                                         default:
207                                                 return (m_rgchBuf == null) ? -1L : (long)m_rgchBuf.Length;
208                 }
209             }
210         }
211
212                 // Property: get a copy of the data in a new char[] array.
213
214                 public char[] Value {
215                         get {
216                 char[] buffer;
217
218                                 switch (m_state) {
219                                         case SqlBytesCharsState.Null: 
220                                                 throw new SqlNullValueException();
221
222                                         case SqlBytesCharsState.Stream:
223                                                 if (m_stream.Length > x_lMaxLen)
224                                             throw new SqlTypeException(Res.GetString(Res.SqlMisc_BufferInsufficientMessage));
225                                                 buffer = new char[m_stream.Length];
226                                                 if (m_stream.Position != 0)
227                                                         m_stream.Seek(0, SeekOrigin.Begin);
228                                                 m_stream.Read(buffer, 0, checked((int)m_stream.Length));
229                                                 break;
230
231                                         default:
232                                                 buffer = new char[m_lCurLen];
233                                                 Array.Copy(m_rgchBuf, buffer, (int)m_lCurLen);
234                                                 break;
235                 }
236
237                                 return buffer;
238             }
239         }
240
241                 // class indexer
242
243                 public char this[long offset] {
244                         get {
245                 if (offset < 0 || offset >= this.Length)
246                     throw new ArgumentOutOfRangeException("offset");
247
248                 if (m_rgchWorkBuf == null)
249                                         m_rgchWorkBuf = new char[1];
250
251                                 Read(offset, m_rgchWorkBuf, 0, 1);
252                                 return m_rgchWorkBuf[0];
253             }
254                         set {
255                                 if (m_rgchWorkBuf == null)
256                                         m_rgchWorkBuf = new char[1];
257                                 m_rgchWorkBuf[0] = value;
258                                 Write(offset, m_rgchWorkBuf, 0, 1);
259             }
260         }
261
262         internal SqlStreamChars Stream {
263             get {
264                 return FStream() ? m_stream : new StreamOnSqlChars(this);
265             }
266             set {
267                 m_lCurLen = x_lNull;
268                 m_stream = value;
269                 m_state = (value == null) ? SqlBytesCharsState.Null : SqlBytesCharsState.Stream;
270
271                 AssertValid();                   
272             }
273         }
274
275             public StorageState Storage {
276                 get {
277                         switch (m_state) {
278                                 case SqlBytesCharsState.Null: 
279                                         throw new SqlNullValueException();
280
281                                 case SqlBytesCharsState.Stream:
282                                     return StorageState.Stream;
283
284                                 case SqlBytesCharsState.Buffer:
285                                     return StorageState.Buffer;
286
287                                 default:
288                                     return StorageState.UnmanagedBuffer;
289                 }
290             }
291         }
292
293                 // --------------------------------------------------------------
294                 //        Public methods
295                 // --------------------------------------------------------------
296
297                 public void SetNull() {
298                 m_lCurLen = x_lNull;
299                 m_stream = null;
300                 m_state = SqlBytesCharsState.Null;
301
302                 AssertValid();
303                 }
304
305                 // Set the current length of the data
306                 // If the SqlChars is Null, setLength will make it non-Null.
307                 public void SetLength(long value) {
308                         if (value < 0)
309                                 throw new ArgumentOutOfRangeException("value");
310
311                         if (FStream()) {
312                                 m_stream.SetLength(value);
313             }
314                         else {
315                                 // If there is a buffer, even the value of SqlChars is Null,
316                                 // still allow setting length to zero, which will make it not Null.
317                                 // If the buffer is null, raise exception
318                                 //
319                                 if (null == m_rgchBuf)
320                     throw new SqlTypeException(Res.GetString(Res.SqlMisc_NoBufferMessage));
321
322                                 if (value > (long)m_rgchBuf.Length)
323                                         throw new ArgumentOutOfRangeException("value");
324
325                                 else if (IsNull)
326                                         // At this point we know that value is small enough
327                                         // Go back in buffer mode
328                                         m_state = SqlBytesCharsState.Buffer;
329                                 
330                                 m_lCurLen = value;
331                                 }
332
333                         AssertValid();
334         }
335
336                 // Read data of specified length from specified offset into a buffer
337
338                 public long Read(long offset, char[] buffer, int offsetInBuffer, int count) {
339                         if (IsNull)
340                 throw new SqlNullValueException();
341
342                         // Validate the arguments
343                         if (buffer == null)
344                                 throw new ArgumentNullException("buffer");
345
346                         if (offset > this.Length || offset < 0)
347                                 throw new ArgumentOutOfRangeException("offset");
348
349                         if (offsetInBuffer > buffer.Length || offsetInBuffer < 0)
350                                 throw new ArgumentOutOfRangeException("offsetInBuffer");
351
352                         if (count < 0 || count > buffer.Length - offsetInBuffer)
353                                 throw new ArgumentOutOfRangeException("count");
354
355                         // Adjust count based on data length
356                         if (count > this.Length - offset)
357                                 count = (int)(this.Length - offset);
358
359                         if (count != 0) {
360                                 switch (m_state) {
361                                         case SqlBytesCharsState.Stream:
362                                             if (m_stream.Position != offset)
363                                                         m_stream.Seek(offset, SeekOrigin.Begin);
364                                                 m_stream.Read(buffer, offsetInBuffer, count);
365                                                 break;
366
367                                         default:
368                                                 Array.Copy(m_rgchBuf, offset, buffer, offsetInBuffer, count);
369                                                 break;
370                 }
371             }
372                         return count;
373         }
374
375                 // Write data of specified length into the SqlChars from specified offset
376
377                 public void Write(long offset, char[] buffer, int offsetInBuffer, int count) {
378                         if (FStream()) {
379                                 if (m_stream.Position != offset)
380                                         m_stream.Seek(offset, SeekOrigin.Begin);
381                                 m_stream.Write(buffer, offsetInBuffer, count);
382             }
383                         else {
384                                 // Validate the arguments
385                                 if (buffer == null)
386                                         throw new ArgumentNullException("buffer");
387
388                                 if (m_rgchBuf == null)
389                     throw new SqlTypeException(Res.GetString(Res.SqlMisc_NoBufferMessage));
390
391                                 if (offset < 0)
392                                         throw new ArgumentOutOfRangeException("offset");
393                                 if (offset > m_rgchBuf.Length)
394                     throw new SqlTypeException(Res.GetString(Res.SqlMisc_BufferInsufficientMessage));
395
396                                 if (offsetInBuffer < 0 || offsetInBuffer > buffer.Length)
397                                         throw new ArgumentOutOfRangeException("offsetInBuffer");
398
399                                 if (count < 0 || count > buffer.Length - offsetInBuffer)
400                                         throw new ArgumentOutOfRangeException("count");
401
402                                 if (count > m_rgchBuf.Length - offset)
403                     throw new SqlTypeException(Res.GetString(Res.SqlMisc_BufferInsufficientMessage));
404
405                                 if (IsNull) {
406                                         // If NULL and there is buffer inside, we only allow writing from 
407                                         // offset zero.
408                                         //
409                                         if (offset != 0)
410                         throw new SqlTypeException(Res.GetString(Res.SqlMisc_WriteNonZeroOffsetOnNullMessage));
411
412                                         // treat as if our current length is zero.
413                                         // Note this has to be done after all inputs are validated, so that
414                                         // we won't throw exception after this point.
415                                         //
416                                         m_lCurLen = 0;
417                     m_state = SqlBytesCharsState.Buffer;
418                 }
419                                 else if (offset > m_lCurLen) {
420                                         // Don't allow writing from an offset that this larger than current length.
421                                         // It would leave uninitialized data in the buffer.
422                                         //
423                     throw new SqlTypeException(Res.GetString(Res.SqlMisc_WriteOffsetLargerThanLenMessage));
424                                 }
425
426                                 if (count != 0) {
427                                         Array.Copy(buffer, offsetInBuffer, m_rgchBuf, offset, count);
428
429                                         // If the last position that has been written is after
430                                         // the current data length, reset the length
431                                         if (m_lCurLen < offset + count)
432                                                 m_lCurLen = offset + count;
433                 }
434             }
435
436                         AssertValid();
437         }
438
439                 public SqlString ToSqlString() {
440                         return IsNull ? SqlString.Null : new String(Value);
441         }
442
443                 // --------------------------------------------------------------
444                 //        Conversion operators
445                 // --------------------------------------------------------------
446
447                 // Alternative method: ToSqlString()
448                 public static explicit operator SqlString(SqlChars value) {
449                         return value.ToSqlString();
450                 }
451
452                 // Alternative method: constructor SqlChars(SqlString)
453                 public static explicit operator SqlChars(SqlString value) {
454                         return new SqlChars(value);
455                 }
456
457                 // --------------------------------------------------------------
458                 //        Private utility functions
459                 // --------------------------------------------------------------
460
461                 [System.Diagnostics.Conditional("DEBUG")] 
462                 private void AssertValid() {
463                         Debug.Assert(m_state >= SqlBytesCharsState.Null && m_state <= SqlBytesCharsState.Stream);
464
465                         if (IsNull)     {
466             }
467                         else {
468                                 Debug.Assert((m_lCurLen >= 0 && m_lCurLen <= x_lMaxLen) || FStream());
469                                 Debug.Assert(FStream() || (m_rgchBuf != null && m_lCurLen <= m_rgchBuf.Length));
470                                 Debug.Assert(!FStream() || (m_lCurLen == x_lNull));
471                         }
472                         Debug.Assert(m_rgchWorkBuf == null || m_rgchWorkBuf.Length == 1);
473                 }
474
475                 // whether the SqlChars contains a Stream
476                 internal bool FStream() {
477                         return m_state == SqlBytesCharsState.Stream;
478         }
479
480                 // Copy the data from the Stream to the array buffer.
481                 // If the SqlChars doesn't hold a buffer or the buffer
482                 // is not big enough, allocate new char array.
483                 private void CopyStreamToBuffer() {
484                         Debug.Assert(FStream());
485
486                         long lStreamLen = m_stream.Length;
487                         if (lStreamLen >= x_lMaxLen)
488                            throw new SqlTypeException(Res.GetString(Res.SqlMisc_BufferInsufficientMessage));
489
490                         if (m_rgchBuf == null || m_rgchBuf.Length < lStreamLen)
491                                 m_rgchBuf = new char[lStreamLen];
492
493                         if (m_stream.Position != 0)
494                                 m_stream.Seek(0, SeekOrigin.Begin);
495
496                         m_stream.Read(m_rgchBuf, 0, (int)lStreamLen);
497                         m_stream = null;
498                         m_lCurLen = lStreamLen;
499                         m_state = SqlBytesCharsState.Buffer;
500
501                         AssertValid();
502         }
503
504                 private void SetBuffer(char[] buffer) {
505                         m_rgchBuf = buffer;
506                         m_lCurLen = (m_rgchBuf == null) ? x_lNull : (long)m_rgchBuf.Length;
507                         m_stream = null;
508                         m_state = (m_rgchBuf == null) ? SqlBytesCharsState.Null : SqlBytesCharsState.Buffer;
509
510                         AssertValid();
511                 }
512
513                 // --------------------------------------------------------------
514                 //              XML Serialization
515                 // --------------------------------------------------------------
516
517
518                 XmlSchema IXmlSerializable.GetSchema() { 
519                         return null; 
520                 }
521                 
522                 void IXmlSerializable.ReadXml(XmlReader r) {
523                         char[] value = null;
524                         
525                         string isNull = r.GetAttribute("nil", XmlSchema.InstanceNamespace);
526                         
527                         if (isNull != null && XmlConvert.ToBoolean(isNull)) {
528                 // VSTFDevDiv# 479603 - SqlTypes read null value infinitely and never read the next value. Fix - Read the next value.
529                 r.ReadElementString();
530                 SetNull();
531                         }
532                         else {
533                                 value = r.ReadElementString().ToCharArray();
534                                 SetBuffer(value);
535                         }
536                 }
537
538                 void IXmlSerializable.WriteXml(XmlWriter writer) {
539                         if (IsNull) {
540                                 writer.WriteAttributeString("xsi", "nil", XmlSchema.InstanceNamespace, "true");
541                         }
542                         else {
543                                 char[] value = this.Buffer;
544                                 writer.WriteString(new String(value, 0, (int)(this.Length)));
545                         }
546                 }
547
548                 public static XmlQualifiedName GetXsdType(XmlSchemaSet schemaSet) {
549                         return new XmlQualifiedName("string", XmlSchema.Namespace);
550                 }
551
552                 // --------------------------------------------------------------
553                 //              Serialization using ISerializable
554                 // --------------------------------------------------------------
555
556                 // State information is not saved. The current state is converted to Buffer and only the underlying
557                 // array is serialized, except for Null, in which case this state is kept.
558                 [SecurityPermissionAttribute(SecurityAction.LinkDemand,SerializationFormatter=true)]
559                 void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) {
560                         switch (m_state)
561                                 {
562                                 case SqlBytesCharsState.Null:
563                                         info.AddValue("IsNull", true);
564                                         break;
565
566                                 case SqlBytesCharsState.Buffer:
567                                         info.AddValue("IsNull", false);
568                                         info.AddValue("data", m_rgchBuf);
569                                         break;
570
571                                 case SqlBytesCharsState.Stream:
572                                         CopyStreamToBuffer();
573                                         goto case SqlBytesCharsState.Buffer;
574
575                                 default:
576                                         Debug.Assert(false);
577                                         goto case SqlBytesCharsState.Null;
578                                 }
579                         }
580
581                 // --------------------------------------------------------------
582                 //        Static fields, properties
583                 // --------------------------------------------------------------
584
585                 // Get a Null instance. 
586                 // Since SqlChars is mutable, have to be property and create a new one each time.
587                 public static SqlChars Null {
588                         get     {
589                                 return new SqlChars((char[])null);
590                         }
591                 }
592         } // class SqlChars
593
594         // StreamOnSqlChars is a stream build on top of SqlChars, and
595         // provides the Stream interface. The purpose is to help users
596         // to read/write SqlChars object. 
597         internal sealed class StreamOnSqlChars : SqlStreamChars
598                 {
599                 // --------------------------------------------------------------
600                 //        Data members
601                 // --------------------------------------------------------------
602
603                 private SqlChars        m_sqlchars;             // the SqlChars object 
604                 private long            m_lPosition;
605
606                 // --------------------------------------------------------------
607                 //        Constructor(s)
608                 // --------------------------------------------------------------
609
610                 internal StreamOnSqlChars(SqlChars s) {
611                         m_sqlchars = s;
612                         m_lPosition = 0;
613         }
614
615                 // --------------------------------------------------------------
616                 //        Public properties
617                 // --------------------------------------------------------------
618
619                 public override bool IsNull     {
620                         get     {
621                         return m_sqlchars == null || m_sqlchars.IsNull;
622             }
623         }
624
625                 // Always can read/write/seek, unless sb is null, 
626                 // which means the stream has been closed.
627                 public override bool CanRead {
628             get {
629                 return m_sqlchars != null && !m_sqlchars.IsNull;
630             }
631         }
632
633                 public override bool CanSeek {
634                         get     {
635                 return m_sqlchars != null;
636             }
637         }
638
639                 public override bool CanWrite {
640                         get {
641                                 return m_sqlchars != null && (!m_sqlchars.IsNull || m_sqlchars.m_rgchBuf != null);
642             }
643         }
644
645                 public override long Length     {
646                         get     {
647                                 CheckIfStreamClosed("get_Length");
648                                 return m_sqlchars.Length;
649             }
650         }
651
652                 public override long Position {
653                         get     {
654                                 CheckIfStreamClosed("get_Position");
655                                 return m_lPosition;
656             }
657                         set     {
658                                 CheckIfStreamClosed("set_Position");
659                                 if (value < 0 || value > m_sqlchars.Length)
660                                         throw new ArgumentOutOfRangeException("value");
661                                 else
662                                         m_lPosition = value;
663             }
664         }
665
666                 // --------------------------------------------------------------
667                 //        Public methods
668                 // --------------------------------------------------------------
669
670                 public override long Seek(long offset, SeekOrigin origin) {
671                         CheckIfStreamClosed("Seek");
672
673                         long lPosition = 0;
674
675                         switch(origin) {
676                                 case SeekOrigin.Begin:
677                                         if (offset < 0 || offset > m_sqlchars.Length)
678                                                 throw ADP.ArgumentOutOfRange("offset");
679                                         m_lPosition = offset;
680                                         break;
681                                         
682                                 case SeekOrigin.Current:
683                                         lPosition = m_lPosition + offset;
684                                         if (lPosition < 0 || lPosition > m_sqlchars.Length)
685                                                 throw ADP.ArgumentOutOfRange("offset");
686                                         m_lPosition = lPosition;
687                                         break;
688                                         
689                                 case SeekOrigin.End:
690                                         lPosition = m_sqlchars.Length + offset;
691                                         if (lPosition < 0 || lPosition > m_sqlchars.Length)
692                                                 throw ADP.ArgumentOutOfRange("offset");
693                                         m_lPosition = lPosition;
694                                         break;
695                                         
696                                 default:
697                     throw ADP.ArgumentOutOfRange("offset");;
698             }
699
700                         return m_lPosition;
701         }
702
703                 // The Read/Write/Readchar/Writechar simply delegates to SqlChars
704                 public override int Read(char[] buffer, int offset, int count) {
705                         CheckIfStreamClosed("Read");
706
707                         if (buffer==null)
708                                 throw new ArgumentNullException("buffer");
709                         if (offset < 0 || offset > buffer.Length)
710                                 throw new ArgumentOutOfRangeException("offset");
711                         if (count < 0 || count > buffer.Length - offset)
712                                 throw new ArgumentOutOfRangeException("count");
713
714                         int icharsRead = (int)m_sqlchars.Read(m_lPosition, buffer, offset, count);
715                         m_lPosition += icharsRead;
716
717                         return icharsRead;
718         }
719
720                 public override void Write(char[] buffer, int offset, int count) {
721                         CheckIfStreamClosed("Write");
722
723                         if (buffer==null)
724                                 throw new ArgumentNullException("buffer");
725                         if (offset < 0 || offset > buffer.Length)
726                                 throw new ArgumentOutOfRangeException("offset");
727                         if (count < 0 || count > buffer.Length - offset)
728                                 throw new ArgumentOutOfRangeException("count");
729
730                         m_sqlchars.Write(m_lPosition, buffer, offset, count);
731                         m_lPosition += count;
732         }
733
734                 public override int ReadChar() {
735                 CheckIfStreamClosed("ReadChar");
736
737                         // If at the end of stream, return -1, rather than call SqlChars.Readchar,
738                         // which will throw exception. This is the behavior for Stream.
739                         //
740                         if (m_lPosition >= m_sqlchars.Length)
741                                 return -1;
742
743                         int ret = m_sqlchars[m_lPosition];
744                         m_lPosition ++;
745                         return ret;
746         }
747
748                 public override void WriteChar(char value) {
749                         CheckIfStreamClosed("WriteChar");
750
751                         m_sqlchars[m_lPosition] = value;
752                         m_lPosition ++;
753         }
754
755                 public override void SetLength(long value) {
756                         CheckIfStreamClosed("SetLength");
757
758                         m_sqlchars.SetLength(value);
759                         if (m_lPosition > value)
760                                 m_lPosition = value;
761         }
762
763                 // Flush is a no-op if underlying SqlChars is not a stream on SqlChars
764                 public override void Flush() {
765                         if (m_sqlchars.FStream())
766                                 m_sqlchars.m_stream.Flush();
767         }
768
769         protected override void Dispose(bool disposing) {           
770                         // When m_sqlchars is null, it means the stream has been closed, and
771                         // any opearation in the future should fail.
772                         // This is the only case that m_sqlchars is null.
773                         m_sqlchars = null;
774         }
775
776                 // --------------------------------------------------------------
777                 //        Private utility functions
778                 // --------------------------------------------------------------
779
780                 private bool FClosed() {
781                         return m_sqlchars == null;
782         }
783
784         private void CheckIfStreamClosed(string methodname)     {
785                         if (FClosed())
786                 throw ADP.StreamClosed(methodname);
787         }
788     } // class StreamOnSqlChars
789 } // namespace System.Data.SqlTypes