NativeBuffer.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / whidbey / NetFxQFE / ndp / fx / src / DataOracleClient / System / Data / OracleClient / NativeBuffer.cs / 1 / NativeBuffer.cs

                            //------------------------------------------------------------------------------ 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// [....] 
//-----------------------------------------------------------------------------
 
namespace System.Data.OracleClient 
{
    using System; 
    using System.Data.Common;
    using System.Data.ProviderBase;
    using System.Diagnostics;
    using System.Runtime.CompilerServices; 
    using System.Runtime.ConstrainedExecution;
    using System.Runtime.InteropServices; 
 
    internal class NativeBuffer : DbBuffer {
        public NativeBuffer (int initialSize, bool zeroBuffer) : base(initialSize, zeroBuffer) { 
        }

        public NativeBuffer (int initialSize) : base(initialSize, false) {
        } 

        internal IntPtr DangerousGetDataPtr() { 
            //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 
            // NOTE: You must have called DangerousAddRef before calling this
            //       method, or you run the risk of allowing Handle Recycling 
            //       to occur!
            //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            return DangerousGetHandle();
        } 

        internal IntPtr DangerousGetDataPtr(int offset) { 
            //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 
            // NOTE: You must have called DangerousAddRef before calling this
            //       method, or you run the risk of allowing Handle Recycling 
            //       to occur!
            //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            return ADP.IntPtrOffset(DangerousGetHandle(), offset);
        } 

        //Version of DangerousGetDataPtr that takes the supplied offset and adds BaseOffset 
        //to take into account buffers that are associated with a DataReader and may have multiple rows 
        //SQLBU:440832
        internal IntPtr DangerousGetDataPtrWithBaseOffset(int offset) 
        {
            //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            // NOTE: You must have called DangerousAddRef before calling this
            //       method, or you run the risk of allowing Handle Recycling 
            //       to occur!
            //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 
            return ADP.IntPtrOffset(DangerousGetHandle(), offset + BaseOffset); 
        }
 
        static internal IntPtr HandleValueToTrace ( NativeBuffer buffer ) {
            return buffer.DangerousGetHandle();  // for tracing purposes, it's safe to just print this -- no handle recycling issues.
        }
 
        internal string PtrToStringAnsi(int offset) {
            offset += BaseOffset; 
            Validate(offset, 1); 
            Debug.Assert(0 == offset%ADP.PtrSize, "invalid alignment");
 
            string value = null;
            bool   mustRelease = false;
            RuntimeHelpers.PrepareConstrainedRegions();
            try { 
                DangerousAddRef(ref mustRelease);
 
                IntPtr ptr = ADP.IntPtrOffset(DangerousGetHandle(), offset); 
                int length = UnsafeNativeMethods.lstrlenA(ptr);
                value = Marshal.PtrToStringAnsi(ptr, length); 
                Validate(offset, length+1);
            }
            finally {
                if (mustRelease) { 
                    DangerousRelease();
                } 
            } 

            return value; 
        }

        internal string PtrToStringAnsi(int offset, int length) {
            offset += BaseOffset; 
            Validate(offset, length);
            Debug.Assert(0 == offset%ADP.PtrSize, "invalid alignment"); 
 
            string value = null;
            bool   mustRelease = false; 
            RuntimeHelpers.PrepareConstrainedRegions();
            try {
                DangerousAddRef(ref mustRelease);
 
                IntPtr ptr = ADP.IntPtrOffset(DangerousGetHandle(), offset);
                value = Marshal.PtrToStringAnsi(ptr, length); 
            } 
            finally {
                if (mustRelease) { 
                    DangerousRelease();
                }
            }
 
            return value;
        } 
 
        internal object PtrToStructure(int offset, Type oftype) {
            Debug.Assert(null != oftype, "null stucture type"); 
            offset += BaseOffset;
            ValidateCheck(offset, Marshal.SizeOf(oftype));

            object value = null; 
            bool   mustRelease = false;
            RuntimeHelpers.PrepareConstrainedRegions(); 
            try { 
                DangerousAddRef(ref mustRelease);
 
                IntPtr ptr = ADP.IntPtrOffset(DangerousGetHandle(), offset);
                value = Marshal.PtrToStructure(ptr, oftype);
            }
            finally { 
                if (mustRelease) {
                    DangerousRelease(); 
                } 
            }
            return value; 
        }

        static internal void SafeDispose (ref NativeBuffer_LongColumnData handle) {
            if (handle != null) { 
                handle.Dispose();
            } 
            handle = null; 
        }
    } 

    sealed internal class NativeBuffer_Exception : NativeBuffer {
        internal NativeBuffer_Exception(int initialSize) : base(initialSize) {}
    } 

    sealed internal class NativeBuffer_LongColumnData : NativeBuffer { 
        //////////////////////////////////////////////////////////////////////// 
        //  This buffer is used to manage out-of-line bindings for large data.
        // 
        //  It is initialized to contain only a single ptr to start with, which
        //  is the head of a singly linked list of chunks of data.
        //
        //      base.handle -> head-of-list -> chunk1 -> chunk2 -> ... -> chunkn -> null 
        //
        //  All chunks are allocated with the same size, except for the head-of-list chunk which 
        //  is just the size of the Reserved portion.  The first ReservedSize bytes 
        //  of each chunk contain a pointer to the next chunk (and the last chunk points
        //  to null. (IntPtr.Zero) ). Usually, Oracle driver will fill the chunks up to 
        //  allocated size, also in some cases it does not do so (for example, when dealing with Unicode characters).
        //  Thus we need to save per chunk the actual number of bytes copied by the driver.
        //
        //  Fix for RFC50002189: 
        //  The next 4 bytes indicate either the state or how many bytes of this chunk are filled by oracle.
        //        -2: (ChunkIsFree) this chunk can be reused 
        //        -1: (ChunkToBeFilled) this chunk is to be used by Oracle driver 
        //    length: count of bytes written to buffer
        //  Note that when this chunk is used, the length can be 
        //  less or equal than the MaxChunkSize (Oracle driver may only partially fill the buffer).
        //  See RFC50002189 for more details...
        //
        //  The GetChunk() method will reliably add a chunk to the end of the 
        //  list, or re-use a chunk that was previously allocated.
        // 
        //  The Reset() method will mark all of the chunks that have been 
        //  allocated as unused (putting ChunkIsFree in length offset) and will reset the buffer
        //  back to it's original state so you can re-use it. 
        //
        //  There are two static CopyOutOfLine...() methods that allow you to
        //  copy the data in the buffer to an array of bytes or an array of
        //  chars. 
        ////////////////////////////////////////////////////////////////////////
 
