Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/corert.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMichal Strehovský <MichalStrehovsky@users.noreply.github.com>2018-07-31 19:04:02 +0300
committerGitHub <noreply@github.com>2018-07-31 19:04:02 +0300
commitebb3632553a4c8f1303c92fc03ce19b4879bb708 (patch)
treed1cc89bc265f5b18d9b012027332d1c319e1ce1a /src
parent7310d4bcf386371660c4bd99d9807fa296ec960d (diff)
parentb7ea3897c635e746573a6ff50bf2f2a54be16126 (diff)
Merge pull request #6158 from dotnet/master
Merge master to nmirror
Diffstat (limited to 'src')
-rw-r--r--src/Common/src/Internal/Runtime/EEType.cs4
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/Compilation.cs6
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeNode.cs9
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs12
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/SealedVTableNode.cs11
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeMetadataNode.cs17
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/UsageBasedMetadataManager.cs23
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/VirtualMethodCallHelper.cs8
-rw-r--r--src/ILCompiler.CppCodeGen/src/CppCodeGen/CppWriter.cs17
-rw-r--r--src/ILCompiler/src/Program.cs10
-rw-r--r--src/System.Private.CoreLib/shared/System/Buffers/Text/FormattingHelpers.CountDigits.cs29
-rw-r--r--src/System.Private.CoreLib/shared/System/Collections/Generic/Dictionary.cs2
-rw-r--r--src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventSource.cs5
-rw-r--r--src/System.Private.CoreLib/shared/System/Double.cs2
-rw-r--r--src/System.Private.CoreLib/shared/System/Globalization/CultureData.Windows.cs2
-rw-r--r--src/System.Private.CoreLib/shared/System/Globalization/DateTimeFormatInfo.cs28
-rw-r--r--src/System.Private.CoreLib/shared/System/Globalization/TimeSpanFormat.cs351
-rw-r--r--src/System.Private.CoreLib/shared/System/Math.cs18
-rw-r--r--src/System.Private.CoreLib/shared/System/MemoryExtensions.Fast.cs4
-rw-r--r--src/System.Private.CoreLib/shared/System/Runtime/InteropServices/MemoryMarshal.Fast.cs4
-rw-r--r--src/System.Private.CoreLib/shared/System/Runtime/InteropServices/MemoryMarshal.cs44
-rw-r--r--src/System.Private.CoreLib/shared/System/Single.cs2
-rw-r--r--src/System.Private.CoreLib/shared/System/TimeSpan.cs2
23 files changed, 467 insertions, 143 deletions
diff --git a/src/Common/src/Internal/Runtime/EEType.cs b/src/Common/src/Internal/Runtime/EEType.cs
index 4ad2aeab6..66d952d2f 100644
--- a/src/Common/src/Internal/Runtime/EEType.cs
+++ b/src/Common/src/Internal/Runtime/EEType.cs
@@ -1035,7 +1035,7 @@ namespace Internal.Runtime
fixed (EEType* pThis = &this)
{
- if (IsDynamicType)
+ if (IsDynamicType || !SupportsRelativePointers)
{
uint cbSealedVirtualSlotsTypeOffset = GetFieldOffset(EETypeField.ETF_SealedVirtualSlots);
IntPtr* pSealedVirtualsSlotTable = *(IntPtr**)((byte*)pThis + cbSealedVirtualSlotsTypeOffset);
@@ -1318,7 +1318,7 @@ namespace Internal.Runtime
// in the case of sealed vtable entries on static types, we have a UInt sized relative pointer
if ((rareFlags & EETypeRareFlags.HasSealedVTableEntriesFlag) != 0)
- cbOffset += (IsDynamicType ? (uint)IntPtr.Size : 4);
+ cbOffset += (IsDynamicType || !SupportsRelativePointers ? (uint)IntPtr.Size : 4);
if (eField == EETypeField.ETF_DynamicDispatchMap)
{
diff --git a/src/ILCompiler.Compiler/src/Compiler/Compilation.cs b/src/ILCompiler.Compiler/src/Compiler/Compilation.cs
index 6a307aac4..defedd0b7 100644
--- a/src/ILCompiler.Compiler/src/Compiler/Compilation.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/Compilation.cs
@@ -419,7 +419,11 @@ namespace ILCompiler
public void RootModuleMetadata(ModuleDesc module, string reason)
{
- _graph.AddRoot(_factory.ModuleMetadata(module), reason);
+ // RootModuleMetadata is kind of a hack - this is pretty much only used to force include
+ // type forwarders from assemblies metadata generator would normally not look at.
+ // This will go away when the temporary RD.XML parser goes away.
+ if (_factory.MetadataManager is UsageBasedMetadataManager)
+ _graph.AddRoot(_factory.ModuleMetadata(module), reason);
}
public void RootReadOnlyDataBlob(byte[] data, int alignment, string reason, string exportName)
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeNode.cs
index 1d2c7832d..6299c3fff 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeNode.cs
@@ -774,7 +774,7 @@ namespace ILCompiler.DependencyAnalysis
// Final NewSlot methods cannot be overridden, and therefore can be placed in the sealed-vtable to reduce the size of the vtable
// of this type and any type that inherits from it.
- if (declMethod.CanMethodBeInSealedVTable() && !declType.IsArrayTypeWithoutGenericInterfaces() && !factory.IsCppCodegenTemporaryWorkaround)
+ if (declMethod.CanMethodBeInSealedVTable() && !declType.IsArrayTypeWithoutGenericInterfaces())
continue;
if (!implMethod.IsAbstract)
@@ -838,7 +838,12 @@ namespace ILCompiler.DependencyAnalysis
SealedVTableNode sealedVTable = factory.SealedVTable(_type.ConvertToCanonForm(CanonicalFormKind.Specific));
if (sealedVTable.BuildSealedVTableSlots(factory, relocsOnly) && sealedVTable.NumSealedVTableEntries > 0)
- objData.EmitReloc(sealedVTable, RelocType.IMAGE_REL_BASED_RELPTR32);
+ {
+ if (factory.Target.SupportsRelativePointers)
+ objData.EmitReloc(sealedVTable, RelocType.IMAGE_REL_BASED_RELPTR32);
+ else
+ objData.EmitPointerReloc(sealedVTable);
+ }
}
}
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs
index d6ecd195c..26b0c703d 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs
@@ -912,6 +912,9 @@ namespace ILCompiler.DependencyAnalysis
internal TypeMetadataNode TypeMetadata(MetadataType type)
{
+ // These are only meaningful for UsageBasedMetadataManager. We should not have them
+ // in the dependency graph otherwise.
+ Debug.Assert(MetadataManager is UsageBasedMetadataManager);
return _typesWithMetadata.GetOrAdd(type);
}
@@ -919,6 +922,9 @@ namespace ILCompiler.DependencyAnalysis
internal MethodMetadataNode MethodMetadata(MethodDesc method)
{
+ // These are only meaningful for UsageBasedMetadataManager. We should not have them
+ // in the dependency graph otherwise.
+ Debug.Assert(MetadataManager is UsageBasedMetadataManager);
return _methodsWithMetadata.GetOrAdd(method);
}
@@ -926,6 +932,9 @@ namespace ILCompiler.DependencyAnalysis
internal FieldMetadataNode FieldMetadata(FieldDesc field)
{
+ // These are only meaningful for UsageBasedMetadataManager. We should not have them
+ // in the dependency graph otherwise.
+ Debug.Assert(MetadataManager is UsageBasedMetadataManager);
return _fieldsWithMetadata.GetOrAdd(field);
}
@@ -933,6 +942,9 @@ namespace ILCompiler.DependencyAnalysis
internal ModuleMetadataNode ModuleMetadata(ModuleDesc module)
{
+ // These are only meaningful for UsageBasedMetadataManager. We should not have them
+ // in the dependency graph otherwise.
+ Debug.Assert(MetadataManager is UsageBasedMetadataManager);
return _modulesWithMetadata.GetOrAdd(module);
}
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/SealedVTableNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/SealedVTableNode.cs
index 942e14dc4..dac6d8dbc 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/SealedVTableNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/SealedVTableNode.cs
@@ -90,10 +90,6 @@ namespace ILCompiler.DependencyAnalysis
_sealedVTableEntries = new List<MethodDesc>();
- // Cpp codegen does not support sealed vtables
- if (factory.IsCppCodegenTemporaryWorkaround)
- return true;
-
IReadOnlyList<MethodDesc> virtualSlots = factory.VTable(declType).Slots;
for (int i = 0; i < virtualSlots.Count; i++)
@@ -153,7 +149,12 @@ namespace ILCompiler.DependencyAnalysis
for (int i = 0; i < _sealedVTableEntries.Count; i++)
{
MethodDesc canonImplMethod = _sealedVTableEntries[i].GetCanonMethodTarget(CanonicalFormKind.Specific);
- objData.EmitReloc(factory.MethodEntrypoint(canonImplMethod, _sealedVTableEntries[i].OwningType.IsValueType), RelocType.IMAGE_REL_BASED_RELPTR32);
+ IMethodNode relocTarget = factory.MethodEntrypoint(canonImplMethod, _sealedVTableEntries[i].OwningType.IsValueType);
+
+ if (factory.Target.SupportsRelativePointers)
+ objData.EmitReloc(relocTarget, RelocType.IMAGE_REL_BASED_RELPTR32);
+ else
+ objData.EmitPointerReloc(relocTarget);
}
}
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeMetadataNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeMetadataNode.cs
index a8416afad..39ad01e9f 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeMetadataNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeMetadataNode.cs
@@ -51,6 +51,23 @@ namespace ILCompiler.DependencyAnalysis
dependencies.Add(factory.MethodMetadata(_type.GetMethod("Invoke", null)), "Delegate invoke method metadata");
}
+ // If the user asked for complete metadata to be generated for all types that are getting metadata, ensure that.
+ var mdManager = (UsageBasedMetadataManager)factory.MetadataManager;
+ if ((mdManager._generationOptions & UsageBasedMetadataGenerationOptions.CompleteTypesOnly) != 0)
+ {
+ foreach (MethodDesc method in _type.GetMethods())
+ {
+ if (!mdManager.IsReflectionBlocked(method))
+ dependencies.Add(factory.MethodMetadata(method), "Complete metadata for type");
+ }
+
+ foreach (FieldDesc field in _type.GetFields())
+ {
+ if (!mdManager.IsReflectionBlocked(field))
+ dependencies.Add(factory.FieldMetadata(field), "Complete metadata for type");
+ }
+ }
+
return dependencies;
}
diff --git a/src/ILCompiler.Compiler/src/Compiler/UsageBasedMetadataManager.cs b/src/ILCompiler.Compiler/src/Compiler/UsageBasedMetadataManager.cs
index 3d13aa547..c6a71b2b4 100644
--- a/src/ILCompiler.Compiler/src/Compiler/UsageBasedMetadataManager.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/UsageBasedMetadataManager.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+using System;
using System.Collections.Generic;
using Internal.TypeSystem;
@@ -23,6 +24,7 @@ namespace ILCompiler
{
private readonly CompilationModuleGroup _compilationModuleGroup;
+ internal readonly UsageBasedMetadataGenerationOptions _generationOptions;
private readonly bool _hasPreciseFieldUsageInformation;
private readonly List<ModuleDesc> _modulesWithMetadata = new List<ModuleDesc>();
@@ -36,12 +38,14 @@ namespace ILCompiler
MetadataBlockingPolicy blockingPolicy,
ManifestResourceBlockingPolicy resourceBlockingPolicy,
string logFile,
- StackTraceEmissionPolicy stackTracePolicy)
+ StackTraceEmissionPolicy stackTracePolicy,
+ UsageBasedMetadataGenerationOptions generationOptions)
: base(typeSystemContext, blockingPolicy, resourceBlockingPolicy, logFile, stackTracePolicy)
{
// We use this to mark places that would behave differently if we tracked exact fields used.
_hasPreciseFieldUsageInformation = false;
_compilationModuleGroup = group;
+ _generationOptions = generationOptions;
}
protected override void Graph_NewMarkedNode(DependencyNodeCore<NodeFactory> obj)
@@ -439,4 +443,21 @@ namespace ILCompiler
}
}
}
+
+ [Flags]
+ public enum UsageBasedMetadataGenerationOptions
+ {
+ None = 0,
+
+ /// <summary>
+ /// Specifies that complete metadata should be generated for types.
+ /// </summary>
+ /// <remarks>
+ /// If this option is set, generated metadata will no longer be pay for play,
+ /// and a certain class of bugs will disappear (APIs returning "member doesn't
+ /// exist" at runtime, even though the member exists and we just didn't generate the metadata).
+ /// Reflection blocking still applies.
+ /// </remarks>
+ CompleteTypesOnly = 1,
+ }
}
diff --git a/src/ILCompiler.Compiler/src/Compiler/VirtualMethodCallHelper.cs b/src/ILCompiler.Compiler/src/Compiler/VirtualMethodCallHelper.cs
index 8266ed8df..19da1f6ae 100644
--- a/src/ILCompiler.Compiler/src/Compiler/VirtualMethodCallHelper.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/VirtualMethodCallHelper.cs
@@ -18,9 +18,7 @@ namespace ILCompiler
/// </summary>
public static int GetVirtualMethodSlot(NodeFactory factory, MethodDesc method, TypeDesc implType, bool countDictionarySlots = true)
{
- // CppCodegen does not yet support sealed vtables.
-
- if (method.CanMethodBeInSealedVTable() && !factory.IsCppCodegenTemporaryWorkaround)
+ if (method.CanMethodBeInSealedVTable())
{
// If the method is a sealed newslot method, it will be put in the sealed vtable instead of the type's vtable. In this
// case, the slot index return should be the index in the sealed vtable, plus the total number of vtable slots.
@@ -71,7 +69,7 @@ namespace ILCompiler
int numSealedVTableEntries = 0;
for (int slot = 0; slot < virtualSlots.Count; slot++)
{
- if (virtualSlots[slot].CanMethodBeInSealedVTable() && !factory.IsCppCodegenTemporaryWorkaround)
+ if (virtualSlots[slot].CanMethodBeInSealedVTable())
{
numSealedVTableEntries++;
continue;
@@ -133,7 +131,7 @@ namespace ILCompiler
foreach (var vtableMethod in baseVirtualSlots)
{
// Methods in the sealed vtable should be excluded from the count
- if (vtableMethod.CanMethodBeInSealedVTable() && !factory.IsCppCodegenTemporaryWorkaround)
+ if (vtableMethod.CanMethodBeInSealedVTable())
continue;
baseSlots++;
}
diff --git a/src/ILCompiler.CppCodeGen/src/CppCodeGen/CppWriter.cs b/src/ILCompiler.CppCodeGen/src/CppCodeGen/CppWriter.cs
index 7472c0893..c813492bf 100644
--- a/src/ILCompiler.CppCodeGen/src/CppCodeGen/CppWriter.cs
+++ b/src/ILCompiler.CppCodeGen/src/CppCodeGen/CppWriter.cs
@@ -815,7 +815,7 @@ namespace ILCompiler.CppCodeGen
string mangledName = ((ISymbolNode)node).GetMangledName(factory.NameMangler);
// Rename generic composition and optional fields nodes to avoid name clash with types
- bool shouldReplaceNamespaceQualifier = node is GenericCompositionNode || node is EETypeOptionalFieldsNode;
+ bool shouldReplaceNamespaceQualifier = node is GenericCompositionNode || node is EETypeOptionalFieldsNode || node is SealedVTableNode;
nodeCode.Append(shouldReplaceNamespaceQualifier ? mangledName.Replace("::", "_") : mangledName);
}
nodeCode.Append("()");
@@ -903,10 +903,15 @@ namespace ILCompiler.CppCodeGen
relocCode.Append("()");
}
// Node is either an non-emitted type or a generic composition - both are ignored for CPP codegen
- else if ((reloc.Target is TypeManagerIndirectionNode || reloc.Target is InterfaceDispatchMapNode || reloc.Target is EETypeOptionalFieldsNode || reloc.Target is GenericCompositionNode) && !(reloc.Target as ObjectNode).ShouldSkipEmittingObjectNode(factory))
+ else if ((reloc.Target is TypeManagerIndirectionNode ||
+ reloc.Target is InterfaceDispatchMapNode ||
+ reloc.Target is EETypeOptionalFieldsNode ||
+ reloc.Target is GenericCompositionNode ||
+ reloc.Target is SealedVTableNode
+ ) && !(reloc.Target as ObjectNode).ShouldSkipEmittingObjectNode(factory))
{
string mangledTargetName = reloc.Target.GetMangledName(factory.NameMangler);
- bool shouldReplaceNamespaceQualifier = reloc.Target is GenericCompositionNode || reloc.Target is EETypeOptionalFieldsNode;
+ bool shouldReplaceNamespaceQualifier = reloc.Target is GenericCompositionNode || reloc.Target is EETypeOptionalFieldsNode || reloc.Target is SealedVTableNode;
relocCode.Append(shouldReplaceNamespaceQualifier ? mangledTargetName.Replace("::", "_") : mangledTargetName);
relocCode.Append("()");
}
@@ -1037,7 +1042,11 @@ namespace ILCompiler.CppCodeGen
{
if (node is EETypeNode)
OutputTypeNode(node as EETypeNode, factory, typeDefinitions, methodTables);
- else if ((node is EETypeOptionalFieldsNode || node is TypeManagerIndirectionNode || node is GenericCompositionNode || node is BlobNode) && !(node as ObjectNode).ShouldSkipEmittingObjectNode(factory))
+ else if ((node is EETypeOptionalFieldsNode ||
+ node is TypeManagerIndirectionNode ||
+ node is GenericCompositionNode ||
+ node is BlobNode ||
+ node is SealedVTableNode) && !(node as ObjectNode).ShouldSkipEmittingObjectNode(factory))
additionalNodes.Append(GetCodeForObjectNode(node as ObjectNode, factory));
else if (node is ArrayOfEmbeddedPointersNode<InterfaceDispatchMapNode> dispatchMap)
{
diff --git a/src/ILCompiler/src/Program.cs b/src/ILCompiler/src/Program.cs
index f31b485f4..90e5a4572 100644
--- a/src/ILCompiler/src/Program.cs
+++ b/src/ILCompiler/src/Program.cs
@@ -50,6 +50,7 @@ namespace ILCompiler
private string _mapFileName;
private string _metadataLogFileName;
private bool _noMetadataBlocking;
+ private bool _completeTypesMetadata;
private string _singleMethodTypeName;
private string _singleMethodName;
@@ -159,6 +160,7 @@ namespace ILCompiler
syntax.DefineOption("map", ref _mapFileName, "Generate a map file");
syntax.DefineOption("metadatalog", ref _metadataLogFileName, "Generate a metadata log file");
syntax.DefineOption("nometadatablocking", ref _noMetadataBlocking, "Ignore metadata blocking for internal implementation details");
+ syntax.DefineOption("completetypemetadata", ref _completeTypesMetadata, "Generate complete metadata for types");
syntax.DefineOption("scan", ref _useScanner, "Use IL scanner to generate optimized code (implied by -O)");
syntax.DefineOption("noscan", ref _noScanner, "Do not use IL scanner to generate optimized code");
syntax.DefineOption("ildump", ref _ilDump, "Dump IL assembly listing for compiler-generated IL");
@@ -418,13 +420,19 @@ namespace ILCompiler
ManifestResourceBlockingPolicy resBlockingPolicy = new NoManifestResourceBlockingPolicy();
+ UsageBasedMetadataGenerationOptions metadataGenerationOptions = UsageBasedMetadataGenerationOptions.None;
+ if (_completeTypesMetadata)
+ metadataGenerationOptions |= UsageBasedMetadataGenerationOptions.CompleteTypesOnly;
+
UsageBasedMetadataManager metadataManager = new UsageBasedMetadataManager(
compilationGroup,
typeSystemContext,
mdBlockingPolicy,
resBlockingPolicy,
_metadataLogFileName,
- stackTracePolicy);
+ stackTracePolicy,
+ metadataGenerationOptions
+ );
// Unless explicitly opted in at the command line, we enable scanner for retail builds by default.
// We don't do this for CppCodegen and Wasm, because those codegens are behind.
diff --git a/src/System.Private.CoreLib/shared/System/Buffers/Text/FormattingHelpers.CountDigits.cs b/src/System.Private.CoreLib/shared/System/Buffers/Text/FormattingHelpers.CountDigits.cs
index 709ac4fba..b6140adba 100644
--- a/src/System.Private.CoreLib/shared/System/Buffers/Text/FormattingHelpers.CountDigits.cs
+++ b/src/System.Private.CoreLib/shared/System/Buffers/Text/FormattingHelpers.CountDigits.cs
@@ -128,5 +128,34 @@ namespace System.Buffers.Text
return digits;
}
+
+
+ // Counts the number of trailing '0' digits in a decimal number.
+ // e.g., value = 0 => retVal = 0, valueWithoutTrailingZeros = 0
+ // value = 1234 => retVal = 0, valueWithoutTrailingZeros = 1234
+ // value = 320900 => retVal = 2, valueWithoutTrailingZeros = 3209
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int CountDecimalTrailingZeros(uint value, out uint valueWithoutTrailingZeros)
+ {
+ int zeroCount = 0;
+
+ if (value != 0)
+ {
+ while (true)
+ {
+ uint temp = value / 10;
+ if (value != (temp * 10))
+ {
+ break;
+ }
+
+ value = temp;
+ zeroCount++;
+ }
+ }
+
+ valueWithoutTrailingZeros = value;
+ return zeroCount;
+ }
}
}
diff --git a/src/System.Private.CoreLib/shared/System/Collections/Generic/Dictionary.cs b/src/System.Private.CoreLib/shared/System/Collections/Generic/Dictionary.cs
index 8f7ad70e2..69363d6c0 100644
--- a/src/System.Private.CoreLib/shared/System/Collections/Generic/Dictionary.cs
+++ b/src/System.Private.CoreLib/shared/System/Collections/Generic/Dictionary.cs
@@ -970,6 +970,7 @@ namespace System.Collections.Generic
int currentCapacity = _entries == null ? 0 : _entries.Length;
if (currentCapacity >= capacity)
return currentCapacity;
+ _version++;
if (_buckets == null)
return Initialize(capacity);
int newSize = HashHelpers.GetPrime(capacity);
@@ -1009,6 +1010,7 @@ namespace System.Collections.Generic
return;
int oldCount = _count;
+ _version++;
Initialize(newSize);
Entry[] entries = _entries;
int[] buckets = _buckets;
diff --git a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventSource.cs b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventSource.cs
index dd2543163..55b19058d 100644
--- a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventSource.cs
+++ b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventSource.cs
@@ -4049,7 +4049,12 @@ namespace System.Diagnostics.Tracing
if (!s_EventSourceShutdownRegistered)
{
s_EventSourceShutdownRegistered = true;
+#if ES_BUILD_PN
AppContext.ProcessExit += DisposeOnShutdown;
+#else
+ AppDomain.CurrentDomain.ProcessExit += DisposeOnShutdown;
+ AppDomain.CurrentDomain.DomainUnload += DisposeOnShutdown;
+#endif
}
diff --git a/src/System.Private.CoreLib/shared/System/Double.cs b/src/System.Private.CoreLib/shared/System/Double.cs
index 181086477..d85b4bca9 100644
--- a/src/System.Private.CoreLib/shared/System/Double.cs
+++ b/src/System.Private.CoreLib/shared/System/Double.cs
@@ -24,7 +24,7 @@ namespace System
[Serializable]
[StructLayout(LayoutKind.Sequential)]
[TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
- public struct Double : IComparable, IConvertible, IFormattable, IComparable<double>, IEquatable<double>, ISpanFormattable
+ public readonly struct Double : IComparable, IConvertible, IFormattable, IComparable<double>, IEquatable<double>, ISpanFormattable
{
private readonly double m_value; // Do not rename (binary serialization)
diff --git a/src/System.Private.CoreLib/shared/System/Globalization/CultureData.Windows.cs b/src/System.Private.CoreLib/shared/System/Globalization/CultureData.Windows.cs
index 393f983bb..75f78620d 100644
--- a/src/System.Private.CoreLib/shared/System/Globalization/CultureData.Windows.cs
+++ b/src/System.Private.CoreLib/shared/System/Globalization/CultureData.Windows.cs
@@ -517,7 +517,7 @@ namespace System.Globalization
// Context for EnumCalendarInfoExEx callback.
- private class EnumLocaleData
+ private struct EnumLocaleData
{
public string regionName;
public string cultureName;
diff --git a/src/System.Private.CoreLib/shared/System/Globalization/DateTimeFormatInfo.cs b/src/System.Private.CoreLib/shared/System/Globalization/DateTimeFormatInfo.cs
index 1b47c372c..8fc6f7beb 100644
--- a/src/System.Private.CoreLib/shared/System/Globalization/DateTimeFormatInfo.cs
+++ b/src/System.Private.CoreLib/shared/System/Globalization/DateTimeFormatInfo.cs
@@ -2010,6 +2010,25 @@ namespace System.Globalization
}
//
+ // Decimal separator used by positive TimeSpan pattern
+ //
+ private string _decimalSeparator;
+ internal string DecimalSeparator
+ {
+ get
+ {
+ if (_decimalSeparator == null)
+ {
+ CultureData cultureDataWithoutUserOverrides = _cultureData.UseUserOverride ?
+ CultureData.GetCultureData(_cultureData.CultureName, false) :
+ _cultureData;
+ _decimalSeparator = new NumberFormatInfo(cultureDataWithoutUserOverrides).NumberDecimalSeparator;
+ }
+ return _decimalSeparator;
+ }
+ }
+
+ //
// Positive TimeSpan Pattern
//
private string _fullTimeSpanPositivePattern;
@@ -2019,14 +2038,7 @@ namespace System.Globalization
{
if (_fullTimeSpanPositivePattern == null)
{
- CultureData cultureDataWithoutUserOverrides;
- if (_cultureData.UseUserOverride)
- cultureDataWithoutUserOverrides = CultureData.GetCultureData(_cultureData.CultureName, false);
- else
- cultureDataWithoutUserOverrides = _cultureData;
- string decimalSeparator = new NumberFormatInfo(cultureDataWithoutUserOverrides).NumberDecimalSeparator;
-
- _fullTimeSpanPositivePattern = "d':'h':'mm':'ss'" + decimalSeparator + "'FFFFFFF";
+ _fullTimeSpanPositivePattern = "d':'h':'mm':'ss'" + DecimalSeparator + "'FFFFFFF";
}
return _fullTimeSpanPositivePattern;
}
diff --git a/src/System.Private.CoreLib/shared/System/Globalization/TimeSpanFormat.cs b/src/System.Private.CoreLib/shared/System/Globalization/TimeSpanFormat.cs
index a66e4600a..5e553d56b 100644
--- a/src/System.Private.CoreLib/shared/System/Globalization/TimeSpanFormat.cs
+++ b/src/System.Private.CoreLib/shared/System/Globalization/TimeSpanFormat.cs
@@ -2,9 +2,11 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-using System.Text;
+using System.Buffers.Text;
using System.Diagnostics;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+using System.Text;
namespace System.Globalization
{
@@ -35,159 +37,300 @@ namespace System.Globalization
internal static readonly FormatLiterals PositiveInvariantFormatLiterals = TimeSpanFormat.FormatLiterals.InitInvariant(isNegative: false);
internal static readonly FormatLiterals NegativeInvariantFormatLiterals = TimeSpanFormat.FormatLiterals.InitInvariant(isNegative: true);
- internal enum Pattern
- {
- None = 0,
- Minimum = 1,
- Full = 2,
- }
/// <summary>Main method called from TimeSpan.ToString.</summary>
- internal static string Format(TimeSpan value, string format, IFormatProvider formatProvider) =>
- StringBuilderCache.GetStringAndRelease(FormatToBuilder(value, format, formatProvider));
-
- /// <summary>Main method called from TimeSpan.TryFormat.</summary>
- internal static bool TryFormat(TimeSpan value, Span<char> destination, out int charsWritten, ReadOnlySpan<char> format, IFormatProvider formatProvider)
+ internal static string Format(TimeSpan value, string format, IFormatProvider formatProvider)
{
- StringBuilder sb = FormatToBuilder(value, format, formatProvider);
- if (sb.Length <= destination.Length)
+ if (string.IsNullOrEmpty(format))
{
- charsWritten = sb.Length;
- sb.CopyTo(0, destination, sb.Length);
- StringBuilderCache.Release(sb);
- return true;
+ return FormatC(value); // formatProvider ignored, as "c" is invariant
}
- else
+
+ if (format.Length == 1)
{
- StringBuilderCache.Release(sb);
- charsWritten = 0;
- return false;
+ char c = format[0];
+
+ if (c == 'c' || (c | 0x20) == 't') // special-case to optimize the default TimeSpan format
+ {
+ return FormatC(value); // formatProvider ignored, as "c" is invariant
+ }
+
+ if ((c | 0x20) == 'g') // special-case to optimize the remaining 'g'/'G' standard formats
+ {
+ return FormatG(value, DateTimeFormatInfo.GetInstance(formatProvider), c == 'G' ? StandardFormat.G : StandardFormat.g);
+ }
+
+ throw new FormatException(SR.Format_InvalidString);
}
+
+ return StringBuilderCache.GetStringAndRelease(FormatCustomized(value, format, DateTimeFormatInfo.GetInstance(formatProvider), result: null));
}
- private static StringBuilder FormatToBuilder(TimeSpan value, ReadOnlySpan<char> format, IFormatProvider formatProvider)
+ /// <summary>Main method called from TimeSpan.TryFormat.</summary>
+ internal static bool TryFormat(TimeSpan value, Span<char> destination, out int charsWritten, ReadOnlySpan<char> format, IFormatProvider formatProvider)
{
if (format.Length == 0)
{
- format = "c";
+ return TryFormatStandard(value, StandardFormat.C, null, destination, out charsWritten);
}
- // Standard formats
if (format.Length == 1)
{
- char f = format[0];
- switch (f)
+ char c = format[0];
+ if (c == 'c' || ((c | 0x20) == 't'))
{
- case 'c':
- case 't':
- case 'T':
- return FormatStandard(
- value,
- isInvariant: true,
- format: format,
- pattern: Pattern.Minimum);
-
- case 'g':
- case 'G':
- DateTimeFormatInfo dtfi = DateTimeFormatInfo.GetInstance(formatProvider);
- return FormatStandard(
- value,
- isInvariant: false,
- format: value.Ticks < 0 ? dtfi.FullTimeSpanNegativePattern : dtfi.FullTimeSpanPositivePattern,
- pattern: f == 'g' ? Pattern.Minimum : Pattern.Full);
-
- default:
+ return TryFormatStandard(value, StandardFormat.C, null, destination, out charsWritten);
+ }
+ else
+ {
+ StandardFormat sf =
+ c == 'g' ? StandardFormat.g :
+ c == 'G' ? StandardFormat.G :
throw new FormatException(SR.Format_InvalidString);
+ return TryFormatStandard(value, sf, DateTimeFormatInfo.GetInstance(formatProvider).DecimalSeparator, destination, out charsWritten);
}
}
- // Custom formats
- return FormatCustomized(value, format, DateTimeFormatInfo.GetInstance(formatProvider), result: null);
+ StringBuilder sb = FormatCustomized(value, format, DateTimeFormatInfo.GetInstance(formatProvider), result: null);
+
+ if (sb.Length <= destination.Length)
+ {
+ sb.CopyTo(0, destination, sb.Length);
+ charsWritten = sb.Length;
+ StringBuilderCache.Release(sb);
+ return true;
+ }
+
+ charsWritten = 0;
+ StringBuilderCache.Release(sb);
+ return false;
}
- /// <summary>Format the TimeSpan instance using the specified format.</summary>
- private static StringBuilder FormatStandard(TimeSpan value, bool isInvariant, ReadOnlySpan<char> format, Pattern pattern)
+ internal static string FormatC(TimeSpan value)
{
- StringBuilder sb = StringBuilderCache.Acquire(InternalGlobalizationHelper.StringBuilderDefaultCapacity);
- int day = (int)(value.Ticks / TimeSpan.TicksPerDay);
- long time = value.Ticks % TimeSpan.TicksPerDay;
+ Span<char> destination = stackalloc char[26]; // large enough for any "c" TimeSpan
+ TryFormatStandard(value, StandardFormat.C, null, destination, out int charsWritten);
+ return new string(destination.Slice(0, charsWritten));
+ }
- if (value.Ticks < 0)
+ private static string FormatG(TimeSpan value, DateTimeFormatInfo dtfi, StandardFormat format)
+ {
+ string decimalSeparator = dtfi.DecimalSeparator;
+ int maxLength = 25 + decimalSeparator.Length; // large enough for any "g"/"G" TimeSpan
+ Span<char> destination = maxLength < 128 ?
+ stackalloc char[maxLength] :
+ new char[maxLength]; // the chances of needing this case are almost 0, as DecimalSeparator.Length will basically always == 1
+ TryFormatStandard(value, format, decimalSeparator, destination, out int charsWritten);
+ return new string(destination.Slice(0, charsWritten));
+ }
+
+ private enum StandardFormat { C, G, g }
+
+ private static bool TryFormatStandard(TimeSpan value, StandardFormat format, string decimalSeparator, Span<char> destination, out int charsWritten)
+ {
+ Debug.Assert(format == StandardFormat.C || format == StandardFormat.G || format == StandardFormat.g);
+
+ // First, calculate how large an output buffer is needed to hold the entire output.
+ int requiredOutputLength = 8; // start with "hh:mm:ss" and adjust as necessary
+
+ uint fraction;
+ ulong totalSecondsRemaining;
{
- day = -day;
- time = -time;
+ // Turn this into a non-negative TimeSpan if possible.
+ long ticks = value.Ticks;
+ if (ticks < 0)
+ {
+ requiredOutputLength = 9; // requiredOutputLength + 1 for the leading '-' sign
+ ticks = -ticks;
+ if (ticks < 0)
+ {
+ Debug.Assert(ticks == long.MinValue /* -9223372036854775808 */);
+
+ // We computed these ahead of time; they're straight from the decimal representation of Int64.MinValue.
+ fraction = 4775808;
+ totalSecondsRemaining = 922337203685;
+ goto AfterComputeFraction;
+ }
+ }
+
+ totalSecondsRemaining = Math.DivRem((ulong)ticks, TimeSpan.TicksPerSecond, out ulong fraction64);
+ fraction = (uint)fraction64;
}
- int hours = (int)(time / TimeSpan.TicksPerHour % 24);
- int minutes = (int)(time / TimeSpan.TicksPerMinute % 60);
- int seconds = (int)(time / TimeSpan.TicksPerSecond % 60);
- int fraction = (int)(time % TimeSpan.TicksPerSecond);
- FormatLiterals literal;
- if (isInvariant)
+ AfterComputeFraction:
+ // Only write out the fraction if it's non-zero, and in that
+ // case write out the entire fraction (all digits).
+ Debug.Assert(fraction < 10_000_000);
+ int fractionDigits = 0;
+ switch (format)
{
- literal = value.Ticks < 0 ?
- NegativeInvariantFormatLiterals :
- PositiveInvariantFormatLiterals;
+ case StandardFormat.C:
+ // "c": Write out a fraction only if it's non-zero, and write out all 7 digits of it.
+ if (fraction != 0)
+ {
+ fractionDigits = DateTimeFormat.MaxSecondsFractionDigits;
+ requiredOutputLength += fractionDigits + 1; // digits plus leading decimal separator
+ }
+ break;
+
+ case StandardFormat.G:
+ // "G": Write out a fraction regardless of whether it's 0, and write out all 7 digits of it.
+ fractionDigits = DateTimeFormat.MaxSecondsFractionDigits;
+ requiredOutputLength += fractionDigits + 1; // digits plus leading decimal separator
+ break;
+
+ default:
+ // "g": Write out a fraction only if it's non-zero, and write out only the most significant digits.
+ Debug.Assert(format == StandardFormat.g);
+ if (fraction != 0)
+ {
+ fractionDigits = DateTimeFormat.MaxSecondsFractionDigits - FormattingHelpers.CountDecimalTrailingZeros(fraction, out fraction);
+ requiredOutputLength += fractionDigits + 1; // digits plus leading decimal separator
+ }
+ break;
}
- else
+
+ ulong totalMinutesRemaining = 0, seconds = 0;
+ if (totalSecondsRemaining > 0)
{
- literal = new FormatLiterals();
- literal.Init(format, pattern == Pattern.Full);
+ // Only compute minutes if the TimeSpan has an absolute value of >= 1 minute.
+ totalMinutesRemaining = Math.DivRem(totalSecondsRemaining, 60 /* seconds per minute */, out seconds);
+ Debug.Assert(seconds < 60);
}
- if (fraction != 0)
+ ulong totalHoursRemaining = 0, minutes = 0;
+ if (totalMinutesRemaining > 0)
{
- // truncate the partial second to the specified length
- fraction = (int)(fraction / TimeSpanParse.Pow10(DateTimeFormat.MaxSecondsFractionDigits - literal.ff));
+ // Only compute hours if the TimeSpan has an absolute value of >= 1 hour.
+ totalHoursRemaining = Math.DivRem(totalMinutesRemaining, 60 /* minutes per hour */, out minutes);
+ Debug.Assert(minutes < 60);
}
- // Pattern.Full: [-]dd.hh:mm:ss.fffffff
- // Pattern.Minimum: [-][d.]hh:mm:ss[.fffffff]
+ // At this point, we can switch over to 32-bit DivRem since the data has shrunk far enough.
+ Debug.Assert(totalHoursRemaining <= uint.MaxValue);
- sb.Append(literal.Start); // [-]
- if (pattern == Pattern.Full || day != 0)
+ uint days = 0, hours = 0;
+ if (totalHoursRemaining > 0)
{
- sb.Append(day); // [dd]
- sb.Append(literal.DayHourSep); // [.]
- } //
- AppendNonNegativeInt32(sb, hours, literal.hh); // hh
- sb.Append(literal.HourMinuteSep); // :
- AppendNonNegativeInt32(sb, minutes, literal.mm); // mm
- sb.Append(literal.MinuteSecondSep); // :
- AppendNonNegativeInt32(sb, seconds, literal.ss); // ss
- if (!isInvariant && pattern == Pattern.Minimum)
+ // Only compute days if the TimeSpan has an absolute value of >= 1 day.
+ days = Math.DivRem((uint)totalHoursRemaining, 24 /* hours per day */, out hours);
+ Debug.Assert(hours < 24);
+ }
+
+ int hourDigits = 2;
+ if (format == StandardFormat.g && hours < 10)
+ {
+ // "g": Only writing a one-digit hour, rather than expected two-digit hour
+ hourDigits = 1;
+ requiredOutputLength--;
+ }
+
+ int dayDigits = 0;
+ if (days > 0)
+ {
+ dayDigits = FormattingHelpers.CountDigits(days);
+ Debug.Assert(dayDigits <= 8);
+ requiredOutputLength += dayDigits + 1; // for the leading "d."
+ }
+ else if (format == StandardFormat.G)
+ {
+ // "G": has a leading "0:" if days is 0
+ requiredOutputLength += 2;
+ dayDigits = 1;
+ }
+
+ if (destination.Length < requiredOutputLength)
{
- int effectiveDigits = literal.ff;
- while (effectiveDigits > 0)
+ charsWritten = 0;
+ return false;
+ }
+
+ // Write leading '-' if necessary
+ int idx = 0;
+ if (value.Ticks < 0)
+ {
+ destination[idx++] = '-';
+ }
+
+ // Write day and separator, if necessary
+ if (dayDigits != 0)
+ {
+ WriteDigits(days, destination.Slice(idx, dayDigits));
+ idx += dayDigits;
+ destination[idx++] = format == StandardFormat.C ? '.' : ':';
+ }
+
+ // Write "[h]h:mm:ss
+ Debug.Assert(hourDigits == 1 || hourDigits == 2);
+ if (hourDigits == 2)
+ {
+ WriteTwoDigits(hours, destination.Slice(idx));
+ idx += 2;
+ }
+ else
+ {
+ destination[idx++] = (char)('0' + hours);
+ }
+ destination[idx++] = ':';
+ WriteTwoDigits((uint)minutes, destination.Slice(idx));
+ idx += 2;
+ destination[idx++] = ':';
+ WriteTwoDigits((uint)seconds, destination.Slice(idx));
+ idx += 2;
+
+ // Write fraction and separator, if necessary
+ if (fractionDigits != 0)
+ {
+ if (format == StandardFormat.C)
{
- if (fraction % 10 == 0)
- {
- fraction = fraction / 10;
- effectiveDigits--;
- }
- else
- {
- break;
- }
+ destination[idx++] = '.';
}
- if (effectiveDigits > 0)
+ else if (decimalSeparator.Length == 1)
{
- sb.Append(literal.SecondFractionSep); // [.FFFFFFF]
- sb.Append((fraction).ToString(DateTimeFormat.fixedNumberFormats[effectiveDigits - 1], CultureInfo.InvariantCulture));
+ destination[idx++] = decimalSeparator[0];
}
+ else
+ {
+ decimalSeparator.AsSpan().CopyTo(destination);
+ idx += decimalSeparator.Length;
+ }
+ WriteDigits(fraction, destination.Slice(idx, fractionDigits));
+ idx += fractionDigits;
}
- else if (pattern == Pattern.Full || fraction != 0)
+
+ Debug.Assert(idx == requiredOutputLength);
+ charsWritten = requiredOutputLength;
+ return true;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static void WriteTwoDigits(uint value, Span<char> buffer)
+ {
+ Debug.Assert(buffer.Length >= 2);
+ uint temp = '0' + value;
+ value /= 10;
+ buffer[1] = (char)(temp - (value * 10));
+ buffer[0] = (char)('0' + value);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static void WriteDigits(uint value, Span<char> buffer)
+ {
+ Debug.Assert(buffer.Length > 0);
+
+ for (int i = buffer.Length - 1; i >= 1; i--)
{
- sb.Append(literal.SecondFractionSep); // [.]
- AppendNonNegativeInt32(sb, fraction, literal.ff); // [fffffff]
+ uint temp = '0' + value;
+ value /= 10;
+ buffer[i] = (char)(temp - (value * 10));
}
- sb.Append(literal.End);
- return sb;
+ Debug.Assert(value < 10);
+ buffer[0] = (char)('0' + value);
}
/// <summary>Format the TimeSpan instance using the specified format.</summary>
- private static StringBuilder FormatCustomized(TimeSpan value, ReadOnlySpan<char> format, DateTimeFormatInfo dtfi, StringBuilder result)
+ private static StringBuilder FormatCustomized(TimeSpan value, ReadOnlySpan<char> format, DateTimeFormatInfo dtfi, StringBuilder result = null)
{
Debug.Assert(dtfi != null);
diff --git a/src/System.Private.CoreLib/shared/System/Math.cs b/src/System.Private.CoreLib/shared/System/Math.cs
index a175103f8..ef46869e2 100644
--- a/src/System.Private.CoreLib/shared/System/Math.cs
+++ b/src/System.Private.CoreLib/shared/System/Math.cs
@@ -123,15 +123,25 @@ namespace System
public static long DivRem(long a, long b, out long result)
{
- // TODO https://github.com/dotnet/coreclr/issues/3439:
- // Restore to using % and / when the JIT is able to eliminate one of the idivs.
- // In the meantime, a * and - is measurably faster than an extra /.
-
long div = a / b;
result = a - (div * b);
return div;
}
+ internal static uint DivRem(uint a, uint b, out uint result)
+ {
+ uint div = a / b;
+ result = a - (div * b);
+ return div;
+ }
+
+ internal static ulong DivRem(ulong a, ulong b, out ulong result)
+ {
+ ulong div = a / b;
+ result = a - (div * b);
+ return div;
+ }
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static decimal Ceiling(decimal d)
{
diff --git a/src/System.Private.CoreLib/shared/System/MemoryExtensions.Fast.cs b/src/System.Private.CoreLib/shared/System/MemoryExtensions.Fast.cs
index 4e84223b2..efd11c9c5 100644
--- a/src/System.Private.CoreLib/shared/System/MemoryExtensions.Fast.cs
+++ b/src/System.Private.CoreLib/shared/System/MemoryExtensions.Fast.cs
@@ -226,6 +226,7 @@ namespace System
/// <param name="culture">An object that supplies culture-specific casing rules.</param>
/// <remarks>If the source and destinations overlap, this method behaves as if the original values are in
/// a temporary location before the destination is overwritten.</remarks>
+ /// <returns>The number of characters written into the destination span. If the destination is too small, returns -1.</returns>
/// <exception cref="System.ArgumentNullException">
/// Thrown when <paramref name="culture"/> is null.
/// </exception>
@@ -253,6 +254,7 @@ namespace System
/// <param name="destination">The destination span which contains the transformed characters.</param>
/// <remarks>If the source and destinations overlap, this method behaves as if the original values are in
/// a temporary location before the destination is overwritten.</remarks>
+ /// <returns>The number of characters written into the destination span. If the destination is too small, returns -1.</returns>
public static int ToLowerInvariant(this ReadOnlySpan<char> source, Span<char> destination)
{
// Assuming that changing case does not affect length
@@ -275,6 +277,7 @@ namespace System
/// <param name="culture">An object that supplies culture-specific casing rules.</param>
/// <remarks>If the source and destinations overlap, this method behaves as if the original values are in
/// a temporary location before the destination is overwritten.</remarks>
+ /// <returns>The number of characters written into the destination span. If the destination is too small, returns -1.</returns>
/// <exception cref="System.ArgumentNullException">
/// Thrown when <paramref name="culture"/> is null.
/// </exception>
@@ -302,6 +305,7 @@ namespace System
/// <param name="destination">The destination span which contains the transformed characters.</param>
/// <remarks>If the source and destinations overlap, this method behaves as if the original values are in
/// a temporary location before the destination is overwritten.</remarks>
+ /// <returns>The number of characters written into the destination span. If the destination is too small, returns -1.</returns>
public static int ToUpperInvariant(this ReadOnlySpan<char> source, Span<char> destination)
{
// Assuming that changing case does not affect length
diff --git a/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/MemoryMarshal.Fast.cs b/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/MemoryMarshal.Fast.cs
index a95452954..e3cf0a84e 100644
--- a/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/MemoryMarshal.Fast.cs
+++ b/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/MemoryMarshal.Fast.cs
@@ -102,7 +102,7 @@ namespace System.Runtime.InteropServices
/// These types may not contain pointers or references. This is checked at runtime in order to preserve type safety.
/// </summary>
/// <remarks>
- /// Supported only for platforms that support misaligned memory access.
+ /// Supported only for platforms that support misaligned memory access or when the memory block is aligned by other means.
/// </remarks>
/// <param name="span">The source slice, of type <typeparamref name="TFrom"/>.</param>
/// <exception cref="System.ArgumentException">
@@ -157,7 +157,7 @@ namespace System.Runtime.InteropServices
/// These types may not contain pointers or references. This is checked at runtime in order to preserve type safety.
/// </summary>
/// <remarks>
- /// Supported only for platforms that support misaligned memory access.
+ /// Supported only for platforms that support misaligned memory access or when the memory block is aligned by other means.
/// </remarks>
/// <param name="span">The source slice, of type <typeparamref name="TFrom"/>.</param>
/// <exception cref="System.ArgumentException">
diff --git a/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/MemoryMarshal.cs b/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/MemoryMarshal.cs
index 5e33ced6b..fcc9c4f41 100644
--- a/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/MemoryMarshal.cs
+++ b/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/MemoryMarshal.cs
@@ -212,6 +212,50 @@ namespace System.Runtime.InteropServices
}
/// <summary>
+ /// Re-interprets a span of bytes as a reference to structure of type T.
+ /// The type may not contain pointers or references. This is checked at runtime in order to preserve type safety.
+ /// </summary>
+ /// <remarks>
+ /// Supported only for platforms that support misaligned memory access or when the memory block is aligned by other means.
+ /// </remarks>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ref T AsRef<T>(Span<byte> span)
+ where T : struct
+ {
+ if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
+ {
+ ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));
+ }
+ if (Unsafe.SizeOf<T>() > (uint)span.Length)
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.length);
+ }
+ return ref Unsafe.As<byte, T>(ref GetReference(span));
+ }
+
+ /// <summary>
+ /// Re-interprets a span of bytes as a reference to structure of type T.
+ /// The type may not contain pointers or references. This is checked at runtime in order to preserve type safety.
+ /// </summary>
+ /// <remarks>
+ /// Supported only for platforms that support misaligned memory access or when the memory block is aligned by other means.
+ /// </remarks>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ref readonly T AsRef<T>(ReadOnlySpan<byte> span)
+ where T : struct
+ {
+ if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
+ {
+ ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));
+ }
+ if (Unsafe.SizeOf<T>() > (uint)span.Length)
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.length);
+ }
+ return ref Unsafe.As<byte, T>(ref GetReference(span));
+ }
+
+ /// <summary>
/// Creates a new memory over the portion of the pre-pinned target array beginning
/// at 'start' index and ending at 'end' index (exclusive).
/// </summary>
diff --git a/src/System.Private.CoreLib/shared/System/Single.cs b/src/System.Private.CoreLib/shared/System/Single.cs
index 6b8440d40..8d1788f73 100644
--- a/src/System.Private.CoreLib/shared/System/Single.cs
+++ b/src/System.Private.CoreLib/shared/System/Single.cs
@@ -23,7 +23,7 @@ namespace System
[Serializable]
[StructLayout(LayoutKind.Sequential)]
[TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
- public struct Single : IComparable, IConvertible, IFormattable, IComparable<float>, IEquatable<float>, ISpanFormattable
+ public readonly struct Single : IComparable, IConvertible, IFormattable, IComparable<float>, IEquatable<float>, ISpanFormattable
{
private readonly float m_value; // Do not rename (binary serialization)
diff --git a/src/System.Private.CoreLib/shared/System/TimeSpan.cs b/src/System.Private.CoreLib/shared/System/TimeSpan.cs
index 4716ab6c1..1b94c9f15 100644
--- a/src/System.Private.CoreLib/shared/System/TimeSpan.cs
+++ b/src/System.Private.CoreLib/shared/System/TimeSpan.cs
@@ -450,7 +450,7 @@ namespace System
}
public override string ToString()
{
- return TimeSpanFormat.Format(this, null, null);
+ return TimeSpanFormat.FormatC(this);
}
public string ToString(string format)
{