Code:
/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / whidbey / NetFXspW7 / 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
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- CalloutQueueItem.cs
- QueryableFilterRepeater.cs
- StickyNoteAnnotations.cs
- SqlClientFactory.cs
- WindowsListBox.cs
- DataGridViewColumnHeaderCell.cs
- FixedSOMGroup.cs
- Attribute.cs
- AnimationLayer.cs
- ExitEventArgs.cs
- GridViewPageEventArgs.cs
- BlockCollection.cs
- WindowsSpinner.cs
- WebPartPersonalization.cs
- DataKeyCollection.cs
- DesignerDataSourceView.cs
- FileChangesMonitor.cs
- AuthenticationConfig.cs
- IteratorFilter.cs
- ReflectPropertyDescriptor.cs
- PeerUnsafeNativeCryptMethods.cs
- StoreConnection.cs
- ByteAnimationBase.cs
- GiveFeedbackEvent.cs
- EventLogEntry.cs
- GlyphCollection.cs
- SystemResources.cs
- MasterPage.cs
- PageStatePersister.cs
- SnapshotChangeTrackingStrategy.cs
- PropertyEmitter.cs
- Semaphore.cs
- Preprocessor.cs
- StreamAsIStream.cs
- DateTimeOffsetStorage.cs
- OptimalTextSource.cs
- ConfigurationSection.cs
- SystemFonts.cs
- StringSource.cs
- JsonStringDataContract.cs
- NumericUpDownAcceleration.cs
- BooleanToVisibilityConverter.cs
- ImportCatalogPart.cs
- VisualTarget.cs
- Utils.cs
- _ListenerAsyncResult.cs
- CaseInsensitiveComparer.cs
- ViewCellSlot.cs
- FeatureSupport.cs
- BlockCollection.cs
- XmlDataProvider.cs
- ColumnProvider.cs
- SQLSingleStorage.cs
- EventListenerClientSide.cs
- FileDialog_Vista.cs
- InlineUIContainer.cs
- Attributes.cs
- PrintDialog.cs
- UnsignedPublishLicense.cs
- TaskForm.cs
- PageThemeParser.cs
- XmlKeywords.cs
- XmlSortKey.cs
- ReadOnlyAttribute.cs
- ISFTagAndGuidCache.cs
- InfocardInteractiveChannelInitializer.cs
- ExpressionNormalizer.cs
- QueryStack.cs
- ListViewDeletedEventArgs.cs
- TextBoxBase.cs
- DeleteIndexBinder.cs
- SafeCryptoHandles.cs
- Context.cs
- EdmProviderManifest.cs
- PaginationProgressEventArgs.cs
- EventHandlersStore.cs
- FrameworkElement.cs
- EventOpcode.cs
- LockRenewalTask.cs
- WorkflowOperationInvoker.cs
- MessageQueue.cs
- SortableBindingList.cs
- SmtpNetworkElement.cs
- ThreadStateException.cs
- ItemDragEvent.cs
- DataTransferEventArgs.cs
- RootProfilePropertySettingsCollection.cs
- PageEventArgs.cs
- AccessViolationException.cs
- StreamingContext.cs
- FeatureManager.cs
- UIHelper.cs
- Compiler.cs
- ResourceManagerWrapper.cs
- Model3D.cs
- SqlSelectStatement.cs
- KeyGesture.cs
- AutomationAttributeInfo.cs
- PageAdapter.cs
- TabItemAutomationPeer.cs