File: System\Data\SqlClient\SqlBuffer.cs
Project: ndp\fx\src\data\System.Data.csproj (System.Data)
//------------------------------------------------------------------------------
// <copyright file="SqlDataReader.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>
// <owner current="true" primary="false">Microsoft</owner>
//------------------------------------------------------------------------------
 
namespace System.Data.SqlClient {
    using System.Threading;
    using System.Diagnostics;
    using System.Reflection;
    using System;
    using System.Data;
    using System.IO;
    using System.Collections;
    using System.Collections.Specialized;
    using System.Data.Sql;
    using System.Data.SqlTypes;
    using System.Data.Common;
    using System.Data.ProviderBase;
    using System.ComponentModel;
    using System.Globalization;
    using System.Xml;
    using System.Runtime.InteropServices;
 
    internal sealed class SqlBuffer {
 
        internal enum StorageType {
            Empty = 0,
            Boolean,
            Byte,
            DateTime,
            Decimal,
            Double,
            Int16,
            Int32,
            Int64,
            Money,
            Single,
            String,
            SqlBinary,
            SqlCachedBuffer,
            SqlGuid,
            SqlXml,
            Date,
            DateTime2,
            DateTimeOffset,
            Time,
        }
 
        internal struct DateTimeInfo {
            // This is used to store DateTime
            internal Int32 daypart;
            internal Int32 timepart;
        }
 
        internal struct NumericInfo {
            // This is used to store Decimal data
            internal Int32 data1;
            internal Int32 data2;
            internal Int32 data3;
            internal Int32 data4;
            internal Byte  precision;
            internal Byte  scale;
            internal Boolean positive;
        }
 
        internal struct TimeInfo {
            internal Int64 ticks;
            internal byte scale;
        }
 
        internal struct DateTime2Info {
            internal Int32 date;
            internal TimeInfo timeInfo;
        }
 
        internal struct DateTimeOffsetInfo {
            internal DateTime2Info dateTime2Info;
            internal Int16 offset;
        }        
 
        [StructLayout(LayoutKind.Explicit)]
        internal struct Storage {
            [FieldOffset(0)] internal Boolean       _boolean;
            [FieldOffset(0)] internal Byte          _byte;
            [FieldOffset(0)] internal DateTimeInfo  _dateTimeInfo;
            [FieldOffset(0)] internal Double        _double;
            [FieldOffset(0)] internal NumericInfo   _numericInfo;
            [FieldOffset(0)] internal Int16         _int16;
            [FieldOffset(0)] internal Int32         _int32;
            [FieldOffset(0)] internal Int64         _int64;     // also used to store Money, UtcDateTime, Date , and Time
            [FieldOffset(0)] internal Single        _single;
            [FieldOffset(0)] internal TimeInfo      _timeInfo;
            [FieldOffset(0)] internal DateTime2Info _dateTime2Info;
            [FieldOffset(0)] internal DateTimeOffsetInfo _dateTimeOffsetInfo;
        }
 
        private bool         _isNull;
        private StorageType  _type;
        private Storage      _value;
        private object       _object;    // String, SqlBinary, SqlCachedBuffer, SqlGuid, SqlString, SqlXml
 
        internal SqlBuffer() {
        }
 
        private SqlBuffer(SqlBuffer value) { // Clone
            // value types
            _isNull    = value._isNull;
            _type      = value._type;
            // ref types - should also be read only unless at some point we allow this data
            // to be mutable, then we will need to copy
            _value     = value._value;
            _object    = value._object;
        }
 
        internal bool IsEmpty {
            get {
                return (StorageType.Empty == _type);
            }
        }
 
        internal bool IsNull {
            get {
                return _isNull;
            }
        }
 
        internal StorageType VariantInternalStorageType
        {
            get { return _type; }
        }
 
        internal Boolean Boolean {
            get {
                ThrowIfNull();
 
                if (StorageType.Boolean == _type) {
                    return _value._boolean;
                }
                return (Boolean)this.Value; // anything else we haven't thought of goes through boxing.
            }
            set {
                Debug.Assert (IsEmpty, "setting value a second time?");
                _value._boolean = value;
                _type = StorageType.Boolean;
                _isNull = false;
            }
        }
 