        // special values for length offset 

        private const int ChunkIsFree        = -2;  // the chunk is free, can be reused 

        private const int ChunkToBeFilled    = -1;  // This state is temporary - chunk is marked with it when it
                                                    // is allocated or recycled, but before the chunk is actually
                                                    // filled with data. We provide this field to Oracle Driver and 
                                                    // it replaces this value with the actual length of written data
 
        private IntPtr _currentChunk = IntPtr.Zero; // ptr to the current chunk (the last one we allocated) 
        private int    _chunkCount = 0;             // how many chunks we've allocated
 
        static private  readonly int AllocationSize   = 8184;                     // just enough to allow us to fit on two pages
        static private  readonly int ReservedSize     = (2 * IntPtr.Size);        // the next pointer, the used indicator/length, some extra space for alignment
        static internal readonly int MaxChunkSize        = AllocationSize - ReservedSize;
        static private  readonly int LengthOrIndicatorOffset = IntPtr.Size;       // offset that holds length/usage indicator value 

        static private readonly OutOfMemoryException OutOfMemory = new OutOfMemoryException(); 
 
        internal NativeBuffer_LongColumnData() : base(ReservedSize) {
            // NOTE: we just allocate enough in the buffer for a pointer, so we 
            //       can store the pointer to our first chunk there, it seems odd,
            //       but we can eliminate special case logic when we want to get
            //       a new chunk or read the values.
            RuntimeHelpers.PrepareConstrainedRegions(); 
            try {} finally {
                _currentChunk = base.handle; 
                Marshal.WriteIntPtr(_currentChunk, 0, IntPtr.Zero); // initialize the forward pointer 
                Marshal.WriteInt32(_currentChunk, LengthOrIndicatorOffset, ChunkIsFree); // mark this chunk as being unused.
            } 
        }

        /// 
        /// gets the total length, in bytes, which is sum of all the chunks 
        /// 
        internal int TotalLengthInBytes { 
            get { 
                // Sum the lengths starting with the first chunk.
                // Note that the first buffer is just a pointer to the first real chunk. 
                // The _chunkCount and the actual list of chunks should be in [....]
                IntPtr ptr = base.handle;
                int cbTotal = 0;
                for (int i = 0; i < _chunkCount; i++) { 
                    ptr = Marshal.ReadIntPtr(ptr);
                    if (ptr == IntPtr.Zero) { 
                        Debug.Assert(false, "Chunks count is not updated - next chunk is zero!"); 
                        throw ADP.InternalError(ADP.InternalErrorCode.InvalidLongBuffer);
                    } 

                    int cbCurrentChunkSize = Marshal.ReadInt32(ptr, LengthOrIndicatorOffset);
                    if (cbCurrentChunkSize <= 0) {
                        Debug.Assert(false, "Free or to-be-filled chunk should not be reflected in the chunk count!"); 
                        throw ADP.InternalError(ADP.InternalErrorCode.InvalidLongBuffer);
                    } 
 
                    if (cbCurrentChunkSize > MaxChunkSize)
                    { 
                        Debug.Assert(false, "The buffer is overflowed.");
                        throw ADP.InternalError(ADP.InternalErrorCode.InvalidLongBuffer);
                    }
 
                    // all is fine, sum this size
                    cbTotal = checked(cbTotal + cbCurrentChunkSize); 
                } 

                return cbTotal; 
            }
        }

