File: System\Diagnostics\Eventing\TraceLogging\DataCollector.cs
Project: ndp\clr\src\bcl\mscorlib.csproj (mscorlib)
using System;
using System.Runtime.InteropServices;
using System.Security;
 
#if ES_BUILD_STANDALONE
using Environment = Microsoft.Diagnostics.Tracing.Internal.Environment;
namespace Microsoft.Diagnostics.Tracing
#else
namespace System.Diagnostics.Tracing
#endif
{
    /// <summary>
    /// TraceLogging: This is the implementation of the DataCollector
    /// functionality. To enable safe access to the DataCollector from
    /// untrusted code, there is one thread-local instance of this structure
    /// per thread. The instance must be Enabled before any data is written to
    /// it. The instance must be Finished before the data is passed to
    /// EventWrite. The instance must be Disabled before the arrays referenced
    /// by the pointers are freed or unpinned.
    /// </summary>
    [SecurityCritical]
    internal unsafe struct DataCollector
    {
        [ThreadStatic]
        internal static DataCollector ThreadInstance;
 
        private byte* scratchEnd;
        private EventSource.EventData* datasEnd;
        private GCHandle* pinsEnd;
        private EventSource.EventData* datasStart;
        private byte* scratch;
        private EventSource.EventData* datas;
        private GCHandle* pins;
        private byte[] buffer;
        private int bufferPos;
        private int bufferNesting;          // We may merge many fields int a single blob.   If we are doing this we increment this. 
        private bool writingScalars;
 
        internal void Enable(
            byte* scratch,
            int scratchSize,
            EventSource.EventData* datas,
            int dataCount,
            GCHandle* pins,
            int pinCount)
        {
            this.datasStart = datas;
            this.scratchEnd = scratch + scratchSize;
            this.datasEnd = datas + dataCount;
            this.pinsEnd = pins + pinCount;
            this.scratch = scratch;
            this.datas = datas;
            this.pins = pins;
            this.writingScalars = false;
        }
 
        internal void Disable()
        {
            this = new DataCollector();
        }
 
        /// <summary>
        /// Completes the list of scalars. Finish must be called before the data
        /// descriptor array is passed to EventWrite.
        /// </summary>
        /// <returns>
        /// A pointer to the next unused data descriptor, or datasEnd if they were
        /// all used. (Descriptors may be unused if a string or array was null.)
        /// </returns>
        internal EventSource.EventData* Finish()
        {
            this.ScalarsEnd();
            return this.datas;
        }
 
        internal void AddScalar(void* value, int size)
        {
            var pb = (byte*)value;
            if (this.bufferNesting == 0)
            {
                var scratchOld = this.scratch;
                var scratchNew = scratchOld + size;
                if (this.scratchEnd < scratchNew)
                {
                    throw new IndexOutOfRangeException(Environment.GetResourceString("EventSource_AddScalarOutOfRange"));
                }
 
                this.ScalarsBegin();
                this.scratch = scratchNew;
 
                for (int i = 0; i != size; i++)
                {
                    scratchOld[i] = pb[i];
                }
            }
            else
            {
                var oldPos = this.bufferPos;
                this.bufferPos = checked(this.bufferPos + size);
                this.EnsureBuffer();
                for (int i = 0; i != size; i++, oldPos++)
                {
                    this.buffer[oldPos] = pb[i];
                }
            }
        }
 
        internal void AddBinary(string value, int size)
        {
            if (size > ushort.MaxValue)
            {
                size = ushort.MaxValue - 1;
            }
 
            if (this.bufferNesting != 0)
            {
                this.EnsureBuffer(size + 2);
            }
 
            this.AddScalar(&size, 2);
 
            if (size != 0)
            {
                if (this.bufferNesting == 0)
                {
                    this.ScalarsEnd();
                    this.PinArray(value, size);
                }
                else
                {
                    var oldPos = this.bufferPos;
                    this.bufferPos = checked(this.bufferPos + size);
                    this.EnsureBuffer();
                    fixed (void* p = value)
                    {
                        Marshal.Copy((IntPtr)p, this.buffer, oldPos, size);
                    }
                }
            }
        }
 
        internal void AddBinary(Array value, int size)
        {
            this.AddArray(value, size, 1);
        }
 
        internal void AddArray(Array value, int length, int itemSize)
        {
            if (length > ushort.MaxValue)
            {
                length = ushort.MaxValue;
            }
 
            var size = length * itemSize;
            if (this.bufferNesting != 0)
            {
                this.EnsureBuffer(size + 2);
            }
 
            this.AddScalar(&length, 2);
 
            if (length != 0)
            {
                if (this.bufferNesting == 0)
                {
                    this.ScalarsEnd();
                    this.PinArray(value, size);
                }
                else
                {
                    var oldPos = this.bufferPos;
                    this.bufferPos = checked(this.bufferPos + size);
                    this.EnsureBuffer();
                    Buffer.BlockCopy(value, 0, this.buffer, oldPos, size);
                }
            }
        }
 
        /// <summary>
        /// Marks the start of a non-blittable array or enumerable.
        /// </summary>
        /// <returns>Bookmark to be passed to EndBufferedArray.</returns>
        internal int BeginBufferedArray()
        {
            this.BeginBuffered();
            this.bufferPos += 2; // Reserve space for the array length (filled in by EndEnumerable)
            return this.bufferPos;
        }
 
        /// <summary>
        /// Marks the end of a non-blittable array or enumerable.
        /// </summary>
        /// <param name="bookmark">The value returned by BeginBufferedArray.</param>
        /// <param name="count">The number of items in the array.</param>
        internal void EndBufferedArray(int bookmark, int count)
        {
            this.EnsureBuffer();
            this.buffer[bookmark - 2] = unchecked((byte)count);
            this.buffer[bookmark - 1] = unchecked((byte)(count >> 8));
            this.EndBuffered();
        }
 
        /// <summary>
        /// Marks the start of dynamically-buffered data.
        /// </summary>
        internal void BeginBuffered()
        {
            this.ScalarsEnd();
            this.bufferNesting += 1;
        }
 
        /// <summary>
        /// Marks the end of dynamically-buffered data.
        /// </summary>
        internal void EndBuffered()
        {
            this.bufferNesting -= 1;
 
            if (this.bufferNesting == 0)
            {
                /*
                
 
 
 
 
*/
 
                this.EnsureBuffer();
                this.PinArray(this.buffer, this.bufferPos);
                this.buffer = null;
                this.bufferPos = 0;
            }
        }
 
        private void EnsureBuffer()
        {
            var required = this.bufferPos;
            if (this.buffer == null || this.buffer.Length < required)
            {
                this.GrowBuffer(required);
            }
        }
 
        private void EnsureBuffer(int additionalSize)
        {
            var required = this.bufferPos + additionalSize;
            if (this.buffer == null || this.buffer.Length < required)
            {
                this.GrowBuffer(required);
            }
        }
 
        private void GrowBuffer(int required)
        {
            var newSize = this.buffer == null ? 64 : this.buffer.Length;
 
            do
            {
                newSize *= 2;
            }
            while (newSize < required);
 
            Array.Resize(ref this.buffer, newSize);
        }
 
        private void PinArray(object value, int size)
        {
            var pinsTemp = this.pins;
            if (this.pinsEnd <= pinsTemp)
            {
                throw new IndexOutOfRangeException(Environment.GetResourceString("EventSource_PinArrayOutOfRange"));
            }
 
            var datasTemp = this.datas;
            if (this.datasEnd <= datasTemp)
            {
                throw new IndexOutOfRangeException(Environment.GetResourceString("EventSource_DataDescriptorsOutOfRange"));
            }
 
            this.pins = pinsTemp + 1;
            this.datas = datasTemp + 1;
 
            *pinsTemp = GCHandle.Alloc(value, GCHandleType.Pinned);
            datasTemp->DataPointer = pinsTemp->AddrOfPinnedObject();
            datasTemp->m_Size = size;
        }
 
        private void ScalarsBegin()
        {
            if (!this.writingScalars)
            {
                var datasTemp = this.datas;
                if (this.datasEnd <= datasTemp)
                {
                    throw new IndexOutOfRangeException(Environment.GetResourceString("EventSource_DataDescriptorsOutOfRange"));
                }
 
                datasTemp->DataPointer = (IntPtr) this.scratch;
                this.writingScalars = true;
            }
        }
 
        private void ScalarsEnd()
        {
            if (this.writingScalars)
            {
                var datasTemp = this.datas;
                datasTemp->m_Size = checked((int)(this.scratch - (byte*)datasTemp->m_Ptr));
                this.datas = datasTemp + 1;
                this.writingScalars = false;
            }
        }
    }
}