        internal Byte Byte {
            get {
                ThrowIfNull();
 
                if (StorageType.Byte == _type) {
                    return _value._byte;
                }
                return (Byte)this.Value; // anything else we haven't thought of goes through boxing.
            }
            set {
                Debug.Assert (IsEmpty, "setting value a second time?");
                _value._byte = value;
                _type = StorageType.Byte;
                _isNull = false;
            }
        }
 
        internal Byte[] ByteArray {
            get {
                ThrowIfNull();
                return this.SqlBinary.Value;    // 
            }
        }
 
        internal DateTime DateTime {
            get {
                ThrowIfNull();
 
                if (StorageType.Date == _type) {
                    return DateTime.MinValue.AddDays(_value._int32);
                }
                if (StorageType.DateTime2 == _type) {
                    return new DateTime(GetTicksFromDateTime2Info(_value._dateTime2Info));
                }
                if (StorageType.DateTime == _type) {
                    return SqlDateTime.ToDateTime(_value._dateTimeInfo.daypart, _value._dateTimeInfo.timepart);
                }
                return (DateTime)this.Value; // anything else we haven't thought of goes through boxing.
            }
        }
 
        internal Decimal Decimal {
            get {
                ThrowIfNull();
 
                if (StorageType.Decimal == _type) {
                    if (_value._numericInfo.data4 != 0 || _value._numericInfo.scale > 28) {
                        throw new OverflowException(SQLResource.ConversionOverflowMessage);
                    }
                    return new Decimal(_value._numericInfo.data1, _value._numericInfo.data2, _value._numericInfo.data3, !_value._numericInfo.positive, _value._numericInfo.scale);
                }
                if (StorageType.Money == _type) {
                    long l = _value._int64;                    
                    bool isNegative = false;
                    if (l < 0) {                        
                        isNegative = true;
                        l = -l;
                    }
                    return new Decimal((int)(l & 0xffffffff), (int)(l >> 32), 0, isNegative, 4);
                }
                return (Decimal)this.Value; // anything else we haven't thought of goes through boxing.
            }
        }
 
        internal Double Double {
            get {
                ThrowIfNull();
 
                if (StorageType.Double == _type) {
                    return _value._double;
                }
                return (Double)this.Value; // anything else we haven't thought of goes through boxing.
            }
            set {
                Debug.Assert (IsEmpty, "setting value a second time?");
                _value._double = value;
                _type = StorageType.Double;
                _isNull = false;
            }
        }
 
        internal Guid Guid {
            get {
                // 
                ThrowIfNull();
                return this.SqlGuid.Value;
            }
        }
 
        internal Int16 Int16 {
            get {
                ThrowIfNull();
 
                if (StorageType.Int16 == _type) {
                    return _value._int16;
                }
                return (Int16)this.Value; // anything else we haven't thought of goes through boxing.
            }
            set {
                Debug.Assert (IsEmpty, "setting value a second time?");
                _value._int16 = value;
                _type = StorageType.Int16;
                _isNull = false;
            }
        }
 
        internal Int32 Int32 {
            get {
                ThrowIfNull();
 
                if (StorageType.Int32 == _type) {
                    return _value._int32;
                }
                return (Int32)this.Value; // anything else we haven't thought of goes through boxing.
            }
            set {
                Debug.Assert (IsEmpty, "setting value a second time?");
                _value._int32 = value;
                _type = StorageType.Int32;
                _isNull = false;
            }
        }
 
        internal Int64 Int64 {
            get {
                ThrowIfNull();
 
                if (StorageType.Int64 == _type) {
                    return _value._int64;
                }
                return (Int64)this.Value; // anything else we haven't thought of goes through boxing.
            }
            set {
                Debug.Assert (IsEmpty, "setting value a second time?");
                _value._int64 = value;
                _type = StorageType.Int64;
                _isNull = false;
            }
        }
 
        internal Single Single {
            get {
                ThrowIfNull();
 
                if (StorageType.Single == _type) {
                    return _value._single;
                }
                return (Single)this.Value; // anything else we haven't thought of goes through boxing.
            }
            set {
                Debug.Assert (IsEmpty, "setting value a second time?");
                _value._single = value;
                _type = StorageType.Single;
                _isNull = false;
            }
        }
 