        // Devnote: This method and CopyOutOfLineChars have identical logic -- keep them in [....] 
        static internal void CopyOutOfLineBytes(IntPtr longBuffer, int cbSourceOffset, byte[] destinationBuffer, int cbDestinationOffset, int cbCount ) {
            if (IntPtr.Zero == longBuffer) { 
                throw ADP.ArgumentNull("longBuffer"); 
            }
 
            // Marks the offset of the previous chunk, relative to the chunk that contains source offset.
            // Note that the chunks may not be contiguous but this gives us a way to figure out how
            // many chunks we have to iterate through until we find the one we need to read from.
            // Starts from 0 because we will use the while loop to increment this value to the appropriate level. 
            int cbPreviousChunkOffset = 0;
 
            int cbRemaining = cbCount; 
            while (cbRemaining > 0) {
                // Each time through the loop, we will move ahead to the 
                // next buffer in the list. (Note that the first buffer is
                // just a pointer to the first real chunk)
                longBuffer = Marshal.ReadIntPtr(longBuffer);
 
                if (IntPtr.Zero == longBuffer) {
                    throw ADP.InternalError(ADP.InternalErrorCode.InvalidLongBuffer); 
                } 

                int cbCurrentChunkSize = Marshal.ReadInt32(longBuffer, LengthOrIndicatorOffset); 
                // Ensure that this buffer was filled by oracle driver.
                // Negative values (ChunkIsFree or ChunkToBeFilled) are not allowed
                // because we expect Oracle drver to put the actual length here, in bytes.
                // Also, we do not expect Oracle to put here 0 or size bigger than available, 
                // than add protection for both cases to be sure we do not overflow
                if (cbCurrentChunkSize <= 0 || cbCurrentChunkSize > MaxChunkSize) { 
                    throw ADP.InternalError(ADP.InternalErrorCode.InvalidLongBuffer); 
                }
 
                // Offset from which to start copying in the current chunk
                int cbCopyFromOffsetInChunk = cbSourceOffset - cbPreviousChunkOffset;

                // If cbCopyFromOffsetInChunk is somehow negative here, we can never get out of 
                // this loop anyway, because that means cbSourceOffset is already less than cbPreviousChunkOffset
                // and cbSourceOffset cannot increase by more than cbPreviousChunkOffset can while in this loop 
                Debug.Assert(cbCopyFromOffsetInChunk >= 0, "cbCopyFromOffsetInChunk should never be negative."); 

                if (cbCopyFromOffsetInChunk < cbCurrentChunkSize) { 
                    // We never want to read more than cbRemaining, but we may have to read even less
                    // than that if reading that many bytes would cause us to read past the end of the current chunk.
                    // cbPreviousChunkOffset marks where the previous chunk ends, and we need to know
                    // the end of the current one, thus adding current chunk size first. 
                    int cbToCopy = Math.Min(cbRemaining, cbPreviousChunkOffset + cbCurrentChunkSize - cbSourceOffset);
 
                    // Once we've found where we need to start copying from, 
                    // and how much data we can copy from this chunk,
                    // then we can start copying data.  Remember, the first 
                    // ReservedSize bytes of each block are the forward pointer
                    // to the next block and the in-use indicator, so we take
                    // that into account when computing the source pointer.
                    Marshal.Copy( 
                        ADP.IntPtrOffset(longBuffer, (cbCopyFromOffsetInChunk + ReservedSize)),
                        destinationBuffer, 
                        cbDestinationOffset, 
                        cbToCopy);
 
                    // We've copied everything out of this block, advance
                    // everything to point to where we want to start next.
                    cbSourceOffset += cbToCopy;
                    cbDestinationOffset += cbToCopy; 
                    cbRemaining -= cbToCopy;
                } 
 
                // Don't forget to move to the next chunk in case we
                // are not done reading data in this loop 
                cbPreviousChunkOffset += cbCurrentChunkSize;
            }
        }
 
        // Devnote: This method and CopyOutOfLineBytes have identical logic -- keep them in [....]
        static internal void CopyOutOfLineChars(IntPtr longBuffer, int cchSourceOffset, char[] destinationBuffer, int cchDestinationOffset, int cchCount ) { 
            if (IntPtr.Zero == longBuffer) { 
                throw ADP.ArgumentNull("longBuffer");
            } 

            // Marks the offset of the previous chunk, relative to the chunk that contains source offset.
            // Note that the chunks may not be contiguous but this gives us a way to figure out how
            // many chunks we have to iterate through until we find the one we need to read from. 
            // Starts from 0 because we will use the while loop to increment this value to the appropriate level.
            int cchPreviousChunkOffset = 0; 
 
            int cchRemaining = cchCount;
            while (cchRemaining > 0) { 
                // Each time through the loop, we will move ahead to the
                // next buffer in the list. (Note that the first buffer is
                // just a pointer to the first real chunk)
                longBuffer = Marshal.ReadIntPtr(longBuffer); 

                if (IntPtr.Zero == longBuffer) { 
                    throw ADP.InternalError(ADP.InternalErrorCode.InvalidLongBuffer); 
                }
 
                // get the chunk size (which is always saved in bytes)
                int cbCurrentChunkSize = Marshal.ReadInt32(longBuffer, LengthOrIndicatorOffset);

                // Ensure that this buffer was filled by oracle driver. 
                // Negative values (ChunkIsFree or ChunkToBeFilled) are not allowed
                // because we expect Oracle drver to put the actual length here, in bytes. 
                // Also, we do not expect Oracle to put here 0 or size bigger than available, 
                // than add protection for both cases to be sure we do not overflow
                if (cbCurrentChunkSize <= 0 || cbCurrentChunkSize > MaxChunkSize) { 
                    throw ADP.InternalError(ADP.InternalErrorCode.InvalidLongBuffer);
                }

                // we do not expect the unicode buffer to have odd size, so raise cast exception in this case 
                if ((cbCurrentChunkSize & 0x1) != 0) {
                    throw ADP.InvalidCast(); 
                } 

                // we need the size in UTF16 characters in order to copy the buffer 
                int cchCurrentChunkSize = cbCurrentChunkSize / 2;

                // Offset from which to start copying in the current chunk
                int cchCopyFromOffsetInChunk = cchSourceOffset - cchPreviousChunkOffset; 

                // If cchCopyFromOffsetInChunk is somehow negative here, we can never get out of 
                // this loop anyway, because that means sourceOffset is already less than cchPreviousChunkOffset 
                // and cchSourceOffset cannot increase by more than cchPreviousChunkOffset can while in this loop
                Debug.Assert(cchCopyFromOffsetInChunk >= 0, "cchCopyFromOffsetInChunk should never be negative."); 

                if (cchCopyFromOffsetInChunk < cchCurrentChunkSize) {
                    // We never want to read more than cchRemaining, but we may have to read even less
                    // than that if reading that many chars would cause us to read past the end of the current chunk. 
                    // cchPreviousChunkOffset marks where the previous chunk ends, and we need to know
                    // the end of the current one, thus adding cchCurrentChunkSize first. 
                    int cchToCopy = Math.Min(cchRemaining, cchPreviousChunkOffset + cchCurrentChunkSize - cchSourceOffset); 

                    // Once we've found where we need to start copying from, 
                    // and how much data we can copy from this chunk,
                    // then we can start copying data.  Remember, the first
                    // ReservedSize bytes of each block are the forward pointer
                    // to the next block and the in-use indicator, so we take 
                    // that into account when computing the source pointer.
                    Marshal.Copy( 
                        ADP.IntPtrOffset(longBuffer, ((cchCopyFromOffsetInChunk * ADP.CharSize) + ReservedSize)), 
                        destinationBuffer,
                        cchDestinationOffset, 
                        cchToCopy);

                    // We've copied everything out of this block, advance
                    // everything to point to where we want to start next. 
                    cchSourceOffset += cchToCopy;
                    cchDestinationOffset += cchToCopy; 
                    cchRemaining -= cchToCopy; 
                }
 
                // Don't forget to move to the next chunk in case we
                // are not done reading data in this loop
                cchPreviousChunkOffset += cchCurrentChunkSize;
            } 
        }
 
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] 
        internal IntPtr GetChunk(out IntPtr lengthPtr) {
            //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 
            // NOTE: You must have called DangerousAddRef before calling this
            //       method, or you run the risk of allowing Handle Recycling
            //       to occur!
            //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 
            IntPtr newChunkPtr = Marshal.ReadIntPtr(_currentChunk);
 
            RuntimeHelpers.PrepareConstrainedRegions(); 
            try {} finally {
                // we may have be reusing a chunk from a previous use of this 
                // buffer...
                if (IntPtr.Zero == newChunkPtr) {
                    // not reusing -- need to allocate
                    newChunkPtr = SafeNativeMethods.LocalAlloc(LMEM_FIXED, (IntPtr)(AllocationSize));// allocate the new chunk 

                    if (IntPtr.Zero != newChunkPtr) { 
                        Marshal.WriteIntPtr(newChunkPtr, IntPtr.Zero);      // make sure the next pointer is nulled out. 
                        Marshal.WriteIntPtr(_currentChunk, newChunkPtr);    // make the last chunk point to our new chunk
                    } 
                }

                // Regardless of how we got the current chunk, we have to mark
                // it as the current one, and we have to indicate that it's 
                // being used and update the rest of the stuff we track.
                if (IntPtr.Zero != newChunkPtr) { 
                    Marshal.WriteInt32(newChunkPtr, LengthOrIndicatorOffset, ChunkToBeFilled); // mark this chunk as 'to-be-filled by oracle driver' 
                    _currentChunk = newChunkPtr;                        // make the current chunk point to the new chunk too.
                    _chunkCount++;                                      // increment our count. 
                }
            }

            if (IntPtr.Zero == newChunkPtr) { 
                throw new OutOfMemoryException();
            } 
            lengthPtr = ADP.IntPtrOffset(newChunkPtr, LengthOrIndicatorOffset); 
            return ADP.IntPtrOffset(newChunkPtr, ReservedSize);     // return a pointer just past the reserved space.
        } 

