|
using System;
using Encoding = System.Text.Encoding;
#if ES_BUILD_STANDALONE
using Environment = Microsoft.Diagnostics.Tracing.Internal.Environment;
namespace Microsoft.Diagnostics.Tracing
#else
namespace System.Diagnostics.Tracing
#endif
{
/// <summary>
/// TraceLogging: Contains the information needed to generate tracelogging
/// metadata for an event field.
/// </summary>
internal class FieldMetadata
{
/// <summary>
/// Name of the field
/// </summary>
private readonly string name;
/// <summary>
/// The number of bytes in the UTF8 Encoding of 'name' INCLUDING a null terminator.
/// </summary>
private readonly int nameSize;
private readonly EventFieldTags tags;
private readonly byte[] custom;
/// <summary>
/// ETW supports fixed sized arrays. If inType has the InTypeFixedCountFlag then this is the
/// statically known count for the array. It is also used to encode the number of bytes of
/// custom meta-data if InTypeCustomCountFlag set.
/// </summary>
private readonly ushort fixedCount;
private byte inType;
private byte outType;
/// <summary>
/// Scalar or variable-length array.
/// </summary>
public FieldMetadata(
string name,
TraceLoggingDataType type,
EventFieldTags tags,
bool variableCount)
: this(
name,
type,
tags,
variableCount ? Statics.InTypeVariableCountFlag : (byte)0,
0,
null)
{
return;
}
/// <summary>
/// Fixed-length array.
/// </summary>
public FieldMetadata(
string name,
TraceLoggingDataType type,
EventFieldTags tags,
ushort fixedCount)
: this(
name,
type,
tags,
Statics.InTypeFixedCountFlag,
fixedCount,
null)
{
return;
}
/// <summary>
/// Custom serializer
/// </summary>
public FieldMetadata(
string name,
TraceLoggingDataType type,
EventFieldTags tags,
byte[] custom)
: this(
name,
type,
tags,
Statics.InTypeCustomCountFlag,
checked((ushort)(custom == null ? 0 : custom.Length)),
custom)
{
return;
}
private FieldMetadata(
string name,
TraceLoggingDataType dataType,
EventFieldTags tags,
byte countFlags,
ushort fixedCount = 0,
byte[] custom = null)
{
if (name == null)
{
throw new ArgumentNullException(
"name",
"This usually means that the object passed to Write is of a type that"
+ " does not support being used as the top-level object in an event,"
+ " e.g. a primitive or built-in type.");
}
Statics.CheckName(name);
var coreType = (int)dataType & Statics.InTypeMask;
this.name = name;
this.nameSize = Encoding.UTF8.GetByteCount(this.name) + 1;
this.inType = (byte)(coreType | countFlags);
this.outType = (byte)(((int)dataType >> 8) & Statics.OutTypeMask);
this.tags = tags;
this.fixedCount = fixedCount;
this.custom = custom;
if (countFlags != 0)
{
if (coreType == (int)TraceLoggingDataType.Nil)
{
throw new NotSupportedException(Environment.GetResourceString("EventSource_NotSupportedArrayOfNil"));
}
if (coreType == (int)TraceLoggingDataType.Binary)
{
throw new NotSupportedException(Environment.GetResourceString("EventSource_NotSupportedArrayOfBinary"));
}
#if !BROKEN_UNTIL_M3
if (coreType == (int)TraceLoggingDataType.Utf16String ||
coreType == (int)TraceLoggingDataType.MbcsString)
{
throw new NotSupportedException(Environment.GetResourceString("EventSource_NotSupportedArrayOfNullTerminatedString"));
}
#endif
}
if (((int)this.tags & 0xfffffff) != 0)
{
this.outType |= Statics.OutTypeChainFlag;
}
if (this.outType != 0)
{
this.inType |= Statics.InTypeChainFlag;
}
}
public void IncrementStructFieldCount()
{
this.inType |= Statics.InTypeChainFlag;
this.outType++;
if ((this.outType & Statics.OutTypeMask) == 0)
{
throw new NotSupportedException(Environment.GetResourceString("EventSource_TooManyFields"));
}
}
/// <summary>
/// This is the main routine for FieldMetaData. Basically it will serialize the data in
/// this structure as TraceLogging style meta-data into the array 'metaArray' starting at
/// 'pos' (pos is updated to reflect the bytes written).
///
/// Note that 'metaData' can be null, in which case it only updates 'pos'. This is useful
/// for a 'two pass' approach where you figure out how big to make the array, and then you
/// fill it in.
/// </summary>
public void Encode(ref int pos, byte[] metadata)
{
// Write out the null terminated UTF8 encoded name
if (metadata != null)
{
Encoding.UTF8.GetBytes(this.name, 0, this.name.Length, metadata, pos);
}
pos += this.nameSize;
// Write 1 byte for inType
if (metadata != null)
{
metadata[pos] = this.inType;
}
pos += 1;
// If InTypeChainFlag set, then write out the outType
if (0 != (this.inType & Statics.InTypeChainFlag))
{
if (metadata != null)
{
metadata[pos] = this.outType;
}
pos += 1;
// If OutTypeChainFlag set, then write out tags
if (0 != (this.outType & Statics.OutTypeChainFlag))
{
Statics.EncodeTags((int)this.tags, ref pos, metadata);
}
}
// If InTypeFixedCountFlag set, write out the fixedCount (2 bytes little endian)
if (0 != (this.inType & Statics.InTypeFixedCountFlag))
{
if (metadata != null)
{
metadata[pos + 0] = unchecked((byte)this.fixedCount);
metadata[pos + 1] = (byte)(this.fixedCount >> 8);
}
pos += 2;
// If InTypeCustomCountFlag set, write out the blob of custom meta-data.
if (Statics.InTypeCustomCountFlag == (this.inType & Statics.InTypeCountMask) &&
this.fixedCount != 0)
{
if (metadata != null)
{
Buffer.BlockCopy(this.custom, 0, metadata, pos, this.fixedCount);
}
pos += this.fixedCount;
}
}
}
}
}
|