        internal String String {
            get {
                ThrowIfNull();
 
                if (StorageType.String == _type) {
                    return (String)_object;
                }
                else if (StorageType.SqlCachedBuffer == _type) {
                    return ((SqlCachedBuffer)(_object)).ToString();
                }
                return (String)this.Value; // anything else we haven't thought of goes through boxing.
            }
        }
 
        // use static list of format strings indexed by scale for perf!
        private static string[] __katmaiDateTimeOffsetFormatByScale = new string[] {
                "yyyy-MM-dd HH:mm:ss zzz",
                "yyyy-MM-dd HH:mm:ss.f zzz",
                "yyyy-MM-dd HH:mm:ss.ff zzz",
                "yyyy-MM-dd HH:mm:ss.fff zzz",
                "yyyy-MM-dd HH:mm:ss.ffff zzz",
                "yyyy-MM-dd HH:mm:ss.fffff zzz",
                "yyyy-MM-dd HH:mm:ss.ffffff zzz",
                "yyyy-MM-dd HH:mm:ss.fffffff zzz",
        };
 
        private static string[] __katmaiDateTime2FormatByScale = new string[] {
                "yyyy-MM-dd HH:mm:ss",
                "yyyy-MM-dd HH:mm:ss.f",
                "yyyy-MM-dd HH:mm:ss.ff",
                "yyyy-MM-dd HH:mm:ss.fff",
                "yyyy-MM-dd HH:mm:ss.ffff",
                "yyyy-MM-dd HH:mm:ss.fffff",
                "yyyy-MM-dd HH:mm:ss.ffffff",
                "yyyy-MM-dd HH:mm:ss.fffffff",
        };
 
        private static string[] __katmaiTimeFormatByScale = new string[] {
                "HH:mm:ss",
                "HH:mm:ss.f",
                "HH:mm:ss.ff",
                "HH:mm:ss.fff",
                "HH:mm:ss.ffff",
                "HH:mm:ss.fffff",
                "HH:mm:ss.ffffff",
                "HH:mm:ss.fffffff",
        };
 
        internal string KatmaiDateTimeString {
            get {
                ThrowIfNull();
 
                if (StorageType.Date == _type) {
                    return this.DateTime.ToString("yyyy-MM-dd", DateTimeFormatInfo.InvariantInfo);
                }
                if (StorageType.Time == _type) {
                    byte scale = _value._timeInfo.scale;
                    return new DateTime(_value._timeInfo.ticks).ToString(__katmaiTimeFormatByScale[scale], DateTimeFormatInfo.InvariantInfo);
                }
                if (StorageType.DateTime2 == _type) {
                    byte scale = _value._dateTime2Info.timeInfo.scale;
                    return this.DateTime.ToString(__katmaiDateTime2FormatByScale[scale], DateTimeFormatInfo.InvariantInfo);
                }
                if (StorageType.DateTimeOffset == _type) {
                    DateTimeOffset dto = this.DateTimeOffset;
                    byte scale = _value._dateTimeOffsetInfo.dateTime2Info.timeInfo.scale;
                    return dto.ToString(__katmaiDateTimeOffsetFormatByScale[scale], DateTimeFormatInfo.InvariantInfo);
                }
                return (String)this.Value; // anything else we haven't thought of goes through boxing.
            }
        }
 
        internal SqlString KatmaiDateTimeSqlString {
            get {
                if (StorageType.Date == _type ||
                    StorageType.Time == _type ||
                    StorageType.DateTime2 == _type ||
                    StorageType.DateTimeOffset == _type) {
                    if (IsNull) {
                        return SqlString.Null;
                    }
                    return new SqlString(KatmaiDateTimeString);
                }
                return (SqlString)this.SqlValue; // anything else we haven't thought of goes through boxing.
            }
        }
 
        internal TimeSpan Time {
            get {
                ThrowIfNull();
 
                if (StorageType.Time == _type) {
                    return new TimeSpan(_value._timeInfo.ticks);
                }
 
                return (TimeSpan)this.Value; // anything else we haven't thought of goes through boxing.
            }
        }
 