        override protected bool ReleaseHandle() {
            // NOTE: The SafeHandle class guarantees this will be called exactly once.
            IntPtr ptr = base.handle; 
            while (IntPtr.Zero != ptr) {
                IntPtr nextPtr = Marshal.ReadIntPtr(ptr); 
                SafeNativeMethods.LocalFree(ptr); 
                ptr = nextPtr;
            } 
            return true;
        }

        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] 
        internal void Reset() {
            // This method will reset the current chunk to point to the head-of-list 
            // pointer.  It can be used to allow you to re-use the buffer without 
            // having to re-allocate all it's chunks.
            bool mustRelease = false; 
            RuntimeHelpers.PrepareConstrainedRegions();
            try {
                DangerousAddRef(ref mustRelease);
 
                RuntimeHelpers.PrepareConstrainedRegions();
                try {} finally { 
                    IntPtr ptr = _currentChunk = base.handle; 
                    while (IntPtr.Zero != ptr) {
                        IntPtr nextPtr = Marshal.ReadIntPtr(ptr); 
                        Marshal.WriteInt32(ptr, LengthOrIndicatorOffset, ChunkIsFree);   // mark this chunk as being unused.
                        ptr = nextPtr;
                    }
                    _chunkCount = 0; 
                }
            } 
            finally { 
                if (mustRelease) {
                    DangerousRelease(); 
                }
            }
        }
    } 

    sealed internal class NativeBuffer_ParameterBuffer : NativeBuffer { 
        internal NativeBuffer_ParameterBuffer(int initialSize) : base(initialSize, true) {} 
    }
 
    sealed internal class NativeBuffer_RowBuffer : NativeBuffer {

        private int _numberOfRows; // maximum number of elements in the array
        private int _rowLength;  // 
        private bool _ready; // only true when we've got something in the buffer
 
        internal NativeBuffer_RowBuffer(int initialSize, int numberOfRows) : base(initialSize * numberOfRows, false) { 
            _rowLength = initialSize;
            _numberOfRows = numberOfRows; 
        }

        internal bool CurrentPositionIsValid {
            get { 
                bool value = (BaseOffset >= 0) && (BaseOffset < (NumberOfRows*RowLength));
                return value; 
            } 
        }
 
        internal int NumberOfRows {
            get {
                return _numberOfRows;
            } 
            set {
                if ( (value < 0) || (Length < (value * RowLength)) ) { 
                    throw ADP.InternalError(ADP.InternalErrorCode.InvalidNumberOfRows); 
                }
                _numberOfRows = value; 
            }
        }

        internal int  RowLength { 
            get {
                return _rowLength; 
            } 
        }
 
        internal void MoveFirst() {
            BaseOffset = 0; // Position at the first element in the array...
            _ready = true;
        } 

        internal bool MoveNext() { 
            if (!_ready) { 
                return false;
            } 

            BaseOffset += RowLength;
            return CurrentPositionIsValid;
        } 

        internal bool MovePrevious() { 
            if (!_ready) { 
                return false;
            } 

            if (BaseOffset <= -RowLength) {  // allow positioning before the first element (for HasRows support)...
                return false;
            } 
            BaseOffset -= RowLength;
            return true; 
        } 
    }
 
    sealed internal class NativeBuffer_ScratchBuffer : NativeBuffer {
        internal NativeBuffer_ScratchBuffer(int initialSize) : base(initialSize) {}
    }
 
    sealed internal class NativeBuffer_ServerVersion : NativeBuffer {
        internal NativeBuffer_ServerVersion(int initialSize) : base(initialSize) {} 
    } 
}
 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//------------------------------------------------------------------------------ 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// [....] 
