< Summary

Line coverage
0%
Covered lines: 0
Uncovered lines: 210
Coverable lines: 210
Total lines: 325
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 58
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Internal/StringHeap.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System.Diagnostics;
 5using System.Reflection.Internal;
 6using System.Runtime.InteropServices;
 7using System.Text;
 8using System.Threading;
 9
 10namespace System.Reflection.Metadata.Ecma335
 11{
 12    internal struct StringHeap
 13    {
 14        private static string[]? s_virtualValues;
 15
 16        internal readonly MemoryBlock Block;
 17        private VirtualHeap? _lazyVirtualHeap;
 18
 19        internal StringHeap(MemoryBlock block, MetadataKind metadataKind)
 020        {
 021            _lazyVirtualHeap = null;
 22
 023            if (s_virtualValues == null && metadataKind != MetadataKind.Ecma335)
 024            {
 25                // Note:
 26                // Virtual values shall not contain surrogates, otherwise StartsWith might be inconsistent
 27                // when comparing to a text that ends with a high surrogate.
 28
 029                var values = new string[(int)StringHandle.VirtualIndex.Count];
 030                values[(int)StringHandle.VirtualIndex.System_Runtime_WindowsRuntime] = "System.Runtime.WindowsRuntime";
 031                values[(int)StringHandle.VirtualIndex.System_Runtime] = "System.Runtime";
 032                values[(int)StringHandle.VirtualIndex.System_ObjectModel] = "System.ObjectModel";
 033                values[(int)StringHandle.VirtualIndex.System_Runtime_WindowsRuntime_UI_Xaml] = "System.Runtime.WindowsRu
 034                values[(int)StringHandle.VirtualIndex.System_Runtime_InteropServices_WindowsRuntime] = "System.Runtime.I
 035                values[(int)StringHandle.VirtualIndex.System_Numerics_Vectors] = "System.Numerics.Vectors";
 36
 037                values[(int)StringHandle.VirtualIndex.Dispose] = "Dispose";
 38
 039                values[(int)StringHandle.VirtualIndex.AttributeTargets] = "AttributeTargets";
 040                values[(int)StringHandle.VirtualIndex.AttributeUsageAttribute] = "AttributeUsageAttribute";
 041                values[(int)StringHandle.VirtualIndex.Color] = "Color";
 042                values[(int)StringHandle.VirtualIndex.CornerRadius] = "CornerRadius";
 043                values[(int)StringHandle.VirtualIndex.DateTimeOffset] = "DateTimeOffset";
 044                values[(int)StringHandle.VirtualIndex.Duration] = "Duration";
 045                values[(int)StringHandle.VirtualIndex.DurationType] = "DurationType";
 046                values[(int)StringHandle.VirtualIndex.EventHandler1] = "EventHandler`1";
 047                values[(int)StringHandle.VirtualIndex.EventRegistrationToken] = "EventRegistrationToken";
 048                values[(int)StringHandle.VirtualIndex.Exception] = "Exception";
 049                values[(int)StringHandle.VirtualIndex.GeneratorPosition] = "GeneratorPosition";
 050                values[(int)StringHandle.VirtualIndex.GridLength] = "GridLength";
 051                values[(int)StringHandle.VirtualIndex.GridUnitType] = "GridUnitType";
 052                values[(int)StringHandle.VirtualIndex.ICommand] = "ICommand";
 053                values[(int)StringHandle.VirtualIndex.IDictionary2] = "IDictionary`2";
 054                values[(int)StringHandle.VirtualIndex.IDisposable] = "IDisposable";
 055                values[(int)StringHandle.VirtualIndex.IEnumerable] = "IEnumerable";
 056                values[(int)StringHandle.VirtualIndex.IEnumerable1] = "IEnumerable`1";
 057                values[(int)StringHandle.VirtualIndex.IList] = "IList";
 058                values[(int)StringHandle.VirtualIndex.IList1] = "IList`1";
 059                values[(int)StringHandle.VirtualIndex.INotifyCollectionChanged] = "INotifyCollectionChanged";
 060                values[(int)StringHandle.VirtualIndex.INotifyPropertyChanged] = "INotifyPropertyChanged";
 061                values[(int)StringHandle.VirtualIndex.IReadOnlyDictionary2] = "IReadOnlyDictionary`2";
 062                values[(int)StringHandle.VirtualIndex.IReadOnlyList1] = "IReadOnlyList`1";
 063                values[(int)StringHandle.VirtualIndex.KeyTime] = "KeyTime";
 064                values[(int)StringHandle.VirtualIndex.KeyValuePair2] = "KeyValuePair`2";
 065                values[(int)StringHandle.VirtualIndex.Matrix] = "Matrix";
 066                values[(int)StringHandle.VirtualIndex.Matrix3D] = "Matrix3D";
 067                values[(int)StringHandle.VirtualIndex.Matrix3x2] = "Matrix3x2";
 068                values[(int)StringHandle.VirtualIndex.Matrix4x4] = "Matrix4x4";
 069                values[(int)StringHandle.VirtualIndex.NotifyCollectionChangedAction] = "NotifyCollectionChangedAction";
 070                values[(int)StringHandle.VirtualIndex.NotifyCollectionChangedEventArgs] = "NotifyCollectionChangedEventA
 071                values[(int)StringHandle.VirtualIndex.NotifyCollectionChangedEventHandler] = "NotifyCollectionChangedEve
 072                values[(int)StringHandle.VirtualIndex.Nullable1] = "Nullable`1";
 073                values[(int)StringHandle.VirtualIndex.Plane] = "Plane";
 074                values[(int)StringHandle.VirtualIndex.Point] = "Point";
 075                values[(int)StringHandle.VirtualIndex.PropertyChangedEventArgs] = "PropertyChangedEventArgs";
 076                values[(int)StringHandle.VirtualIndex.PropertyChangedEventHandler] = "PropertyChangedEventHandler";
 077                values[(int)StringHandle.VirtualIndex.Quaternion] = "Quaternion";
 078                values[(int)StringHandle.VirtualIndex.Rect] = "Rect";
 079                values[(int)StringHandle.VirtualIndex.RepeatBehavior] = "RepeatBehavior";
 080                values[(int)StringHandle.VirtualIndex.RepeatBehaviorType] = "RepeatBehaviorType";
 081                values[(int)StringHandle.VirtualIndex.Size] = "Size";
 082                values[(int)StringHandle.VirtualIndex.System] = "System";
 083                values[(int)StringHandle.VirtualIndex.System_Collections] = "System.Collections";
 084                values[(int)StringHandle.VirtualIndex.System_Collections_Generic] = "System.Collections.Generic";
 085                values[(int)StringHandle.VirtualIndex.System_Collections_Specialized] = "System.Collections.Specialized"
 086                values[(int)StringHandle.VirtualIndex.System_ComponentModel] = "System.ComponentModel";
 087                values[(int)StringHandle.VirtualIndex.System_Numerics] = "System.Numerics";
 088                values[(int)StringHandle.VirtualIndex.System_Windows_Input] = "System.Windows.Input";
 089                values[(int)StringHandle.VirtualIndex.Thickness] = "Thickness";
 090                values[(int)StringHandle.VirtualIndex.TimeSpan] = "TimeSpan";
 091                values[(int)StringHandle.VirtualIndex.Type] = "Type";
 092                values[(int)StringHandle.VirtualIndex.Uri] = "Uri";
 093                values[(int)StringHandle.VirtualIndex.Vector2] = "Vector2";
 094                values[(int)StringHandle.VirtualIndex.Vector3] = "Vector3";
 095                values[(int)StringHandle.VirtualIndex.Vector4] = "Vector4";
 096                values[(int)StringHandle.VirtualIndex.Windows_Foundation] = "Windows.Foundation";
 097                values[(int)StringHandle.VirtualIndex.Windows_UI] = "Windows.UI";
 098                values[(int)StringHandle.VirtualIndex.Windows_UI_Xaml] = "Windows.UI.Xaml";
 099                values[(int)StringHandle.VirtualIndex.Windows_UI_Xaml_Controls_Primitives] = "Windows.UI.Xaml.Controls.P
 0100                values[(int)StringHandle.VirtualIndex.Windows_UI_Xaml_Media] = "Windows.UI.Xaml.Media";
 0101                values[(int)StringHandle.VirtualIndex.Windows_UI_Xaml_Media_Animation] = "Windows.UI.Xaml.Media.Animatio
 0102                values[(int)StringHandle.VirtualIndex.Windows_UI_Xaml_Media_Media3D] = "Windows.UI.Xaml.Media.Media3D";
 103
 0104                s_virtualValues = values;
 0105                AssertFilled();
 0106            }
 107
 0108            this.Block = TrimEnd(block);
 0109        }
 110
 111        [Conditional("DEBUG")]
 112        private static void AssertFilled()
 0113        {
 0114            for (int i = 0; i < s_virtualValues!.Length; i++)
 0115            {
 0116                Debug.Assert(s_virtualValues[i] != null, $"Missing virtual value for StringHandle.VirtualIndex.{(StringH
 0117            }
 0118        }
 119
 120        // Trims the alignment padding of the heap.
 121        // See StgStringPool::InitOnMem in ndp\clr\src\Utilcode\StgPool.cpp.
 122
 123        // This is especially important for EnC.
 124        private static MemoryBlock TrimEnd(MemoryBlock block)
 0125        {
 0126            if (block.Length == 0)
 0127            {
 0128                return block;
 129            }
 130
 0131            int i = block.Length - 1;
 0132            while (i >= 0 && block.PeekByte(i) == 0)
 0133            {
 0134                i--;
 0135            }
 136
 137            // this shouldn't happen in valid metadata:
 0138            if (i == block.Length - 1)
 0139            {
 0140                return block;
 141            }
 142
 143            // +1 for terminating \0
 0144            return block.GetMemoryBlockAt(0, i + 2);
 0145        }
 146
 147        internal string GetString(StringHandle handle, MetadataStringDecoder utf8Decoder)
 0148        {
 0149            return handle.IsVirtual ? GetVirtualHandleString(handle, utf8Decoder) : GetNonVirtualString(handle, utf8Deco
 0150        }
 151
 152        internal MemoryBlock GetMemoryBlock(StringHandle handle)
 0153        {
 0154            return handle.IsVirtual ? GetVirtualHandleMemoryBlock(handle) : GetNonVirtualStringMemoryBlock(handle);
 0155        }
 156
 157        internal static string GetVirtualString(StringHandle.VirtualIndex index)
 0158        {
 0159            return s_virtualValues![(int)index];
 0160        }
 161
 162        private string GetNonVirtualString(StringHandle handle, MetadataStringDecoder utf8Decoder, byte[]? prefixOpt)
 0163        {
 0164            Debug.Assert(handle.StringKind != StringKind.Virtual);
 165
 0166            char otherTerminator = handle.StringKind == StringKind.DotTerminated ? '.' : '\0';
 0167            return Block.PeekUtf8NullTerminated(handle.GetHeapOffset(), prefixOpt, utf8Decoder, out _, otherTerminator);
 0168        }
 169
 170        private unsafe MemoryBlock GetNonVirtualStringMemoryBlock(StringHandle handle)
 0171        {
 0172            Debug.Assert(handle.StringKind != StringKind.Virtual);
 173
 0174            char otherTerminator = handle.StringKind == StringKind.DotTerminated ? '.' : '\0';
 0175            int offset = handle.GetHeapOffset();
 0176            int length = Block.GetUtf8NullTerminatedLength(offset, out _, otherTerminator);
 177
 0178            return new MemoryBlock(Block.Pointer + offset, length);
 0179        }
 180
 181        private unsafe byte[] GetNonVirtualStringBytes(StringHandle handle, byte[] prefix)
 0182        {
 0183            Debug.Assert(handle.StringKind != StringKind.Virtual);
 184
 0185            var block = GetNonVirtualStringMemoryBlock(handle);
 0186            var bytes = new byte[prefix.Length + block.Length];
 0187            Buffer.BlockCopy(prefix, 0, bytes, 0, prefix.Length);
 0188            Marshal.Copy((IntPtr)block.Pointer, bytes, prefix.Length, block.Length);
 0189            return bytes;
 0190        }
 191
 192        private string GetVirtualHandleString(StringHandle handle, MetadataStringDecoder utf8Decoder)
 0193        {
 0194            Debug.Assert(handle.IsVirtual);
 195
 0196            return handle.StringKind switch
 0197            {
 0198                StringKind.Virtual => GetVirtualString(handle.GetVirtualIndex()),
 0199                StringKind.WinRTPrefixed => GetNonVirtualString(handle, utf8Decoder, MetadataReader.WinRTPrefix),
 0200                _ => throw ExceptionUtilities.UnexpectedValue(handle.StringKind),
 0201            };
 0202        }
 203
 204        private MemoryBlock GetVirtualHandleMemoryBlock(StringHandle handle)
 0205        {
 0206            Debug.Assert(handle.IsVirtual);
 0207            var heap = VirtualHeap.GetOrCreateVirtualHeap(ref _lazyVirtualHeap);
 208
 0209            lock (heap)
 0210            {
 0211                if (!heap.TryGetMemoryBlock(handle.RawValue, out var block))
 0212                {
 0213                    byte[] bytes = handle.StringKind switch
 0214                    {
 0215                        StringKind.Virtual => Encoding.UTF8.GetBytes(GetVirtualString(handle.GetVirtualIndex())),
 0216                        StringKind.WinRTPrefixed => GetNonVirtualStringBytes(handle, MetadataReader.WinRTPrefix),
 0217                        _ => throw ExceptionUtilities.UnexpectedValue(handle.StringKind),
 0218                    };
 0219                    block = heap.AddBlob(handle.RawValue, bytes);
 0220                }
 221
 0222                return block;
 223            }
 0224        }
 225
 226        internal BlobReader GetBlobReader(StringHandle handle)
 0227        {
 0228            return new BlobReader(GetMemoryBlock(handle));
 0229        }
 230
 231        internal StringHandle GetNextHandle(StringHandle handle)
 0232        {
 0233            if (handle.IsVirtual)
 0234            {
 0235                return default(StringHandle);
 236            }
 237
 0238            int terminator = this.Block.IndexOf(0, handle.GetHeapOffset());
 0239            if (terminator == -1 || terminator == Block.Length - 1)
 0240            {
 0241                return default(StringHandle);
 242            }
 243
 0244            return StringHandle.FromOffset(terminator + 1);
 0245        }
 246
 247        internal bool Equals(StringHandle handle, string value, MetadataStringDecoder utf8Decoder, bool ignoreCase)
 0248        {
 0249            Debug.Assert(value != null);
 250
 0251            if (handle.IsVirtual)
 0252            {
 253                // TODO: This can allocate unnecessarily for <WinRT> prefixed handles.
 0254                return string.Equals(GetString(handle, utf8Decoder), value, ignoreCase ? StringComparison.OrdinalIgnoreC
 255            }
 256
 0257            if (handle.IsNil)
 0258            {
 0259                return value.Length == 0;
 260            }
 261
 0262            char otherTerminator = handle.StringKind == StringKind.DotTerminated ? '.' : '\0';
 0263            return this.Block.Utf8NullTerminatedEquals(handle.GetHeapOffset(), value, utf8Decoder, otherTerminator, igno
 0264        }
 265
 266        internal bool StartsWith(StringHandle handle, string value, MetadataStringDecoder utf8Decoder, bool ignoreCase)
 0267        {
 0268            Debug.Assert(value != null);
 269
 0270            if (handle.IsVirtual)
 0271            {
 272                // TODO: This can allocate unnecessarily for <WinRT> prefixed handles.
 0273                return GetString(handle, utf8Decoder).StartsWith(value, ignoreCase ? StringComparison.OrdinalIgnoreCase 
 274            }
 275
 0276            if (handle.IsNil)
 0277            {
 0278                return value.Length == 0;
 279            }
 280
 0281            char otherTerminator = handle.StringKind == StringKind.DotTerminated ? '.' : '\0';
 0282            return this.Block.Utf8NullTerminatedStartsWith(handle.GetHeapOffset(), value, utf8Decoder, otherTerminator, 
 0283        }
 284
 285        /// <summary>
 286        /// Returns true if the given raw (non-virtual) handle represents the same string as given ASCII string.
 287        /// </summary>
 288        internal bool EqualsRaw(StringHandle rawHandle, string asciiString)
 0289        {
 0290            Debug.Assert(!rawHandle.IsVirtual);
 0291            Debug.Assert(rawHandle.StringKind != StringKind.DotTerminated, "Not supported");
 0292            return this.Block.CompareUtf8NullTerminatedStringWithAsciiString(rawHandle.GetHeapOffset(), asciiString) == 
 0293        }
 294
 295        /// <summary>
 296        /// Returns the heap index of the given ASCII character or -1 if not found prior null terminator or end of heap.
 297        /// </summary>
 298        internal int IndexOfRaw(int startIndex, char asciiChar)
 0299        {
 0300            Debug.Assert(asciiChar != 0 && asciiChar <= 0x7f);
 0301            return this.Block.Utf8NullTerminatedOffsetOfAsciiChar(startIndex, asciiChar);
 0302        }
 303
 304        /// <summary>
 305        /// Returns true if the given raw (non-virtual) handle represents a string that starts with given ASCII prefix.
 306        /// </summary>
 307        internal bool StartsWithRaw(StringHandle rawHandle, string asciiPrefix)
 0308        {
 0309            Debug.Assert(!rawHandle.IsVirtual);
 0310            Debug.Assert(rawHandle.StringKind != StringKind.DotTerminated, "Not supported");
 0311            return this.Block.Utf8NullTerminatedStringStartsWithAsciiPrefix(rawHandle.GetHeapOffset(), asciiPrefix);
 0312        }
 313
 314        /// <summary>
 315        /// Equivalent to Array.BinarySearch, searches for given raw (non-virtual) handle in given array of ASCII string
 316        /// </summary>
 317        internal int BinarySearchRaw(string[] asciiKeys, StringHandle rawHandle)
 0318        {
 0319            Debug.Assert(!rawHandle.IsVirtual);
 0320            Debug.Assert(rawHandle.StringKind != StringKind.DotTerminated, "Not supported");
 0321            return this.Block.BinarySearch(asciiKeys, rawHandle.GetHeapOffset());
 0322        }
 323    }
 324}
 325