//------------------------------------------------------------ // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------ namespace System.ServiceModel.Dispatcher { using System.Runtime; enum ValueDataType : byte { None = 0, Boolean, Double, StackFrame, Sequence, String } // Value is like Variant. Since a Value is only temporary storage, we use memory to avoid typecasting type // casting, which in C# is expensive. Value contains storage for every possible XPath data type. // We avoid data copying by being smart about how we pass Value around - values are either accessed via // method calls, or the value object itself is passed by ref // // The filter engine never deals with a single value per se. We only work with Sets of Values. A single value // is a ValueSet of size 1 // internal struct Value { bool boolVal; double dblVal; StackFrame frame; NodeSequence sequence; string strVal; ValueDataType type; internal bool Boolean { get { return this.boolVal; } set { this.type = ValueDataType.Boolean; this.boolVal = value; } } internal double Double { get { return this.dblVal; } set { this.type = ValueDataType.Double; this.dblVal = value; } } internal StackFrame Frame { get { return this.frame; } #if NO set { this.type = ValueDataType.StackFrame; this.frame = value; } #endif } #if NO internal int FrameCount { get { return this.frame.Count; } } #endif internal int FrameEndPtr { #if NO get { return this.frame.endPtr; } #endif set { Fx.Assert(this.IsType(ValueDataType.StackFrame), ""); this.frame.EndPtr = value; } } #if NO internal int FrameBasePtr { get { return this.frame.basePtr; } } internal int StackPtr { get { return this.frame.basePtr - 1; } } #endif internal int NodeCount { get { return this.sequence.Count; } } internal NodeSequence Sequence { get { return this.sequence; } set { this.type = ValueDataType.Sequence; this.sequence = value; } } internal string String { get { return this.strVal; } set { this.type = ValueDataType.String; this.strVal = value; } } internal ValueDataType Type { get { return this.type; } } internal void Add(double val) { Fx.Assert(ValueDataType.Double == this.type, ""); this.dblVal += val; } #if NO internal void Clear() { this.type = ValueDataType.None; this.sequence = null; } #endif internal void Clear(ProcessingContext context) { if (ValueDataType.Sequence == this.type) { this.ReleaseSequence(context); } this.type = ValueDataType.None; } // Fully general compare internal bool CompareTo(ref Value val, RelationOperator op) { switch (this.type) { default: throw DiagnosticUtility.ExceptionUtility.ThrowHelperCritical(new QueryProcessingException(QueryProcessingError.TypeMismatch)); case ValueDataType.Boolean: switch (val.type) { default: throw DiagnosticUtility.ExceptionUtility.ThrowHelperCritical(new QueryProcessingException(QueryProcessingError.TypeMismatch)); case ValueDataType.Boolean: return QueryValueModel.Compare(this.boolVal, val.boolVal, op); case ValueDataType.Double: return QueryValueModel.Compare(this.boolVal, val.dblVal, op); case ValueDataType.Sequence: return QueryValueModel.Compare(this.boolVal, val.sequence, op); case ValueDataType.String: return QueryValueModel.Compare(this.boolVal, val.strVal, op); } case ValueDataType.Double: switch (val.type) { default: throw DiagnosticUtility.ExceptionUtility.ThrowHelperCritical(new QueryProcessingException(QueryProcessingError.TypeMismatch)); case ValueDataType.Boolean: return QueryValueModel.Compare(this.dblVal, val.boolVal, op); case ValueDataType.Double: return QueryValueModel.Compare(this.dblVal, val.dblVal, op); case ValueDataType.Sequence: return QueryValueModel.Compare(this.dblVal, val.sequence, op); case ValueDataType.String: return QueryValueModel.Compare(this.dblVal, val.strVal, op); } case ValueDataType.Sequence: switch (val.type) { default: throw DiagnosticUtility.ExceptionUtility.ThrowHelperCritical(new QueryProcessingException(QueryProcessingError.TypeMismatch)); case ValueDataType.Boolean: return QueryValueModel.Compare(this.sequence, val.boolVal, op); case ValueDataType.Double: return QueryValueModel.Compare(this.sequence, val.dblVal, op); case ValueDataType.Sequence: return QueryValueModel.Compare(this.sequence, val.sequence, op); case ValueDataType.String: return QueryValueModel.Compare(this.sequence, val.strVal, op); } case ValueDataType.String: switch (val.type) { default: throw DiagnosticUtility.ExceptionUtility.ThrowHelperCritical(new QueryProcessingException(QueryProcessingError.TypeMismatch)); case ValueDataType.Boolean: return QueryValueModel.Compare(this.strVal, val.boolVal, op); case ValueDataType.Double: return QueryValueModel.Compare(this.strVal, val.dblVal, op); case ValueDataType.Sequence: return QueryValueModel.Compare(this.strVal, val.sequence, op); case ValueDataType.String: return QueryValueModel.Compare(this.strVal, val.strVal, op); } } } internal bool CompareTo(double val, RelationOperator op) { switch (this.type) { default: throw DiagnosticUtility.ExceptionUtility.ThrowHelperCritical(new QueryProcessingException(QueryProcessingError.TypeMismatch)); case ValueDataType.Boolean: return QueryValueModel.Compare(this.boolVal, val, op); case ValueDataType.Double: return QueryValueModel.Compare(this.dblVal, val, op); case ValueDataType.Sequence: return QueryValueModel.Compare(this.sequence, val, op); case ValueDataType.String: return QueryValueModel.Compare(this.strVal, val, op); } } internal void ConvertTo(ProcessingContext context, ValueDataType newType) { Fx.Assert(null != context, ""); if (newType == this.type) { return; } switch (newType) { default: break; case ValueDataType.Boolean: this.boolVal = this.ToBoolean(); break; case ValueDataType.Double: this.dblVal = this.ToDouble(); break; case ValueDataType.String: this.strVal = this.ToString(); break; } if (ValueDataType.Sequence == this.type) { this.ReleaseSequence(context); } this.type = newType; } internal bool Equals(string val) { switch (this.type) { default: Fx.Assert("Invalid Type"); return false; case ValueDataType.Boolean: return QueryValueModel.Equals(this.boolVal, val); case ValueDataType.Double: return QueryValueModel.Equals(this.dblVal, val); case ValueDataType.Sequence: return QueryValueModel.Equals(this.sequence, val); case ValueDataType.String: return QueryValueModel.Equals(this.strVal, val); } } internal bool Equals(double val) { switch (this.type) { default: Fx.Assert("Invalid Type"); return false; case ValueDataType.Boolean: return QueryValueModel.Equals(this.boolVal, val); case ValueDataType.Double: return QueryValueModel.Equals(this.dblVal, val); case ValueDataType.Sequence: return QueryValueModel.Equals(this.sequence, val); case ValueDataType.String: return QueryValueModel.Equals(val, this.strVal); } } #if NO internal bool Equals(bool val) { switch (this.type) { default: throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new QueryProcessingException(QueryProcessingError.TypeMismatch), TraceEventType.Critical); case ValueDataType.Boolean: return QueryValueModel.Equals(this.boolVal, val); case ValueDataType.Double: return QueryValueModel.Equals(this.dblVal, val); case ValueDataType.Sequence: return QueryValueModel.Equals(this.sequence, val); case ValueDataType.String: return QueryValueModel.Equals(this.strVal, val); } } #endif internal bool GetBoolean() { if (ValueDataType.Boolean != this.type) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperCritical(new QueryProcessingException(QueryProcessingError.TypeMismatch)); } return this.boolVal; } internal double GetDouble() { if (ValueDataType.Double != this.type) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperCritical(new QueryProcessingException(QueryProcessingError.TypeMismatch)); } return this.dblVal; } internal NodeSequence GetSequence() { if (ValueDataType.Sequence != this.type) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperCritical(new QueryProcessingException(QueryProcessingError.TypeMismatch)); } return this.sequence; } internal string GetString() { if (ValueDataType.String != this.type) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperCritical(new QueryProcessingException(QueryProcessingError.TypeMismatch)); } return this.strVal; } internal bool IsType(ValueDataType type) { return (type == this.type); } internal void Multiply(double val) { Fx.Assert(ValueDataType.Double == this.type, ""); this.dblVal *= val; } internal void Negate() { Fx.Assert(this.type == ValueDataType.Double, ""); this.dblVal = -this.dblVal; } internal void Not() { Fx.Assert(this.type == ValueDataType.Boolean, ""); this.boolVal = !this.boolVal; } internal void ReleaseSequence(ProcessingContext context) { Fx.Assert(null != context && this.type == ValueDataType.Sequence && null != this.sequence, ""); context.ReleaseSequence(this.sequence); this.sequence = null; } internal void StartFrame(int start) { this.type = ValueDataType.StackFrame; this.frame.basePtr = start + 1; this.frame.endPtr = start; } #if NO internal void Subtract(double dblVal) { Fx.Assert(ValueDataType.Double == this.type, ""); this.dblVal -= dblVal; } #endif internal bool ToBoolean() { switch (this.type) { default: throw DiagnosticUtility.ExceptionUtility.ThrowHelperCritical(new QueryProcessingException(QueryProcessingError.TypeMismatch)); case ValueDataType.Boolean: return this.boolVal; case ValueDataType.Double: return QueryValueModel.Boolean(this.dblVal); case ValueDataType.Sequence: return QueryValueModel.Boolean(this.sequence); case ValueDataType.String: return QueryValueModel.Boolean(this.strVal); } } internal double ToDouble() { switch (this.type) { default: throw DiagnosticUtility.ExceptionUtility.ThrowHelperCritical(new QueryProcessingException(QueryProcessingError.TypeMismatch)); case ValueDataType.Boolean: return QueryValueModel.Double(this.boolVal); case ValueDataType.Double: return this.dblVal; case ValueDataType.Sequence: return QueryValueModel.Double(this.sequence); case ValueDataType.String: return QueryValueModel.Double(this.strVal); } } public override string ToString() { switch (this.type) { default: throw DiagnosticUtility.ExceptionUtility.ThrowHelperCritical(new QueryProcessingException(QueryProcessingError.TypeMismatch)); case ValueDataType.Boolean: return QueryValueModel.String(this.boolVal); case ValueDataType.Double: return QueryValueModel.String(this.dblVal); case ValueDataType.Sequence: return QueryValueModel.String(this.sequence); case ValueDataType.String: return this.strVal; } } internal void Update(ProcessingContext context, bool val) { if (ValueDataType.Sequence == this.type) { context.ReleaseSequence(this.sequence); } this.Boolean = val; } internal void Update(ProcessingContext context, double val) { if (ValueDataType.Sequence == this.type) { context.ReleaseSequence(this.sequence); } this.Double = val; } internal void Update(ProcessingContext context, string val) { if (ValueDataType.Sequence == this.type) { context.ReleaseSequence(this.sequence); } this.String = val; } internal void Update(ProcessingContext context, NodeSequence val) { if (ValueDataType.Sequence == this.type) { context.ReleaseSequence(this.sequence); } this.Sequence = val; } } }