//-----------------------------------------------------------------------------
 
namespace System.Data.OracleClient 
{
    using System; 
    using System.Data.Common;
    using System.Data.ProviderBase;
    using System.Diagnostics;
    using System.Runtime.CompilerServices; 
    using System.Runtime.ConstrainedExecution;
    using System.Runtime.InteropServices; 
 
    internal class NativeBuffer : DbBuffer {
        public NativeBuffer (int initialSize, bool zeroBuffer) : base(initialSize, zeroBuffer) { 
        }

        public NativeBuffer (int initialSize) : base(initialSize, false) {
        } 

        internal IntPtr DangerousGetDataPtr() { 
            //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 
            // NOTE: You must have called DangerousAddRef before calling this
            //       method, or you run the risk of allowing Handle Recycling 
            //       to occur!
            //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            return DangerousGetHandle();
        } 

        internal IntPtr DangerousGetDataPtr(int offset) { 
            //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 
            // NOTE: You must have called DangerousAddRef before calling this
            //       method, or you run the risk of allowing Handle Recycling 
            //       to occur!
            //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            return ADP.IntPtrOffset(DangerousGetHandle(), offset);
        } 

        //Version of DangerousGetDataPtr that takes the supplied offset and adds BaseOffset 
        //to take into account buffers that are associated with a DataReader and may have multiple rows 
        //SQLBU:440832
        internal IntPtr DangerousGetDataPtrWithBaseOffset(int offset) 
        {
            //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            // NOTE: You must have called DangerousAddRef before calling this
            //       method, or you run the risk of allowing Handle Recycling 
            //       to occur!
            //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 
            return ADP.IntPtrOffset(DangerousGetHandle(), offset + BaseOffset); 
        }
 
        static internal IntPtr HandleValueToTrace ( NativeBuffer buffer ) {
            return buffer.DangerousGetHandle();  // for tracing purposes, it's safe to just print this -- no handle recycling issues.
        }
 
        internal string PtrToStringAnsi(int offset) {
            offset += BaseOffset; 
            Validate(offset, 1); 
            Debug.Assert(0 == offset%ADP.PtrSize, "invalid alignment");
 
            string value = null;
            bool   mustRelease = false;
            RuntimeHelpers.PrepareConstrainedRegions();
            try { 
                DangerousAddRef(ref mustRelease);
 
                IntPtr ptr = ADP.IntPtrOffset(DangerousGetHandle(), offset); 
                int length = UnsafeNativeMethods.lstrlenA(ptr);
                value = Marshal.PtrToStringAnsi(ptr, length); 
                Validate(offset, length+1);
            }
            finally {
                if (mustRelease) { 
                    DangerousRelease();
                } 
            } 

            return value; 
        }

        internal string PtrToStringAnsi(int offset, int length) {
            offset += BaseOffset; 
            Validate(offset, length);
            Debug.Assert(0 == offset%ADP.PtrSize, "invalid alignment"); 
 
            string value = null;
            bool   mustRelease = false; 
            RuntimeHelpers.PrepareConstrainedRegions();
            try {
                DangerousAddRef(ref mustRelease);
 
                IntPtr ptr = ADP.IntPtrOffset(DangerousGetHandle(), offset);
                value = Marshal.PtrToStringAnsi(ptr, length); 
            } 
            finally {
                if (mustRelease) { 
                    DangerousRelease();
                }
            }
 
            return value;
        } 
 
        internal object PtrToStructure(int offset, Type oftype) {
            Debug.Assert(null != oftype, "null stucture type"); 
            offset += BaseOffset;
            ValidateCheck(offset, Marshal.SizeOf(oftype));

            object value = null; 
            bool   mustRelease = false;
            RuntimeHelpers.PrepareConstrainedRegions(); 
            try { 
                DangerousAddRef(ref mustRelease);
 
                IntPtr ptr = ADP.IntPtrOffset(DangerousGetHandle(), offset);
                value = Marshal.PtrToStructure(ptr, oftype);
            }
            finally { 
                if (mustRelease) {
                    DangerousRelease(); 
                } 
            }
            return value; 
        }

        static internal void SafeDispose (ref NativeBuffer_LongColumnData handle) {
            if (handle != null) { 
                handle.Dispose();
            } 
            handle = null; 
        }
    } 

    sealed internal class NativeBuffer_Exception : NativeBuffer {
        internal NativeBuffer_Exception(int initialSize) : base(initialSize) {}
    } 

    sealed internal class NativeBuffer_LongColumnData : NativeBuffer { 
        //////////////////////////////////////////////////////////////////////// 
        //  This buffer is used to manage out-of-line bindings for large data.
        // 
        //  It is initialized to contain only a single ptr to start with, which
        //  is the head of a singly linked list of chunks of data.
        //
        //      base.handle -> head-of-list -> chunk1 -> chunk2 -> ... -> chunkn -> null 
        //
        //  All chunks are allocated with the same size, except for the head-of-list chunk which 
        //  is just the size of the Reserved portion.  The first ReservedSize bytes 
        //  of each chunk contain a pointer to the next chunk (and the last chunk points
        //  to null. (IntPtr.Zero) ). Usually, Oracle driver will fill the chunks up to 
        //  allocated size, also in some cases it does not do so (for example, when dealing with Unicode characters).
        //  Thus we need to save per chunk the actual number of bytes copied by the driver.
        //
        //  Fix for RFC50002189: 
        //  The next 4 bytes indicate either the state or how many bytes of this chunk are filled by oracle.
        //        -2: (ChunkIsFree) this chunk can be reused 
        //        -1: (ChunkToBeFilled) this chunk is to be used by Oracle driver 
        //    length: count of bytes written to buffer
        //  Note that when this chunk is used, the length can be 
        //  less or equal than the MaxChunkSize (Oracle driver may only partially fill the buffer).
        //  See RFC50002189 for more details...
        //
        //  The GetChunk() method will reliably add a chunk to the end of the 
        //  list, or re-use a chunk that was previously allocated.
        // 
        //  The Reset() method will mark all of the chunks that have been 
        //  allocated as unused (putting ChunkIsFree in length offset) and will reset the buffer
        //  back to it's original state so you can re-use it. 
        //
        //  There are two static CopyOutOfLine...() methods that allow you to
        //  copy the data in the buffer to an array of bytes or an array of
        //  chars. 
        ////////////////////////////////////////////////////////////////////////
 
