| File: System\Xml\Xsl\Runtime\XmlQueryStaticData.cs | Web Access |
| Project: src\runtime\src\libraries\System.Private.Xml\src\System.Private.Xml.csproj (System.Private.Xml) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Xml.Xsl.IlGen; using System.Xml.Xsl.Qil; namespace System.Xml.Xsl.Runtime { /// <summary> /// Contains all static data that is used by the runtime. /// </summary> internal sealed class XmlQueryStaticData { // Name of the field to serialize to public const string DataFieldName = "staticData"; public const string TypesFieldName = "ebTypes"; // Format version marker to support versioning: (major << 8) | minor private const int CurrentFormatVersion = (0 << 8) | 0; private readonly XmlWriterSettings _defaultWriterSettings; private readonly IList<WhitespaceRule>? _whitespaceRules; private readonly string[]? _names; private readonly StringPair[][]? _prefixMappingsList; private readonly Int32Pair[]? _filters; private readonly XmlQueryType[]? _types; private readonly XmlCollation[]? _collations; private readonly string[]? _globalNames; private readonly EarlyBoundInfo[]? _earlyBound; /// <summary> /// Constructor. /// </summary> [RequiresUnreferencedCode("This method will create a copy that uses earlybound types which cannot be statically analyzed.")] public XmlQueryStaticData(XmlWriterSettings defaultWriterSettings, IList<WhitespaceRule> whitespaceRules, StaticDataManager staticData) { Debug.Assert(defaultWriterSettings != null && staticData != null); _defaultWriterSettings = defaultWriterSettings; _whitespaceRules = whitespaceRules; _names = staticData.Names; _prefixMappingsList = staticData.PrefixMappingsList; _filters = staticData.NameFilters; _types = staticData.XmlTypes; _collations = staticData.Collations; _globalNames = staticData.GlobalNames; _earlyBound = staticData.EarlyBound; #if DEBUG // Round-trip check byte[] data; Type[]? ebTypes; this.GetObjectData(out data, out ebTypes); XmlQueryStaticData copy = new XmlQueryStaticData(data, ebTypes); _defaultWriterSettings = copy._defaultWriterSettings; _whitespaceRules = copy._whitespaceRules; _names = copy._names; _prefixMappingsList = copy._prefixMappingsList; _filters = copy._filters; _types = copy._types; _collations = copy._collations; _globalNames = copy._globalNames; _earlyBound = copy._earlyBound; #endif } /// <summary> /// Deserialize XmlQueryStaticData object from a byte array. /// </summary> [RequiresUnreferencedCode("This method will create EarlyBoundInfo from passed in ebTypes array which cannot be statically analyzed.")] public XmlQueryStaticData(byte[] data, Type[]? ebTypes) { MemoryStream dataStream = new MemoryStream(data, writable: false); XmlQueryDataReader dataReader = new XmlQueryDataReader(dataStream); int length; // Read a format version int formatVersion = dataReader.Read7BitEncodedInt(); // Changes in the major part of version are not supported if ((formatVersion & ~0xff) > CurrentFormatVersion) throw new NotSupportedException(); // XmlWriterSettings defaultWriterSettings; _defaultWriterSettings = new XmlWriterSettings(dataReader); // IList<WhitespaceRule> whitespaceRules; length = dataReader.ReadInt32(); if (length != 0) { _whitespaceRules = new WhitespaceRule[length]; for (int idx = 0; idx < length; idx++) { _whitespaceRules[idx] = new WhitespaceRule(dataReader); } } // string[] names; length = dataReader.ReadInt32(); if (length != 0) { _names = new string[length]; for (int idx = 0; idx < length; idx++) { _names[idx] = dataReader.ReadString(); } } // StringPair[][] prefixMappingsList; length = dataReader.ReadInt32(); if (length != 0) { _prefixMappingsList = new StringPair[length][]; for (int idx = 0; idx < length; idx++) { int length2 = dataReader.ReadInt32(); _prefixMappingsList[idx] = new StringPair[length2]; for (int idx2 = 0; idx2 < length2; idx2++) { _prefixMappingsList[idx][idx2] = new StringPair(dataReader.ReadString(), dataReader.ReadString()); } } } // Int32Pair[] filters; length = dataReader.ReadInt32(); if (length != 0) { _filters = new Int32Pair[length]; for (int idx = 0; idx < length; idx++) { _filters[idx] = new Int32Pair(dataReader.Read7BitEncodedInt(), dataReader.Read7BitEncodedInt()); } } // XmlQueryType[] types; length = dataReader.ReadInt32(); if (length != 0) { _types = new XmlQueryType[length]; for (int idx = 0; idx < length; idx++) { _types[idx] = XmlQueryTypeFactory.Deserialize(dataReader); } } // XmlCollation[] collations; length = dataReader.ReadInt32(); if (length != 0) { _collations = new XmlCollation[length]; for (int idx = 0; idx < length; idx++) { _collations[idx] = new XmlCollation(dataReader); } } // string[] globalNames; length = dataReader.ReadInt32(); if (length != 0) { _globalNames = new string[length]; for (int idx = 0; idx < length; idx++) { _globalNames[idx] = dataReader.ReadString(); } } // EarlyBoundInfo[] earlyBound; length = dataReader.ReadInt32(); if (length != 0) { _earlyBound = new EarlyBoundInfo[length]; for (int idx = 0; idx < length; idx++) { _earlyBound[idx] = new EarlyBoundInfo(dataReader.ReadString(), ebTypes![idx]); } } Debug.Assert(formatVersion != CurrentFormatVersion || dataReader.Read() == -1, "Extra data at the end of the stream"); dataReader.Dispose(); } /// <summary> /// Serialize XmlQueryStaticData object into a byte array. /// </summary> public void GetObjectData(out byte[] data, out Type[]? ebTypes) { MemoryStream dataStream = new MemoryStream(4096); XmlQueryDataWriter dataWriter = new XmlQueryDataWriter(dataStream); // First put the format version dataWriter.Write7BitEncodedInt(CurrentFormatVersion); // XmlWriterSettings defaultWriterSettings; _defaultWriterSettings.GetObjectData(dataWriter); // IList<WhitespaceRule> whitespaceRules; if (_whitespaceRules == null) { dataWriter.Write(0); } else { dataWriter.Write(_whitespaceRules.Count); foreach (WhitespaceRule rule in _whitespaceRules) { rule.GetObjectData(dataWriter); } } // string[] names; if (_names == null) { dataWriter.Write(0); } else { dataWriter.Write(_names.Length); foreach (string name in _names) { dataWriter.Write(name); } } // StringPair[][] prefixMappingsList; if (_prefixMappingsList == null) { dataWriter.Write(0); } else { dataWriter.Write(_prefixMappingsList.Length); foreach (StringPair[] mappings in _prefixMappingsList) { dataWriter.Write(mappings.Length); foreach (StringPair mapping in mappings) { dataWriter.Write(mapping.Left); dataWriter.Write(mapping.Right); } } } // Int32Pair[] filters; if (_filters == null) { dataWriter.Write(0); } else { dataWriter.Write(_filters.Length); foreach (Int32Pair filter in _filters) { dataWriter.Write7BitEncodedInt(filter.Left); dataWriter.Write7BitEncodedInt(filter.Right); } } // XmlQueryType[] types; if (_types == null) { dataWriter.Write(0); } else { dataWriter.Write(_types.Length); foreach (XmlQueryType type in _types) { XmlQueryTypeFactory.Serialize(dataWriter, type); } } // XmlCollation[] collations; if (_collations == null) { dataWriter.Write(0); } else { dataWriter.Write(_collations.Length); foreach (XmlCollation collation in _collations) { collation.GetObjectData(dataWriter); } } // string[] globalNames; if (_globalNames == null) { dataWriter.Write(0); } else { dataWriter.Write(_globalNames.Length); foreach (string name in _globalNames) { dataWriter.Write(name); } } // EarlyBoundInfo[] earlyBound; if (_earlyBound == null) { dataWriter.Write(0); ebTypes = null; } else { dataWriter.Write(_earlyBound.Length); ebTypes = new Type[_earlyBound.Length]; int idx = 0; foreach (EarlyBoundInfo info in _earlyBound) { dataWriter.Write(info.NamespaceUri); ebTypes[idx++] = info.EarlyBoundType; } } dataWriter.Dispose(); data = dataStream.ToArray(); } /// <summary> /// Return the default writer settings. /// </summary> public XmlWriterSettings DefaultWriterSettings { get { return _defaultWriterSettings; } } /// <summary> /// Return the rules used for whitespace stripping/preservation. /// </summary> public IList<WhitespaceRule>? WhitespaceRules { get { return _whitespaceRules; } } /// <summary> /// Return array of names used by this query. /// </summary> public string[]? Names { get { return _names; } } /// <summary> /// Return array of prefix mappings used by this query. /// </summary> public StringPair[][]? PrefixMappingsList { get { return _prefixMappingsList; } } /// <summary> /// Return array of name filter specifications used by this query. /// </summary> public Int32Pair[]? Filters { get { return _filters; } } /// <summary> /// Return array of types used by this query. /// </summary> public XmlQueryType[]? Types { get { return _types; } } /// <summary> /// Return array of collations used by this query. /// </summary> public XmlCollation[]? Collations { get { return _collations; } } /// <summary> /// Return names of all global variables and parameters used by this query. /// </summary> public string[]? GlobalNames { get { return _globalNames; } } /// <summary> /// Return array of early bound object information related to this query. /// </summary> public EarlyBoundInfo[]? EarlyBound { get { return _earlyBound; } } } /// <summary> /// Subclass of BinaryReader used to serialize query static data. /// </summary> internal sealed class XmlQueryDataReader : BinaryReader { public XmlQueryDataReader(Stream input) : base(input) { } /// <summary> /// Read a string value from the stream. Value can be null. /// </summary> public string? ReadStringQ() { return ReadBoolean() ? ReadString() : null; } /// <summary> /// Read a signed byte value from the stream and check if it belongs to the given diapason. /// </summary> public sbyte ReadSByte(sbyte minValue, sbyte maxValue) { sbyte value = ReadSByte(); ArgumentOutOfRangeException.ThrowIfGreaterThan(minValue, value); ArgumentOutOfRangeException.ThrowIfLessThan(maxValue, value); return value; } } /// <summary> /// Subclass of BinaryWriter used to deserialize query static data. /// </summary> internal sealed class XmlQueryDataWriter : BinaryWriter { public XmlQueryDataWriter(Stream output) : base(output) { } /// <summary> /// Write a string value to the stream. Value can be null. /// </summary> public void WriteStringQ(string? value) { Write(value != null); if (value != null) { Write(value); } } } }