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
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- AtomContentProperty.cs
- WorkflowOperationErrorHandler.cs
- PolicyLevel.cs
- TextRangeEdit.cs
- RichTextBox.cs
- WebHttpSecurityModeHelper.cs
- DependencyObject.cs
- Opcode.cs
- NamedPipeProcessProtocolHandler.cs
- ValidationHelper.cs
- ClaimSet.cs
- ProviderCommandInfoUtils.cs
- TextTreeRootTextBlock.cs
- Command.cs
- Deflater.cs
- COM2DataTypeToManagedDataTypeConverter.cs
- TraceInternal.cs
- DataGridColumnFloatingHeader.cs
- UICuesEvent.cs
- GlobalProxySelection.cs
- PropertyChangingEventArgs.cs
- DispatcherFrame.cs
- ConversionValidationRule.cs
- MobileSysDescriptionAttribute.cs
- EditableRegion.cs
- Registry.cs
- PasswordRecovery.cs
- DefaultAuthorizationContext.cs
- CodeNamespace.cs
- DelegateSerializationHolder.cs
- ITreeGenerator.cs
- XmlSortKeyAccumulator.cs
- InfoCardProofToken.cs
- CompilationUnit.cs
- DataTableReader.cs
- DragCompletedEventArgs.cs
- GeneralTransform.cs
- TypeSemantics.cs
- ThumbButtonInfo.cs
- ProcessModuleCollection.cs
- CultureInfoConverter.cs
- HtmlEncodedRawTextWriter.cs
- ItemTypeToolStripMenuItem.cs
- CommandConverter.cs
- StringConverter.cs
- DesignerRegion.cs
- RemotingConfigParser.cs
- OpenTypeLayoutCache.cs
- documentation.cs
- AccessedThroughPropertyAttribute.cs
- DXD.cs
- ResourceSetExpression.cs
- ListViewUpdatedEventArgs.cs
- XmlSerializationWriter.cs
- DataSetUtil.cs
- TraceContextEventArgs.cs
- UseManagedPresentationBindingElementImporter.cs
- StrokeIntersection.cs
- ApplicationServiceManager.cs
- ServiceHttpHandlerFactory.cs
- CellParaClient.cs
- DependencyObjectProvider.cs
- PagesChangedEventArgs.cs
- ObjectQuery_EntitySqlExtensions.cs
- AssemblyUtil.cs
- LongPath.cs
- MissingMethodException.cs
- Formatter.cs
- CommandDevice.cs
- AutomationElement.cs
- ManualResetEvent.cs
- Match.cs
- MatrixAnimationBase.cs
- DataGridViewAutoSizeColumnsModeEventArgs.cs
- SecurityDescriptor.cs
- ThreadStaticAttribute.cs
- HttpModuleAction.cs
- OrderPreservingSpoolingTask.cs
- EntityDataSourceSelectedEventArgs.cs
- TabItemAutomationPeer.cs
- AnnotationComponentManager.cs
- CaretElement.cs
- ToolBarButtonClickEvent.cs
- ServiceContractViewControl.Designer.cs
- XmlAnyAttributeAttribute.cs
- SizeFConverter.cs
- PagedDataSource.cs
- CorePropertiesFilter.cs
- PlatformCulture.cs
- HeaderUtility.cs
- ChannelManager.cs
- ListBindableAttribute.cs
- XPathNodeIterator.cs
- PartialCachingAttribute.cs
- WindowsToolbarItemAsMenuItem.cs
- RemoteX509Token.cs
- IPAddressCollection.cs
- ObjectStateManager.cs
- SqlBuilder.cs
- ByteRangeDownloader.cs