        // special values for length offset 

        private const int ChunkIsFree        = -2;  // the chunk is free, can be reused 

        private const int ChunkToBeFilled    = -1;  // This state is temporary - chunk is marked with it when it
                                                    // is allocated or recycled, but before the chunk is actually
                                                    // filled with data. We provide this field to Oracle Driver and 
                                                    // it replaces this value with the actual length of written data
 
        private IntPtr _currentChunk = IntPtr.Zero; // ptr to the current chunk (the last one we allocated) 
        private int    _chunkCount = 0;             // how many chunks we've allocated
 
        static private  readonly int AllocationSize   = 8184;                     // just enough to allow us to fit on two pages
        static private  readonly int ReservedSize     = (2 * IntPtr.Size);        // the next pointer, the used indicator/length, some extra space for alignment
        static internal readonly int MaxChunkSize        = AllocationSize - ReservedSize;
        static private  readonly int LengthOrIndicatorOffset = IntPtr.Size;       // offset that holds length/usage indicator value 

        static private readonly OutOfMemoryException OutOfMemory = new OutOfMemoryException(); 
 
        internal NativeBuffer_LongColumnData() : base(ReservedSize) {
            // NOTE: we just allocate enough in the buffer for a pointer, so we 
            //       can store the pointer to our first chunk there, it seems odd,
            //       but we can eliminate special case logic when we want to get
            //       a new chunk or read the values.
            RuntimeHelpers.PrepareConstrainedRegions(); 
            try {} finally {
                _currentChunk = base.handle; 
                Marshal.WriteIntPtr(_currentChunk, 0, IntPtr.Zero); // initialize the forward pointer 
                Marshal.WriteInt32(_currentChunk, LengthOrIndicatorOffset, ChunkIsFree); // mark this chunk as being unused.
            } 
        }

        /// 
        /// gets the total length, in bytes, which is sum of all the chunks 
        /// 
        internal int TotalLengthInBytes { 
            get { 
                // Sum the lengths starting with the first chunk.
                // Note that the first buffer is just a pointer to the first real chunk. 
                // The _chunkCount and the actual list of chunks should be in [....]
                IntPtr ptr = base.handle;
                int cbTotal = 0;
                for (int i = 0; i < _chunkCount; i++) { 
                    ptr = Marshal.ReadIntPtr(ptr);
                    if (ptr == IntPtr.Zero) { 
                        Debug.Assert(false, "Chunks count is not updated - next chunk is zero!"); 
                        throw ADP.InternalError(ADP.InternalErrorCode.InvalidLongBuffer);
                    } 

                    int cbCurrentChunkSize = Marshal.ReadInt32(ptr, LengthOrIndicatorOffset);
                    if (cbCurrentChunkSize <= 0) {
                        Debug.Assert(false, "Free or to-be-filled chunk should not be reflected in the chunk count!"); 
                        throw ADP.InternalError(ADP.InternalErrorCode.InvalidLongBuffer);
                    } 
 
                    if (cbCurrentChunkSize > MaxChunkSize)
                    { 
                        Debug.Assert(false, "The buffer is overflowed.");
                        throw ADP.InternalError(ADP.InternalErrorCode.InvalidLongBuffer);
                    }
 
                    // all is fine, sum this size
                    cbTotal = checked(cbTotal + cbCurrentChunkSize); 
                } 

                return cbTotal; 
            }
        }

