Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / whidbey / netfxsp / ndp / fx / src / DataOracleClient / System / Data / OracleClient / NativeBuffer.cs / 6 / 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
- RootNamespaceAttribute.cs
- PeerNameRecord.cs
- ProxyManager.cs
- CharacterHit.cs
- BuildProviderUtils.cs
- HwndProxyElementProvider.cs
- PeerHopCountAttribute.cs
- InstanceLockLostException.cs
- HebrewNumber.cs
- DataGridViewRowCollection.cs
- CustomBindingCollectionElement.cs
- Button.cs
- BinaryMessageFormatter.cs
- LogFlushAsyncResult.cs
- SqlCacheDependencyDatabaseCollection.cs
- SQLChars.cs
- Validator.cs
- ZipPackage.cs
- SparseMemoryStream.cs
- Stroke.cs
- DataServiceQueryProvider.cs
- BufferedStream2.cs
- FontNamesConverter.cs
- IdentitySection.cs
- controlskin.cs
- WinEventWrap.cs
- WindowsRichEdit.cs
- PropertyNames.cs
- CookielessData.cs
- InlineUIContainer.cs
- ListControl.cs
- PageSettings.cs
- SystemParameters.cs
- basevalidator.cs
- HandlerBase.cs
- FontWeightConverter.cs
- GeometryHitTestResult.cs
- ToggleButtonAutomationPeer.cs
- Triangle.cs
- COM2Enum.cs
- TcpProcessProtocolHandler.cs
- ParallelTimeline.cs
- PageHandlerFactory.cs
- PaintValueEventArgs.cs
- MemberCollection.cs
- CodeTypeMember.cs
- TextReader.cs
- QueueProcessor.cs
- AppSettings.cs
- FrameSecurityDescriptor.cs
- TrackingProfile.cs
- CopyNamespacesAction.cs
- SqlCacheDependencyDatabase.cs
- Double.cs
- SerializationHelper.cs
- AdapterDictionary.cs
- ServiceReflector.cs
- OrderablePartitioner.cs
- Decorator.cs
- SmiContextFactory.cs
- StringArrayConverter.cs
- KnownTypeAttribute.cs
- WebPartZoneCollection.cs
- TypeForwardedToAttribute.cs
- WebServiceEnumData.cs
- StringDictionary.cs
- GuidelineCollection.cs
- CompilerState.cs
- ApplicationId.cs
- SupportsEventValidationAttribute.cs
- InkCollectionBehavior.cs
- SqlInternalConnection.cs
- sqlstateclientmanager.cs
- CommandConverter.cs
- COM2ICategorizePropertiesHandler.cs
- DataControlLinkButton.cs
- SqlInternalConnectionSmi.cs
- FixedElement.cs
- BulletedList.cs
- ListControlDesigner.cs
- ipaddressinformationcollection.cs
- Vertex.cs
- SqlRemoveConstantOrderBy.cs
- JsonQNameDataContract.cs
- EntityCollectionChangedParams.cs
- ComContractElement.cs
- DataBindingHandlerAttribute.cs
- BackEase.cs
- WorkflowOwnershipException.cs
- Emitter.cs
- CqlWriter.cs
- WeakHashtable.cs
- Error.cs
- dbdatarecord.cs
- COSERVERINFO.cs
- HotSpotCollection.cs
- XmlBinaryReader.cs
- ProcessThread.cs
- Assert.cs
- TypeValidationEventArgs.cs