        internal DateTimeOffset DateTimeOffset {
            get {
                ThrowIfNull();
 
                if (StorageType.DateTimeOffset == _type) {
                    TimeSpan offset = new TimeSpan(0, _value._dateTimeOffsetInfo.offset, 0);
                    // datetime part presents time in UTC
                    return new DateTimeOffset(GetTicksFromDateTime2Info(_value._dateTimeOffsetInfo.dateTime2Info) + offset.Ticks, offset);
                }
 
                return (DateTimeOffset)this.Value; // anything else we haven't thought of goes through boxing.
            }
        }
 
        private static long GetTicksFromDateTime2Info(DateTime2Info dateTime2Info) {
            return (dateTime2Info.date * TimeSpan.TicksPerDay + dateTime2Info.timeInfo.ticks);
        }
 
        internal SqlBinary SqlBinary {
            get {
                if (StorageType.SqlBinary == _type) {
                    return (SqlBinary)_object;
                }
                return (SqlBinary)this.SqlValue; // anything else we haven't thought of goes through boxing.
            }
            set {
                Debug.Assert (IsEmpty, "setting value a second time?");
                _object = value;
                _type = StorageType.SqlBinary;
                _isNull = value.IsNull;
            }
        }
 
        internal SqlBoolean SqlBoolean {
            get {
                if (StorageType.Boolean == _type) {
                    if (IsNull) {
                        return SqlBoolean.Null;
                    }
                    return new SqlBoolean(_value._boolean);
                }
                return (SqlBoolean)this.SqlValue; // anything else we haven't thought of goes through boxing.
            }
        }
 
        internal SqlByte SqlByte {
            get {
                if (StorageType.Byte == _type) {
                    if (IsNull) {
                        return SqlByte.Null;
                    }
                    return new SqlByte(_value._byte);
                }
                return (SqlByte)this.SqlValue; // anything else we haven't thought of goes through boxing.
            }
        }
 
        internal SqlCachedBuffer SqlCachedBuffer {
            get {
                if (StorageType.SqlCachedBuffer == _type) {
                    if (IsNull) {
                        return SqlCachedBuffer.Null;
                    }
                    return (SqlCachedBuffer)_object;
                }
                return (SqlCachedBuffer)this.SqlValue; // anything else we haven't thought of goes through boxing.
            }
            set {
                Debug.Assert (IsEmpty, "setting value a second time?");
                _object = value;
                _type = StorageType.SqlCachedBuffer;
                _isNull = value.IsNull;
            }
        }
 
        internal SqlXml SqlXml {
            get {
                if (StorageType.SqlXml == _type) {
                    if (IsNull) {
                        return SqlXml.Null;
                    }
                    return (SqlXml)_object;
                }
                return (SqlXml)this.SqlValue; // anything else we haven't thought of goes through boxing.
            }
            set {
                Debug.Assert (IsEmpty, "setting value a second time?");
                _object = value;
                _type = StorageType.SqlXml;
                _isNull = value.IsNull;
            }
        }
 
        internal SqlDateTime SqlDateTime {
            get {
                if (StorageType.DateTime == _type) {
                    if (IsNull) {
                        return SqlDateTime.Null;
                    }
                    return new SqlDateTime(_value._dateTimeInfo.daypart, _value._dateTimeInfo.timepart);
                }
                return (SqlDateTime)SqlValue; // anything else we haven't thought of goes through boxing.
            }
        }
 
        internal SqlDecimal SqlDecimal {
            get {
                if (StorageType.Decimal == _type) {
                    if (IsNull) {
                        return SqlDecimal.Null;
                    }
                    return new SqlDecimal(_value._numericInfo.precision,
                                          _value._numericInfo.scale,
                                          _value._numericInfo.positive,
                                          _value._numericInfo.data1,
                                          _value._numericInfo.data2,
                                          _value._numericInfo.data3,
                                          _value._numericInfo.data4
                                          );
                }
                return (SqlDecimal)this.SqlValue; // anything else we haven't thought of goes through boxing.
            }
        }
 
        internal SqlDouble SqlDouble {
            get {
                if (StorageType.Double == _type) {
                    if (IsNull) {
                        return SqlDouble.Null;
                    }
                    return new SqlDouble(_value._double);
                }
                return (SqlDouble)this.SqlValue; // anything else we haven't thought of goes through boxing.
            }
        }
 