        // Devnote: This method and CopyOutOfLineChars have identical logic -- keep them in [....] 
        static internal void CopyOutOfLineBytes(IntPtr longBuffer, int cbSourceOffset, byte[] destinationBuffer, int cbDestinationOffset, int cbCount ) {
            if (IntPtr.Zero == longBuffer) { 
                throw ADP.ArgumentNull("longBuffer"); 
            }
 
            // Marks the offset of the previous chunk, relative to the chunk that contains source offset.
            // Note that the chunks may not be contiguous but this gives us a way to figure out how
            // many chunks we have to iterate through until we find the one we need to read from.
            // Starts from 0 because we will use the while loop to increment this value to the appropriate level. 
            int cbPreviousChunkOffset = 0;
 
            int cbRemaining = cbCount; 
            while (cbRemaining > 0) {
                // Each time through the loop, we will move ahead to the 
                // next buffer in the list. (Note that the first buffer is
                // just a pointer to the first real chunk)
                longBuffer = Marshal.ReadIntPtr(longBuffer);
 
                if (IntPtr.Zero == longBuffer) {
                    throw ADP.InternalError(ADP.InternalErrorCode.InvalidLongBuffer); 
                } 

                int cbCurrentChunkSize = Marshal.ReadInt32(longBuffer, LengthOrIndicatorOffset); 
                // Ensure that this buffer was filled by oracle driver.
                // Negative values (ChunkIsFree or ChunkToBeFilled) are not allowed
                // because we expect Oracle drver to put the actual length here, in bytes.
                // Also, we do not expect Oracle to put here 0 or size bigger than available, 
                // than add protection for both cases to be sure we do not overflow
                if (cbCurrentChunkSize <= 0 || cbCurrentChunkSize > MaxChunkSize) { 
                    throw ADP.InternalError(ADP.InternalErrorCode.InvalidLongBuffer); 
                }
 
                // Offset from which to start copying in the current chunk
                int cbCopyFromOffsetInChunk = cbSourceOffset - cbPreviousChunkOffset;

                // If cbCopyFromOffsetInChunk is somehow negative here, we can never get out of 
                // this loop anyway, because that means cbSourceOffset is already less than cbPreviousChunkOffset
                // and cbSourceOffset cannot increase by more than cbPreviousChunkOffset can while in this loop 
                Debug.Assert(cbCopyFromOffsetInChunk >= 0, "cbCopyFromOffsetInChunk should never be negative."); 

                if (cbCopyFromOffsetInChunk < cbCurrentChunkSize) { 
                    // We never want to read more than cbRemaining, but we may have to read even less
                    // than that if reading that many bytes would cause us to read past the end of the current chunk.
                    // cbPreviousChunkOffset marks where the previous chunk ends, and we need to know
                    // the end of the current one, thus adding current chunk size first. 
                    int cbToCopy = Math.Min(cbRemaining, cbPreviousChunkOffset + cbCurrentChunkSize - cbSourceOffset);
 
                    // Once we've found where we need to start copying from, 
                    // and how much data we can copy from this chunk,
                    // then we can start copying data.  Remember, the first 
                    // ReservedSize bytes of each block are the forward pointer
                    // to the next block and the in-use indicator, so we take
                    // that into account when computing the source pointer.
                    Marshal.Copy( 
                        ADP.IntPtrOffset(longBuffer, (cbCopyFromOffsetInChunk + ReservedSize)),
                        destinationBuffer, 
                        cbDestinationOffset, 
                        cbToCopy);
 
                    // We've copied everything out of this block, advance
                    // everything to point to where we want to start next.
                    cbSourceOffset += cbToCopy;
                    cbDestinationOffset += cbToCopy; 
                    cbRemaining -= cbToCopy;
                } 
 
                // Don't forget to move to the next chunk in case we
                // are not done reading data in this loop 
                cbPreviousChunkOffset += cbCurrentChunkSize;
            }
        }
 
        // Devnote: This method and CopyOutOfLineBytes have identical logic -- keep them in [....]
        static internal void CopyOutOfLineChars(IntPtr longBuffer, int cchSourceOffset, char[] destinationBuffer, int cchDestinationOffset, int cchCount ) { 
            if (IntPtr.Zero == longBuffer) { 
                throw ADP.ArgumentNull("longBuffer");
            } 

            // Marks the offset of the previous chunk, relative to the chunk that contains source offset.
            // Note that the chunks may not be contiguous but this gives us a way to figure out how
            // many chunks we have to iterate through until we find the one we need to read from. 
            // Starts from 0 because we will use the while loop to increment this value to the appropriate level.
            int cchPreviousChunkOffset = 0; 
 
            int cchRemaining = cchCount;
            while (cchRemaining > 0) { 
                // Each time through the loop, we will move ahead to the
                // next buffer in the list. (Note that the first buffer is
                // just a pointer to the first real chunk)
                longBuffer = Marshal.ReadIntPtr(longBuffer); 

                if (IntPtr.Zero == longBuffer) { 
                    throw ADP.InternalError(ADP.InternalErrorCode.InvalidLongBuffer); 
                }
 
                // get the chunk size (which is always saved in bytes)
                int cbCurrentChunkSize = Marshal.ReadInt32(longBuffer, LengthOrIndicatorOffset);

                // Ensure that this buffer was filled by oracle driver. 
                // Negative values (ChunkIsFree or ChunkToBeFilled) are not allowed
                // because we expect Oracle drver to put the actual length here, in bytes. 
                // Also, we do not expect Oracle to put here 0 or size bigger than available, 
                // than add protection for both cases to be sure we do not overflow
                if (cbCurrentChunkSize <= 0 || cbCurrentChunkSize > MaxChunkSize) { 
                    throw ADP.InternalError(ADP.InternalErrorCode.InvalidLongBuffer);
                }

                // we do not expect the unicode buffer to have odd size, so raise cast exception in this case 
                if ((cbCurrentChunkSize & 0x1) != 0) {
                    throw ADP.InvalidCast(); 
                } 

                // we need the size in UTF16 characters in order to copy the buffer 
                int cchCurrentChunkSize = cbCurrentChunkSize / 2;

                // Offset from which to start copying in the current chunk
                int cchCopyFromOffsetInChunk = cchSourceOffset - cchPreviousChunkOffset; 

                // If cchCopyFromOffsetInChunk is somehow negative here, we can never get out of 
                // this loop anyway, because that means sourceOffset is already less than cchPreviousChunkOffset 
                // and cchSourceOffset cannot increase by more than cchPreviousChunkOffset can while in this loop
                Debug.Assert(cchCopyFromOffsetInChunk >= 0, "cchCopyFromOffsetInChunk should never be negative."); 

                if (cchCopyFromOffsetInChunk < cchCurrentChunkSize) {
                    // We never want to read more than cchRemaining, but we may have to read even less
                    // than that if reading that many chars would cause us to read past the end of the current chunk. 
                    // cchPreviousChunkOffset marks where the previous chunk ends, and we need to know
                    // the end of the current one, thus adding cchCurrentChunkSize first. 
                    int cchToCopy = Math.Min(cchRemaining, cchPreviousChunkOffset + cchCurrentChunkSize - cchSourceOffset); 

                    // Once we've found where we need to start copying from, 
                    // and how much data we can copy from this chunk,
                    // then we can start copying data.  Remember, the first
                    // ReservedSize bytes of each block are the forward pointer
                    // to the next block and the in-use indicator, so we take 
                    // that into account when computing the source pointer.
                    Marshal.Copy( 
                        ADP.IntPtrOffset(longBuffer, ((cchCopyFromOffsetInChunk * ADP.CharSize) + ReservedSize)), 
                        destinationBuffer,
                        cchDestinationOffset, 
                        cchToCopy);

                    // We've copied everything out of this block, advance
                    // everything to point to where we want to start next. 
                    cchSourceOffset += cchToCopy;
                    cchDestinationOffset += cchToCopy; 
                    cchRemaining -= cchToCopy; 
                }
 
                // Don't forget to move to the next chunk in case we
                // are not done reading data in this loop
                cchPreviousChunkOffset += cchCurrentChunkSize;
            } 
        }
 
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] 
        internal IntPtr GetChunk(out IntPtr lengthPtr) {
            //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 
            // NOTE: You must have called DangerousAddRef before calling this
            //       method, or you run the risk of allowing Handle Recycling
            //       to occur!
            //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 
            IntPtr newChunkPtr = Marshal.ReadIntPtr(_currentChunk);
 
            RuntimeHelpers.PrepareConstrainedRegions(); 
            try {} finally {
                // we may have be reusing a chunk from a previous use of this 
                // buffer...
                if (IntPtr.Zero == newChunkPtr) {
                    // not reusing -- need to allocate
                    newChunkPtr = SafeNativeMethods.LocalAlloc(LMEM_FIXED, (IntPtr)(AllocationSize));// allocate the new chunk 

                    if (IntPtr.Zero != newChunkPtr) { 
                        Marshal.WriteIntPtr(newChunkPtr, IntPtr.Zero);      // make sure the next pointer is nulled out. 
                        Marshal.WriteIntPtr(_currentChunk, newChunkPtr);    // make the last chunk point to our new chunk
                    } 
                }

                // Regardless of how we got the current chunk, we have to mark
                // it as the current one, and we have to indicate that it's 
                // being used and update the rest of the stuff we track.
                if (IntPtr.Zero != newChunkPtr) { 
                    Marshal.WriteInt32(newChunkPtr, LengthOrIndicatorOffset, ChunkToBeFilled); // mark this chunk as 'to-be-filled by oracle driver' 
                    _currentChunk = newChunkPtr;                        // make the current chunk point to the new chunk too.
                    _chunkCount++;                                      // increment our count. 
                }
            }

            if (IntPtr.Zero == newChunkPtr) { 
                throw new OutOfMemoryException();
            } 
            lengthPtr = ADP.IntPtrOffset(newChunkPtr, LengthOrIndicatorOffset); 
            return ADP.IntPtrOffset(newChunkPtr, ReservedSize);     // return a pointer just past the reserved space.
        } 

