|
//------------------------------------------------------------------------------
// <copyright file="SqlCachedBuffer.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">Microsoft</owner>
// <owner current="true" primary="false">Microsoft</owner>
//------------------------------------------------------------------------------
namespace System.Data.SqlClient {
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.Common;
using System.Diagnostics;
using System.Globalization;
using System.Text;
using System.Xml;
using System.Data.SqlTypes;
using System.IO;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Runtime.CompilerServices;
// Caches the bytes returned from partial length prefixed datatypes, like XML
sealed internal class SqlCachedBuffer : System.Data.SqlTypes.INullable{
public static readonly SqlCachedBuffer Null = new SqlCachedBuffer();
private const int _maxChunkSize = 2048; // Arbitrary value for chunk size. Revisit this later for better perf
private List<byte[]> _cachedBytes;
private SqlCachedBuffer() {
// For constructing Null
}
private SqlCachedBuffer(List<byte[]> cachedBytes) {
_cachedBytes = cachedBytes;
}
internal List<byte[]> CachedBytes {
get { return _cachedBytes; }
}
// Reads off from the network buffer and caches bytes. Only reads one column value in the current row.
static internal bool TryCreate(SqlMetaDataPriv metadata, TdsParser parser, TdsParserStateObject stateObj, out SqlCachedBuffer buffer) {
int cb = 0;
ulong plplength;
byte[] byteArr;
List<byte[]> cachedBytes = new List<byte[]>();
buffer = null;
// the very first length is already read.
if (!parser.TryPlpBytesLeft(stateObj, out plplength)) {
return false;
}
// For now we only handle Plp data from the parser directly.
Debug.Assert(metadata.metaType.IsPlp, "SqlCachedBuffer call on a non-plp data");
do {
if (plplength == 0)
break;
do {
cb = (plplength > (ulong) _maxChunkSize) ? _maxChunkSize : (int)plplength ;
byteArr = new byte[cb];
if (!stateObj.TryReadPlpBytes(ref byteArr, 0, cb, out cb)) {
return false;
}
Debug.Assert(cb == byteArr.Length);
if (cachedBytes.Count == 0) {
// Add the Byte order mark if needed if we read the first array
AddByteOrderMark(byteArr, cachedBytes);
}
cachedBytes.Add(byteArr);
plplength -= (ulong)cb;
} while (plplength > 0);
if (!parser.TryPlpBytesLeft(stateObj, out plplength)) {
return false;
}
} while (plplength > 0);
Debug.Assert(stateObj._longlen == 0 && stateObj._longlenleft == 0);
buffer = new SqlCachedBuffer(cachedBytes);
return true;
}
private static void AddByteOrderMark(byte[] byteArr, List<byte[]> cachedBytes) {
// Need to find out if we should add byte order mark or not.
// We need to add this if we are getting ntext xml, not if we are getting binary xml
// Binary Xml always begins with the bytes 0xDF and 0xFF
// If we aren't getting these, then we are getting unicode xml
if ((byteArr.Length < 2 ) || (byteArr[0] != 0xDF) || (byteArr[1] != 0xFF)){
Debug.Assert(cachedBytes.Count == 0);
cachedBytes.Add(TdsEnums.XMLUNICODEBOMBYTES);
}
}
internal Stream ToStream() {
return new SqlCachedStream(this);
}
override public string ToString() {
if (IsNull)
throw new SqlNullValueException();
if (_cachedBytes.Count == 0) {
return String.Empty;
}
SqlXml sxml = new SqlXml(ToStream());
return sxml.Value;
}
internal SqlString ToSqlString() {
if (IsNull)
return SqlString.Null;
string str = ToString();
return new SqlString(str);
}
internal SqlXml ToSqlXml() {
SqlXml sx = new SqlXml(ToStream());
return sx;
}
// 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.
[MethodImpl(MethodImplOptions.NoInlining)]
internal XmlReader ToXmlReader() {
//XmlTextReader xr = new XmlTextReader(fragment, XmlNodeType.Element, null);
XmlReaderSettings readerSettings = new XmlReaderSettings();
readerSettings.ConformanceLevel = ConformanceLevel.Fragment;
// Call internal XmlReader.CreateSqlReader from System.Xml.
// Signature: internal static XmlReader CreateSqlReader(Stream input, XmlReaderSettings settings, XmlParserContext inputContext);
MethodInfo createSqlReaderMethodInfo = typeof(System.Xml.XmlReader).GetMethod("CreateSqlReader", BindingFlags.Static | BindingFlags.NonPublic);
object[] args = new object[3] { ToStream(), readerSettings, null };
XmlReader xr;
new System.Security.Permissions.ReflectionPermission(System.Security.Permissions.ReflectionPermissionFlag.MemberAccess).Assert();
try {
xr = (XmlReader)createSqlReaderMethodInfo.Invoke(null, args);
}
finally {
System.Security.Permissions.ReflectionPermission.RevertAssert();
}
return xr;
}
public bool IsNull {
get {
return (_cachedBytes == null) ? true : false ;
}
}
}
}
|