        internal SqlGuid SqlGuid {
            get {
                if (StorageType.SqlGuid == _type) {
                    return (SqlGuid)_object;
                }
                return (SqlGuid)this.SqlValue; // anything else we haven't thought of goes through boxing.
            }
            set {
                Debug.Assert (IsEmpty, "setting value a second time?");
                _object = value;
                _type = StorageType.SqlGuid;
                _isNull = value.IsNull;
            }
        }
 
        internal SqlInt16 SqlInt16 {
            get {
                if (StorageType.Int16 == _type) {
                    if (IsNull) {
                        return SqlInt16.Null;
                    }
                    return new SqlInt16(_value._int16);
                }
                return (SqlInt16)this.SqlValue; // anything else we haven't thought of goes through boxing.
            }
        }
 
        internal SqlInt32 SqlInt32 {
            get {
                if (StorageType.Int32 == _type) {
                    if (IsNull) {
                        return SqlInt32.Null;
                    }
                    return new SqlInt32(_value._int32);
                }
                return (SqlInt32)this.SqlValue; // anything else we haven't thought of goes through boxing.
            }
        }
 
        internal SqlInt64 SqlInt64 {
            get {
                if (StorageType.Int64 == _type) {
                    if (IsNull) {
                        return SqlInt64.Null;
                    }
                    return new SqlInt64(_value._int64);
                }
                return (SqlInt64)this.SqlValue; // anything else we haven't thought of goes through boxing.
            }
        }
 
        internal SqlMoney SqlMoney {
            get {
                if (StorageType.Money == _type) {
                    if (IsNull) {
                        return SqlMoney.Null;
                    }
                    return new SqlMoney(_value._int64, 1/*ignored*/);
                }
                return (SqlMoney)this.SqlValue; // anything else we haven't thought of goes through boxing.
            }
        }
 
        internal SqlSingle SqlSingle {
            get {
                if (StorageType.Single == _type) {
                    if (IsNull) {
                        return SqlSingle.Null;
                    }
                    return new SqlSingle(_value._single);
                }
                return (SqlSingle)this.SqlValue; // anything else we haven't thought of goes through boxing.
            }
        }
 
        internal SqlString SqlString {
            get {
                if (StorageType.String == _type) {
                    if (IsNull) {
                        return SqlString.Null;
                    }
                    return new SqlString((String)_object);
 
                }
                else if (StorageType.SqlCachedBuffer == _type) {
                    SqlCachedBuffer data = (SqlCachedBuffer)(_object);
                    if (data.IsNull) {
                        return SqlString.Null;
                    }
                    return data.ToSqlString();
                }
                return (SqlString)this.SqlValue; // anything else we haven't thought of goes through boxing.
            }
        }
 
        internal object SqlValue {
            get {
                switch (_type) {
                    case StorageType.Empty:           return DBNull.Value;
                    case StorageType.Boolean:         return SqlBoolean;
                    case StorageType.Byte:            return SqlByte;
                    case StorageType.DateTime:        return SqlDateTime;
                    case StorageType.Decimal:         return SqlDecimal;
                    case StorageType.Double:          return SqlDouble;
                    case StorageType.Int16:           return SqlInt16;
                    case StorageType.Int32:           return SqlInt32;
                    case StorageType.Int64:           return SqlInt64;
                    case StorageType.Money:           return SqlMoney;
                    case StorageType.Single:          return SqlSingle;
                    case StorageType.String:          return SqlString;
                    case StorageType.SqlCachedBuffer:
                    {
                        SqlCachedBuffer data = (SqlCachedBuffer)(_object);
                        if (data.IsNull) {
                            return SqlXml.Null;
                        }
                        return data.ToSqlXml();
                    }
 
                    case StorageType.SqlBinary:
                    case StorageType.SqlGuid:
                        return _object;
 
                    case StorageType.SqlXml: {
                        if (_isNull) {
                            return SqlXml.Null;
                        }
                        Debug.Assert(null != _object);
                        return (SqlXml) _object;
                    }
                    case StorageType.Date:
                    case StorageType.DateTime2:
                        if (_isNull) {
                            return DBNull.Value;
                        }
                        return DateTime;
                    case StorageType.DateTimeOffset:
                        if (_isNull) {
                            return DBNull.Value;
                        }
                        return DateTimeOffset;
                    case StorageType.Time:
                        if (_isNull) {
                            return DBNull.Value;
                        }
                        return Time;
                }
                return null; // need to return the value as an object of some SQL type
            }
        }
 