        override protected bool ReleaseHandle() {
            // NOTE: The SafeHandle class guarantees this will be called exactly once.
            IntPtr ptr = base.handle; 
            while (IntPtr.Zero != ptr) {
                IntPtr nextPtr = Marshal.ReadIntPtr(ptr); 
                SafeNativeMethods.LocalFree(ptr); 
                ptr = nextPtr;
            } 
            return true;
        }

        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] 
        internal void Reset() {
            // This method will reset the current chunk to point to the head-of-list 
            // pointer.  It can be used to allow you to re-use the buffer without 
            // having to re-allocate all it's chunks.
            bool mustRelease = false; 
            RuntimeHelpers.PrepareConstrainedRegions();
            try {
                DangerousAddRef(ref mustRelease);
 
                RuntimeHelpers.PrepareConstrainedRegions();
                try {} finally { 
                    IntPtr ptr = _currentChunk = base.handle; 
                    while (IntPtr.Zero != ptr) {
                        IntPtr nextPtr = Marshal.ReadIntPtr(ptr); 
                        Marshal.WriteInt32(ptr, LengthOrIndicatorOffset, ChunkIsFree);   // mark this chunk as being unused.
                        ptr = nextPtr;
                    }
                    _chunkCount = 0; 
                }
            } 
            finally { 
                if (mustRelease) {
                    DangerousRelease(); 
                }
            }
        }
    } 

    sealed internal class NativeBuffer_ParameterBuffer : NativeBuffer { 
        internal NativeBuffer_ParameterBuffer(int initialSize) : base(initialSize, true) {} 
    }
 
    sealed internal class NativeBuffer_RowBuffer : NativeBuffer {

        private int _numberOfRows; // maximum number of elements in the array
        private int _rowLength;  // 
        private bool _ready; // only true when we've got something in the buffer
 
        internal NativeBuffer_RowBuffer(int initialSize, int numberOfRows) : base(initialSize * numberOfRows, false) { 
            _rowLength = initialSize;
            _numberOfRows = numberOfRows; 
        }

        internal bool CurrentPositionIsValid {
            get { 
                bool value = (BaseOffset >= 0) && (BaseOffset < (NumberOfRows*RowLength));
                return value; 
            } 
        }
 
        internal int NumberOfRows {
            get {
                return _numberOfRows;
            } 
            set {
                if ( (value < 0) || (Length < (value * RowLength)) ) { 
                    throw ADP.InternalError(ADP.InternalErrorCode.InvalidNumberOfRows); 
                }
                _numberOfRows = value; 
            }
        }

        internal int  RowLength { 
            get {
                return _rowLength; 
            } 
        }
 
        internal void MoveFirst() {
            BaseOffset = 0; // Position at the first element in the array...
            _ready = true;
        } 

        internal bool MoveNext() { 
            if (!_ready) { 
                return false;
            } 

            BaseOffset += RowLength;
            return CurrentPositionIsValid;
        } 

        internal bool MovePrevious() { 
            if (!_ready) { 
                return false;
            } 

            if (BaseOffset <= -RowLength) {  // allow positioning before the first element (for HasRows support)...
                return false;
            } 
            BaseOffset -= RowLength;
            return true; 
        } 
    }
 
    sealed internal class NativeBuffer_ScratchBuffer : NativeBuffer {
        internal NativeBuffer_ScratchBuffer(int initialSize) : base(initialSize) {}
    }
 
    sealed internal class NativeBuffer_ServerVersion : NativeBuffer {
        internal NativeBuffer_ServerVersion(int initialSize) : base(initialSize) {} 
    } 
}
 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK