Update Reference Sources to .NET Framework 4.6.1
[mono.git] / mcs / class / referencesource / System.Data / System / Data / SqlClient / SqlCachedBuffer.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="SqlCachedBuffer.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">[....]</owner>
6 // <owner current="true" primary="false">[....]</owner>
7 //------------------------------------------------------------------------------
8
9 namespace System.Data.SqlClient {
10
11     using System;
12     using System.Collections.Generic;
13     using System.ComponentModel;
14     using System.Data;
15     using System.Data.Common;
16     using System.Diagnostics;
17     using System.Globalization;
18     using System.Text;
19     using System.Xml;
20     using System.Data.SqlTypes;
21     using System.IO;
22     using System.Runtime.InteropServices;
23     using System.Reflection;
24     using System.Runtime.CompilerServices;
25
26     // Caches the bytes returned from partial length prefixed datatypes, like XML
27     sealed internal class SqlCachedBuffer : System.Data.SqlTypes.INullable{
28         public static readonly SqlCachedBuffer Null = new SqlCachedBuffer();
29         private const int _maxChunkSize = 2048; // Arbitrary value for chunk size. Revisit this later for better perf
30         
31         private List<byte[]> _cachedBytes;
32
33         private SqlCachedBuffer() {
34             // For constructing Null
35         }
36         
37         private SqlCachedBuffer(List<byte[]> cachedBytes) {
38             _cachedBytes = cachedBytes;
39         }
40
41         internal List<byte[]> CachedBytes {
42             get { return _cachedBytes;  }
43         }
44
45         // Reads off from the network buffer and caches bytes. Only reads one column value in the current row.
46         static internal bool TryCreate(SqlMetaDataPriv metadata, TdsParser parser, TdsParserStateObject stateObj, out SqlCachedBuffer buffer) {
47             int cb = 0;
48             ulong  plplength;
49             byte[] byteArr;
50             
51             List<byte[]> cachedBytes = new List<byte[]>();
52             buffer = null;
53
54             // the very first length is already read.
55             if (!parser.TryPlpBytesLeft(stateObj, out plplength)) {
56                 return false;
57             }
58             // For now we  only handle Plp data from the parser directly.
59             Debug.Assert(metadata.metaType.IsPlp, "SqlCachedBuffer call on a non-plp data");
60             do {
61                 if (plplength == 0) 
62                     break;
63                 do {
64                     cb = (plplength > (ulong) _maxChunkSize) ?  _maxChunkSize : (int)plplength ;
65                     byteArr = new byte[cb];
66                     if (!stateObj.TryReadPlpBytes(ref byteArr, 0, cb, out cb)) {
67                         return false;
68                     }
69                     Debug.Assert(cb == byteArr.Length);
70                     if (cachedBytes.Count == 0) {
71                         // Add the Byte order mark if needed if we read the first array
72                         AddByteOrderMark(byteArr, cachedBytes);
73                     }
74                     cachedBytes.Add(byteArr);
75                     plplength -= (ulong)cb;
76                 } while (plplength > 0);
77                 if (!parser.TryPlpBytesLeft(stateObj, out plplength)) {
78                     return false;
79                 }
80             } while (plplength > 0);                    
81             Debug.Assert(stateObj._longlen == 0 && stateObj._longlenleft == 0);
82
83             buffer = new SqlCachedBuffer(cachedBytes);
84             return true;
85         }
86
87         private static void AddByteOrderMark(byte[] byteArr, List<byte[]> cachedBytes) {
88             // Need to find out if we should add byte order mark or not. 
89             // We need to add this if we are getting ntext xml, not if we are getting binary xml
90             // Binary Xml always begins with the bytes 0xDF and 0xFF
91             // If we aren't getting these, then we are getting unicode xml
92             if ((byteArr.Length < 2 ) || (byteArr[0] != 0xDF) || (byteArr[1] != 0xFF)){
93                 Debug.Assert(cachedBytes.Count == 0);
94                 cachedBytes.Add(TdsEnums.XMLUNICODEBOMBYTES);
95             }
96         }
97         
98         internal Stream ToStream() {
99             return new SqlCachedStream(this);
100         }
101         
102         override public string ToString() {
103             if (IsNull)
104                 throw new SqlNullValueException();
105
106             if (_cachedBytes.Count == 0) {
107                 return String.Empty;
108             }
109             SqlXml   sxml = new SqlXml(ToStream());
110             return sxml.Value;
111         }
112
113         internal SqlString ToSqlString() {
114             if (IsNull)
115                 return SqlString.Null;
116             string str = ToString();
117             return new SqlString(str);
118         }
119
120         internal SqlXml ToSqlXml() {
121             SqlXml  sx = new SqlXml(ToStream());
122             return sx;
123         }
124
125         // Prevent inlining so that reflection calls are not moved to caller that may be in a different assembly that may have a different grant set.
126         [MethodImpl(MethodImplOptions.NoInlining)]
127         internal XmlReader ToXmlReader() {
128             //XmlTextReader xr = new XmlTextReader(fragment, XmlNodeType.Element, null);
129             XmlReaderSettings readerSettings = new XmlReaderSettings();
130             readerSettings.ConformanceLevel = ConformanceLevel.Fragment;
131
132             // Call internal XmlReader.CreateSqlReader from System.Xml.
133             // Signature: internal static XmlReader CreateSqlReader(Stream input, XmlReaderSettings settings, XmlParserContext inputContext);
134             MethodInfo createSqlReaderMethodInfo = typeof(System.Xml.XmlReader).GetMethod("CreateSqlReader", BindingFlags.Static | BindingFlags.NonPublic);
135             object[] args = new object[3] { ToStream(), readerSettings, null };
136             XmlReader xr;
137
138             new System.Security.Permissions.ReflectionPermission(System.Security.Permissions.ReflectionPermissionFlag.MemberAccess).Assert();
139             try {
140                 xr = (XmlReader)createSqlReaderMethodInfo.Invoke(null, args);
141             }
142             finally {
143                 System.Security.Permissions.ReflectionPermission.RevertAssert();
144             }
145             return xr;
146         }
147
148         public bool IsNull {
149             get {
150                 return (_cachedBytes == null) ? true : false ;
151             }
152         }
153
154     }
155     
156 }