        internal object Value {
            get {
                if (IsNull) {
                    return DBNull.Value;
                }
                switch (_type) {
                    case StorageType.Empty:           return DBNull.Value;
                    case StorageType.Boolean:         return Boolean;
                    case StorageType.Byte:            return Byte;
                    case StorageType.DateTime:        return DateTime;
                    case StorageType.Decimal:         return Decimal;
                    case StorageType.Double:          return Double;
                    case StorageType.Int16:           return Int16;
                    case StorageType.Int32:           return Int32;
                    case StorageType.Int64:           return Int64;
                    case StorageType.Money:           return Decimal;
                    case StorageType.Single:          return Single;
                    case StorageType.String:          return String;
                    case StorageType.SqlBinary:       return ByteArray;
                    case StorageType.SqlCachedBuffer:
                    {
                        // If we have a CachedBuffer, it's because it's an XMLTYPE column
                        // and we have to return a string when they're asking for the CLS
                        // value of the column.
                        return ((SqlCachedBuffer)(_object)).ToString();
                    }
                    case StorageType.SqlGuid:         return Guid;
                    case StorageType.SqlXml: {
                        // XMLTYPE columns must be returned as string when asking for the CLS value
                        SqlXml data = (SqlXml)_object;
                        string s = data.Value;
                        return s;
                    }
                    case StorageType.Date:            return DateTime;
                    case StorageType.DateTime2:       return DateTime;
                    case StorageType.DateTimeOffset:  return DateTimeOffset;
                    case StorageType.Time:            return Time;
                }
                return null; // need to return the value as an object of some CLS type
            }
        }
 
        internal Type GetTypeFromStorageType (bool isSqlType) {            
            if (isSqlType) {
                switch (_type) {
                    case SqlBuffer.StorageType.Empty:           return null;
                    case SqlBuffer.StorageType.Boolean:         return typeof(SqlBoolean);
                    case SqlBuffer.StorageType.Byte:            return typeof(SqlByte);
                    case SqlBuffer.StorageType.DateTime:        return typeof(SqlDateTime);
                    case SqlBuffer.StorageType.Decimal:         return typeof(SqlDecimal);
                    case SqlBuffer.StorageType.Double:          return typeof(SqlDouble);
                    case SqlBuffer.StorageType.Int16:           return typeof(SqlInt16);
                    case SqlBuffer.StorageType.Int32:           return typeof(SqlInt32);
                    case SqlBuffer.StorageType.Int64:           return typeof(SqlInt64);
                    case SqlBuffer.StorageType.Money:           return typeof(SqlMoney);
                    case SqlBuffer.StorageType.Single:          return typeof(SqlSingle);
                    case SqlBuffer.StorageType.String:          return typeof(SqlString);
                    case SqlBuffer.StorageType.SqlCachedBuffer: return typeof(SqlString);
                    case SqlBuffer.StorageType.SqlBinary:       return typeof(object);
                    case SqlBuffer.StorageType.SqlGuid:         return typeof(object);
                    case SqlBuffer.StorageType.SqlXml:          return typeof(SqlXml);
                }                    
            }
            else { //Is CLR Type
                switch (_type) {
                    case SqlBuffer.StorageType.Empty:           return null;
                    case SqlBuffer.StorageType.Boolean:         return typeof(Boolean);
                    case SqlBuffer.StorageType.Byte:            return typeof(Byte);
                    case SqlBuffer.StorageType.DateTime:        return typeof(DateTime);
                    case SqlBuffer.StorageType.Decimal:         return typeof(Decimal);
                    case SqlBuffer.StorageType.Double:          return typeof(Double);
                    case SqlBuffer.StorageType.Int16:           return typeof(Int16);
                    case SqlBuffer.StorageType.Int32:           return typeof(Int32);
                    case SqlBuffer.StorageType.Int64:           return typeof(Int64);
                    case SqlBuffer.StorageType.Money:           return typeof(Decimal);
                    case SqlBuffer.StorageType.Single:          return typeof(Single);
                    case SqlBuffer.StorageType.String:          return typeof(String);
                    case SqlBuffer.StorageType.SqlBinary:       return typeof(Byte[]);
                    case SqlBuffer.StorageType.SqlCachedBuffer: return typeof(string);
                    case SqlBuffer.StorageType.SqlGuid:         return typeof(Guid);
                    case SqlBuffer.StorageType.SqlXml:          return typeof(string);
                }       
            }
 
            return null; // need to return the value as an object of some CLS type            
        }
 
        internal static SqlBuffer[] CreateBufferArray(int length) {
            SqlBuffer[] buffers = new SqlBuffer[length];
            for(int i = 0; i < buffers.Length; ++i) {
                buffers[i] = new SqlBuffer();
            }
            return buffers;
        }
 
        internal static SqlBuffer[] CloneBufferArray(SqlBuffer[] values) {
            SqlBuffer[] copy = new SqlBuffer[values.Length];
            for (int i=0; i<values.Length; i++) {
                copy[i] = new SqlBuffer(values[i]);
            }
            return copy;
        }
 
        internal static void Clear(SqlBuffer[] values) {
            if (null != values) {
                for(int i = 0; i < values.Length; ++i) {
                    values[i].Clear();
                }
            }
        }
 
        internal void Clear() {
            _isNull = false;
            _type = StorageType.Empty;
            _object = null;
        }
 
        internal void SetToDateTime(int daypart, int timepart) {
            Debug.Assert (IsEmpty, "setting value a second time?");
            _value._dateTimeInfo.daypart = daypart;
            _value._dateTimeInfo.timepart = timepart;
            _type = StorageType.DateTime;
            _isNull = false;
        }
 
        internal void SetToDecimal(byte precision, byte scale, bool positive, int[] bits) {
            Debug.Assert (IsEmpty, "setting value a second time?");
            _value._numericInfo.precision = precision;
            _value._numericInfo.scale = scale;
            _value._numericInfo.positive = positive;
            _value._numericInfo.data1 = bits[0];
            _value._numericInfo.data2 = bits[1];
            _value._numericInfo.data3 = bits[2];
            _value._numericInfo.data4 = bits[3];
            _type = StorageType.Decimal;
            _isNull = false;
        }
 
        internal void SetToMoney(long value) {
            Debug.Assert (IsEmpty, "setting value a second time?");
            _value._int64 = value;
            _type = StorageType.Money;
            _isNull = false;
        }
 
        internal void SetToNullOfType(StorageType storageType) {
            Debug.Assert (IsEmpty, "setting value a second time?");
            _type = storageType;
            _isNull = true;
            _object = null;
        }
 
        internal void SetToString(string value) {
            Debug.Assert (IsEmpty, "setting value a second time?");
            _object = value;
            _type = StorageType.String;
            _isNull = false;
        }
 
        internal void SetToDate(byte[] bytes) {
            Debug.Assert(IsEmpty, "setting value a second time?");
 
            _type = StorageType.Date;
            _value._int32 = GetDateFromByteArray(bytes, 0);
            _isNull = false;
        }
 
        internal void SetToDate(DateTime date) {
            Debug.Assert(IsEmpty, "setting value a second time?");
 
            _type = StorageType.Date;
            _value._int32 = date.Subtract(DateTime.MinValue).Days;
            _isNull = false;
        }
 
        internal void SetToTime(byte[] bytes, int length, byte scale, byte denormalizedScale) {
            Debug.Assert(IsEmpty, "setting value a second time?");
 
            _type = StorageType.Time;
            FillInTimeInfo(ref _value._timeInfo, bytes, length, scale, denormalizedScale);
            _isNull = false;
        }
 
        internal void SetToTime(TimeSpan timeSpan, byte scale) {
            Debug.Assert(IsEmpty, "setting value a second time?");
 
            _type = StorageType.Time;
            _value._timeInfo.ticks = timeSpan.Ticks;
            _value._timeInfo.scale = scale;
            _isNull = false;
        }
 
        internal void SetToDateTime2(byte[] bytes, int length, byte scale, byte denormalizedScale) {
            Debug.Assert(IsEmpty, "setting value a second time?");
 
            _type = StorageType.DateTime2;
            FillInTimeInfo(ref _value._dateTime2Info.timeInfo, bytes, length - 3, scale, denormalizedScale); // remaining 3 bytes is for date
            _value._dateTime2Info.date = GetDateFromByteArray(bytes, length - 3); // 3 bytes for date
            _isNull = false;
        }
 
        internal void SetToDateTime2(DateTime dateTime, byte scale) {
            Debug.Assert(IsEmpty, "setting value a second time?");
 
            _type = StorageType.DateTime2;
            _value._dateTime2Info.timeInfo.ticks = dateTime.TimeOfDay.Ticks;
            _value._dateTime2Info.timeInfo.scale = scale;
            _value._dateTime2Info.date = dateTime.Subtract(DateTime.MinValue).Days;
            _isNull = false;
        }
 
        internal void SetToDateTimeOffset(byte[] bytes, int length, byte scale, byte denormalizedScale) {
            Debug.Assert(IsEmpty, "setting value a second time?");
 
            _type = StorageType.DateTimeOffset;
            FillInTimeInfo(ref _value._dateTimeOffsetInfo.dateTime2Info.timeInfo, bytes, length - 5, scale, denormalizedScale); // remaining 5 bytes are for date and offset
            _value._dateTimeOffsetInfo.dateTime2Info.date = GetDateFromByteArray(bytes, length - 5); // 3 bytes for date
            _value._dateTimeOffsetInfo.offset = (Int16)(bytes[length - 2] + (bytes[length - 1] << 8)); // 2 bytes for offset (Int16)
            _isNull = false;
        }
 
        internal void SetToDateTimeOffset(DateTimeOffset dateTimeOffset, byte scale) {
            Debug.Assert(IsEmpty, "setting value a second time?");
 
            _type = StorageType.DateTimeOffset;
            DateTime utcDateTime = dateTimeOffset.UtcDateTime; // timeInfo stores the utc datetime of a datatimeoffset
            _value._dateTimeOffsetInfo.dateTime2Info.timeInfo.ticks = utcDateTime.TimeOfDay.Ticks;
            _value._dateTimeOffsetInfo.dateTime2Info.timeInfo.scale = scale;
            _value._dateTimeOffsetInfo.dateTime2Info.date = utcDateTime.Subtract(DateTime.MinValue).Days;
            _value._dateTimeOffsetInfo.offset = (Int16)dateTimeOffset.Offset.TotalMinutes;
            _isNull = false;
        }
 
        private static void FillInTimeInfo(ref TimeInfo timeInfo, byte[] timeBytes, int length, byte scale, byte denormalizedScale) {
            Debug.Assert(3 <= length && length <= 5, "invalid data length for timeInfo: " + length);
            Debug.Assert(0 <= scale && scale <= 7, "invalid scale: " + scale);
            Debug.Assert(0 <= denormalizedScale && denormalizedScale <= 7, "invalid denormalized scale: " + denormalizedScale);
 
            Int64 tickUnits = (Int64)timeBytes[0] + ((Int64)timeBytes[1] << 8) + ((Int64)timeBytes[2] << 16);
            if (length > 3) {
                tickUnits += ((Int64)timeBytes[3] << 24);
            }
            if (length > 4) {
                tickUnits += ((Int64)timeBytes[4] << 32);
            }
            timeInfo.ticks = tickUnits * TdsEnums.TICKS_FROM_SCALE[scale];
 
            // Once the deserialization has been completed using the value scale, we need to set the actual denormalized scale, 
            // coming from the data type, on the original result, so that it has the proper scale setting.
            // This only applies for values that got serialized/deserialized for encryption. Otherwise, both scales should be equal.
            timeInfo.scale = denormalizedScale;
        }
 
        private static Int32 GetDateFromByteArray(byte[] buf, int offset) {
            return buf[offset] + (buf[offset + 1] << 8) + (buf[offset + 2] << 16);
        }
 
        private void ThrowIfNull() {
            if (IsNull) {
                throw new SqlNullValueException();
            }
        }
    }
}// namespace